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