diff options
author | Josh Gao | 2016-10-28 17:23:25 -0500 |
---|---|---|
committer | Josh Gao | 2016-11-14 15:42:18 -0600 |
commit | 911d729c8a01fbc052a8e15322bad5413a1138a9 (patch) | |
tree | d3e2f90c4643de9a2cad98d93c91bb0206990a27 /libprocinfo | |
parent | f77d8b04528775e7a2e22255bb9a46b0e1df8ef1 (diff) | |
download | platform-system-core-911d729c8a01fbc052a8e15322bad5413a1138a9.tar.gz platform-system-core-911d729c8a01fbc052a8e15322bad5413a1138a9.tar.xz platform-system-core-911d729c8a01fbc052a8e15322bad5413a1138a9.zip |
libprocinfo: introduce.
Add a new library for parsing /proc files. Start with helpers for
parsing /proc/<pid>/status and /proc/<pid>/task.
Bug: http://b/30705528
Test: libprocinfo_test32/64 on host/bullhead
Change-Id: I5757514c0aede8a9d75834b55aae42a5cf762b95
Diffstat (limited to 'libprocinfo')
-rw-r--r-- | libprocinfo/.clang-format | 14 | ||||
-rw-r--r-- | libprocinfo/Android.bp | 73 | ||||
-rw-r--r-- | libprocinfo/include/procinfo/process.h | 106 | ||||
-rw-r--r-- | libprocinfo/process.cpp | 109 | ||||
-rw-r--r-- | libprocinfo/process_test.cpp | 84 |
5 files changed, 386 insertions, 0 deletions
diff --git a/libprocinfo/.clang-format b/libprocinfo/.clang-format new file mode 100644 index 000000000..b8c642840 --- /dev/null +++ b/libprocinfo/.clang-format | |||
@@ -0,0 +1,14 @@ | |||
1 | BasedOnStyle: Google | ||
2 | AllowShortBlocksOnASingleLine: false | ||
3 | AllowShortFunctionsOnASingleLine: false | ||
4 | |||
5 | ColumnLimit: 100 | ||
6 | CommentPragmas: NOLINT:.* | ||
7 | DerivePointerAlignment: false | ||
8 | IndentWidth: 2 | ||
9 | PointerAlignment: Left | ||
10 | TabWidth: 2 | ||
11 | UseTab: Never | ||
12 | PenaltyExcessCharacter: 32 | ||
13 | |||
14 | Cpp11BracedListStyle: false | ||
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp new file mode 100644 index 000000000..8e17f1b1b --- /dev/null +++ b/libprocinfo/Android.bp | |||
@@ -0,0 +1,73 @@ | |||
1 | // | ||
2 | // Copyright (C) 2015 The Android Open Source Project | ||
3 | // | ||
4 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | // you may not use this file except in compliance with the License. | ||
6 | // You may obtain a copy of the License at | ||
7 | // | ||
8 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | // | ||
10 | // Unless required by applicable law or agreed to in writing, software | ||
11 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | // See the License for the specific language governing permissions and | ||
14 | // limitations under the License. | ||
15 | // | ||
16 | |||
17 | libprocinfo_cppflags = [ | ||
18 | "-Wall", | ||
19 | "-Wextra", | ||
20 | "-Werror", | ||
21 | ] | ||
22 | |||
23 | cc_library { | ||
24 | name: "libprocinfo", | ||
25 | host_supported: true, | ||
26 | srcs: [ | ||
27 | "process.cpp", | ||
28 | ], | ||
29 | cppflags: libprocinfo_cppflags, | ||
30 | |||
31 | local_include_dirs: ["include"], | ||
32 | export_include_dirs: ["include"], | ||
33 | shared_libs: ["libbase"], | ||
34 | target: { | ||
35 | darwin: { | ||
36 | enabled: false, | ||
37 | }, | ||
38 | windows: { | ||
39 | enabled: false, | ||
40 | }, | ||
41 | }, | ||
42 | } | ||
43 | |||
44 | // Tests | ||
45 | // ------------------------------------------------------------------------------ | ||
46 | cc_test { | ||
47 | name: "libprocinfo_test", | ||
48 | host_supported: true, | ||
49 | srcs: [ | ||
50 | "process_test.cpp", | ||
51 | ], | ||
52 | target: { | ||
53 | darwin: { | ||
54 | enabled: false, | ||
55 | }, | ||
56 | windows: { | ||
57 | enabled: false, | ||
58 | }, | ||
59 | }, | ||
60 | |||
61 | cppflags: libprocinfo_cppflags, | ||
62 | shared_libs: ["libbase", "libprocinfo"], | ||
63 | |||
64 | compile_multilib: "both", | ||
65 | multilib: { | ||
66 | lib32: { | ||
67 | suffix: "32", | ||
68 | }, | ||
69 | lib64: { | ||
70 | suffix: "64", | ||
71 | }, | ||
72 | }, | ||
73 | } | ||
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h new file mode 100644 index 000000000..fb140ff3d --- /dev/null +++ b/libprocinfo/include/procinfo/process.h | |||
@@ -0,0 +1,106 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #pragma once | ||
18 | |||
19 | #include <dirent.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <sys/types.h> | ||
23 | #include <unistd.h> | ||
24 | |||
25 | #include <memory> | ||
26 | #include <string> | ||
27 | #include <type_traits> | ||
28 | |||
29 | #include <android-base/logging.h> | ||
30 | #include <android-base/parseint.h> | ||
31 | #include <android-base/unique_fd.h> | ||
32 | |||
33 | namespace android { | ||
34 | namespace procinfo { | ||
35 | |||
36 | #if defined(__linux__) | ||
37 | |||
38 | struct ProcessInfo { | ||
39 | std::string name; | ||
40 | pid_t tid; | ||
41 | pid_t pid; | ||
42 | pid_t ppid; | ||
43 | pid_t tracer; | ||
44 | uid_t uid; | ||
45 | uid_t gid; | ||
46 | }; | ||
47 | |||
48 | // Parse the contents of /proc/<tid>/status into |process_info|. | ||
49 | bool GetProcessInfo(pid_t tid, ProcessInfo* process_info); | ||
50 | |||
51 | // Parse the contents of <fd>/status into |process_info|. | ||
52 | // |fd| should be an fd pointing at a /proc/<pid> directory. | ||
53 | bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info); | ||
54 | |||
55 | // Fetch the list of threads from a given process's /proc/<pid> directory. | ||
56 | // |fd| should be an fd pointing at a /proc/<pid> directory. | ||
57 | template <typename Collection> | ||
58 | auto GetProcessTidsFromProcPidFd(int fd, Collection* out) -> | ||
59 | typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type { | ||
60 | out->clear(); | ||
61 | |||
62 | int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC); | ||
63 | std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir); | ||
64 | if (!dir) { | ||
65 | PLOG(ERROR) << "failed to open task directory"; | ||
66 | return false; | ||
67 | } | ||
68 | |||
69 | struct dirent* dent; | ||
70 | while ((dent = readdir(dir.get()))) { | ||
71 | if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) { | ||
72 | pid_t tid; | ||
73 | if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) { | ||
74 | LOG(ERROR) << "failed to parse task id: " << dent->d_name; | ||
75 | return false; | ||
76 | } | ||
77 | |||
78 | out->insert(out->end(), tid); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | return true; | ||
83 | } | ||
84 | |||
85 | template <typename Collection> | ||
86 | auto GetProcessTids(pid_t pid, Collection* out) -> | ||
87 | typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type { | ||
88 | char task_path[PATH_MAX]; | ||
89 | if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) { | ||
90 | LOG(ERROR) << "task path overflow (pid = " << pid << ")"; | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC)); | ||
95 | if (fd == -1) { | ||
96 | PLOG(ERROR) << "failed to open " << task_path; | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | return GetProcessTidsFromProcPidFd(fd.get(), out); | ||
101 | } | ||
102 | |||
103 | #endif | ||
104 | |||
105 | } /* namespace procinfo */ | ||
106 | } /* namespace android */ | ||
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp new file mode 100644 index 000000000..c513e16f5 --- /dev/null +++ b/libprocinfo/process.cpp | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <procinfo/process.h> | ||
18 | |||
19 | #include <fcntl.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | #include <unistd.h> | ||
24 | |||
25 | #include <string> | ||
26 | |||
27 | #include <android-base/unique_fd.h> | ||
28 | |||
29 | using android::base::unique_fd; | ||
30 | |||
31 | namespace android { | ||
32 | namespace procinfo { | ||
33 | |||
34 | bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) { | ||
35 | char path[PATH_MAX]; | ||
36 | snprintf(path, sizeof(path), "/proc/%d", tid); | ||
37 | |||
38 | unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY)); | ||
39 | if (dirfd == -1) { | ||
40 | PLOG(ERROR) << "failed to open " << path; | ||
41 | return false; | ||
42 | } | ||
43 | |||
44 | return GetProcessInfoFromProcPidFd(dirfd.get(), process_info); | ||
45 | } | ||
46 | |||
47 | bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) { | ||
48 | int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC); | ||
49 | |||
50 | if (status_fd == -1) { | ||
51 | PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd"; | ||
52 | return false; | ||
53 | } | ||
54 | |||
55 | std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose); | ||
56 | if (!fp) { | ||
57 | PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd"; | ||
58 | close(status_fd); | ||
59 | return false; | ||
60 | } | ||
61 | |||
62 | int field_bitmap = 0; | ||
63 | static constexpr int finished_bitmap = 127; | ||
64 | char* line = nullptr; | ||
65 | size_t len = 0; | ||
66 | |||
67 | while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) { | ||
68 | char* tab = strchr(line, '\t'); | ||
69 | if (tab == nullptr) { | ||
70 | continue; | ||
71 | } | ||
72 | |||
73 | size_t header_len = tab - line; | ||
74 | std::string header = std::string(line, header_len); | ||
75 | if (header == "Name:") { | ||
76 | std::string name = line + header_len + 1; | ||
77 | |||
78 | // line includes the trailing newline. | ||
79 | name.pop_back(); | ||
80 | process_info->name = std::move(name); | ||
81 | |||
82 | field_bitmap |= 1; | ||
83 | } else if (header == "Pid:") { | ||
84 | process_info->tid = atoi(tab + 1); | ||
85 | field_bitmap |= 2; | ||
86 | } else if (header == "Tgid:") { | ||
87 | process_info->pid = atoi(tab + 1); | ||
88 | field_bitmap |= 4; | ||
89 | } else if (header == "PPid:") { | ||
90 | process_info->ppid = atoi(tab + 1); | ||
91 | field_bitmap |= 8; | ||
92 | } else if (header == "TracerPid:") { | ||
93 | process_info->tracer = atoi(tab + 1); | ||
94 | field_bitmap |= 16; | ||
95 | } else if (header == "Uid:") { | ||
96 | process_info->uid = atoi(tab + 1); | ||
97 | field_bitmap |= 32; | ||
98 | } else if (header == "Gid:") { | ||
99 | process_info->gid = atoi(tab + 1); | ||
100 | field_bitmap |= 64; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | free(line); | ||
105 | return field_bitmap == finished_bitmap; | ||
106 | } | ||
107 | |||
108 | } /* namespace procinfo */ | ||
109 | } /* namespace android */ | ||
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp new file mode 100644 index 000000000..5ffd2365b --- /dev/null +++ b/libprocinfo/process_test.cpp | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <procinfo/process.h> | ||
18 | |||
19 | #include <fcntl.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <sys/types.h> | ||
22 | #include <unistd.h> | ||
23 | |||
24 | #include <set> | ||
25 | #include <thread> | ||
26 | #include <vector> | ||
27 | |||
28 | #include <gtest/gtest.h> | ||
29 | |||
30 | #include <android-base/stringprintf.h> | ||
31 | |||
32 | #if !defined(__BIONIC__) | ||
33 | #include <syscall.h> | ||
34 | static pid_t gettid() { | ||
35 | return syscall(__NR_gettid); | ||
36 | } | ||
37 | #endif | ||
38 | |||
39 | TEST(process_info, process_info_smoke) { | ||
40 | android::procinfo::ProcessInfo self; | ||
41 | ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self)); | ||
42 | ASSERT_EQ(gettid(), self.tid); | ||
43 | ASSERT_EQ(getpid(), self.pid); | ||
44 | ASSERT_EQ(getppid(), self.ppid); | ||
45 | ASSERT_EQ(getuid(), self.uid); | ||
46 | ASSERT_EQ(getgid(), self.gid); | ||
47 | } | ||
48 | |||
49 | TEST(process_info, process_info_proc_pid_fd_smoke) { | ||
50 | android::procinfo::ProcessInfo self; | ||
51 | int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY); | ||
52 | ASSERT_NE(-1, fd); | ||
53 | ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self)); | ||
54 | |||
55 | // Process name is capped at 15 bytes. | ||
56 | ASSERT_EQ("libprocinfo_tes", self.name); | ||
57 | ASSERT_EQ(gettid(), self.tid); | ||
58 | ASSERT_EQ(getpid(), self.pid); | ||
59 | ASSERT_EQ(getppid(), self.ppid); | ||
60 | ASSERT_EQ(getuid(), self.uid); | ||
61 | ASSERT_EQ(getgid(), self.gid); | ||
62 | close(fd); | ||
63 | } | ||
64 | |||
65 | TEST(process_info, process_tids_smoke) { | ||
66 | pid_t main_tid = gettid(); | ||
67 | std::thread([main_tid]() { | ||
68 | pid_t thread_tid = gettid(); | ||
69 | |||
70 | { | ||
71 | std::vector<pid_t> vec; | ||
72 | ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec)); | ||
73 | ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid)); | ||
74 | ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid)); | ||
75 | } | ||
76 | |||
77 | { | ||
78 | std::set<pid_t> set; | ||
79 | ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set)); | ||
80 | ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid)); | ||
81 | ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid)); | ||
82 | } | ||
83 | }).join(); | ||
84 | } | ||