// 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