• 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 "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