Merge "libprocinfo: introduce."
diff --git a/libprocinfo/.clang-format b/libprocinfo/.clang-format
new file mode 100644
index 0000000..b8c6428
--- /dev/null
+++ b/libprocinfo/.clang-format
@@ -0,0 +1,14 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
new file mode 100644
index 0000000..8e17f1b
--- /dev/null
+++ b/libprocinfo/Android.bp
@@ -0,0 +1,73 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+libprocinfo_cppflags = [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+]
+
+cc_library {
+ name: "libprocinfo",
+ host_supported: true,
+ srcs: [
+ "process.cpp",
+ ],
+ cppflags: libprocinfo_cppflags,
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: ["libbase"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+ name: "libprocinfo_test",
+ host_supported: true,
+ srcs: [
+ "process_test.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+
+ cppflags: libprocinfo_cppflags,
+ shared_libs: ["libbase", "libprocinfo"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
new file mode 100644
index 0000000..fb140ff
--- /dev/null
+++ b/libprocinfo/include/procinfo/process.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace procinfo {
+
+#if defined(__linux__)
+
+struct ProcessInfo {
+ std::string name;
+ pid_t tid;
+ pid_t pid;
+ pid_t ppid;
+ pid_t tracer;
+ uid_t uid;
+ uid_t gid;
+};
+
+// Parse the contents of /proc/<tid>/status into |process_info|.
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info);
+
+// 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);
+
+// Fetch the list of threads from a given process's /proc/<pid> directory.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+template <typename Collection>
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ out->clear();
+
+ 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) {
+ PLOG(ERROR) << "failed to open task directory";
+ return false;
+ }
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ 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())) {
+ LOG(ERROR) << "failed to parse task id: " << dent->d_name;
+ return false;
+ }
+
+ out->insert(out->end(), tid);
+ }
+ }
+
+ return true;
+}
+
+template <typename Collection>
+auto GetProcessTids(pid_t pid, Collection* out) ->
+ 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) {
+ LOG(ERROR) << "task path overflow (pid = " << pid << ")";
+ return false;
+ }
+
+ android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to open " << task_path;
+ return false;
+ }
+
+ return GetProcessTidsFromProcPidFd(fd.get(), out);
+}
+
+#endif
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
new file mode 100644
index 0000000..c513e16
--- /dev/null
+++ b/libprocinfo/process.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace procinfo {
+
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d", tid);
+
+ unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
+ if (dirfd == -1) {
+ PLOG(ERROR) << "failed to open " << path;
+ return false;
+ }
+
+ return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
+}
+
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
+ int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
+
+ if (status_fd == -1) {
+ PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd";
+ return false;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
+ if (!fp) {
+ PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd";
+ close(status_fd);
+ return false;
+ }
+
+ int field_bitmap = 0;
+ static constexpr int finished_bitmap = 127;
+ char* line = nullptr;
+ size_t len = 0;
+
+ while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
+ char* tab = strchr(line, '\t');
+ if (tab == nullptr) {
+ continue;
+ }
+
+ size_t header_len = tab - line;
+ std::string header = std::string(line, header_len);
+ if (header == "Name:") {
+ std::string name = line + header_len + 1;
+
+ // line includes the trailing newline.
+ name.pop_back();
+ process_info->name = std::move(name);
+
+ field_bitmap |= 1;
+ } 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;
+ } else if (header == "Uid:") {
+ process_info->uid = atoi(tab + 1);
+ field_bitmap |= 32;
+ } else if (header == "Gid:") {
+ process_info->gid = atoi(tab + 1);
+ field_bitmap |= 64;
+ }
+ }
+
+ free(line);
+ return field_bitmap == finished_bitmap;
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
new file mode 100644
index 0000000..5ffd236
--- /dev/null
+++ b/libprocinfo/process_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#if !defined(__BIONIC__)
+#include <syscall.h>
+static pid_t gettid() {
+ return syscall(__NR_gettid);
+}
+#endif
+
+TEST(process_info, process_info_smoke) {
+ android::procinfo::ProcessInfo self;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self));
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+}
+
+TEST(process_info, process_info_proc_pid_fd_smoke) {
+ 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));
+
+ // Process name is capped at 15 bytes.
+ ASSERT_EQ("libprocinfo_tes", self.name);
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+ close(fd);
+}
+
+TEST(process_info, process_tids_smoke) {
+ pid_t main_tid = gettid();
+ std::thread([main_tid]() {
+ pid_t thread_tid = gettid();
+
+ {
+ std::vector<pid_t> vec;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid));
+ }
+
+ {
+ std::set<pid_t> set;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid));
+ }
+ }).join();
+}