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