1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/process_info_snapshot.h"
6
7#include <sys/types.h>  // For |uid_t| (and |pid_t|).
8#include <unistd.h>  // For |getpid()|, |getuid()|, etc.
9
10#include <vector>
11
12#include "base/command_line.h"
13#include "base/files/file_path.h"
14#include "base/logging.h"
15#include "base/mac/mac_util.h"
16#include "base/posix/eintr_wrapper.h"
17#include "base/process/kill.h"
18#include "base/process/launch.h"
19#include "base/process/process_handle.h"
20#include "base/process/process_metrics.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23typedef testing::Test ProcessInfoSnapshotMacTest;
24
25TEST_F(ProcessInfoSnapshotMacTest, FindPidOneTest) {
26  // Sample process with PID 1, which should exist and presumably belong to
27  // root.
28  std::vector<base::ProcessId> pid_list;
29  pid_list.push_back(1);
30  ProcessInfoSnapshot snapshot;
31  ASSERT_TRUE(snapshot.Sample(pid_list));
32
33  ProcessInfoSnapshot::ProcInfoEntry proc_info;
34  ASSERT_TRUE(snapshot.GetProcInfo(1, &proc_info));
35  EXPECT_EQ(1, static_cast<int64>(proc_info.pid));
36  EXPECT_EQ(0, static_cast<int64>(proc_info.ppid));
37  EXPECT_EQ(0, static_cast<int64>(proc_info.uid));
38  EXPECT_EQ(0, static_cast<int64>(proc_info.euid));
39  EXPECT_GE(proc_info.rss, 0u);
40  EXPECT_GT(proc_info.vsize, 0u);
41
42  // Try out the |Get...OfPID()|, but don't examine the results, since they
43  // depend on how we map |ProcInfoEntry| to |...KBytes|.
44  base::CommittedKBytes usage;
45  EXPECT_TRUE(snapshot.GetCommittedKBytesOfPID(1, &usage));
46  base::WorkingSetKBytes ws_usage;
47  EXPECT_TRUE(snapshot.GetWorkingSetKBytesOfPID(1, &ws_usage));
48
49  // Make sure it hasn't picked up some other PID (say, 2).
50  EXPECT_FALSE(snapshot.GetProcInfo(2, &proc_info));
51
52  // Make sure PID 2 still isn't there (in case I mess up my use of std::map).
53  EXPECT_FALSE(snapshot.GetProcInfo(2, &proc_info));
54
55  // Test |Reset()|.
56  snapshot.Reset();
57  EXPECT_FALSE(snapshot.GetProcInfo(1, &proc_info));
58}
59
60TEST_F(ProcessInfoSnapshotMacTest, FindPidSelfTest) {
61  // Sample this process and its parent.
62  base::ProcessId pid = static_cast<base::ProcessId>(getpid());
63  base::ProcessId ppid = static_cast<base::ProcessId>(getppid());
64  uid_t uid = getuid();
65  uid_t euid = geteuid();
66  EXPECT_NE(static_cast<int64>(ppid), 0);
67
68  std::vector<base::ProcessId> pid_list;
69  pid_list.push_back(pid);
70  pid_list.push_back(ppid);
71  ProcessInfoSnapshot snapshot;
72  ASSERT_TRUE(snapshot.Sample(pid_list));
73
74  // Find our process.
75  ProcessInfoSnapshot::ProcInfoEntry proc_info;
76  ASSERT_TRUE(snapshot.GetProcInfo(pid, &proc_info));
77  EXPECT_EQ(pid, proc_info.pid);
78  EXPECT_EQ(ppid, proc_info.ppid);
79  EXPECT_EQ(uid, proc_info.uid);
80  EXPECT_EQ(euid, proc_info.euid);
81  // Sanity check: we're running, so we should occupy at least 100 kilobytes.
82  EXPECT_GE(proc_info.rss, 100u);
83  // Sanity check: our |vsize| is presumably at least a megabyte.
84  EXPECT_GE(proc_info.vsize, 1024u);
85
86  // Collection of some memory statistics is broken in OSX 10.9+.
87  // http://crbug.com/383553
88  if (!base::mac::IsOSMavericksOrLater()) {
89    // Shared memory should also > 1 MB.
90    EXPECT_GE(proc_info.rshrd, 1024u);
91    // Same with private memory.
92    EXPECT_GE(proc_info.rprvt, 1024u);
93  }
94
95  // Find our parent.
96  ASSERT_TRUE(snapshot.GetProcInfo(ppid, &proc_info));
97  EXPECT_EQ(ppid, proc_info.pid);
98  EXPECT_NE(static_cast<int64>(proc_info.ppid), 0);
99  EXPECT_EQ(uid, proc_info.uid);    // This (and the following) should be true
100  EXPECT_EQ(euid, proc_info.euid);  // under reasonable circumstances.
101  // Can't say anything definite about its |rss|.
102  EXPECT_GT(proc_info.vsize, 0u);   // Its |vsize| should be nonzero though.
103}
104
105// To verify that ProcessInfoSnapshot is getting the actual uid and effective
106// uid, this test runs top. top should have a uid of the caller and effective
107// uid of 0 (root).
108TEST_F(ProcessInfoSnapshotMacTest, EffectiveVsRealUserIDTest) {
109  // Create a pipe to be able to read top's output.
110  int fds[2];
111  PCHECK(pipe(fds) == 0);
112  base::FileHandleMappingVector fds_to_remap;
113  fds_to_remap.push_back(std::make_pair(fds[1], 1));
114
115  // Hook up top's stderr to the test process' stderr.
116  fds_to_remap.push_back(std::make_pair(fileno(stderr), 2));
117
118  std::vector<std::string> argv;
119  argv.push_back("/usr/bin/top");
120  argv.push_back("-l");
121  argv.push_back("0");
122
123  base::ProcessHandle process_handle;
124  base::LaunchOptions options;
125  options.fds_to_remap = &fds_to_remap;
126  ASSERT_TRUE(base::LaunchProcess(argv, options, &process_handle));
127  PCHECK(IGNORE_EINTR(close(fds[1])) == 0);
128
129  // Wait until there's some output form top. This is an easy way to tell that
130  // the exec() call is done and top is actually running.
131  char buf[1];
132  PCHECK(HANDLE_EINTR(read(fds[0], buf, 1)) == 1);
133
134  std::vector<base::ProcessId> pid_list;
135  pid_list.push_back(process_handle);
136  ProcessInfoSnapshot snapshot;
137  ASSERT_TRUE(snapshot.Sample(pid_list));
138
139  ProcessInfoSnapshot::ProcInfoEntry proc_info;
140  ASSERT_TRUE(snapshot.GetProcInfo(process_handle, &proc_info));
141  // Effective user ID should be 0 (root).
142  EXPECT_EQ(proc_info.euid, 0u);
143  // Real user ID should match the calling process's user id.
144  EXPECT_EQ(proc_info.uid, geteuid());
145
146  ASSERT_TRUE(base::KillProcess(process_handle, 0, true));
147  PCHECK(IGNORE_EINTR(close(fds[0])) == 0);
148}
149