#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
#include <set>
#include <vector>
#include "base/files/file_util.h"
#include "base/memory/page_size.h"
#include "base/process/process_handle.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_MAC)
#include <libgen.h>
#include <mach-o/dyld.h>
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/strings/sys_string_conversions.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
#include <sys/mman.h>
#endif
namespace memory_instrumentation {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
namespace {
const char kTestSmaps1[] = …;
const char kTestSmaps2[] = …
"7fe7ce79c000-7fe7ce79c000 ---p 00000000 00:00 0 \n"
"Size: 4 kB\n"
"Rss: 0 kB\n"
"Pss: 0 kB\n"
"Shared_Clean: 0 kB\n"
"Shared_Dirty: 0 kB\n"
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
"Anonymous: 0 kB\n"
"AnonHugePages: 0 kB\n"
"Swap: 0 kB\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n"
"Locked: 0 kB\n"
"VmFlags: rd ex mr mw me dw sd\n"
"00400000-00200000 ---p 00000000 00:00 0 \n"
"Size: 4 kB\n"
"Rss: 0 kB\n"
"Pss: 0 kB\n"
"Shared_Clean: 0 kB\n"
"Shared_Dirty: 0 kB\n"
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
"Anonymous: 0 kB\n"
"AnonHugePages: 0 kB\n"
"Swap: 0 kB\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n"
"Locked: 0 kB\n"
"VmFlags: rd ex mr mw me dw sd\n"
"7fe7ce79c000-7fe7ce7a8000 ---p 00000000 00:00 0 \n"
"Size: 48 kB\n"
"Rss: 40 kB\n"
"Pss: 32 kB\n"
"Shared_Clean: 16 kB\n"
"Shared_Dirty: 12 kB\n"
"Private_Clean: 8 kB\n"
"Private_Dirty: 4 kB\n"
"Referenced: 40 kB\n"
"Anonymous: 16 kB\n"
"AnonHugePages: 0 kB\n"
"Swap: 0 kB\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n"
"Locked: 11 kB\n"
"VmFlags: rd wr mr mw me ac sd\n";
void CreateTempFileWithContents(const char* contents, base::ScopedFILE* file) { … }
}
#endif
TEST(OSMetricsTest, GivesNonZeroResults) { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST(OSMetricsTest, ParseProcSmaps) { … }
TEST(OSMetricsTest, GetMappedAndResidentPages) { … }
#endif
#if BUILDFLAG(IS_WIN)
void DummyFunction() {}
TEST(OSMetricsTest, TestWinModuleReading) {
auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
wchar_t module_name[MAX_PATH];
DWORD result = GetModuleFileName(nullptr, module_name, MAX_PATH);
ASSERT_TRUE(result);
std::string executable_name = base::SysWideToNativeMB(module_name);
HMODULE module_containing_dummy = nullptr;
uintptr_t dummy_function_address =
reinterpret_cast<uintptr_t>(&DummyFunction);
result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast<LPCWSTR>(dummy_function_address),
&module_containing_dummy);
ASSERT_TRUE(result);
result = GetModuleFileName(nullptr, module_name, MAX_PATH);
ASSERT_TRUE(result);
std::string module_containing_dummy_name =
base::SysWideToNativeMB(module_name);
bool found_executable = false;
bool found_region_with_dummy = false;
for (const mojom::VmRegionPtr& region : maps) {
if (region->byte_stats_proportional_resident > 0) {
EXPECT_EQ(0u, region->start_address);
EXPECT_EQ(0u, region->size_in_bytes);
continue;
}
EXPECT_NE(0u, region->start_address);
EXPECT_NE(0u, region->size_in_bytes);
if (region->mapped_file.find(executable_name) != std::string::npos)
found_executable = true;
if (dummy_function_address >= region->start_address &&
dummy_function_address <
region->start_address + region->size_in_bytes) {
found_region_with_dummy = true;
EXPECT_EQ(module_containing_dummy_name, region->mapped_file);
}
}
EXPECT_TRUE(found_executable);
EXPECT_TRUE(found_region_with_dummy);
}
#endif
#if BUILDFLAG(IS_MAC)
namespace {
void CheckMachORegions(const std::vector<mojom::VmRegionPtr>& maps) {
constexpr uint32_t kSize = 100;
char full_path[kSize];
uint32_t buf_size = kSize;
int result = _NSGetExecutablePath(full_path, &buf_size);
ASSERT_EQ(0, result);
std::string name = basename(full_path);
bool found_appkit = false;
bool found_components_unittests = false;
for (const mojom::VmRegionPtr& region : maps) {
EXPECT_NE(0u, region->start_address);
EXPECT_NE(0u, region->size_in_bytes);
EXPECT_LT(region->size_in_bytes, 1ull << 32);
uint32_t required_protection_flags = mojom::VmRegion::kProtectionFlagsRead |
mojom::VmRegion::kProtectionFlagsExec;
if (region->mapped_file.find(name) != std::string::npos &&
region->protection_flags == required_protection_flags) {
found_components_unittests = true;
}
if (region->mapped_file.find("AppKit") != std::string::npos) {
found_appkit = true;
}
}
EXPECT_TRUE(found_components_unittests);
EXPECT_TRUE(found_appkit);
}
}
TEST(OSMetricsTest, DISABLED_TestMachOReading) {
auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
CheckMachORegions(maps);
maps = OSMetrics::GetProcessModules(base::kNullProcessId);
CheckMachORegions(maps);
}
#endif
}