blob: bff871e45a8d25b3651b982b1868b74787e26081 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/extended_crash_reporting.h"
#include <windows.h>
#include <memory>
#include "base/debug/activity_tracker.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/win/pe_image.h"
#include "build/build_config.h"
#include "components/browser_watcher/activity_data_names.h"
#include "components/browser_watcher/activity_report.pb.h"
#include "components/browser_watcher/activity_tracker_annotation.h"
#include "components/browser_watcher/extended_crash_reporting.h"
#include "components/browser_watcher/extended_crash_reporting_metrics.h"
#include "components/browser_watcher/features.h"
#if defined(OS_WIN)
// https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483.
extern "C" IMAGE_DOS_HEADER __ImageBase;
#endif
namespace browser_watcher {
namespace {
ExtendedCrashReporting* g_instance = nullptr;
uintptr_t GetProgramCounter(const CONTEXT& context) {
#if defined(ARCH_CPU_X86)
return context.Eip;
#elif defined(ARCH_CPU_X86_64)
return context.Rip;
#elif defined(ARCH_CPU_ARM64)
return context.Pc;
#endif
}
LONG CALLBACK VectoredExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
base::debug::GlobalActivityTracker* tracker =
base::debug::GlobalActivityTracker::Get();
if (tracker) {
EXCEPTION_RECORD* record = exception_pointers->ExceptionRecord;
uintptr_t pc = GetProgramCounter(*exception_pointers->ContextRecord);
tracker->RecordException(reinterpret_cast<void*>(pc),
record->ExceptionAddress, record->ExceptionCode);
}
return EXCEPTION_CONTINUE_SEARCH; // Continue to the next handler.
}
// Record information about the chrome module.
void RecordChromeModuleInfo(
base::debug::GlobalActivityTracker* global_tracker) {
DCHECK(global_tracker);
base::debug::GlobalActivityTracker::ModuleInfo module;
module.is_loaded = true;
module.address = reinterpret_cast<uintptr_t>(&__ImageBase);
base::win::PEImage pe(&__ImageBase);
PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
CHECK(headers);
module.size = headers->OptionalHeader.SizeOfImage;
module.timestamp = headers->FileHeader.TimeDateStamp;
GUID guid;
DWORD age;
LPCSTR pdb_filename = nullptr;
size_t pdb_filename_length = 0;
if (pe.GetDebugId(&guid, &age, &pdb_filename, &pdb_filename_length)) {
module.age = age;
static_assert(sizeof(module.identifier) >= sizeof(guid),
"Identifier field must be able to contain a GUID.");
memcpy(module.identifier, &guid, sizeof(guid));
} else {
memset(module.identifier, 0, sizeof(module.identifier));
}
module.file = "chrome.dll";
module.debug_file =
base::StringPiece(pdb_filename, pdb_filename_length).as_string();
global_tracker->RecordModuleInfo(module);
}
} // namespace
ExtendedCrashReporting::ExtendedCrashReporting(
base::debug::GlobalActivityTracker* tracker)
: tracker_(tracker) {}
ExtendedCrashReporting::~ExtendedCrashReporting() {
if (veh_handle_)
::RemoveVectoredExceptionHandler(veh_handle_);
}
ExtendedCrashReporting* ExtendedCrashReporting::SetUpIfEnabled(
ProcessType process_type) {
DCHECK_EQ(nullptr, g_instance);
if (!base::FeatureList::IsEnabled(kExtendedCrashReportingFeature)) {
return nullptr;
}
return SetUpImpl(process_type);
}
ExtendedCrashReporting* ExtendedCrashReporting::GetInstance() {
return g_instance;
}
void ExtendedCrashReporting::SetProductStrings(
const base::string16& product_name,
const base::string16& product_version,
const base::string16& channel_name,
const base::string16& special_build) {
base::debug::ActivityUserData& proc_data = tracker_->process_data();
proc_data.SetString(kActivityProduct, product_name);
proc_data.SetString(kActivityVersion, product_version);
proc_data.SetString(kActivityChannel, channel_name);
proc_data.SetString(kActivitySpecialBuild, special_build);
}
void ExtendedCrashReporting::SetBool(base::StringPiece name, bool value) {
tracker_->process_data().SetBool(name, value);
}
void ExtendedCrashReporting::SetInt(base::StringPiece name, int64_t value) {
tracker_->process_data().SetInt(name, value);
}
void ExtendedCrashReporting::SetDataBool(base::StringPiece name, bool value) {
if (g_instance)
g_instance->SetBool(name, value);
}
void ExtendedCrashReporting::SetDataInt(base::StringPiece name, int64_t value) {
if (g_instance)
g_instance->SetInt(name, value);
}
void ExtendedCrashReporting::RegisterVEH() {
#if defined(ADDRESS_SANITIZER)
// ASAN on windows x64 is dynamically allocating the shadow memory on a
// memory access violation by setting up an vector exception handler.
// When instrumented with ASAN, this code may trigger an exception by
// accessing unallocated shadow memory, which is causing an infinite
// recursion (i.e. infinite memory access violation).
(void)&VectoredExceptionHandler;
#else
DCHECK_EQ(nullptr, veh_handle_);
// Register a vectored exception handler and request it be first. Note that
// subsequent registrations may also request to be first, in which case this
// one will be bumped.
// TODO(manzagop): Depending on observations, it may be necessary to
// consider refreshing the registration, either periodically or at opportune
// (e.g. risky) times.
veh_handle_ = ::AddVectoredExceptionHandler(1, &VectoredExceptionHandler);
DCHECK(veh_handle_);
#endif // ADDRESS_SANITIZER
}
void ExtendedCrashReporting::SetUpForTesting() {
ExtendedCrashReporting::SetUpImpl(kBrowserProcess);
}
void ExtendedCrashReporting::TearDownForTesting() {
if (g_instance) {
ExtendedCrashReporting* instance_to_delete = g_instance;
g_instance = nullptr;
delete instance_to_delete;
}
// Clear the crash annotation.
ActivityTrackerAnnotation::GetInstance()->Clear();
}
ExtendedCrashReporting* ExtendedCrashReporting::SetUpImpl(
ProcessType process_type) {
DCHECK_EQ(nullptr, g_instance);
// TODO(https://crbug.com/1044707): Adjust these numbers once there is real
// data to show just how much of an arena is necessary.
const size_t kMemorySize = 1 << 20; // 1 MiB
const int kStackDepth = 4;
const uint64_t kAllocatorId = 0;
base::debug::GlobalActivityTracker::CreateWithAllocator(
std::make_unique<base::LocalPersistentMemoryAllocator>(
kMemorySize, kAllocatorId, kExtendedCrashReportingFeature.name),
kStackDepth, 0);
// Track code activities (such as posting task, blocking on locks, and
// joining threads) that can cause hanging threads and general instability
base::debug::GlobalActivityTracker* global_tracker =
base::debug::GlobalActivityTracker::Get();
DCHECK(global_tracker);
// Construct the instance with the new global tracker, this object is
// intentionally leaked.
std::unique_ptr<ExtendedCrashReporting> new_instance =
base::WrapUnique(new ExtendedCrashReporting(global_tracker));
new_instance->Initialize(process_type);
g_instance = new_instance.release();
return g_instance;
}
void ExtendedCrashReporting::Initialize(ProcessType process_type) {
// Record the location and size of the tracker memory range in a Crashpad
// annotation to allow the handler to retrieve it on crash.
// Record the buffer size and location for the annotation beacon.
auto* allocator = tracker_->allocator();
ActivityTrackerAnnotation::GetInstance()->SetValue(allocator->data(),
allocator->size());
// Record the main DLL module info for easier symbolization.
RecordChromeModuleInfo(tracker_);
LogActivityRecordEvent(ActivityRecordEvent::kGotTracker);
base::debug::ActivityUserData& proc_data = tracker_->process_data();
#if defined(ARCH_CPU_X86)
proc_data.SetString(kActivityPlatform, "Win32");
#elif defined(ARCH_CPU_X86_64)
proc_data.SetString(kActivityPlatform, "Win64");
#endif
proc_data.SetInt(
kActivityStartTimestamp,
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
if (process_type == kBrowserProcess)
proc_data.SetInt(kActivityProcessType, ProcessState::BROWSER_PROCESS);
RegisterVEH();
}
} // namespace browser_watcher