#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/process/process_metrics.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/test/gmock_expected_support.h"
#include "base/test/gtest_util.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "base/types/expected.h"
#include "build/blink_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#if BUILDFLAG(IS_APPLE)
#include <sys/mman.h>
#endif
#if BUILDFLAG(IS_MAC)
#include <mach/mach.h>
#include "base/apple/mach_logging.h"
#include "base/apple/scoped_mach_port.h"
#include "base/mac/mach_port_rendezvous.h"
#include "base/process/port_provider_mac.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_WIN) || \
BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_APPLE)
#define ENABLE_CPU_TESTS …
#else
#define ENABLE_CPU_TESTS …
#endif
namespace base::debug {
namespace {
ErrorIs;
ValueIs;
_;
AssertionFailure;
AssertionResult;
AssertionSuccess;
Ge;
#if ENABLE_CPU_TESTS
void BusyWork(std::vector<std::string>* vec) { … }
TimeDelta TestCumulativeCPU(ProcessMetrics* metrics, TimeDelta prev_cpu_usage) { … }
#endif
class TestChildLauncher { … };
#if BUILDFLAG(IS_MAC)
constexpr MachPortsForRendezvous::key_type kTestChildRendezvousKey = 'test';
class TestChildLauncher::TestChildPortProvider final : public PortProvider {
public:
TestChildPortProvider(ProcessHandle handle, apple::ScopedMachSendRight port)
: handle_(handle), port_(std::move(port)) {}
~TestChildPortProvider() final = default;
TestChildPortProvider(const TestChildPortProvider&) = delete;
TestChildPortProvider& operator=(const TestChildPortProvider&) = delete;
mach_port_t TaskForHandle(ProcessHandle process_handle) const final {
return process_handle == handle_ ? port_.get() : MACH_PORT_NULL;
}
private:
ProcessHandle handle_;
apple::ScopedMachSendRight port_;
};
AssertionResult TestChildLauncher::SpawnChildProcess(
const std::string& procname) {
apple::ScopedMachReceiveRight receive_port;
if (!apple::CreateMachPort(&receive_port, nullptr)) {
return AssertionFailure() << "Failed to allocate receive port";
}
LaunchOptions options = LaunchOptionsForTest();
options.mach_ports_for_rendezvous.emplace(
kTestChildRendezvousKey,
MachRendezvousPort(receive_port.get(), MACH_MSG_TYPE_MAKE_SEND));
child_process_ =
SpawnMultiProcessTestChild(procname, command_line_, std::move(options));
if (!child_process_.IsValid()) {
return AssertionFailure() << "Failed to launch child process.";
}
struct : mach_msg_base_t {
mach_msg_port_descriptor_t task_port;
mach_msg_trailer_t trailer;
} msg{};
kern_return_t kr =
mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg),
receive_port.get(),
TestTimeouts::action_timeout().InMilliseconds(), MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
return AssertionFailure()
<< "Failed to read mach_task_self from child process: "
<< mach_error_string(kr);
}
port_provider_ = std::make_unique<TestChildPortProvider>(
child_process_.Handle(), apple::ScopedMachSendRight(msg.task_port.name));
return AssertionSuccess();
}
std::unique_ptr<ProcessMetrics> TestChildLauncher::CreateChildProcessMetrics() {
#if BUILDFLAG(IS_MAC)
return ProcessMetrics::CreateProcessMetrics(child_process_.Handle(),
port_provider_.get());
#else
return ProcessMetrics::CreateProcessMetrics(child_process_.Handle());
#endif
}
bool TestChildLauncher::TerminateChildProcess() {
return TerminateMultiProcessTestChild(child_process_, 0,
true);
}
void TestChildLauncher::NotifyParent() {
auto* client = MachPortRendezvousClient::GetInstance();
ASSERT_TRUE(client);
apple::ScopedMachSendRight send_port =
client->TakeSendRight(kTestChildRendezvousKey);
ASSERT_TRUE(send_port.is_valid());
struct : mach_msg_base_t {
mach_msg_port_descriptor_t task_port;
} msg{};
msg.header.msgh_bits =
MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) | MACH_MSGH_BITS_COMPLEX;
msg.header.msgh_remote_port = send_port.get();
msg.header.msgh_size = sizeof(msg);
msg.body.msgh_descriptor_count = 1;
msg.task_port.name = mach_task_self();
msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
kern_return_t kr =
mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
MACH_CHECK(kr == KERN_SUCCESS, kr);
}
#else
AssertionResult TestChildLauncher::SpawnChildProcess(
const std::string& procname) { … }
std::unique_ptr<ProcessMetrics> TestChildLauncher::CreateChildProcessMetrics() { … }
bool TestChildLauncher::TerminateChildProcess() { … }
void TestChildLauncher::NotifyParent() { … }
#endif
}
class SystemMetricsTest : public testing::Test { … };
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST_F(SystemMetricsTest, IsValidDiskName) { … }
TEST_F(SystemMetricsTest, ParseMeminfo) { … }
TEST_F(SystemMetricsTest, ParseVmstat) { … }
#endif
#if ENABLE_CPU_TESTS
TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) { … }
#if !BUILDFLAG(IS_APPLE)
MULTIPROCESS_TEST_MAIN(CPUUsageChildMain) { … }
TEST_F(SystemMetricsTest, MeasureChildCpuUsage) { … }
#endif
TEST_F(SystemMetricsTest, InvalidProcessCpuUsage) { … }
#endif
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(SystemMetricsTest, ParseZramMmStat) {
SwapInfo swapinfo;
const char invalid_input1[] = "aaa";
const char invalid_input2[] = "1 2 3 4 5 6";
const char invalid_input3[] = "a 2 3 4 5 6 7";
EXPECT_FALSE(ParseZramMmStat(invalid_input1, &swapinfo));
EXPECT_FALSE(ParseZramMmStat(invalid_input2, &swapinfo));
EXPECT_FALSE(ParseZramMmStat(invalid_input3, &swapinfo));
const char valid_input1[] =
"17715200 5008166 566062 0 1225715712 127 183842";
EXPECT_TRUE(ParseZramMmStat(valid_input1, &swapinfo));
EXPECT_EQ(17715200ULL, swapinfo.orig_data_size);
EXPECT_EQ(5008166ULL, swapinfo.compr_data_size);
EXPECT_EQ(566062ULL, swapinfo.mem_used_total);
}
TEST_F(SystemMetricsTest, ParseZramStat) {
SwapInfo swapinfo;
const char invalid_input1[] = "aaa";
const char invalid_input2[] = "1 2 3 4 5 6 7 8 9 10";
const char invalid_input3[] = "a 2 3 4 5 6 7 8 9 10 11";
EXPECT_FALSE(ParseZramStat(invalid_input1, &swapinfo));
EXPECT_FALSE(ParseZramStat(invalid_input2, &swapinfo));
EXPECT_FALSE(ParseZramStat(invalid_input3, &swapinfo));
const char valid_input1[] =
"299 0 2392 0 1 0 8 0 0 0 0";
EXPECT_TRUE(ParseZramStat(valid_input1, &swapinfo));
EXPECT_EQ(299ULL, swapinfo.num_reads);
EXPECT_EQ(1ULL, swapinfo.num_writes);
}
#endif
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST(SystemMetrics2Test, GetSystemMemoryInfo) { … }
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST(ProcessMetricsTest, ParseProcStatCPU) { … }
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
TEST(ProcessMetricsTest, DISABLED_GetNumberOfThreads) { … }
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
namespace {
#define ChildMain …
#define ChildMainString …
const char kTempDirFlag[] = …;
const char kSignalReady[] = …;
const char kSignalReadyAck[] = …;
const char kSignalOpened[] = …;
const char kSignalOpenedAck[] = …;
const char kSignalClosed[] = …;
const int kChildNumFilesToOpen = …;
bool SignalEvent(const FilePath& signal_dir, const char* signal_file) { … }
bool CheckEvent(const FilePath& signal_dir, const char* signal_file) { … }
void WaitForEvent(const FilePath& signal_dir, const char* signal_file) { … }
MULTIPROCESS_TEST_MAIN(ChildMain) { … }
}
TEST(ProcessMetricsTest, GetChildOpenFdCount) { … }
TEST(ProcessMetricsTest, GetOpenFdCount) { … }
#endif
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
TEST(ProcessMetricsTestLinux, GetPageFaultCounts) { … }
TEST(ProcessMetricsTestLinux, GetCumulativeCPUUsagePerThread) { … }
#endif
}