1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/process/process_metrics.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <memory>
11 #include <sstream>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "base/command_line.h"
17 #include "base/files/file.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/files/scoped_temp_dir.h"
21 #include "base/functional/bind.h"
22 #include "base/memory/shared_memory_mapping.h"
23 #include "base/memory/writable_shared_memory_region.h"
24 #include "base/process/launch.h"
25 #include "base/process/process.h"
26 #include "base/process/process_handle.h"
27 #include "base/ranges/algorithm.h"
28 #include "base/strings/string_number_conversions.h"
29 #include "base/strings/string_util.h"
30 #include "base/strings/stringprintf.h"
31 #include "base/system/sys_info.h"
32 #include "base/test/gmock_expected_support.h"
33 #include "base/test/gtest_util.h"
34 #include "base/test/multiprocess_test.h"
35 #include "base/test/test_timeouts.h"
36 #include "base/threading/thread.h"
37 #include "base/types/expected.h"
38 #include "build/blink_buildflags.h"
39 #include "build/build_config.h"
40 #include "build/chromeos_buildflags.h"
41 #include "testing/gmock/include/gmock/gmock.h"
42 #include "testing/gtest/include/gtest/gtest.h"
43 #include "testing/multiprocess_func_list.h"
44
45 #if BUILDFLAG(IS_APPLE)
46 #include <sys/mman.h>
47 #endif
48
49 #if BUILDFLAG(IS_MAC)
50 #include <mach/mach.h>
51
52 #include "base/apple/mach_logging.h"
53 #include "base/apple/scoped_mach_port.h"
54 #include "base/mac/mach_port_rendezvous.h"
55 #include "base/process/port_provider_mac.h"
56 #endif
57
58 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
59 BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_WIN) || \
60 BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_APPLE)
61 #define ENABLE_CPU_TESTS 1
62 #else
63 #define ENABLE_CPU_TESTS 0
64 #endif
65
66 namespace base::debug {
67
68 namespace {
69
70 using base::test::ErrorIs;
71 using base::test::ValueIs;
72 using ::testing::_;
73 using ::testing::AssertionFailure;
74 using ::testing::AssertionResult;
75 using ::testing::AssertionSuccess;
76 using ::testing::Ge;
77
78 #if ENABLE_CPU_TESTS
79
BusyWork(std::vector<std::string> * vec)80 void BusyWork(std::vector<std::string>* vec) {
81 int64_t test_value = 0;
82 for (int i = 0; i < 100000; ++i) {
83 ++test_value;
84 vec->push_back(NumberToString(test_value));
85 }
86 }
87
88 // Tests that GetCumulativeCPUUsage() returns a valid result that's equal to or
89 // greater than `prev_cpu_usage`, and returns the result. If
90 // GetCumulativeCPUUsage() returns an error, records a failed expectation and
91 // returns an empty TimeDelta so that each test doesn't need to check for
92 // nullopt repeatedly.
TestCumulativeCPU(ProcessMetrics * metrics,TimeDelta prev_cpu_usage)93 TimeDelta TestCumulativeCPU(ProcessMetrics* metrics, TimeDelta prev_cpu_usage) {
94 const base::expected<TimeDelta, ProcessCPUUsageError> current_cpu_usage =
95 metrics->GetCumulativeCPUUsage();
96 EXPECT_THAT(current_cpu_usage, ValueIs(Ge(prev_cpu_usage)));
97 EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ValueIs(Ge(0.0)));
98 return current_cpu_usage.value_or(TimeDelta());
99 }
100
101 #endif // ENABLE_CPU_TESTS
102
103 // Helper to deal with Mac process launching complexity. On other platforms this
104 // is just a thin wrapper around SpawnMultiProcessTestChild.
105 class TestChildLauncher {
106 public:
107 TestChildLauncher() = default;
108 ~TestChildLauncher() = default;
109
110 TestChildLauncher(const TestChildLauncher&) = delete;
111 TestChildLauncher& operator=(const TestChildLauncher&) = delete;
112
113 // Returns a reference to the command line for the child process. This can be
114 // used to add extra parameters before calling SpawnChildProcess().
command_line()115 CommandLine& command_line() { return command_line_; }
116
117 // Returns a reference to the child process object, which will be invalid
118 // until SpawnChildProcess() is called.
child_process()119 Process& child_process() { return child_process_; }
120
121 // Spawns a multiprocess test child to execute the function `procname`.
122 AssertionResult SpawnChildProcess(const std::string& procname);
123
124 // Returns a ProcessMetrics object for the child process created by
125 // SpawnChildProcess().
126 std::unique_ptr<ProcessMetrics> CreateChildProcessMetrics();
127
128 // Terminates the child process created by SpawnChildProcess(). Returns true
129 // if the process successfully terminates within the allowed time.
130 bool TerminateChildProcess();
131
132 // Called from the child process to send data back to the parent if needed.
133 static void NotifyParent();
134
135 private:
136 CommandLine command_line_ = GetMultiProcessTestChildBaseCommandLine();
137 Process child_process_;
138
139 #if BUILDFLAG(IS_MAC)
140 class TestChildPortProvider;
141 std::unique_ptr<TestChildPortProvider> port_provider_;
142 #endif
143 };
144
145 #if BUILDFLAG(IS_MAC)
146
147 // Adapted from base/mac/mach_port_rendezvous_unittest.cc and
148 // https://mw.foldr.org/posts/computers/macosx/task-info-fun-with-mach/
149
150 constexpr MachPortsForRendezvous::key_type kTestChildRendezvousKey = 'test';
151
152 // A PortProvider that tracks child processes spawned by TestChildLauncher.
153 class TestChildLauncher::TestChildPortProvider final : public PortProvider {
154 public:
TestChildPortProvider(ProcessHandle handle,apple::ScopedMachSendRight port)155 TestChildPortProvider(ProcessHandle handle, apple::ScopedMachSendRight port)
156 : handle_(handle), port_(std::move(port)) {}
157
158 ~TestChildPortProvider() final = default;
159
160 TestChildPortProvider(const TestChildPortProvider&) = delete;
161 TestChildPortProvider& operator=(const TestChildPortProvider&) = delete;
162
TaskForHandle(ProcessHandle process_handle) const163 mach_port_t TaskForHandle(ProcessHandle process_handle) const final {
164 return process_handle == handle_ ? port_.get() : MACH_PORT_NULL;
165 }
166
167 private:
168 ProcessHandle handle_;
169 apple::ScopedMachSendRight port_;
170 };
171
SpawnChildProcess(const std::string & procname)172 AssertionResult TestChildLauncher::SpawnChildProcess(
173 const std::string& procname) {
174 // Allocate a port for the parent to receive details from the child process.
175 apple::ScopedMachReceiveRight receive_port;
176 if (!apple::CreateMachPort(&receive_port, nullptr)) {
177 return AssertionFailure() << "Failed to allocate receive port";
178 }
179
180 // Pass the sending end of the port to the child.
181 LaunchOptions options = LaunchOptionsForTest();
182 options.mach_ports_for_rendezvous.emplace(
183 kTestChildRendezvousKey,
184 MachRendezvousPort(receive_port.get(), MACH_MSG_TYPE_MAKE_SEND));
185 child_process_ =
186 SpawnMultiProcessTestChild(procname, command_line_, std::move(options));
187 if (!child_process_.IsValid()) {
188 return AssertionFailure() << "Failed to launch child process.";
189 }
190
191 // Wait for the child to send back its mach_task_self().
192 struct : mach_msg_base_t {
193 mach_msg_port_descriptor_t task_port;
194 mach_msg_trailer_t trailer;
195 } msg{};
196 kern_return_t kr =
197 mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg),
198 receive_port.get(),
199 TestTimeouts::action_timeout().InMilliseconds(), MACH_PORT_NULL);
200 if (kr != KERN_SUCCESS) {
201 return AssertionFailure()
202 << "Failed to read mach_task_self from child process: "
203 << mach_error_string(kr);
204 }
205 port_provider_ = std::make_unique<TestChildPortProvider>(
206 child_process_.Handle(), apple::ScopedMachSendRight(msg.task_port.name));
207 return AssertionSuccess();
208 }
209
CreateChildProcessMetrics()210 std::unique_ptr<ProcessMetrics> TestChildLauncher::CreateChildProcessMetrics() {
211 #if BUILDFLAG(IS_MAC)
212 return ProcessMetrics::CreateProcessMetrics(child_process_.Handle(),
213 port_provider_.get());
214 #else
215 return ProcessMetrics::CreateProcessMetrics(child_process_.Handle());
216 #endif
217 }
218
TerminateChildProcess()219 bool TestChildLauncher::TerminateChildProcess() {
220 return TerminateMultiProcessTestChild(child_process_, /*exit_code=*/0,
221 /*wait=*/true);
222 }
223
224 // static
NotifyParent()225 void TestChildLauncher::NotifyParent() {
226 auto* client = MachPortRendezvousClient::GetInstance();
227 ASSERT_TRUE(client);
228 apple::ScopedMachSendRight send_port =
229 client->TakeSendRight(kTestChildRendezvousKey);
230 ASSERT_TRUE(send_port.is_valid());
231
232 // Send mach_task_self to the parent process so that it can use the port to
233 // create ProcessMetrics.
234 struct : mach_msg_base_t {
235 mach_msg_port_descriptor_t task_port;
236 } msg{};
237 msg.header.msgh_bits =
238 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) | MACH_MSGH_BITS_COMPLEX;
239 msg.header.msgh_remote_port = send_port.get();
240 msg.header.msgh_size = sizeof(msg);
241 msg.body.msgh_descriptor_count = 1;
242 msg.task_port.name = mach_task_self();
243 msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
244 msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
245 kern_return_t kr =
246 mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0,
247 MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
248 MACH_CHECK(kr == KERN_SUCCESS, kr);
249 }
250
251 #else
252
SpawnChildProcess(const std::string & procname)253 AssertionResult TestChildLauncher::SpawnChildProcess(
254 const std::string& procname) {
255 child_process_ = SpawnMultiProcessTestChild(procname, command_line_,
256 LaunchOptionsForTest());
257 return child_process_.IsValid()
258 ? AssertionSuccess()
259 : AssertionFailure() << "Failed to launch child process.";
260 }
261
CreateChildProcessMetrics()262 std::unique_ptr<ProcessMetrics> TestChildLauncher::CreateChildProcessMetrics() {
263 return ProcessMetrics::CreateProcessMetrics(child_process_.Handle());
264 }
265
TerminateChildProcess()266 bool TestChildLauncher::TerminateChildProcess() {
267 [[maybe_unused]] const ProcessHandle child_handle = child_process_.Handle();
268 if (!TerminateMultiProcessTestChild(child_process_, /*exit_code=*/0,
269 /*wait=*/true)) {
270 return false;
271 }
272 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
273 // After the process exits, ProcessMetrics races to read /proc/<pid>/stat
274 // before it's deleted. Wait until it's definitely gone.
275 const auto stat_path = FilePath(FILE_PATH_LITERAL("/proc"))
276 .AppendASCII(NumberToString(child_handle))
277 .Append(FILE_PATH_LITERAL("stat"));
278
279 while (PathExists(stat_path)) {
280 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
281 }
282 #endif
283 return true;
284 }
285
286 // static
NotifyParent()287 void TestChildLauncher::NotifyParent() {
288 // Do nothing.
289 }
290
291 #endif // BUILDFLAG(IS_MAC)
292
293 } // namespace
294
295 // Tests for SystemMetrics.
296 // Exists as a class so it can be a friend of SystemMetrics.
297 class SystemMetricsTest : public testing::Test {
298 public:
299 SystemMetricsTest() = default;
300
301 SystemMetricsTest(const SystemMetricsTest&) = delete;
302 SystemMetricsTest& operator=(const SystemMetricsTest&) = delete;
303 };
304
305 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST_F(SystemMetricsTest,IsValidDiskName)306 TEST_F(SystemMetricsTest, IsValidDiskName) {
307 const char invalid_input1[] = "";
308 const char invalid_input2[] = "s";
309 const char invalid_input3[] = "sdz+";
310 const char invalid_input4[] = "hda0";
311 const char invalid_input5[] = "mmcbl";
312 const char invalid_input6[] = "mmcblka";
313 const char invalid_input7[] = "mmcblkb";
314 const char invalid_input8[] = "mmmblk0";
315
316 EXPECT_FALSE(IsValidDiskName(invalid_input1));
317 EXPECT_FALSE(IsValidDiskName(invalid_input2));
318 EXPECT_FALSE(IsValidDiskName(invalid_input3));
319 EXPECT_FALSE(IsValidDiskName(invalid_input4));
320 EXPECT_FALSE(IsValidDiskName(invalid_input5));
321 EXPECT_FALSE(IsValidDiskName(invalid_input6));
322 EXPECT_FALSE(IsValidDiskName(invalid_input7));
323 EXPECT_FALSE(IsValidDiskName(invalid_input8));
324
325 const char valid_input1[] = "sda";
326 const char valid_input2[] = "sdaaaa";
327 const char valid_input3[] = "hdz";
328 const char valid_input4[] = "mmcblk0";
329 const char valid_input5[] = "mmcblk999";
330
331 EXPECT_TRUE(IsValidDiskName(valid_input1));
332 EXPECT_TRUE(IsValidDiskName(valid_input2));
333 EXPECT_TRUE(IsValidDiskName(valid_input3));
334 EXPECT_TRUE(IsValidDiskName(valid_input4));
335 EXPECT_TRUE(IsValidDiskName(valid_input5));
336 }
337
TEST_F(SystemMetricsTest,ParseMeminfo)338 TEST_F(SystemMetricsTest, ParseMeminfo) {
339 SystemMemoryInfoKB meminfo;
340 const char invalid_input1[] = "abc";
341 const char invalid_input2[] = "MemTotal:";
342 // Partial file with no MemTotal
343 const char invalid_input3[] =
344 "MemFree: 3913968 kB\n"
345 "Buffers: 2348340 kB\n"
346 "Cached: 49071596 kB\n"
347 "SwapCached: 12 kB\n"
348 "Active: 36393900 kB\n"
349 "Inactive: 21221496 kB\n"
350 "Active(anon): 5674352 kB\n"
351 "Inactive(anon): 633992 kB\n";
352 EXPECT_FALSE(ParseProcMeminfo(invalid_input1, &meminfo));
353 EXPECT_FALSE(ParseProcMeminfo(invalid_input2, &meminfo));
354 EXPECT_FALSE(ParseProcMeminfo(invalid_input3, &meminfo));
355
356 const char valid_input1[] =
357 "MemTotal: 3981504 kB\n"
358 "MemFree: 140764 kB\n"
359 "MemAvailable: 535413 kB\n"
360 "Buffers: 116480 kB\n"
361 "Cached: 406160 kB\n"
362 "SwapCached: 21304 kB\n"
363 "Active: 3152040 kB\n"
364 "Inactive: 472856 kB\n"
365 "Active(anon): 2972352 kB\n"
366 "Inactive(anon): 270108 kB\n"
367 "Active(file): 179688 kB\n"
368 "Inactive(file): 202748 kB\n"
369 "Unevictable: 0 kB\n"
370 "Mlocked: 0 kB\n"
371 "SwapTotal: 5832280 kB\n"
372 "SwapFree: 3672368 kB\n"
373 "Dirty: 184 kB\n"
374 "Writeback: 0 kB\n"
375 "AnonPages: 3101224 kB\n"
376 "Mapped: 142296 kB\n"
377 "Shmem: 140204 kB\n"
378 "Slab: 54212 kB\n"
379 "SReclaimable: 30936 kB\n"
380 "SUnreclaim: 23276 kB\n"
381 "KernelStack: 2464 kB\n"
382 "PageTables: 24812 kB\n"
383 "NFS_Unstable: 0 kB\n"
384 "Bounce: 0 kB\n"
385 "WritebackTmp: 0 kB\n"
386 "CommitLimit: 7823032 kB\n"
387 "Committed_AS: 7973536 kB\n"
388 "VmallocTotal: 34359738367 kB\n"
389 "VmallocUsed: 375940 kB\n"
390 "VmallocChunk: 34359361127 kB\n"
391 "DirectMap4k: 72448 kB\n"
392 "DirectMap2M: 4061184 kB\n";
393 // output from a much older kernel where the Active and Inactive aren't
394 // broken down into anon and file and Huge Pages are enabled
395 const char valid_input2[] =
396 "MemTotal: 255908 kB\n"
397 "MemFree: 69936 kB\n"
398 "Buffers: 15812 kB\n"
399 "Cached: 115124 kB\n"
400 "SwapCached: 0 kB\n"
401 "Active: 92700 kB\n"
402 "Inactive: 63792 kB\n"
403 "HighTotal: 0 kB\n"
404 "HighFree: 0 kB\n"
405 "LowTotal: 255908 kB\n"
406 "LowFree: 69936 kB\n"
407 "SwapTotal: 524280 kB\n"
408 "SwapFree: 524200 kB\n"
409 "Dirty: 4 kB\n"
410 "Writeback: 0 kB\n"
411 "Mapped: 42236 kB\n"
412 "Slab: 25912 kB\n"
413 "Committed_AS: 118680 kB\n"
414 "PageTables: 1236 kB\n"
415 "VmallocTotal: 3874808 kB\n"
416 "VmallocUsed: 1416 kB\n"
417 "VmallocChunk: 3872908 kB\n"
418 "HugePages_Total: 0\n"
419 "HugePages_Free: 0\n"
420 "Hugepagesize: 4096 kB\n";
421
422 EXPECT_TRUE(ParseProcMeminfo(valid_input1, &meminfo));
423 EXPECT_EQ(meminfo.total, 3981504);
424 EXPECT_EQ(meminfo.free, 140764);
425 EXPECT_EQ(meminfo.available, 535413);
426 EXPECT_EQ(meminfo.buffers, 116480);
427 EXPECT_EQ(meminfo.cached, 406160);
428 EXPECT_EQ(meminfo.active_anon, 2972352);
429 EXPECT_EQ(meminfo.active_file, 179688);
430 EXPECT_EQ(meminfo.inactive_anon, 270108);
431 EXPECT_EQ(meminfo.inactive_file, 202748);
432 EXPECT_EQ(meminfo.swap_total, 5832280);
433 EXPECT_EQ(meminfo.swap_free, 3672368);
434 EXPECT_EQ(meminfo.dirty, 184);
435 EXPECT_EQ(meminfo.reclaimable, 30936);
436 #if BUILDFLAG(IS_CHROMEOS)
437 EXPECT_EQ(meminfo.shmem, 140204);
438 EXPECT_EQ(meminfo.slab, 54212);
439 #endif
440 EXPECT_EQ(355725u,
441 base::SysInfo::AmountOfAvailablePhysicalMemory(meminfo) / 1024);
442 // Simulate as if there is no MemAvailable.
443 meminfo.available = 0;
444 EXPECT_EQ(374448u,
445 base::SysInfo::AmountOfAvailablePhysicalMemory(meminfo) / 1024);
446 meminfo = {};
447 EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo));
448 EXPECT_EQ(meminfo.total, 255908);
449 EXPECT_EQ(meminfo.free, 69936);
450 EXPECT_EQ(meminfo.available, 0);
451 EXPECT_EQ(meminfo.buffers, 15812);
452 EXPECT_EQ(meminfo.cached, 115124);
453 EXPECT_EQ(meminfo.swap_total, 524280);
454 EXPECT_EQ(meminfo.swap_free, 524200);
455 EXPECT_EQ(meminfo.dirty, 4);
456 EXPECT_EQ(69936u,
457 base::SysInfo::AmountOfAvailablePhysicalMemory(meminfo) / 1024);
458
459 // output from a system with a large page cache, to catch arithmetic errors
460 // that incorrectly assume free + buffers + cached <= total. (Copied from
461 // ash/components/arc/test/data/mem_profile/16G.)
462 const char large_cache_input[] =
463 "MemTotal: 18025572 kB\n"
464 "MemFree: 13150176 kB\n"
465 "MemAvailable: 15447672 kB\n"
466 "Buffers: 1524852 kB\n"
467 "Cached: 12645260 kB\n"
468 "SwapCached: 0 kB\n"
469 "Active: 2572904 kB\n"
470 "Inactive: 1064976 kB\n"
471 "Active(anon): 1047836 kB\n"
472 "Inactive(anon): 11736 kB\n"
473 "Active(file): 1525068 kB\n"
474 "Inactive(file): 1053240 kB\n"
475 "Unevictable: 611904 kB\n"
476 "Mlocked: 32884 kB\n"
477 "SwapTotal: 11756208 kB\n"
478 "SwapFree: 11756208 kB\n"
479 "Dirty: 4152 kB\n"
480 "Writeback: 0 kB\n"
481 "AnonPages: 1079660 kB\n"
482 "Mapped: 782152 kB\n"
483 "Shmem: 591820 kB\n"
484 "Slab: 366104 kB\n"
485 "SReclaimable: 254356 kB\n"
486 "SUnreclaim: 111748 kB\n"
487 "KernelStack: 22652 kB\n"
488 "PageTables: 41540 kB\n"
489 "NFS_Unstable: 0 kB\n"
490 "Bounce: 0 kB\n"
491 "WritebackTmp: 0 kB\n"
492 "CommitLimit: 15768992 kB\n"
493 "Committed_AS: 36120244 kB\n"
494 "VmallocTotal: 34359738367 kB\n"
495 "VmallocUsed: 0 kB\n"
496 "VmallocChunk: 0 kB\n"
497 "Percpu: 3328 kB\n"
498 "AnonHugePages: 32768 kB\n"
499 "ShmemHugePages: 0 kB\n"
500 "ShmemPmdMapped: 0 kB\n"
501 "DirectMap4k: 293036 kB\n"
502 "DirectMap2M: 6918144 kB\n"
503 "DirectMap1G: 2097152 kB\n";
504
505 meminfo = {};
506 EXPECT_TRUE(ParseProcMeminfo(large_cache_input, &meminfo));
507 EXPECT_EQ(meminfo.total, 18025572);
508 EXPECT_EQ(meminfo.free, 13150176);
509 EXPECT_EQ(meminfo.buffers, 1524852);
510 EXPECT_EQ(meminfo.cached, 12645260);
511 EXPECT_EQ(GetSystemCommitChargeFromMeminfo(meminfo), 0u);
512 }
513
TEST_F(SystemMetricsTest,ParseVmstat)514 TEST_F(SystemMetricsTest, ParseVmstat) {
515 VmStatInfo vmstat;
516 // Part of vmstat from a 4.19 kernel.
517 const char valid_input1[] =
518 "pgpgin 2358216\n"
519 "pgpgout 296072\n"
520 "pswpin 345219\n"
521 "pswpout 2605828\n"
522 "pgalloc_dma32 8380235\n"
523 "pgalloc_normal 3384525\n"
524 "pgalloc_movable 0\n"
525 "allocstall_dma32 0\n"
526 "allocstall_normal 2028\n"
527 "allocstall_movable 32559\n"
528 "pgskip_dma32 0\n"
529 "pgskip_normal 0\n"
530 "pgskip_movable 0\n"
531 "pgfree 11802722\n"
532 "pgactivate 894917\n"
533 "pgdeactivate 3255711\n"
534 "pglazyfree 48\n"
535 "pgfault 10043657\n"
536 "pgmajfault 358901\n"
537 "pgmajfault_s 2100\n"
538 "pgmajfault_a 343211\n"
539 "pgmajfault_f 13590\n"
540 "pglazyfreed 0\n"
541 "pgrefill 3429488\n"
542 "pgsteal_kswapd 1466893\n"
543 "pgsteal_direct 1771759\n"
544 "pgscan_kswapd 1907332\n"
545 "pgscan_direct 2118930\n"
546 "pgscan_direct_throttle 154\n"
547 "pginodesteal 3176\n"
548 "slabs_scanned 293804\n"
549 "kswapd_inodesteal 16753\n"
550 "kswapd_low_wmark_hit_quickly 10\n"
551 "kswapd_high_wmark_hit_quickly 423\n"
552 "pageoutrun 441\n"
553 "pgrotated 1636\n"
554 "drop_pagecache 0\n"
555 "drop_slab 0\n"
556 "oom_kill 18\n";
557 const char valid_input2[] =
558 "pgpgin 2606135\n"
559 "pgpgout 1359128\n"
560 "pswpin 899959\n"
561 "pswpout 19761244\n"
562 "pgalloc_dma 31\n"
563 "pgalloc_dma32 18139339\n"
564 "pgalloc_normal 44085950\n"
565 "pgalloc_movable 0\n"
566 "allocstall_dma 0\n"
567 "allocstall_dma32 0\n"
568 "allocstall_normal 18881\n"
569 "allocstall_movable 169527\n"
570 "pgskip_dma 0\n"
571 "pgskip_dma32 0\n"
572 "pgskip_normal 0\n"
573 "pgskip_movable 0\n"
574 "pgfree 63060999\n"
575 "pgactivate 1703494\n"
576 "pgdeactivate 20537803\n"
577 "pglazyfree 163\n"
578 "pgfault 45201169\n"
579 "pgmajfault 609626\n"
580 "pgmajfault_s 7488\n"
581 "pgmajfault_a 591793\n"
582 "pgmajfault_f 10345\n"
583 "pglazyfreed 0\n"
584 "pgrefill 20673453\n"
585 "pgsteal_kswapd 11802772\n"
586 "pgsteal_direct 8618160\n"
587 "pgscan_kswapd 12640517\n"
588 "pgscan_direct 9092230\n"
589 "pgscan_direct_throttle 638\n"
590 "pginodesteal 1716\n"
591 "slabs_scanned 2594642\n"
592 "kswapd_inodesteal 67358\n"
593 "kswapd_low_wmark_hit_quickly 52\n"
594 "kswapd_high_wmark_hit_quickly 11\n"
595 "pageoutrun 83\n"
596 "pgrotated 977\n"
597 "drop_pagecache 1\n"
598 "drop_slab 1\n"
599 "oom_kill 1\n"
600 "pgmigrate_success 3202\n"
601 "pgmigrate_fail 795\n";
602 const char valid_input3[] =
603 "pswpin 12\n"
604 "pswpout 901\n"
605 "pgmajfault 18881\n";
606 EXPECT_TRUE(ParseProcVmstat(valid_input1, &vmstat));
607 EXPECT_EQ(345219LU, vmstat.pswpin);
608 EXPECT_EQ(2605828LU, vmstat.pswpout);
609 EXPECT_EQ(358901LU, vmstat.pgmajfault);
610 EXPECT_EQ(18LU, vmstat.oom_kill);
611 EXPECT_TRUE(ParseProcVmstat(valid_input2, &vmstat));
612 EXPECT_EQ(899959LU, vmstat.pswpin);
613 EXPECT_EQ(19761244LU, vmstat.pswpout);
614 EXPECT_EQ(609626LU, vmstat.pgmajfault);
615 EXPECT_EQ(1LU, vmstat.oom_kill);
616 EXPECT_TRUE(ParseProcVmstat(valid_input3, &vmstat));
617 EXPECT_EQ(12LU, vmstat.pswpin);
618 EXPECT_EQ(901LU, vmstat.pswpout);
619 EXPECT_EQ(18881LU, vmstat.pgmajfault);
620 EXPECT_EQ(0LU, vmstat.oom_kill);
621
622 const char missing_pgmajfault_input[] =
623 "pswpin 12\n"
624 "pswpout 901\n";
625 EXPECT_FALSE(ParseProcVmstat(missing_pgmajfault_input, &vmstat));
626 const char empty_input[] = "";
627 EXPECT_FALSE(ParseProcVmstat(empty_input, &vmstat));
628 }
629 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
630 // BUILDFLAG(IS_ANDROID)
631
632 #if ENABLE_CPU_TESTS
633 // Test that ProcessMetrics::GetPlatformIndependentCPUUsage() doesn't return
634 // negative values when the number of threads running on the process decreases
635 // between two successive calls to it.
TEST_F(SystemMetricsTest,TestNoNegativeCpuUsage)636 TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
637 std::unique_ptr<ProcessMetrics> metrics =
638 ProcessMetrics::CreateCurrentProcessMetrics();
639
640 EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ValueIs(Ge(0.0)));
641
642 Thread thread1("thread1");
643 Thread thread2("thread2");
644 Thread thread3("thread3");
645
646 thread1.StartAndWaitForTesting();
647 thread2.StartAndWaitForTesting();
648 thread3.StartAndWaitForTesting();
649
650 ASSERT_TRUE(thread1.IsRunning());
651 ASSERT_TRUE(thread2.IsRunning());
652 ASSERT_TRUE(thread3.IsRunning());
653
654 std::vector<std::string> vec1;
655 std::vector<std::string> vec2;
656 std::vector<std::string> vec3;
657
658 thread1.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec1));
659 thread2.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec2));
660 thread3.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec3));
661
662 TimeDelta prev_cpu_usage = TestCumulativeCPU(metrics.get(), TimeDelta());
663
664 thread1.Stop();
665 prev_cpu_usage = TestCumulativeCPU(metrics.get(), prev_cpu_usage);
666
667 thread2.Stop();
668 prev_cpu_usage = TestCumulativeCPU(metrics.get(), prev_cpu_usage);
669
670 thread3.Stop();
671 prev_cpu_usage = TestCumulativeCPU(metrics.get(), prev_cpu_usage);
672 }
673
674 #if !BUILDFLAG(IS_APPLE)
675
676 // Subprocess to test the child CPU usage.
MULTIPROCESS_TEST_MAIN(CPUUsageChildMain)677 MULTIPROCESS_TEST_MAIN(CPUUsageChildMain) {
678 TestChildLauncher::NotifyParent();
679 // Busy wait until terminated.
680 while (true) {
681 std::vector<std::string> vec;
682 BusyWork(&vec);
683 }
684 }
685
TEST_F(SystemMetricsTest,MeasureChildCpuUsage)686 TEST_F(SystemMetricsTest, MeasureChildCpuUsage) {
687 TestChildLauncher child_launcher;
688 ASSERT_TRUE(child_launcher.SpawnChildProcess("CPUUsageChildMain"));
689 std::unique_ptr<ProcessMetrics> metrics =
690 child_launcher.CreateChildProcessMetrics();
691
692 const TimeDelta cpu_usage1 = TestCumulativeCPU(metrics.get(), TimeDelta());
693 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
694
695 const TimeDelta cpu_usage2 = TestCumulativeCPU(metrics.get(), cpu_usage1);
696 EXPECT_TRUE(cpu_usage2.is_positive());
697
698 ASSERT_TRUE(child_launcher.TerminateChildProcess());
699
700 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
701 // Windows and Fuchsia return final measurements of a process after it exits.
702 TestCumulativeCPU(metrics.get(), cpu_usage2);
703 #else
704 // All other platforms return an error.
705 EXPECT_THAT(metrics->GetCumulativeCPUUsage(), ErrorIs(_));
706 EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ErrorIs(_));
707 #endif
708 }
709
710 #endif // !BUILDFLAG(IS_APPLE)
711
TEST_F(SystemMetricsTest,InvalidProcessCpuUsage)712 TEST_F(SystemMetricsTest, InvalidProcessCpuUsage) {
713 #if BUILDFLAG(IS_MAC)
714 std::unique_ptr<ProcessMetrics> metrics =
715 ProcessMetrics::CreateProcessMetrics(kNullProcessHandle, nullptr);
716 #else
717 std::unique_ptr<ProcessMetrics> metrics =
718 ProcessMetrics::CreateProcessMetrics(kNullProcessHandle);
719 #endif
720 EXPECT_THAT(metrics->GetCumulativeCPUUsage(), ErrorIs(_));
721 EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ErrorIs(_));
722 }
723
724 #endif // ENABLE_CPU_TESTS
725
726 #if BUILDFLAG(IS_CHROMEOS)
TEST_F(SystemMetricsTest,ParseZramMmStat)727 TEST_F(SystemMetricsTest, ParseZramMmStat) {
728 SwapInfo swapinfo;
729
730 const char invalid_input1[] = "aaa";
731 const char invalid_input2[] = "1 2 3 4 5 6";
732 const char invalid_input3[] = "a 2 3 4 5 6 7";
733 EXPECT_FALSE(ParseZramMmStat(invalid_input1, &swapinfo));
734 EXPECT_FALSE(ParseZramMmStat(invalid_input2, &swapinfo));
735 EXPECT_FALSE(ParseZramMmStat(invalid_input3, &swapinfo));
736
737 const char valid_input1[] =
738 "17715200 5008166 566062 0 1225715712 127 183842";
739 EXPECT_TRUE(ParseZramMmStat(valid_input1, &swapinfo));
740 EXPECT_EQ(17715200ULL, swapinfo.orig_data_size);
741 EXPECT_EQ(5008166ULL, swapinfo.compr_data_size);
742 EXPECT_EQ(566062ULL, swapinfo.mem_used_total);
743 }
744
TEST_F(SystemMetricsTest,ParseZramStat)745 TEST_F(SystemMetricsTest, ParseZramStat) {
746 SwapInfo swapinfo;
747
748 const char invalid_input1[] = "aaa";
749 const char invalid_input2[] = "1 2 3 4 5 6 7 8 9 10";
750 const char invalid_input3[] = "a 2 3 4 5 6 7 8 9 10 11";
751 EXPECT_FALSE(ParseZramStat(invalid_input1, &swapinfo));
752 EXPECT_FALSE(ParseZramStat(invalid_input2, &swapinfo));
753 EXPECT_FALSE(ParseZramStat(invalid_input3, &swapinfo));
754
755 const char valid_input1[] =
756 "299 0 2392 0 1 0 8 0 0 0 0";
757 EXPECT_TRUE(ParseZramStat(valid_input1, &swapinfo));
758 EXPECT_EQ(299ULL, swapinfo.num_reads);
759 EXPECT_EQ(1ULL, swapinfo.num_writes);
760 }
761 #endif // BUILDFLAG(IS_CHROMEOS)
762
763 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
764 BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST(SystemMetrics2Test,GetSystemMemoryInfo)765 TEST(SystemMetrics2Test, GetSystemMemoryInfo) {
766 SystemMemoryInfoKB info;
767 EXPECT_TRUE(GetSystemMemoryInfo(&info));
768
769 // Ensure each field received a value.
770 EXPECT_GT(info.total, 0);
771 #if BUILDFLAG(IS_WIN)
772 EXPECT_GT(info.avail_phys, 0);
773 #else
774 EXPECT_GT(info.free, 0);
775 #endif
776 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
777 EXPECT_GT(info.buffers, 0);
778 EXPECT_GT(info.cached, 0);
779 EXPECT_GT(info.active_anon + info.inactive_anon, 0);
780 EXPECT_GT(info.active_file + info.inactive_file, 0);
781 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
782 // BUILDFLAG(IS_ANDROID)
783
784 // All the values should be less than the total amount of memory.
785 #if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_IOS)
786 // TODO(crbug.com/711450): re-enable the following assertion on iOS.
787 EXPECT_LT(info.free, info.total);
788 #endif
789 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
790 EXPECT_LT(info.buffers, info.total);
791 EXPECT_LT(info.cached, info.total);
792 EXPECT_LT(info.active_anon, info.total);
793 EXPECT_LT(info.inactive_anon, info.total);
794 EXPECT_LT(info.active_file, info.total);
795 EXPECT_LT(info.inactive_file, info.total);
796 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
797 // BUILDFLAG(IS_ANDROID)
798
799 #if BUILDFLAG(IS_APPLE)
800 EXPECT_GT(info.file_backed, 0);
801 #endif
802
803 #if BUILDFLAG(IS_CHROMEOS)
804 // Chrome OS exposes shmem.
805 EXPECT_GT(info.shmem, 0);
806 EXPECT_LT(info.shmem, info.total);
807 #endif
808 }
809 #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
810 // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
811
812 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST(ProcessMetricsTest,ParseProcStatCPU)813 TEST(ProcessMetricsTest, ParseProcStatCPU) {
814 // /proc/self/stat for a process running "top".
815 const char kTopStat[] =
816 "960 (top) S 16230 960 16230 34818 960 "
817 "4202496 471 0 0 0 "
818 "12 16 0 0 " // <- These are the goods.
819 "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 "
820 "4246868 140733983044336 18446744073709551615 140244213071219 "
821 "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0";
822 EXPECT_EQ(12 + 16, ParseProcStatCPU(kTopStat));
823
824 // cat /proc/self/stat on a random other machine I have.
825 const char kSelfStat[] =
826 "5364 (cat) R 5354 5364 5354 34819 5364 "
827 "0 142 0 0 0 "
828 "0 0 0 0 " // <- No CPU, apparently.
829 "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 "
830 "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0";
831
832 EXPECT_EQ(0, ParseProcStatCPU(kSelfStat));
833
834 // Some weird long-running process with a weird name that I created for the
835 // purposes of this test.
836 const char kWeirdNameStat[] =
837 "26115 (Hello) You ())) ) R 24614 26115 24614"
838 " 34839 26115 4218880 227 0 0 0 "
839 "5186 11 0 0 "
840 "20 0 1 0 36933953 4296704 90 18446744073709551615 4194304 4196116 "
841 "140735857761568 140735857761160 4195644 0 0 0 0 0 0 0 17 14 0 0 0 0 0 "
842 "6295056 6295616 16519168 140735857770710 140735857770737 "
843 "140735857770737 140735857774557 0";
844 EXPECT_EQ(5186 + 11, ParseProcStatCPU(kWeirdNameStat));
845 }
846 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
847 // BUILDFLAG(IS_ANDROID)
848
849 // Disable on Android because base_unittests runs inside a Dalvik VM that
850 // starts and stop threads (crbug.com/175563).
851 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
852 // http://crbug.com/396455
TEST(ProcessMetricsTest,DISABLED_GetNumberOfThreads)853 TEST(ProcessMetricsTest, DISABLED_GetNumberOfThreads) {
854 const ProcessHandle current = GetCurrentProcessHandle();
855 const int64_t initial_threads = GetNumberOfThreads(current);
856 ASSERT_GT(initial_threads, 0);
857 const int kNumAdditionalThreads = 10;
858 {
859 std::unique_ptr<Thread> my_threads[kNumAdditionalThreads];
860 for (int i = 0; i < kNumAdditionalThreads; ++i) {
861 my_threads[i] = std::make_unique<Thread>("GetNumberOfThreadsTest");
862 my_threads[i]->Start();
863 ASSERT_EQ(GetNumberOfThreads(current), initial_threads + 1 + i);
864 }
865 }
866 // The Thread destructor will stop them.
867 ASSERT_EQ(initial_threads, GetNumberOfThreads(current));
868 }
869 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
870
871 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
872 namespace {
873
874 // Keep these in sync so the GetChildOpenFdCount test can refer to correct test
875 // main.
876 #define ChildMain ChildFdCount
877 #define ChildMainString "ChildFdCount"
878
879 // Command line flag name and file name used for synchronization.
880 const char kTempDirFlag[] = "temp-dir";
881
882 const char kSignalReady[] = "ready";
883 const char kSignalReadyAck[] = "ready-ack";
884 const char kSignalOpened[] = "opened";
885 const char kSignalOpenedAck[] = "opened-ack";
886 const char kSignalClosed[] = "closed";
887
888 const int kChildNumFilesToOpen = 100;
889
SignalEvent(const FilePath & signal_dir,const char * signal_file)890 bool SignalEvent(const FilePath& signal_dir, const char* signal_file) {
891 File file(signal_dir.AppendASCII(signal_file),
892 File::FLAG_CREATE | File::FLAG_WRITE);
893 return file.IsValid();
894 }
895
896 // Check whether an event was signaled.
CheckEvent(const FilePath & signal_dir,const char * signal_file)897 bool CheckEvent(const FilePath& signal_dir, const char* signal_file) {
898 File file(signal_dir.AppendASCII(signal_file),
899 File::FLAG_OPEN | File::FLAG_READ);
900 return file.IsValid();
901 }
902
903 // Busy-wait for an event to be signaled.
WaitForEvent(const FilePath & signal_dir,const char * signal_file)904 void WaitForEvent(const FilePath& signal_dir, const char* signal_file) {
905 while (!CheckEvent(signal_dir, signal_file)) {
906 PlatformThread::Sleep(Milliseconds(10));
907 }
908 }
909
910 // Subprocess to test the number of open file descriptors.
MULTIPROCESS_TEST_MAIN(ChildMain)911 MULTIPROCESS_TEST_MAIN(ChildMain) {
912 TestChildLauncher::NotifyParent();
913
914 CommandLine* command_line = CommandLine::ForCurrentProcess();
915 const FilePath temp_path = command_line->GetSwitchValuePath(kTempDirFlag);
916 CHECK(DirectoryExists(temp_path));
917
918 CHECK(SignalEvent(temp_path, kSignalReady));
919 WaitForEvent(temp_path, kSignalReadyAck);
920
921 std::vector<File> files;
922 for (int i = 0; i < kChildNumFilesToOpen; ++i) {
923 files.emplace_back(temp_path.AppendASCII(StringPrintf("file.%d", i)),
924 File::FLAG_CREATE | File::FLAG_WRITE);
925 }
926
927 CHECK(SignalEvent(temp_path, kSignalOpened));
928 WaitForEvent(temp_path, kSignalOpenedAck);
929
930 files.clear();
931
932 CHECK(SignalEvent(temp_path, kSignalClosed));
933
934 // Wait to be terminated.
935 while (true) {
936 PlatformThread::Sleep(Seconds(1));
937 }
938 }
939
940 } // namespace
941
TEST(ProcessMetricsTest,GetChildOpenFdCount)942 TEST(ProcessMetricsTest, GetChildOpenFdCount) {
943 ScopedTempDir temp_dir;
944 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
945 const FilePath temp_path = temp_dir.GetPath();
946
947 TestChildLauncher child_launcher;
948 child_launcher.command_line().AppendSwitchPath(kTempDirFlag, temp_path);
949 ASSERT_TRUE(child_launcher.SpawnChildProcess(ChildMainString));
950
951 WaitForEvent(temp_path, kSignalReady);
952
953 std::unique_ptr<ProcessMetrics> metrics =
954 child_launcher.CreateChildProcessMetrics();
955
956 const int fd_count = metrics->GetOpenFdCount();
957 EXPECT_GE(fd_count, 0);
958
959 ASSERT_TRUE(SignalEvent(temp_path, kSignalReadyAck));
960 WaitForEvent(temp_path, kSignalOpened);
961
962 EXPECT_EQ(fd_count + kChildNumFilesToOpen, metrics->GetOpenFdCount());
963 ASSERT_TRUE(SignalEvent(temp_path, kSignalOpenedAck));
964
965 WaitForEvent(temp_path, kSignalClosed);
966
967 EXPECT_EQ(fd_count, metrics->GetOpenFdCount());
968
969 ASSERT_TRUE(child_launcher.TerminateChildProcess());
970 }
971
TEST(ProcessMetricsTest,GetOpenFdCount)972 TEST(ProcessMetricsTest, GetOpenFdCount) {
973 std::unique_ptr<ProcessMetrics> metrics =
974 ProcessMetrics::CreateCurrentProcessMetrics();
975
976 ScopedTempDir temp_dir;
977 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
978
979 int fd_count = metrics->GetOpenFdCount();
980 EXPECT_GT(fd_count, 0);
981 File file(temp_dir.GetPath().AppendASCII("file"),
982 File::FLAG_CREATE | File::FLAG_WRITE);
983 int new_fd_count = metrics->GetOpenFdCount();
984 EXPECT_GT(new_fd_count, 0);
985 EXPECT_EQ(new_fd_count, fd_count + 1);
986 }
987 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
988
989 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
990
TEST(ProcessMetricsTestLinux,GetPageFaultCounts)991 TEST(ProcessMetricsTestLinux, GetPageFaultCounts) {
992 std::unique_ptr<ProcessMetrics> process_metrics =
993 ProcessMetrics::CreateCurrentProcessMetrics();
994
995 PageFaultCounts counts;
996 ASSERT_TRUE(process_metrics->GetPageFaultCounts(&counts));
997 ASSERT_GT(counts.minor, 0);
998 ASSERT_GE(counts.major, 0);
999
1000 // Allocate and touch memory. Touching it is required to make sure that the
1001 // page fault count goes up, as memory is typically mapped lazily.
1002 {
1003 const size_t kMappedSize = 4 << 20; // 4 MiB.
1004
1005 WritableSharedMemoryRegion region =
1006 WritableSharedMemoryRegion::Create(kMappedSize);
1007 ASSERT_TRUE(region.IsValid());
1008
1009 WritableSharedMemoryMapping mapping = region.Map();
1010 ASSERT_TRUE(mapping.IsValid());
1011
1012 memset(mapping.memory(), 42, kMappedSize);
1013 }
1014
1015 PageFaultCounts counts_after;
1016 ASSERT_TRUE(process_metrics->GetPageFaultCounts(&counts_after));
1017 ASSERT_GT(counts_after.minor, counts.minor);
1018 ASSERT_GE(counts_after.major, counts.major);
1019 }
1020
TEST(ProcessMetricsTestLinux,GetCumulativeCPUUsagePerThread)1021 TEST(ProcessMetricsTestLinux, GetCumulativeCPUUsagePerThread) {
1022 std::unique_ptr<ProcessMetrics> metrics =
1023 ProcessMetrics::CreateCurrentProcessMetrics();
1024
1025 Thread thread1("thread1");
1026 thread1.StartAndWaitForTesting();
1027 ASSERT_TRUE(thread1.IsRunning());
1028
1029 std::vector<std::string> vec1;
1030 thread1.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec1));
1031
1032 ProcessMetrics::CPUUsagePerThread prev_thread_times;
1033 EXPECT_TRUE(metrics->GetCumulativeCPUUsagePerThread(prev_thread_times));
1034
1035 // Should have at least the test runner thread and the thread spawned above.
1036 EXPECT_GE(prev_thread_times.size(), 2u);
1037 EXPECT_TRUE(ranges::any_of(
1038 prev_thread_times,
1039 [&thread1](const std::pair<PlatformThreadId, base::TimeDelta>& entry) {
1040 return entry.first == thread1.GetThreadId();
1041 }));
1042 EXPECT_TRUE(ranges::any_of(
1043 prev_thread_times,
1044 [](const std::pair<PlatformThreadId, base::TimeDelta>& entry) {
1045 return entry.first == base::PlatformThread::CurrentId();
1046 }));
1047
1048 for (const auto& entry : prev_thread_times) {
1049 EXPECT_GE(entry.second, base::TimeDelta());
1050 }
1051
1052 thread1.Stop();
1053
1054 ProcessMetrics::CPUUsagePerThread current_thread_times;
1055 EXPECT_TRUE(metrics->GetCumulativeCPUUsagePerThread(current_thread_times));
1056
1057 // The stopped thread may still be reported until the kernel cleans it up.
1058 EXPECT_GE(prev_thread_times.size(), 1u);
1059 EXPECT_TRUE(ranges::any_of(
1060 current_thread_times,
1061 [](const std::pair<PlatformThreadId, base::TimeDelta>& entry) {
1062 return entry.first == base::PlatformThread::CurrentId();
1063 }));
1064
1065 // Reported times should not decrease.
1066 for (const auto& entry : current_thread_times) {
1067 auto prev_it = ranges::find_if(
1068 prev_thread_times,
1069 [&entry](
1070 const std::pair<PlatformThreadId, base::TimeDelta>& prev_entry) {
1071 return entry.first == prev_entry.first;
1072 });
1073
1074 if (prev_it != prev_thread_times.end()) {
1075 EXPECT_GE(entry.second, prev_it->second);
1076 }
1077 }
1078 }
1079 #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||
1080 // BUILDFLAG(IS_CHROMEOS)
1081
1082 } // namespace base::debug
1083