chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#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[] =// An invalid region, with zero size and overlapping with the last one
    // (See crbug.com/461237).
    "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"
    // A invalid region with its range going backwards.
    "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"
    // A good anonymous region at the end.
    "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) {}

}  // namespace
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
        // BUILDFLAG(IS_ANDROID)

TEST(OSMetricsTest, GivesNonZeroResults) {}

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST(OSMetricsTest, ParseProcSmaps) {}

TEST(OSMetricsTest, GetMappedAndResidentPages) {}

#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
        // BUILDFLAG(IS_ANDROID)

#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) {
    // We add a region just for byte_stats_proportional_resident which
    // is empty other than that one stat.
    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  // BUILDFLAG(IS_WIN)

#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);
}

}  // namespace

// Test failing on Mac ASan 64: https://crbug.com/852690
TEST(OSMetricsTest, DISABLED_TestMachOReading) {
  auto maps = OSMetrics::GetProcessMemoryMaps(base::kNullProcessId);
  CheckMachORegions(maps);
  maps = OSMetrics::GetProcessModules(base::kNullProcessId);
  CheckMachORegions(maps);
}
#endif  // BUILDFLAG(IS_MAC)

}  // namespace memory_instrumentation