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