chromium/components/stability_report/user_stream_data_source_win.cc

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

#include "components/stability_report/user_stream_data_source_win.h"

#include <windows.h>

// Must be included after windows.h.
#include <psapi.h>

#include <string>
#include <utility>

#include "base/check.h"
#include "base/dcheck_is_on.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/memory.h"
#include "components/stability_report/stability_report_data_source.h"
#include "third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h"
#include "third_party/crashpad/crashpad/snapshot/exception_snapshot.h"
#include "third_party/crashpad/crashpad/snapshot/process_snapshot.h"

namespace stability_report {

namespace {

// System memory metrics are reported in pages. Use this page size to scale
// process memory metrics to the same units.
constexpr size_t kPageSize = 4096;

// Adds system metrics to `report`.
void CollectSystemPerformanceMetrics(StabilityReport* report) {
  // Grab system commit memory. Best effort.
  PERFORMANCE_INFORMATION perf_info = {sizeof(perf_info)};
  if (!::GetPerformanceInfo(&perf_info, sizeof(perf_info))) {
    return;
  }
  SystemMemoryState::WindowsMemory* memory_state =
      report->mutable_system_memory_state()->mutable_windows_memory();
  memory_state->set_system_commit_limit(perf_info.CommitLimit);
  memory_state->set_system_commit_remaining(perf_info.CommitLimit -
                                            perf_info.CommitTotal);
  memory_state->set_system_handle_count(perf_info.HandleCount);
  // The process memory metrics won't be scaled correctly with an unexpected
  // page size.
  DCHECK_EQ(perf_info.PageSize, kPageSize);
}

// Adds metrics for the process in `process_snapshot` to `report`.
void CollectProcessPerformanceMetrics(
    const crashpad::ProcessSnapshot& process_snapshot,
    StabilityReport* report) {
  const base::ProcessId process_id = process_snapshot.ProcessID();
  ProcessState& process_state = AddProcessForSnapshot(process_id, report);

  ProcessState::MemoryState::WindowsMemory* memory_state =
      process_state.mutable_memory_state()->mutable_windows_memory();

  // Grab the requested allocation size in case of OOM exception.
  const crashpad::ExceptionSnapshot* const exception =
      process_snapshot.Exception();
  if (exception && exception->Exception() == base::win::kOomExceptionCode &&
      !exception->Codes().empty()) {
    // The first parameter, if present, is the size of the allocation attempt.
    // Note Codes() contains 64-bit values but `process_allocation_attempt` is
    // a uint32.
    memory_state->set_process_allocation_attempt(
        base::saturated_cast<uint32_t>(exception->Codes().front()));
  }

  const base::Process process = base::Process::OpenWithAccess(
      process_id, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ);
  if (!process.IsValid()) {
    return;
  }

  PROCESS_MEMORY_COUNTERS_EX process_memory = {sizeof(process_memory)};
  if (::GetProcessMemoryInfo(
          process.Handle(),
          reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&process_memory),
          sizeof(process_memory))) {
    // This is in units of bytes, re-scale to pages for consistency with system
    // metrics.
    memory_state->set_process_private_usage(process_memory.PrivateUsage /
                                            kPageSize);
    memory_state->set_process_peak_workingset_size(
        process_memory.PeakWorkingSetSize / kPageSize);
    memory_state->set_process_peak_pagefile_usage(
        process_memory.PeakPagefileUsage / kPageSize);
  }

  DWORD process_handle_count = 0;
  if (::GetProcessHandleCount(process.Handle(), &process_handle_count)) {
    ProcessState::FileSystemState::WindowsFileSystemState* file_system_state =
        process_state.mutable_file_system_state()
            ->mutable_windows_file_system_state();
    file_system_state->set_process_handle_count(process_handle_count);
  }
}

}  // namespace

std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
UserStreamDataSourceWin::ProduceStreamData(
    crashpad::ProcessSnapshot* process_snapshot) {
  DCHECK(process_snapshot);

  StabilityReport report;
  CollectSystemPerformanceMetrics(&report);
  CollectProcessPerformanceMetrics(*process_snapshot, &report);

  return std::make_unique<StabilityReportDataSource>(report);
}

}  // namespace stability_report