Snap for 10453563 from a36da3bd16a5e4bde39e4ec2b10e4ddc1390a10d to mainline-os-statsd-release
Change-Id: I7dd06218947c5e120b90239e6206f9d4aa63836c
diff --git a/TEST_MAPPING b/TEST_MAPPING
index ef6fb27..180859b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "libprocinfo_test"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "libprocinfo_test"
}
diff --git a/include/procinfo/process.h b/include/procinfo/process.h
index ee245e4..92d5997 100644
--- a/include/procinfo/process.h
+++ b/include/procinfo/process.h
@@ -17,6 +17,7 @@
#pragma once
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -29,6 +30,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
namespace android {
@@ -59,12 +61,15 @@
uint64_t starttime;
};
+bool SetError(std::string* error, int errno_value, const char* fmt, ...);
+
// Parse the contents of /proc/<tid>/status into |process_info|.
bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error = nullptr);
// Parse the contents of <fd>/status into |process_info|.
// |fd| should be an fd pointing at a /proc/<pid> directory.
-bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error = nullptr);
+// |pid| is used for error messages.
+bool GetProcessInfoFromProcPidFd(int fd, int pid, ProcessInfo* process_info, std::string* error = nullptr);
// Fetch the list of threads from a given process's /proc/<pid> directory.
// |fd| should be an fd pointing at a /proc/<pid> directory.
@@ -76,10 +81,7 @@
int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
if (!dir) {
- if (error != nullptr) {
- *error = "failed to open task directory";
- }
- return false;
+ return SetError(error, errno, "failed to open task directory");
}
struct dirent* dent;
@@ -87,10 +89,7 @@
if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
pid_t tid;
if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
- if (error != nullptr) {
- *error = std::string("failed to parse task id: ") + dent->d_name;
- }
- return false;
+ return SetError(error, 0, "failed to parse task id %s", dent->d_name);
}
out->insert(out->end(), tid);
@@ -103,20 +102,11 @@
template <typename Collection>
auto GetProcessTids(pid_t pid, Collection* out, std::string* error = nullptr) ->
typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
- char task_path[PATH_MAX];
- if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
- if (error != nullptr) {
- *error = "task path overflow (pid = " + std::to_string(pid) + ")";
- }
- return false;
- }
-
+ char task_path[32];
+ snprintf(task_path, sizeof(task_path), "/proc/%d", pid);
android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
if (fd == -1) {
- if (error != nullptr) {
- *error = std::string("failed to open ") + task_path;
- }
- return false;
+ return SetError(error, errno, "failed to open %s", task_path);
}
return GetProcessTidsFromProcPidFd(fd.get(), out, error);
diff --git a/include/procinfo/process_map.h b/include/procinfo/process_map.h
index d8a563c..3c9d144 100644
--- a/include/procinfo/process_map.h
+++ b/include/procinfo/process_map.h
@@ -83,15 +83,15 @@
return true;
}
-// Parses a line given p pointing at proc/<pid>/maps content buffer and returns true on success
-// and false on failure parsing. The next end of line will be replaced by null character and the
-// immediate offset after the parsed line will be returned in next_line.
+// Parses the given line p pointing at proc/<pid>/maps content buffer and returns true on success
+// and false on failure parsing. The first new line character of line will be replaced by the
+// null character and *next_line will point to the character after the null.
//
// Example of how a parsed line look line:
// 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
static inline bool ParseMapsFileLine(char* p, uint64_t& start_addr, uint64_t& end_addr, uint16_t& flags,
uint64_t& pgoff, ino_t& inode, char** name, bool& shared, char** next_line) {
- // Make end of line be null
+ // Make the first new line character null.
*next_line = strchr(p, '\n');
if (*next_line != nullptr) {
**next_line = '\0';
@@ -167,6 +167,7 @@
return false;
}
+ // Assumes that the first new character was replaced with null.
*name = p;
return true;
@@ -229,21 +230,33 @@
return ReadMapFileContent(&content[0], callback);
}
+
+inline bool ReadMapFile(const std::string& map_file, const MapInfoParamsCallback& callback,
+ std::string& mapsBuffer) {
+ if (!android::base::ReadFileToString(map_file, &mapsBuffer)) {
+ return false;
+ }
+ return ReadMapFileContent(&mapsBuffer[0], callback);
+}
+
inline bool ReadMapFile(const std::string& map_file,
const MapInfoParamsCallback& callback) {
std::string content;
- if (!android::base::ReadFileToString(map_file, &content)) {
- return false;
- }
- return ReadMapFileContent(&content[0], callback);
+ return ReadMapFile(map_file, callback, content);
}
inline bool ReadProcessMaps(pid_t pid, const MapInfoCallback& callback) {
return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
}
+inline bool ReadProcessMaps(pid_t pid, const MapInfoParamsCallback& callback,
+ std::string& mapsBuffer) {
+ return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback, mapsBuffer);
+}
+
inline bool ReadProcessMaps(pid_t pid, const MapInfoParamsCallback& callback) {
- return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
+ std::string content;
+ return ReadProcessMaps(pid, callback, content);
}
inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
diff --git a/process.cpp b/process.cpp
index 92f58d9..884570d 100644
--- a/process.cpp
+++ b/process.cpp
@@ -31,19 +31,34 @@
namespace android {
namespace procinfo {
-bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
- char path[PATH_MAX];
- snprintf(path, sizeof(path), "/proc/%d", tid);
+bool SetError(std::string* error, int errno_value, const char* fmt, ...) {
+ if (error == nullptr) return false;
- unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
- if (dirfd == -1) {
- if (error != nullptr) {
- *error = std::string("failed to open ") + path;
- }
- return false;
+ std::string result;
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&result, fmt, ap);
+ va_end(ap);
+
+ if (errno_value != 0) {
+ result += ": ";
+ result += strerror(errno_value);
}
- return GetProcessInfoFromProcPidFd(dirfd.get(), process_info, error);
+ *error = result;
+ return false;
+}
+
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
+ char path[32];
+ snprintf(path, sizeof(path), "/proc/%d", tid);
+
+ unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (dirfd == -1) {
+ return SetError(error, errno, "failed to open %s", path);
+ }
+
+ return GetProcessInfoFromProcPidFd(dirfd.get(), tid, process_info, error);
}
static ProcessState parse_state(char state) {
@@ -63,26 +78,18 @@
}
}
-bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info,
+bool GetProcessInfoFromProcPidFd(int fd, int pid, ProcessInfo* process_info,
std::string* error /* can be nullptr */) {
int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
-
- auto set_error = [&error](const char* err) {
- if (error != nullptr) {
- *error = err;
- }
- };
-
if (status_fd == -1) {
- set_error("failed to open status fd in GetProcessInfoFromProcPidFd");
- return false;
+ return SetError(error, errno,
+ "failed to open /proc/%d/status in GetProcessInfoFromProcPidFd", pid);
}
- std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "re"), fclose);
if (!fp) {
- set_error("failed to open status file in GetProcessInfoFromProcPidFd");
close(status_fd);
- return false;
+ return SetError(error, errno, "failed to open FILE for /proc/%d/status in GetProcessInfoFromProcPidFd", pid);
}
int field_bitmap = 0;
@@ -126,19 +133,17 @@
free(line);
if (field_bitmap != finished_bitmap) {
- set_error("failed to parse /proc/<pid>/status");
- return false;
+ return SetError(error, 0, "failed to parse /proc/%d/status", pid);
}
unique_fd stat_fd(openat(fd, "stat", O_RDONLY | O_CLOEXEC));
if (stat_fd == -1) {
- set_error("failed to open /proc/<pid>/stat");
+ return SetError(error, errno, "failed to open /proc/%d/stat", pid);
}
std::string stat;
if (!android::base::ReadFdToString(stat_fd, &stat)) {
- set_error("failed to read /proc/<pid>/stat");
- return false;
+ return SetError(error, errno, "failed to read /proc/%d/stat", pid);
}
// See man 5 proc. There's no reason comm can't contain ' ' or ')',
@@ -173,8 +178,7 @@
unsigned long long start_time = 0;
int rc = sscanf(end_of_comm + 2, pattern, &state, &ppid, &start_time);
if (rc != 3) {
- set_error("failed to parse /proc/<pid>/stat");
- return false;
+ return SetError(error, 0, "failed to parse /proc/%d/stat", pid);
}
process_info->state = parse_state(state);
diff --git a/process_test.cpp b/process_test.cpp
index 0f25f92..a9d2f19 100644
--- a/process_test.cpp
+++ b/process_test.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
#include <chrono>
#include <set>
#include <thread>
@@ -56,7 +57,7 @@
android::procinfo::ProcessInfo self;
int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY);
ASSERT_NE(-1, fd);
- ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self));
+ ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, gettid(), &self));
// Process name is capped at 15 bytes.
ASSERT_EQ("libprocinfo_tes", self.name);
@@ -102,19 +103,22 @@
_exit(0);
}
- // Give the child some time to get to the read.
- std::this_thread::sleep_for(100ms);
+ // Give the child some time to get to the read.
android::procinfo::ProcessInfo procinfo;
- ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ for (int loop = 0; loop < 50 && procinfo.state != android::procinfo::kProcessStateSleeping; loop++) {
+ std::this_thread::sleep_for(100ms);
+ 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));
+ for (int loop = 0; loop < 50 && procinfo.state != android::procinfo::kProcessStateZombie; loop++) {
+ 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));
@@ -170,29 +174,31 @@
// failed to open status file error
// No segfault if not given error string.
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo));
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo));
// Set error when given error string.
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo, &error));
- ASSERT_EQ(error, "failed to open status fd in GetProcessInfoFromProcPidFd");
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo, &error));
+ ASSERT_EQ(error, "failed to open /proc/0/status in GetProcessInfoFromProcPidFd: No such file or directory");
// failed to parse status file error
std::string status_file = std::string(tmp_dir.path) + "/status";
ASSERT_TRUE(android::base::WriteStringToFile("invalid data", status_file));
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo));
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo, &error));
- ASSERT_EQ(error, "failed to parse /proc/<pid>/status");
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo));
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo, &error));
+ ASSERT_EQ(error, "failed to parse /proc/0/status");
- // failed to read stat file error
+ // Give the "/status" file valid contents.
ASSERT_TRUE(android::base::WriteStringToFile(
"Name:\tsh\nTgid:\t0\nPid:\t0\nTracerPid:\t0\nUid:\t0\nGid:\t0\n", status_file));
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo));
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo, &error));
- ASSERT_EQ(error, "failed to read /proc/<pid>/stat");
+
+ // failed to open stat file error
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo));
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo, &error));
+ ASSERT_EQ(error, "failed to open /proc/0/stat: No such file or directory");
// failed to parse stat file error
std::string stat_file = std::string(tmp_dir.path) + "/stat";
ASSERT_TRUE(android::base::WriteStringToFile("2027 (sh) invalid data", stat_file));
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo));
- ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), &procinfo, &error));
- ASSERT_EQ(error, "failed to parse /proc/<pid>/stat");
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo));
+ ASSERT_FALSE(android::procinfo::GetProcessInfoFromProcPidFd(dirfd.get(), 0, &procinfo, &error));
+ ASSERT_EQ(error, "failed to parse /proc/0/stat");
}