diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h index fb140ff3d..db56fc1a5 100644 --- a/libprocinfo/include/procinfo/process.h +++ b/libprocinfo/include/procinfo/process.h @@ -35,8 +35,18 @@ namespace procinfo { #if defined(__linux__) +enum ProcessState { + kProcessStateUnknown, + kProcessStateRunning, + kProcessStateSleeping, + kProcessStateUninterruptibleWait, + kProcessStateStopped, + kProcessStateZombie, +}; + struct ProcessInfo { std::string name; + ProcessState state; pid_t tid; pid_t pid; pid_t ppid; diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp index c513e16f5..6e5be6e56 100644 --- a/libprocinfo/process.cpp +++ b/libprocinfo/process.cpp @@ -44,6 +44,24 @@ bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) { return GetProcessInfoFromProcPidFd(dirfd.get(), process_info); } +static ProcessState parse_state(const char* state) { + switch (*state) { + case 'R': + return kProcessStateRunning; + case 'S': + return kProcessStateSleeping; + case 'D': + return kProcessStateUninterruptibleWait; + case 'T': + return kProcessStateStopped; + case 'Z': + return kProcessStateZombie; + default: + LOG(ERROR) << "unknown process state: " << *state; + return kProcessStateUnknown; + } +} + bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) { int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC); @@ -60,7 +78,7 @@ bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) { } int field_bitmap = 0; - static constexpr int finished_bitmap = 127; + static constexpr int finished_bitmap = 255; char* line = nullptr; size_t len = 0; @@ -98,6 +116,9 @@ bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) { } else if (header == "Gid:") { process_info->gid = atoi(tab + 1); field_bitmap |= 64; + } else if (header == "State:") { + process_info->state = parse_state(tab + 1); + field_bitmap |= 128; } } diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp index 5ffd2365b..9da927899 100644 --- a/libprocinfo/process_test.cpp +++ b/libprocinfo/process_test.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,8 @@ #include +using namespace std::chrono_literals; + #if !defined(__BIONIC__) #include static pid_t gettid() { @@ -82,3 +85,34 @@ TEST(process_info, process_tids_smoke) { } }).join(); } + +TEST(process_info, process_state) { + int pipefd[2]; + ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC)); + pid_t forkpid = fork(); + + ASSERT_NE(-1, forkpid); + if (forkpid == 0) { + close(pipefd[1]); + char buf; + TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1)); + _exit(0); + } + + // Give the child some time to get to the read. + std::this_thread::sleep_for(100ms); + + android::procinfo::ProcessInfo procinfo; + ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo)); + ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state); + + ASSERT_EQ(0, kill(forkpid, SIGKILL)); + + // Give the kernel some time to kill the child. + std::this_thread::sleep_for(100ms); + + ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo)); + ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state); + + ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0)); +}