1 /*
2 * Copyright (C) 2018 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 "src/traced/probes/ps/process_stats_data_source.h"
18
19 #include <dirent.h>
20
21 #include "perfetto/ext/base/file_utils.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/ext/base/temp_file.h"
24 #include "perfetto/protozero/scattered_heap_buffer.h"
25 #include "perfetto/tracing/core/data_source_config.h"
26 #include "src/base/test/test_task_runner.h"
27 #include "src/traced/probes/common/cpu_freq_info_for_testing.h"
28 #include "src/tracing/core/trace_writer_for_testing.h"
29 #include "test/gtest_and_gmock.h"
30
31 #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
32 #include "protos/perfetto/trace/ps/process_stats.gen.h"
33 #include "protos/perfetto/trace/ps/process_tree.gen.h"
34
35 using ::perfetto::protos::gen::ProcessStatsConfig;
36 using ::testing::_;
37 using ::testing::Contains;
38 using ::testing::ElementsAre;
39 using ::testing::ElementsAreArray;
40 using ::testing::Invoke;
41 using ::testing::Mock;
42 using ::testing::Return;
43 using ::testing::Truly;
44
45 namespace perfetto {
46 namespace {
47
48 class TestProcessStatsDataSource : public ProcessStatsDataSource {
49 public:
TestProcessStatsDataSource(base::TaskRunner * task_runner,TracingSessionID id,std::unique_ptr<TraceWriter> writer,const DataSourceConfig & config,std::unique_ptr<CpuFreqInfo> cpu_freq_info)50 TestProcessStatsDataSource(base::TaskRunner* task_runner,
51 TracingSessionID id,
52 std::unique_ptr<TraceWriter> writer,
53 const DataSourceConfig& config,
54 std::unique_ptr<CpuFreqInfo> cpu_freq_info)
55 : ProcessStatsDataSource(task_runner,
56 id,
57 std::move(writer),
58 config,
59 std::move(cpu_freq_info)) {}
60
61 MOCK_METHOD(const char*, GetProcMountpoint, (), (override));
62 MOCK_METHOD(base::ScopedDir, OpenProcDir, (), (override));
63 MOCK_METHOD(std::string,
64 ReadProcPidFile,
65 (int32_t pid, const std::string&),
66 (override));
67 };
68
69 class ProcessStatsDataSourceTest : public ::testing::Test {
70 protected:
ProcessStatsDataSourceTest()71 ProcessStatsDataSourceTest() {}
72
GetProcessStatsDataSource(const DataSourceConfig & cfg)73 std::unique_ptr<TestProcessStatsDataSource> GetProcessStatsDataSource(
74 const DataSourceConfig& cfg) {
75 auto writer =
76 std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
77 writer_raw_ = writer.get();
78 return std::unique_ptr<TestProcessStatsDataSource>(
79 new TestProcessStatsDataSource(
80 &task_runner_, 0, std::move(writer), cfg,
81 cpu_freq_info_for_testing.GetInstance()));
82 }
83
84 base::TestTaskRunner task_runner_;
85 TraceWriterForTesting* writer_raw_;
86 CpuFreqInfoForTesting cpu_freq_info_for_testing;
87 };
88
TEST_F(ProcessStatsDataSourceTest,WriteOnceProcess)89 TEST_F(ProcessStatsDataSourceTest, WriteOnceProcess) {
90 auto data_source = GetProcessStatsDataSource(DataSourceConfig());
91 EXPECT_CALL(*data_source, ReadProcPidFile(42, "status"))
92 .WillOnce(Return(
93 "Name: foo\nTgid:\t42\nPid: 42\nPPid: 17\nUid: 43 44 45 56\n"));
94 EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline"))
95 .WillOnce(Return(std::string("foo\0bar\0baz\0", 12)));
96
97 data_source->OnPids({42});
98
99 auto trace = writer_raw_->GetAllTracePackets();
100 ASSERT_EQ(trace.size(), 1u);
101 auto ps_tree = trace[0].process_tree();
102 ASSERT_EQ(ps_tree.processes_size(), 1);
103 auto first_process = ps_tree.processes()[0];
104 ASSERT_EQ(first_process.pid(), 42);
105 ASSERT_EQ(first_process.ppid(), 17);
106 ASSERT_EQ(first_process.uid(), 43);
107 ASSERT_THAT(first_process.cmdline(), ElementsAreArray({"foo", "bar", "baz"}));
108 }
109
110 // Regression test for b/147438623.
TEST_F(ProcessStatsDataSourceTest,NonNulTerminatedCmdline)111 TEST_F(ProcessStatsDataSourceTest, NonNulTerminatedCmdline) {
112 auto data_source = GetProcessStatsDataSource(DataSourceConfig());
113 EXPECT_CALL(*data_source, ReadProcPidFile(42, "status"))
114 .WillOnce(Return(
115 "Name: foo\nTgid:\t42\nPid: 42\nPPid: 17\nUid: 43 44 45 56\n"));
116 EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline"))
117 .WillOnce(Return(std::string("surfaceflinger", 14)));
118
119 data_source->OnPids({42});
120
121 auto trace = writer_raw_->GetAllTracePackets();
122 ASSERT_EQ(trace.size(), 1u);
123 auto ps_tree = trace[0].process_tree();
124 ASSERT_EQ(ps_tree.processes_size(), 1);
125 auto first_process = ps_tree.processes()[0];
126 ASSERT_THAT(first_process.cmdline(), ElementsAreArray({"surfaceflinger"}));
127 }
128
TEST_F(ProcessStatsDataSourceTest,DontRescanCachedPIDsAndTIDs)129 TEST_F(ProcessStatsDataSourceTest, DontRescanCachedPIDsAndTIDs) {
130 // assertion helpers
131 auto expected_process = [](int pid) {
132 return [pid](protos::gen::ProcessTree::Process process) {
133 return process.pid() == pid && process.cmdline_size() > 0 &&
134 process.cmdline()[0] == "proc_" + std::to_string(pid);
135 };
136 };
137 auto expected_thread = [](int tid) {
138 return [tid](protos::gen::ProcessTree::Thread thread) {
139 return thread.tid() == tid && thread.tgid() == tid / 10 * 10 &&
140 thread.name() == "thread_" + std::to_string(tid);
141 };
142 };
143
144 DataSourceConfig ds_config;
145 ProcessStatsConfig cfg;
146 cfg.set_record_thread_names(true);
147 ds_config.set_process_stats_config_raw(cfg.SerializeAsString());
148 auto data_source = GetProcessStatsDataSource(ds_config);
149 for (int p : {10, 11, 12, 20, 21, 22, 30, 31, 32}) {
150 EXPECT_CALL(*data_source, ReadProcPidFile(p, "status"))
151 .WillOnce(Invoke([](int32_t pid, const std::string&) {
152 int32_t tgid = (pid / 10) * 10;
153 return "Name: \tthread_" + std::to_string(pid) +
154 "\nTgid: " + std::to_string(tgid) +
155 "\nPid: " + std::to_string(pid) + "\nPPid: 1\n";
156 }));
157 if (p % 10 == 0) {
158 std::string proc_name = "proc_" + std::to_string(p);
159 proc_name.resize(proc_name.size() + 1); // Add a trailing \0.
160 EXPECT_CALL(*data_source, ReadProcPidFile(p, "cmdline"))
161 .WillOnce(Return(proc_name));
162 }
163 }
164
165 data_source->OnPids({10, 11, 12, 20, 21, 22, 10, 20, 11, 21});
166 data_source->OnPids({30});
167 data_source->OnPids({10, 30, 10, 31, 32});
168
169 // check written contents
170 auto trace = writer_raw_->GetAllTracePackets();
171 EXPECT_EQ(trace.size(), 3u);
172
173 // first packet - two unique processes, four threads
174 auto ps_tree = trace[0].process_tree();
175 EXPECT_THAT(ps_tree.processes(),
176 UnorderedElementsAre(Truly(expected_process(10)),
177 Truly(expected_process(20))));
178 EXPECT_THAT(ps_tree.threads(),
179 UnorderedElementsAre(
180 Truly(expected_thread(11)), Truly(expected_thread(12)),
181 Truly(expected_thread(21)), Truly(expected_thread(22))));
182
183 // second packet - one new process
184 ps_tree = trace[1].process_tree();
185 EXPECT_THAT(ps_tree.processes(),
186 UnorderedElementsAre(Truly(expected_process(30))));
187 EXPECT_EQ(ps_tree.threads_size(), 0);
188
189 // third packet - two threads that haven't been seen before
190 ps_tree = trace[2].process_tree();
191 EXPECT_EQ(ps_tree.processes_size(), 0);
192 EXPECT_THAT(ps_tree.threads(),
193 UnorderedElementsAre(Truly(expected_thread(31)),
194 Truly(expected_thread(32))));
195 }
196
TEST_F(ProcessStatsDataSourceTest,IncrementalStateClear)197 TEST_F(ProcessStatsDataSourceTest, IncrementalStateClear) {
198 auto data_source = GetProcessStatsDataSource(DataSourceConfig());
199 EXPECT_CALL(*data_source, ReadProcPidFile(42, "status"))
200 .WillOnce(Return("Name: foo\nTgid:\t42\nPid: 42\nPPid: 17\n"));
201 EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline"))
202 .WillOnce(Return(std::string("first_cmdline\0", 14)));
203
204 data_source->OnPids({42});
205
206 {
207 auto trace = writer_raw_->GetAllTracePackets();
208 ASSERT_EQ(trace.size(), 1u);
209 auto packet = trace[0];
210 // First packet in the trace has no previous state, so the clear marker is
211 // emitted.
212 ASSERT_TRUE(packet.incremental_state_cleared());
213
214 auto ps_tree = packet.process_tree();
215 ASSERT_EQ(ps_tree.processes_size(), 1);
216 ASSERT_EQ(ps_tree.processes()[0].pid(), 42);
217 ASSERT_EQ(ps_tree.processes()[0].ppid(), 17);
218 ASSERT_THAT(ps_tree.processes()[0].cmdline(),
219 ElementsAreArray({"first_cmdline"}));
220 }
221
222 // Look up the same pid, which shouldn't be re-emitted.
223 Mock::VerifyAndClearExpectations(data_source.get());
224 EXPECT_CALL(*data_source, ReadProcPidFile(42, "status")).Times(0);
225 EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline")).Times(0);
226
227 data_source->OnPids({42});
228
229 {
230 auto trace = writer_raw_->GetAllTracePackets();
231 ASSERT_EQ(trace.size(), 1u);
232 }
233
234 // Invalidate incremental state, and look up the same pid again, which should
235 // re-emit the proc tree info.
236 Mock::VerifyAndClearExpectations(data_source.get());
237 EXPECT_CALL(*data_source, ReadProcPidFile(42, "status"))
238 .WillOnce(Return("Name: foo\nTgid:\t42\nPid: 42\nPPid: 18\n"));
239 EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline"))
240 .WillOnce(Return(std::string("second_cmdline\0", 15)));
241
242 data_source->ClearIncrementalState();
243 data_source->OnPids({42});
244
245 {
246 // Second packet with new proc information.
247 auto trace = writer_raw_->GetAllTracePackets();
248 ASSERT_EQ(trace.size(), 2u);
249 auto packet = trace[1];
250 ASSERT_TRUE(packet.incremental_state_cleared());
251
252 auto ps_tree = packet.process_tree();
253 ASSERT_EQ(ps_tree.processes_size(), 1);
254 ASSERT_EQ(ps_tree.processes()[0].pid(), 42);
255 ASSERT_EQ(ps_tree.processes()[0].ppid(), 18);
256 ASSERT_THAT(ps_tree.processes()[0].cmdline(),
257 ElementsAreArray({"second_cmdline"}));
258 }
259 }
260
TEST_F(ProcessStatsDataSourceTest,RenamePids)261 TEST_F(ProcessStatsDataSourceTest, RenamePids) {
262 // assertion helpers
263 auto expected_old_process = [](int pid) {
264 return [pid](protos::gen::ProcessTree::Process process) {
265 return process.pid() == pid && process.cmdline_size() > 0 &&
266 process.cmdline()[0] == "proc_" + std::to_string(pid);
267 };
268 };
269 auto expected_new_process = [](int pid) {
270 return [pid](protos::gen::ProcessTree::Process process) {
271 return process.pid() == pid && process.cmdline_size() > 0 &&
272 process.cmdline()[0] == "new_" + std::to_string(pid);
273 };
274 };
275
276 DataSourceConfig config;
277 auto data_source = GetProcessStatsDataSource(config);
278 for (int p : {10, 20}) {
279 EXPECT_CALL(*data_source, ReadProcPidFile(p, "status"))
280 .WillRepeatedly(Invoke([](int32_t pid, const std::string&) {
281 return "Name: \tthread_" + std::to_string(pid) +
282 "\nTgid: " + std::to_string(pid) +
283 "\nPid: " + std::to_string(pid) + "\nPPid: 1\n";
284 }));
285
286 std::string old_proc_name = "proc_" + std::to_string(p);
287 old_proc_name.resize(old_proc_name.size() + 1); // Add a trailing \0.
288
289 std::string new_proc_name = "new_" + std::to_string(p);
290 new_proc_name.resize(new_proc_name.size() + 1); // Add a trailing \0.
291 EXPECT_CALL(*data_source, ReadProcPidFile(p, "cmdline"))
292 .WillOnce(Return(old_proc_name))
293 .WillOnce(Return(new_proc_name));
294 }
295
296 data_source->OnPids({10, 20});
297 data_source->OnRenamePids({10});
298 data_source->OnPids({10, 20});
299 data_source->OnRenamePids({20});
300 data_source->OnPids({10, 20});
301
302 // check written contents
303 auto trace = writer_raw_->GetAllTracePackets();
304 EXPECT_EQ(trace.size(), 3u);
305
306 // first packet - two unique processes
307 auto ps_tree = trace[0].process_tree();
308 EXPECT_THAT(ps_tree.processes(),
309 UnorderedElementsAre(Truly(expected_old_process(10)),
310 Truly(expected_old_process(20))));
311 EXPECT_EQ(ps_tree.threads_size(), 0);
312
313 // second packet - one new process
314 ps_tree = trace[1].process_tree();
315 EXPECT_THAT(ps_tree.processes(),
316 UnorderedElementsAre(Truly(expected_new_process(10))));
317 EXPECT_EQ(ps_tree.threads_size(), 0);
318
319 // third packet - two threads that haven't been seen before
320 ps_tree = trace[2].process_tree();
321 EXPECT_THAT(ps_tree.processes(),
322 UnorderedElementsAre(Truly(expected_new_process(20))));
323 EXPECT_EQ(ps_tree.threads_size(), 0);
324 }
325
TEST_F(ProcessStatsDataSourceTest,ProcessStats)326 TEST_F(ProcessStatsDataSourceTest, ProcessStats) {
327 DataSourceConfig ds_config;
328 ProcessStatsConfig cfg;
329 cfg.set_proc_stats_poll_ms(1);
330 cfg.set_resolve_process_fds(true);
331 cfg.add_quirks(ProcessStatsConfig::DISABLE_ON_DEMAND);
332 ds_config.set_process_stats_config_raw(cfg.SerializeAsString());
333 auto data_source = GetProcessStatsDataSource(ds_config);
334
335 // Populate a fake /proc/ directory.
336 auto fake_proc = base::TempDir::Create();
337 const int kPids[] = {1, 2};
338 const uint64_t kFds[] = {5u, 7u};
339 const char kDevice[] = "/dev/dummy";
340 std::vector<std::string> dirs_to_delete;
341 std::vector<std::string> links_to_delete;
342 for (int pid : kPids) {
343 base::StackString<256> path("%s/%d", fake_proc.path().c_str(), pid);
344 dirs_to_delete.push_back(path.ToStdString());
345 EXPECT_EQ(mkdir(path.c_str(), 0755), 0)
346 << "mkdir('" << path.c_str() << "') failed";
347
348 base::StackString<256> path_fd("%s/fd", path.c_str());
349 dirs_to_delete.push_back(path_fd.ToStdString());
350 EXPECT_EQ(mkdir(path_fd.c_str(), 0755), 0)
351 << "mkdir('" << path_fd.c_str() << "') failed";
352
353 for (auto fd : kFds) {
354 base::StackString<256> link("%s/%" PRIu64, path_fd.c_str(), fd);
355 links_to_delete.push_back(link.ToStdString());
356 EXPECT_EQ(symlink(kDevice, link.c_str()), 0)
357 << "symlink('" << kDevice << "','" << link.c_str() << "') failed";
358 }
359 }
360
361 auto checkpoint = task_runner_.CreateCheckpoint("all_done");
362
363 const auto fake_proc_path = fake_proc.path();
364 EXPECT_CALL(*data_source, OpenProcDir())
365 .WillRepeatedly(Invoke([&fake_proc_path] {
366 return base::ScopedDir(opendir(fake_proc_path.c_str()));
367 }));
368 EXPECT_CALL(*data_source, GetProcMountpoint())
369 .WillRepeatedly(
370 Invoke([&fake_proc_path] { return fake_proc_path.c_str(); }));
371
372 const int kNumIters = 4;
373 int iter = 0;
374 for (int pid : kPids) {
375 EXPECT_CALL(*data_source, ReadProcPidFile(pid, "status"))
376 .WillRepeatedly(
377 Invoke([checkpoint, &iter](int32_t p, const std::string&) {
378 base::StackString<1024> ret(
379 "Name: pid_10\nVmSize: %d kB\nVmRSS:\t%d kB\n",
380 p * 100 + iter * 10 + 1, p * 100 + iter * 10 + 2);
381 return ret.ToStdString();
382 }));
383
384 // By default scan_smaps_rollup is off and /proc/<pid>/smaps_rollup
385 // shouldn't be read.
386 EXPECT_CALL(*data_source, ReadProcPidFile(pid, "smaps_rollup")).Times(0);
387
388 EXPECT_CALL(*data_source, ReadProcPidFile(pid, "oom_score_adj"))
389 .WillRepeatedly(Invoke(
390 [checkpoint, kPids, &iter](int32_t inner_pid, const std::string&) {
391 auto oom_score = inner_pid * 100 + iter * 10 + 3;
392 if (inner_pid == kPids[base::ArraySize(kPids) - 1]) {
393 if (++iter == kNumIters)
394 checkpoint();
395 }
396 return std::to_string(oom_score);
397 }));
398 }
399
400 data_source->Start();
401 task_runner_.RunUntilCheckpoint("all_done");
402 data_source->Flush(1 /* FlushRequestId */, []() {});
403
404 std::vector<protos::gen::ProcessStats::Process> processes;
405 auto trace = writer_raw_->GetAllTracePackets();
406 for (const auto& packet : trace) {
407 for (const auto& process : packet.process_stats().processes()) {
408 processes.push_back(process);
409 }
410 }
411 ASSERT_EQ(processes.size(), kNumIters * base::ArraySize(kPids));
412 iter = 0;
413 for (const auto& proc_counters : processes) {
414 int32_t pid = proc_counters.pid();
415 ASSERT_EQ(static_cast<int>(proc_counters.vm_size_kb()),
416 pid * 100 + iter * 10 + 1);
417 ASSERT_EQ(static_cast<int>(proc_counters.vm_rss_kb()),
418 pid * 100 + iter * 10 + 2);
419 ASSERT_EQ(static_cast<int>(proc_counters.oom_score_adj()),
420 pid * 100 + iter * 10 + 3);
421 ASSERT_EQ(proc_counters.fds().size(), base::ArraySize(kFds));
422 for (const auto& fd_path : proc_counters.fds()) {
423 ASSERT_THAT(kFds, Contains(fd_path.fd()));
424 ASSERT_EQ(fd_path.path(), kDevice);
425 }
426 if (pid == kPids[base::ArraySize(kPids) - 1])
427 iter++;
428 }
429
430 // Cleanup |fake_proc|. TempDir checks that the directory is empty.
431 for (auto path = links_to_delete.rbegin(); path != links_to_delete.rend();
432 path++)
433 unlink(path->c_str());
434 for (auto path = dirs_to_delete.rbegin(); path != dirs_to_delete.rend();
435 path++)
436 base::Rmdir(*path);
437 }
438
TEST_F(ProcessStatsDataSourceTest,CacheProcessStats)439 TEST_F(ProcessStatsDataSourceTest, CacheProcessStats) {
440 DataSourceConfig ds_config;
441 ProcessStatsConfig cfg;
442 cfg.set_proc_stats_poll_ms(105);
443 cfg.set_proc_stats_cache_ttl_ms(220);
444 cfg.add_quirks(ProcessStatsConfig::DISABLE_ON_DEMAND);
445 ds_config.set_process_stats_config_raw(cfg.SerializeAsString());
446 auto data_source = GetProcessStatsDataSource(ds_config);
447
448 // Populate a fake /proc/ directory.
449 auto fake_proc = base::TempDir::Create();
450 const int kPid = 1;
451
452 base::StackString<256> path("%s/%d", fake_proc.path().c_str(), kPid);
453 mkdir(path.c_str(), 0755);
454
455 auto checkpoint = task_runner_.CreateCheckpoint("all_done");
456
457 EXPECT_CALL(*data_source, OpenProcDir()).WillRepeatedly(Invoke([&fake_proc] {
458 return base::ScopedDir(opendir(fake_proc.path().c_str()));
459 }));
460
461 const int kNumIters = 4;
462 int iter = 0;
463 EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "status"))
464 .WillRepeatedly(Invoke([checkpoint](int32_t p, const std::string&) {
465 base::StackString<1024> ret(
466 "Name: pid_10\nVmSize: %d kB\nVmRSS:\t%d kB\n", p * 100 + 1,
467 p * 100 + 2);
468 return ret.ToStdString();
469 }));
470
471 EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "oom_score_adj"))
472 .WillRepeatedly(
473 Invoke([checkpoint, &iter](int32_t inner_pid, const std::string&) {
474 if (++iter == kNumIters)
475 checkpoint();
476 return std::to_string(inner_pid * 100);
477 }));
478
479 data_source->Start();
480 task_runner_.RunUntilCheckpoint("all_done");
481 data_source->Flush(1 /* FlushRequestId */, []() {});
482
483 std::vector<protos::gen::ProcessStats::Process> processes;
484 auto trace = writer_raw_->GetAllTracePackets();
485 for (const auto& packet : trace) {
486 for (const auto& process : packet.process_stats().processes()) {
487 processes.push_back(process);
488 }
489 }
490 // We should get two counter events because:
491 // a) emissions happen at 0ms, 105ms, 210ms, 315ms
492 // b) clear events happen at 220ms, 440ms...
493 // Therefore, we should see the emissions at 0ms and 315ms.
494 ASSERT_EQ(processes.size(), 2u);
495 for (const auto& proc_counters : processes) {
496 ASSERT_EQ(proc_counters.pid(), kPid);
497 ASSERT_EQ(static_cast<int>(proc_counters.vm_size_kb()), kPid * 100 + 1);
498 ASSERT_EQ(static_cast<int>(proc_counters.vm_rss_kb()), kPid * 100 + 2);
499 ASSERT_EQ(static_cast<int>(proc_counters.oom_score_adj()), kPid * 100);
500 }
501
502 // Cleanup |fake_proc|. TempDir checks that the directory is empty.
503 base::Rmdir(path.ToStdString());
504 }
505
TEST_F(ProcessStatsDataSourceTest,NamespacedProcess)506 TEST_F(ProcessStatsDataSourceTest, NamespacedProcess) {
507 auto data_source = GetProcessStatsDataSource(DataSourceConfig());
508 EXPECT_CALL(*data_source, ReadProcPidFile(42, "status"))
509 .WillOnce(Return(
510 "Name: foo\nTgid:\t42\nPid: 42\nPPid: 17\nNSpid:\t42\t2\n"));
511 EXPECT_CALL(*data_source, ReadProcPidFile(42, "cmdline"))
512 .WillOnce(Return(std::string("foo\0bar\0baz\0", 12)));
513
514 EXPECT_CALL(*data_source, ReadProcPidFile(43, "status"))
515 .WillOnce(Return(
516 "Name: foo\nTgid:\t42\nPid: 43\nPPid: 17\nNSpid:\t43\t3\n"));
517
518 // It's possible that OnPids() is called with a non-main thread is seen before
519 // the main thread for a process. When this happens, the data source
520 // will WriteProcess(42) first and then WriteThread(43).
521 data_source->OnPids({43});
522 data_source->OnPids({42}); // This will be a no-op.
523
524 auto trace = writer_raw_->GetAllTracePackets();
525 ASSERT_EQ(trace.size(), 1u);
526 auto ps_tree = trace[0].process_tree();
527 ASSERT_EQ(ps_tree.processes_size(), 1);
528 auto first_process = ps_tree.processes()[0];
529 ASSERT_EQ(first_process.pid(), 42);
530 ASSERT_EQ(first_process.ppid(), 17);
531 auto nspid = first_process.nspid();
532 EXPECT_THAT(nspid, ElementsAre(2));
533
534 ASSERT_EQ(ps_tree.threads_size(), 1);
535 auto first_thread = ps_tree.threads()[0];
536 ASSERT_EQ(first_thread.tid(), 43);
537 ASSERT_EQ(first_thread.tgid(), 42);
538 auto nstid = first_thread.nstid();
539 EXPECT_THAT(nstid, ElementsAre(3));
540 }
541
TEST_F(ProcessStatsDataSourceTest,ScanSmapsRollupIsOn)542 TEST_F(ProcessStatsDataSourceTest, ScanSmapsRollupIsOn) {
543 DataSourceConfig ds_config;
544 ProcessStatsConfig cfg;
545 cfg.set_proc_stats_poll_ms(1);
546 cfg.set_resolve_process_fds(true);
547 cfg.set_scan_smaps_rollup(true);
548 cfg.add_quirks(ProcessStatsConfig::DISABLE_ON_DEMAND);
549 ds_config.set_process_stats_config_raw(cfg.SerializeAsString());
550 auto data_source = GetProcessStatsDataSource(ds_config);
551
552 // Populate a fake /proc/ directory.
553 auto fake_proc = base::TempDir::Create();
554 const int kPids[] = {1, 2};
555 std::vector<std::string> dirs_to_delete;
556 for (int pid : kPids) {
557 base::StackString<256> path("%s/%d", fake_proc.path().c_str(), pid);
558 dirs_to_delete.push_back(path.ToStdString());
559 EXPECT_EQ(mkdir(path.c_str(), 0755), 0)
560 << "mkdir('" << path.c_str() << "') failed";
561 }
562
563 auto checkpoint = task_runner_.CreateCheckpoint("all_done");
564 const auto fake_proc_path = fake_proc.path();
565 EXPECT_CALL(*data_source, OpenProcDir())
566 .WillRepeatedly(Invoke([&fake_proc_path] {
567 return base::ScopedDir(opendir(fake_proc_path.c_str()));
568 }));
569 EXPECT_CALL(*data_source, GetProcMountpoint())
570 .WillRepeatedly(
571 Invoke([&fake_proc_path] { return fake_proc_path.c_str(); }));
572
573 const int kNumIters = 4;
574 int iter = 0;
575 for (int pid : kPids) {
576 EXPECT_CALL(*data_source, ReadProcPidFile(pid, "status"))
577 .WillRepeatedly(
578 Invoke([checkpoint, &iter](int32_t p, const std::string&) {
579 base::StackString<1024> ret(
580 "Name: pid_10\nVmSize: %d kB\nVmRSS:\t%d kB\n",
581 p * 100 + iter * 10 + 1, p * 100 + iter * 10 + 2);
582 return ret.ToStdString();
583 }));
584 EXPECT_CALL(*data_source, ReadProcPidFile(pid, "smaps_rollup"))
585 .WillRepeatedly(
586 Invoke([checkpoint, &iter](int32_t p, const std::string&) {
587 base::StackString<1024> ret(
588 "Name: pid_10\nRss: %d kB\nPss:\t%d kB\n",
589 p * 100 + iter * 10 + 4, p * 100 + iter * 10 + 5);
590 return ret.ToStdString();
591 }));
592
593 EXPECT_CALL(*data_source, ReadProcPidFile(pid, "oom_score_adj"))
594 .WillRepeatedly(Invoke(
595 [checkpoint, kPids, &iter](int32_t inner_pid, const std::string&) {
596 auto oom_score = inner_pid * 100 + iter * 10 + 3;
597 if (inner_pid == kPids[base::ArraySize(kPids) - 1]) {
598 if (++iter == kNumIters)
599 checkpoint();
600 }
601 return std::to_string(oom_score);
602 }));
603 }
604
605 data_source->Start();
606 task_runner_.RunUntilCheckpoint("all_done");
607 data_source->Flush(1 /* FlushRequestId */, []() {});
608
609 std::vector<protos::gen::ProcessStats::Process> processes;
610 auto trace = writer_raw_->GetAllTracePackets();
611 for (const auto& packet : trace) {
612 for (const auto& process : packet.process_stats().processes()) {
613 processes.push_back(process);
614 }
615 }
616 ASSERT_EQ(processes.size(), kNumIters * base::ArraySize(kPids));
617 iter = 0;
618 for (const auto& proc_counters : processes) {
619 int32_t pid = proc_counters.pid();
620 ASSERT_EQ(static_cast<int>(proc_counters.smr_rss_kb()),
621 pid * 100 + iter * 10 + 4);
622 ASSERT_EQ(static_cast<int>(proc_counters.smr_pss_kb()),
623 pid * 100 + iter * 10 + 5);
624 if (pid == kPids[base::ArraySize(kPids) - 1])
625 iter++;
626 }
627 for (auto path = dirs_to_delete.rbegin(); path != dirs_to_delete.rend();
628 path++)
629 base::Rmdir(*path);
630 }
631
632 } // namespace
633 } // namespace perfetto
634