123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // Copyright 2017 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/activity_report_user_stream_data_source.h"
- #include <string>
- #include <utility>
- #include <windows.h>
- // Must be included after windows.h.
- #include <psapi.h>
- #include "base/files/file.h"
- #include "base/files/file_util.h"
- #include "base/memory/free_deleter.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/metrics/persistent_memory_allocator.h"
- #include "base/process/memory.h"
- #include "base/process/process.h"
- #include "base/time/time.h"
- #include "components/browser_watcher/activity_report_extractor.h"
- #include "components/browser_watcher/activity_tracker_annotation.h"
- #include "components/browser_watcher/extended_crash_reporting_metrics.h"
- #include "components/browser_watcher/minidump_user_streams.h"
- #include "third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h"
- #include "third_party/crashpad/crashpad/snapshot/annotation_snapshot.h"
- #include "third_party/crashpad/crashpad/snapshot/exception_snapshot.h"
- #include "third_party/crashpad/crashpad/snapshot/module_snapshot.h"
- #include "third_party/crashpad/crashpad/snapshot/process_snapshot.h"
- #include "third_party/crashpad/crashpad/util/process/process_memory.h"
- namespace browser_watcher {
- namespace {
- // TODO(siggi): Refactor this to harmonize with the activity tracker setup.
- const size_t kMaxActivityAnnotationSize = 2 << 20;
- using UniqueMallocPtr = std::unique_ptr<void, base::FreeDeleter>;
- UniqueMallocPtr UncheckedAllocate(size_t size) {
- void* raw_ptr = nullptr;
- if (!base::UncheckedMalloc(size, &raw_ptr))
- return UniqueMallocPtr();
- return UniqueMallocPtr(raw_ptr);
- }
- // A PersistentMemoryAllocator subclass that can take ownership of a buffer
- // that's allocated with a malloc-compatible allocation function.
- class MallocMemoryAllocator : public base::PersistentMemoryAllocator {
- public:
- MallocMemoryAllocator(UniqueMallocPtr buffer, size_t size);
- ~MallocMemoryAllocator() override;
- };
- MallocMemoryAllocator::MallocMemoryAllocator(UniqueMallocPtr buffer,
- size_t size)
- : base::PersistentMemoryAllocator(buffer.release(), size, 0, 0, "", true) {}
- MallocMemoryAllocator::~MallocMemoryAllocator() {
- free(const_cast<char*>(mem_base_));
- }
- class BufferExtensionStreamDataSource final
- : public crashpad::MinidumpUserExtensionStreamDataSource {
- public:
- explicit BufferExtensionStreamDataSource(uint32_t stream_type);
- BufferExtensionStreamDataSource(const BufferExtensionStreamDataSource&) =
- delete;
- BufferExtensionStreamDataSource& operator=(
- const BufferExtensionStreamDataSource&) = delete;
- bool Init(const StabilityReport& report);
- size_t StreamDataSize() override;
- bool ReadStreamData(Delegate* delegate) override;
- private:
- std::string data_;
- };
- BufferExtensionStreamDataSource::BufferExtensionStreamDataSource(
- uint32_t stream_type)
- : crashpad::MinidumpUserExtensionStreamDataSource(stream_type) {}
- bool BufferExtensionStreamDataSource::Init(const StabilityReport& report) {
- if (report.SerializeToString(&data_))
- return true;
- data_.clear();
- return false;
- }
- size_t BufferExtensionStreamDataSource::StreamDataSize() {
- DCHECK(!data_.empty());
- return data_.size();
- }
- bool BufferExtensionStreamDataSource::ReadStreamData(Delegate* delegate) {
- DCHECK(!data_.empty());
- return delegate->ExtensionStreamDataSourceRead(
- data_.size() ? data_.data() : nullptr, data_.size());
- }
- // TODO(manzagop): Collection should factor in whether this is a true crash or
- // dump without crashing.
- bool CollectStabilityReport(
- std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer,
- StabilityReport* report) {
- CollectionStatus status = ANALYZER_CREATION_FAILED;
- if (global_analyzer)
- status = Extract(std::move(global_analyzer), report);
- base::UmaHistogramEnumeration("ActivityTracker.CollectCrash.Status", status,
- COLLECTION_STATUS_MAX);
- if (status != SUCCESS)
- return false;
- LogCollectOnCrashEvent(CollectOnCrashEvent::kReportExtractionSuccess);
- return true;
- }
- void CollectSystemPerformanceMetrics(StabilityReport* report) {
- // Grab system commit memory. Also best effort.
- PERFORMANCE_INFORMATION perf_info = {sizeof(perf_info)};
- if (GetPerformanceInfo(&perf_info, sizeof(perf_info))) {
- auto* 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);
- }
- }
- void CollectProcessPerformanceMetrics(
- crashpad::ProcessSnapshot* process_snapshot,
- StabilityReport* report) {
- const crashpad::ExceptionSnapshot* exception = process_snapshot->Exception();
- if (!exception)
- return;
- // Find or create the ProcessState for the process in question.
- base::ProcessId pid = process_snapshot->ProcessID();
- ProcessState* process_state = nullptr;
- for (int i = 0; i < report->process_states_size(); ++i) {
- ProcessState* temp = report->mutable_process_states(i);
- if (temp->has_process_id() && temp->process_id() == pid) {
- process_state = temp;
- break;
- }
- }
- if (!process_state) {
- process_state = report->add_process_states();
- process_state->set_process_id(pid);
- }
- auto* memory_state =
- process_state->mutable_memory_state()->mutable_windows_memory();
- // Grab the requested allocation size in case of OOM exception.
- if (exception->Exception() == base::win::kOomExceptionCode) {
- const auto& codes = exception->Codes();
- if (codes.size()) {
- // The first parameter, if present, is the size of the allocation attempt.
- memory_state->set_process_allocation_attempt(codes[0]);
- }
- }
- base::Process process(base::Process::OpenWithAccess(
- pid, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ));
- if (process.IsValid()) {
- 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.
- const uint64_t kPageSize = 4096;
- 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)) {
- memory_state->set_process_handle_count(process_handle_count);
- }
- }
- }
- // If the process has a beacon for in-memory activities, returns an analyzer
- // for it.
- std::unique_ptr<base::debug::GlobalActivityAnalyzer>
- MaybeGetInMemoryActivityAnalyzer(crashpad::ProcessSnapshot* process_snapshot) {
- if (!process_snapshot->Memory())
- return nullptr;
- auto modules = process_snapshot->Modules();
- for (auto* module : modules) {
- auto annotations = module->AnnotationObjects();
- for (const auto& annotation : annotations) {
- if (annotation.name == ActivityTrackerAnnotation::kAnnotationName &&
- annotation.type == static_cast<uint16_t>(
- ActivityTrackerAnnotation::kAnnotationType) &&
- annotation.value.size() ==
- sizeof(ActivityTrackerAnnotation::ValueType)) {
- // Re-cast the annotation to its value type.
- ActivityTrackerAnnotation::ValueType value;
- memcpy(&value, annotation.value.data(), sizeof(value));
- // Check the size field for sanity.
- if (value.size > kMaxActivityAnnotationSize)
- continue;
- // Allocate the buffer with no terminate-on-exhaustion to make sure
- // this can't be used to bring down the handler and thus elide
- // crash reporting.
- UniqueMallocPtr buffer = UncheckedAllocate(value.size);
- if (!buffer || !base::PersistentMemoryAllocator::IsMemoryAcceptable(
- buffer.get(), value.size, 0, true)) {
- continue;
- }
- // Read the activity tracker data from the crashed process.
- if (process_snapshot->Memory()->Read(value.address, value.size,
- buffer.get())) {
- // Success - wrap an allocator on the buffer, and an analyzer on that.
- std::unique_ptr<MallocMemoryAllocator> allocator =
- std::make_unique<MallocMemoryAllocator>(std::move(buffer),
- value.size);
- return base::debug::GlobalActivityAnalyzer::CreateWithAllocator(
- std::move(allocator));
- } else {
- return nullptr;
- }
- }
- }
- }
- return nullptr;
- }
- } // namespace
- ActivityReportUserStreamDataSource::ActivityReportUserStreamDataSource(
- const base::FilePath& user_data_dir)
- : user_data_dir_(user_data_dir) {}
- std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
- ActivityReportUserStreamDataSource::ProduceStreamData(
- crashpad::ProcessSnapshot* process_snapshot) {
- DCHECK(process_snapshot);
- LogCollectOnCrashEvent(CollectOnCrashEvent::kCollectAttempt);
- StabilityReport report;
- // See whether there's an activity tracking report beacon in the process'
- // annotations.
- std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer =
- MaybeGetInMemoryActivityAnalyzer(process_snapshot);
- bool collected_report = false;
- if (global_analyzer) {
- LogCollectOnCrashEvent(CollectOnCrashEvent::kInMemoryAnnotationExists);
- collected_report =
- CollectStabilityReport(std::move(global_analyzer), &report);
- }
- CollectSystemPerformanceMetrics(&report);
- CollectProcessPerformanceMetrics(process_snapshot, &report);
- std::unique_ptr<BufferExtensionStreamDataSource> source(
- new BufferExtensionStreamDataSource(kActivityReportStreamType));
- if (!source->Init(report))
- return nullptr;
- if (collected_report)
- LogCollectOnCrashEvent(CollectOnCrashEvent::kSuccess);
- return source;
- }
- } // namespace browser_watcher
|