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