Merge "Parse process starttime."
diff --git a/include/procinfo/process.h b/include/procinfo/process.h
index 9278e18..ee245e4 100644
--- a/include/procinfo/process.h
+++ b/include/procinfo/process.h
@@ -26,6 +26,7 @@
#include <string>
#include <type_traits>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
@@ -53,6 +54,9 @@
pid_t tracer;
uid_t uid;
uid_t gid;
+
+ // Start time of the process since boot, measured in clock ticks.
+ uint64_t starttime;
};
// Parse the contents of /proc/<tid>/status into |process_info|.
diff --git a/process.cpp b/process.cpp
index 2efd49c..4831ae8 100644
--- a/process.cpp
+++ b/process.cpp
@@ -46,8 +46,8 @@
return GetProcessInfoFromProcPidFd(dirfd.get(), process_info, error);
}
-static ProcessState parse_state(const char* state) {
- switch (*state) {
+static ProcessState parse_state(char state) {
+ switch (state) {
case 'R':
return kProcessStateRunning;
case 'S':
@@ -83,7 +83,7 @@
}
int field_bitmap = 0;
- static constexpr int finished_bitmap = 255;
+ static constexpr int finished_bitmap = 63;
char* line = nullptr;
size_t len = 0;
@@ -103,32 +103,81 @@
process_info->name = std::move(name);
field_bitmap |= 1;
+ } else if (header == "Tgid:") {
+ process_info->pid = atoi(tab + 1);
+ field_bitmap |= 2;
} else if (header == "Pid:") {
process_info->tid = atoi(tab + 1);
- field_bitmap |= 2;
- } else if (header == "Tgid:") {
- process_info->pid = atoi(tab + 1);
field_bitmap |= 4;
- } else if (header == "PPid:") {
- process_info->ppid = atoi(tab + 1);
- field_bitmap |= 8;
} else if (header == "TracerPid:") {
process_info->tracer = atoi(tab + 1);
- field_bitmap |= 16;
+ field_bitmap |= 8;
} else if (header == "Uid:") {
process_info->uid = atoi(tab + 1);
- field_bitmap |= 32;
+ field_bitmap |= 16;
} 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;
+ field_bitmap |= 32;
}
}
free(line);
- return field_bitmap == finished_bitmap;
+ if (field_bitmap != finished_bitmap) {
+ *error = "failed to parse /proc/<pid>/status";
+ return false;
+ }
+
+ unique_fd stat_fd(openat(fd, "stat", O_RDONLY | O_CLOEXEC));
+ if (stat_fd == -1) {
+ *error = "failed to open /proc/<pid>/stat";
+ }
+
+ std::string stat;
+ if (!android::base::ReadFdToString(stat_fd, &stat)) {
+ *error = "failed to read /proc/<pid>/stat";
+ return false;
+ }
+
+ // See man 5 proc. There's no reason comm can't contain ' ' or ')',
+ // so we search backwards for the end of it.
+ const char* end_of_comm = strrchr(stat.c_str(), ')');
+
+ static constexpr const char* pattern =
+ "%c " // state
+ "%d " // ppid
+ "%*d " // pgrp
+ "%*d " // session
+ "%*d " // tty_nr
+ "%*d " // tpgid
+ "%*u " // flags
+ "%*lu " // minflt
+ "%*lu " // cminflt
+ "%*lu " // majflt
+ "%*lu " // cmajflt
+ "%*lu " // utime
+ "%*lu " // stime
+ "%*ld " // cutime
+ "%*ld " // cstime
+ "%*ld " // priority
+ "%*ld " // nice
+ "%*ld " // num_threads
+ "%*ld " // itrealvalue
+ "%llu " // starttime
+ ;
+
+ char state = '\0';
+ int ppid = 0;
+ unsigned long long start_time = 0;
+ int rc = sscanf(end_of_comm + 2, pattern, &state, &ppid, &start_time);
+ if (rc != 3) {
+ *error = "failed to parse /proc/<pid>/stat";
+ return false;
+ }
+
+ process_info->state = parse_state(state);
+ process_info->ppid = ppid;
+ process_info->starttime = start_time;
+ return true;
}
} /* namespace procinfo */
diff --git a/process_test.cpp b/process_test.cpp
index 9da9278..be00a17 100644
--- a/process_test.cpp
+++ b/process_test.cpp
@@ -28,6 +28,7 @@
#include <gtest/gtest.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
using namespace std::chrono_literals;
@@ -116,3 +117,44 @@
ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
}
+
+static uint64_t read_uptime_secs() {
+ std::string uptime;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
+ PLOG(FATAL) << "failed to read /proc/uptime";
+ }
+ return strtoll(uptime.c_str(), nullptr, 10);
+}
+
+TEST(process_info, process_start_time) {
+ uint64_t start = read_uptime_secs();
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+
+ std::this_thread::sleep_for(1000ms);
+
+ 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);
+ }
+
+ std::this_thread::sleep_for(1000ms);
+
+ uint64_t end = read_uptime_secs();
+
+ android::procinfo::ProcessInfo procinfo;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+
+ // starttime is measured in clock ticks: uptime is in seconds:
+ uint64_t process_start = procinfo.starttime / sysconf(_SC_CLK_TCK);
+ ASSERT_LE(start, process_start);
+ ASSERT_LE(process_start, end);
+
+ ASSERT_EQ(0, kill(forkpid, SIGKILL));
+ ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
+}