0

Remove browser_watcher (2/3): Delete components/browser_watcher.

Also deletes the related classes in base/debug/activity_analyzer.* and
base/debug/activity_tracker.*.

Many files include base/debug/activity_tracker.h and do work iff a
GlobalActivityTracker object exists. To keep this patch self-contained
it replaces the classes in activity_tracker.h with no-op stubs instead
of deleting it, so that all files that include it will still compile.
A followup will delete activity_tracker.h and all uses of it.

This should not cause any behaviour change because GlobalActivityTracker
is only created when the ExtendedCrashReporting feature is enabled, so
all uses of activity_tracker.h are already no-ops in production.

Bug: 1415328
Change-Id: I940fb0e013a3ae7a939136099f34791dec914db9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4265975
Reviewed-by: Colin Blundell <blundell@chromium.org>
Commit-Queue: Joe Mason <joenotcharles@google.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1107725}
This commit is contained in:
Joe Mason
2023-02-21 14:38:25 +00:00
committed by Chromium LUCI CQ
parent 03594b2987
commit 804d3d151b
51 changed files with 84 additions and 7745 deletions

@ -2949,7 +2949,6 @@ def CheckSpamLogging(input_api, output_api):
r"^chrome/chrome_elf/dll_hash/dll_hash_main\.cc$",
r"^chrome/installer/setup/.*",
r"^chromecast/",
r"^components/browser_watcher/dump_stability_report_main_win\.cc$",
r"^components/media_control/renderer/media_playback_options\.cc$",
r"^components/policy/core/common/policy_logger\.cc$",
r"^components/viz/service/display/"

@ -308,9 +308,6 @@ component("base") {
"cxx20_is_constant_evaluated.h",
"cxx20_to_address.h",
"dcheck_is_on.h",
"debug/activity_analyzer.cc",
"debug/activity_analyzer.h",
"debug/activity_tracker.cc",
"debug/activity_tracker.h",
"debug/alias.cc",
"debug/alias.h",
@ -3033,8 +3030,6 @@ test("base_unittests") {
"cpu_unittest.cc",
"cxx17_backports_unittest.cc",
"cxx20_is_constant_evaluated_unittest.cc",
"debug/activity_analyzer_unittest.cc",
"debug/activity_tracker_unittest.cc",
"debug/alias_unittest.cc",
"debug/asan_service_unittest.cc",
"debug/crash_logging_unittest.cc",
@ -4650,13 +4645,6 @@ fuzzer_test("base_json_string_escape_fuzzer") {
deps = [ "//base" ]
}
fuzzer_test("base_activity_analyzer_fuzzer") {
sources = [ "debug/activity_analyzer_fuzzer.cc" ]
deps = [ "//base" ]
dict = "debug/activity_analyzer_fuzzer.dict"
seed_corpus = "test/data/activity_analyzer_fuzzer"
}
if (is_mac) {
protoc_convert("base_mach_port_rendezvous_convert_corpus") {
sources = [

@ -1,5 +1,2 @@
# For activity tracking:
per-file activity_*=bcwhite@chromium.org
# For ASan integration:
per-file asan_service*=file://base/memory/MIRACLE_PTR_OWNERS

@ -1,407 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/activity_analyzer.h"
#include <utility>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
namespace base {
namespace debug {
namespace {
const ActivityUserData::Snapshot& GetEmptyUserDataSnapshot() {
// An empty snapshot that can be returned when there otherwise is none.
static const NoDestructor<ActivityUserData::Snapshot> empty_snapshot;
return *empty_snapshot;
}
// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
enum AnalyzerCreationError {
kInvalidMemoryMappedFile,
kPmaBadFile,
kPmaUninitialized,
kPmaDeleted,
kPmaCorrupt,
kAnalyzerCreationErrorMax // Keep this last.
};
void LogAnalyzerCreationError(AnalyzerCreationError error) {
UmaHistogramEnumeration("ActivityTracker.Collect.AnalyzerCreationError",
error, kAnalyzerCreationErrorMax);
}
} // namespace
ThreadActivityAnalyzer::Snapshot::Snapshot() = default;
ThreadActivityAnalyzer::Snapshot::~Snapshot() = default;
ThreadActivityAnalyzer::ThreadActivityAnalyzer(
const ThreadActivityTracker& tracker)
: activity_snapshot_valid_(tracker.CreateSnapshot(&activity_snapshot_)) {}
ThreadActivityAnalyzer::ThreadActivityAnalyzer(void* base, size_t size)
: ThreadActivityAnalyzer(ThreadActivityTracker(base, size)) {}
ThreadActivityAnalyzer::ThreadActivityAnalyzer(
PersistentMemoryAllocator* allocator,
PersistentMemoryAllocator::Reference reference)
: ThreadActivityAnalyzer(allocator->GetAsArray<char>(
reference,
GlobalActivityTracker::kTypeIdActivityTracker,
PersistentMemoryAllocator::kSizeAny),
allocator->GetAllocSize(reference)) {}
ThreadActivityAnalyzer::~ThreadActivityAnalyzer() = default;
void ThreadActivityAnalyzer::AddGlobalInformation(
GlobalActivityAnalyzer* global) {
if (!IsValid())
return;
// User-data is held at the global scope even though it's referenced at the
// thread scope.
activity_snapshot_.user_data_stack.clear();
for (auto& activity : activity_snapshot_.activity_stack) {
// The global GetUserDataSnapshot will return an empty snapshot if the ref
// or id is not valid.
activity_snapshot_.user_data_stack.push_back(global->GetUserDataSnapshot(
activity_snapshot_.process_id, activity.user_data_ref,
activity.user_data_id));
}
}
GlobalActivityAnalyzer::GlobalActivityAnalyzer(
std::unique_ptr<PersistentMemoryAllocator> allocator)
: allocator_(std::move(allocator)),
analysis_stamp_(0LL),
allocator_iterator_(allocator_.get()) {
DCHECK(allocator_);
}
GlobalActivityAnalyzer::~GlobalActivityAnalyzer() = default;
// static
std::unique_ptr<GlobalActivityAnalyzer>
GlobalActivityAnalyzer::CreateWithAllocator(
std::unique_ptr<PersistentMemoryAllocator> allocator) {
if (allocator->GetMemoryState() ==
PersistentMemoryAllocator::MEMORY_UNINITIALIZED) {
LogAnalyzerCreationError(kPmaUninitialized);
return nullptr;
}
if (allocator->GetMemoryState() ==
PersistentMemoryAllocator::MEMORY_DELETED) {
LogAnalyzerCreationError(kPmaDeleted);
return nullptr;
}
if (allocator->IsCorrupt()) {
LogAnalyzerCreationError(kPmaCorrupt);
return nullptr;
}
return std::make_unique<GlobalActivityAnalyzer>(std::move(allocator));
}
#if !BUILDFLAG(IS_NACL)
// static
std::unique_ptr<GlobalActivityAnalyzer> GlobalActivityAnalyzer::CreateWithFile(
const FilePath& file_path) {
// Map the file read-write so it can guarantee consistency between
// the analyzer and any trackers that my still be active.
std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
if (!mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE)) {
LogAnalyzerCreationError(kInvalidMemoryMappedFile);
return nullptr;
}
if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
LogAnalyzerCreationError(kPmaBadFile);
return nullptr;
}
return CreateWithAllocator(std::make_unique<FilePersistentMemoryAllocator>(
std::move(mmfile), 0, 0, StringPiece(), /*readonly=*/true));
}
#endif // !BUILDFLAG(IS_NACL)
// static
std::unique_ptr<GlobalActivityAnalyzer>
GlobalActivityAnalyzer::CreateWithSharedMemory(
base::ReadOnlySharedMemoryMapping mapping) {
if (!mapping.IsValid() ||
!ReadOnlySharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(
mapping)) {
return nullptr;
}
return CreateWithAllocator(
std::make_unique<ReadOnlySharedPersistentMemoryAllocator>(
std::move(mapping), 0, StringPiece()));
}
ProcessId GlobalActivityAnalyzer::GetFirstProcess() {
PrepareAllAnalyzers();
return GetNextProcess();
}
ProcessId GlobalActivityAnalyzer::GetNextProcess() {
if (process_ids_.empty())
return 0;
ProcessId pid = process_ids_.back();
process_ids_.pop_back();
return pid;
}
ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(
ProcessId pid) {
analyzers_iterator_ = analyzers_.begin();
analyzers_iterator_pid_ = pid;
if (analyzers_iterator_ == analyzers_.end())
return nullptr;
int64_t create_stamp;
if (analyzers_iterator_->second->GetProcessId(&create_stamp) == pid &&
create_stamp <= analysis_stamp_) {
return analyzers_iterator_->second.get();
}
return GetNextAnalyzer();
}
ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetNextAnalyzer() {
DCHECK(analyzers_iterator_ != analyzers_.end());
int64_t create_stamp;
do {
++analyzers_iterator_;
if (analyzers_iterator_ == analyzers_.end())
return nullptr;
} while (analyzers_iterator_->second->GetProcessId(&create_stamp) !=
analyzers_iterator_pid_ ||
create_stamp > analysis_stamp_);
return analyzers_iterator_->second.get();
}
ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetAnalyzerForThread(
const ThreadKey& key) {
auto found = analyzers_.find(key);
if (found == analyzers_.end())
return nullptr;
return found->second.get();
}
ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
ProcessId pid,
uint32_t ref,
uint32_t id) {
ActivityUserData::Snapshot snapshot;
void* memory = allocator_->GetAsArray<char>(
ref, GlobalActivityTracker::kTypeIdUserDataRecord,
PersistentMemoryAllocator::kSizeAny);
if (memory) {
size_t size = allocator_->GetAllocSize(ref);
const ActivityUserData user_data(memory, size);
user_data.CreateSnapshot(&snapshot);
ProcessId process_id;
int64_t create_stamp;
if (!ActivityUserData::GetOwningProcessId(memory, &process_id,
&create_stamp) ||
process_id != pid || user_data.id() != id) {
// This allocation has been overwritten since it was created. Return an
// empty snapshot because whatever was captured is incorrect.
snapshot.clear();
}
}
return snapshot;
}
const ActivityUserData::Snapshot&
GlobalActivityAnalyzer::GetProcessDataSnapshot(ProcessId pid) {
auto iter = process_data_.find(pid);
if (iter == process_data_.end())
return GetEmptyUserDataSnapshot();
if (iter->second.create_stamp > analysis_stamp_)
return GetEmptyUserDataSnapshot();
DCHECK_EQ(pid, iter->second.process_id);
return iter->second.data;
}
std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
std::vector<std::string> messages;
PersistentMemoryAllocator::Reference ref;
PersistentMemoryAllocator::Iterator iter(allocator_.get());
while ((ref = iter.GetNextOfType(
GlobalActivityTracker::kTypeIdGlobalLogMessage)) != 0) {
const char* message = allocator_->GetAsArray<char>(
ref, GlobalActivityTracker::kTypeIdGlobalLogMessage,
PersistentMemoryAllocator::kSizeAny);
if (message)
messages.push_back(message);
}
return messages;
}
std::vector<GlobalActivityTracker::ModuleInfo>
GlobalActivityAnalyzer::GetModules(ProcessId pid) {
std::vector<GlobalActivityTracker::ModuleInfo> modules;
PersistentMemoryAllocator::Iterator iter(allocator_.get());
const GlobalActivityTracker::ModuleInfoRecord* record;
while (
(record =
iter.GetNextOfObject<GlobalActivityTracker::ModuleInfoRecord>()) !=
nullptr) {
ProcessId process_id;
int64_t create_stamp;
if (!OwningProcess::GetOwningProcessId(&record->owner, &process_id,
&create_stamp) ||
pid != process_id || create_stamp > analysis_stamp_) {
continue;
}
GlobalActivityTracker::ModuleInfo info;
if (record->DecodeTo(&info, allocator_->GetAllocSize(
allocator_->GetAsReference(record)))) {
modules.push_back(std::move(info));
}
}
return modules;
}
GlobalActivityAnalyzer::ProgramLocation
GlobalActivityAnalyzer::GetProgramLocationFromAddress(uint64_t address) {
// This should be implemented but it's never been a priority.
return { 0, 0 };
}
bool GlobalActivityAnalyzer::IsDataComplete() const {
DCHECK(allocator_);
return !allocator_->IsFull();
}
GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot() = default;
GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
const UserDataSnapshot& rhs) = default;
GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
UserDataSnapshot&& rhs) = default;
GlobalActivityAnalyzer::UserDataSnapshot::~UserDataSnapshot() = default;
void GlobalActivityAnalyzer::PrepareAllAnalyzers() {
// Record the time when analysis started.
analysis_stamp_ = base::Time::Now().ToInternalValue();
// Fetch all the records. This will retrieve only ones created since the
// last run since the PMA iterator will continue from where it left off.
uint32_t type;
PersistentMemoryAllocator::Reference ref;
while ((ref = allocator_iterator_.GetNext(&type)) != 0) {
switch (type) {
case GlobalActivityTracker::kTypeIdActivityTracker:
case GlobalActivityTracker::kTypeIdActivityTrackerFree:
case GlobalActivityTracker::kTypeIdProcessDataRecord:
case GlobalActivityTracker::kTypeIdProcessDataRecordFree:
case PersistentMemoryAllocator::kTypeIdTransitioning:
// Active, free, or transitioning: add it to the list of references
// for later analysis.
memory_references_.insert(ref);
break;
}
}
// Clear out any old information.
analyzers_.clear();
process_data_.clear();
process_ids_.clear();
std::set<ProcessId> seen_pids;
// Go through all the known references and create objects for them with
// snapshots of the current state.
for (PersistentMemoryAllocator::Reference memory_ref : memory_references_) {
// Get the actual data segment for the tracker. Any type will do since it
// is checked below.
void* const base = allocator_->GetAsArray<char>(
memory_ref, PersistentMemoryAllocator::kTypeIdAny,
PersistentMemoryAllocator::kSizeAny);
const size_t size = allocator_->GetAllocSize(memory_ref);
if (!base)
continue;
switch (allocator_->GetType(memory_ref)) {
case GlobalActivityTracker::kTypeIdActivityTracker: {
// Create the analyzer on the data. This will capture a snapshot of the
// tracker state. This can fail if the tracker is somehow corrupted or
// is in the process of shutting down.
std::unique_ptr<ThreadActivityAnalyzer> analyzer(
new ThreadActivityAnalyzer(base, size));
if (!analyzer->IsValid())
continue;
analyzer->AddGlobalInformation(this);
// Track PIDs.
ProcessId pid = analyzer->GetProcessId();
if (seen_pids.find(pid) == seen_pids.end()) {
process_ids_.push_back(pid);
seen_pids.insert(pid);
}
// Add this analyzer to the map of known ones, indexed by a unique
// thread
// identifier.
DCHECK(!base::Contains(analyzers_, analyzer->GetThreadKey()));
analyzer->allocator_reference_ = ref;
analyzers_[analyzer->GetThreadKey()] = std::move(analyzer);
} break;
case GlobalActivityTracker::kTypeIdProcessDataRecord: {
// Get the PID associated with this data record.
ProcessId process_id;
int64_t create_stamp;
ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
DCHECK(!base::Contains(process_data_, process_id));
// Create a snapshot of the data. This can fail if the data is somehow
// corrupted or the process shutdown and the memory being released.
UserDataSnapshot& snapshot = process_data_[process_id];
snapshot.process_id = process_id;
snapshot.create_stamp = create_stamp;
const ActivityUserData process_data(base, size);
if (!process_data.CreateSnapshot(&snapshot.data))
break;
// Check that nothing changed. If it did, forget what was recorded.
ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
if (process_id != snapshot.process_id ||
create_stamp != snapshot.create_stamp) {
process_data_.erase(process_id);
break;
}
// Track PIDs.
if (seen_pids.find(process_id) == seen_pids.end()) {
process_ids_.push_back(process_id);
seen_pids.insert(process_id);
}
} break;
}
}
// Reverse the list of PIDs so that they get popped in the order found.
ranges::reverse(process_ids_);
}
} // namespace debug
} // namespace base

@ -1,260 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_ACTIVITY_ANALYZER_H_
#define BASE_DEBUG_ACTIVITY_ANALYZER_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/base_export.h"
#include "base/debug/activity_tracker.h"
#include "base/memory/shared_memory_mapping.h"
#include "build/build_config.h"
namespace base {
namespace debug {
class GlobalActivityAnalyzer;
// This class provides analysis of data captured from a ThreadActivityTracker.
// When created, it takes a snapshot of the data held by the tracker and
// makes that information available to other code.
class BASE_EXPORT ThreadActivityAnalyzer {
public:
struct BASE_EXPORT Snapshot : ThreadActivityTracker::Snapshot {
Snapshot();
~Snapshot();
// The user-data snapshot for an activity, matching the |activity_stack|
// of ThreadActivityTracker::Snapshot, if any.
std::vector<ActivityUserData::Snapshot> user_data_stack;
};
// This class provides keys that uniquely identify a thread, even across
// multiple processes.
class ThreadKey {
public:
ThreadKey(ProcessId pid, int64_t tid) : pid_(pid), tid_(tid) {}
bool operator<(const ThreadKey& rhs) const {
if (pid_ != rhs.pid_)
return pid_ < rhs.pid_;
return tid_ < rhs.tid_;
}
bool operator==(const ThreadKey& rhs) const {
return (pid_ == rhs.pid_ && tid_ == rhs.tid_);
}
private:
ProcessId pid_;
int64_t tid_;
};
// Creates an analyzer for an existing activity |tracker|. A snapshot is taken
// immediately and the tracker is not referenced again.
explicit ThreadActivityAnalyzer(const ThreadActivityTracker& tracker);
// Creates an analyzer for a block of memory currently or previously in-use
// by an activity-tracker. A snapshot is taken immediately and the memory
// is not referenced again.
ThreadActivityAnalyzer(void* base, size_t size);
// Creates an analyzer for a block of memory held within a persistent-memory
// |allocator| at the given |reference|. A snapshot is taken immediately and
// the memory is not referenced again.
ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator,
PersistentMemoryAllocator::Reference reference);
ThreadActivityAnalyzer(const ThreadActivityAnalyzer&) = delete;
ThreadActivityAnalyzer& operator=(const ThreadActivityAnalyzer&) = delete;
~ThreadActivityAnalyzer();
// Adds information from the global analyzer.
void AddGlobalInformation(GlobalActivityAnalyzer* global);
// Returns true iff the contained data is valid. Results from all other
// methods are undefined if this returns false.
bool IsValid() { return activity_snapshot_valid_; }
// Gets the process id and its creation stamp.
ProcessId GetProcessId(int64_t* out_stamp = nullptr) {
if (out_stamp)
*out_stamp = activity_snapshot_.create_stamp;
return activity_snapshot_.process_id;
}
// Gets the name of the thread.
const std::string& GetThreadName() {
return activity_snapshot_.thread_name;
}
// Gets the TheadKey for this thread.
ThreadKey GetThreadKey() {
return ThreadKey(activity_snapshot_.process_id,
activity_snapshot_.thread_id);
}
const Snapshot& activity_snapshot() { return activity_snapshot_; }
private:
friend class GlobalActivityAnalyzer;
// The snapshot of the activity tracker taken at the moment of construction.
Snapshot activity_snapshot_;
// Flag indicating if the snapshot data is valid.
bool activity_snapshot_valid_;
// A reference into a persistent memory allocator, used by the global
// analyzer to know where this tracker came from.
PersistentMemoryAllocator::Reference allocator_reference_ = 0;
};
// This class manages analyzers for all known processes and threads as stored
// in a persistent memory allocator. It supports retrieval of them through
// iteration and directly using a ThreadKey, which allows for cross-references
// to be resolved.
// Note that though atomic snapshots are used and everything has its snapshot
// taken at the same time, the multi-snapshot itself is not atomic and thus may
// show small inconsistencies between threads if attempted on a live system.
class BASE_EXPORT GlobalActivityAnalyzer {
public:
struct ProgramLocation {
int module;
uintptr_t offset;
};
using ThreadKey = ThreadActivityAnalyzer::ThreadKey;
// Creates a global analyzer from a persistent memory allocator.
explicit GlobalActivityAnalyzer(
std::unique_ptr<PersistentMemoryAllocator> allocator);
GlobalActivityAnalyzer(const GlobalActivityAnalyzer&) = delete;
GlobalActivityAnalyzer& operator=(const GlobalActivityAnalyzer&) = delete;
~GlobalActivityAnalyzer();
// Creates a global analyzer using a given persistent-memory |allocator|.
static std::unique_ptr<GlobalActivityAnalyzer> CreateWithAllocator(
std::unique_ptr<PersistentMemoryAllocator> allocator);
#if !BUILDFLAG(IS_NACL)
// Creates a global analyzer using the contents of a file given in
// |file_path|.
static std::unique_ptr<GlobalActivityAnalyzer> CreateWithFile(
const FilePath& file_path);
#endif // !BUILDFLAG(IS_NACL)
// Like above but accesses an allocator in a mapped shared-memory segment.
static std::unique_ptr<GlobalActivityAnalyzer> CreateWithSharedMemory(
base::ReadOnlySharedMemoryMapping mapping);
// Iterates over all known valid processes and returns their PIDs or zero
// if there are no more. Calls to GetFirstProcess() will perform a global
// snapshot in order to provide a relatively consistent state across the
// future calls to GetNextProcess() and GetFirst/NextAnalyzer(). PIDs are
// returned in the order they're found meaning that a first-launched
// controlling process will be found first. Note, however, that space
// freed by an exiting process may be re-used by a later process.
ProcessId GetFirstProcess();
ProcessId GetNextProcess();
// Iterates over all known valid analyzers for the a given process or returns
// null if there are no more.
//
// GetFirstProcess() must be called first in order to capture a global
// snapshot! Ownership stays with the global analyzer object and all existing
// analyzer pointers are invalidated when GetFirstProcess() is called.
ThreadActivityAnalyzer* GetFirstAnalyzer(ProcessId pid);
ThreadActivityAnalyzer* GetNextAnalyzer();
// Gets the analyzer for a specific thread or null if there is none.
// Ownership stays with the global analyzer object.
ThreadActivityAnalyzer* GetAnalyzerForThread(const ThreadKey& key);
// Extract user data based on a reference and its identifier.
ActivityUserData::Snapshot GetUserDataSnapshot(ProcessId pid,
uint32_t ref,
uint32_t id);
// Extract the data for a specific process. An empty snapshot will be
// returned if the process is not known.
const ActivityUserData::Snapshot& GetProcessDataSnapshot(ProcessId pid);
// Gets all log messages stored within.
std::vector<std::string> GetLogMessages();
// Gets modules corresponding to a pid. This pid must come from a call to
// GetFirst/NextProcess. Only modules that were first registered prior to
// GetFirstProcess's snapshot are returned.
std::vector<GlobalActivityTracker::ModuleInfo> GetModules(ProcessId pid);
// Gets the corresponding "program location" for a given "program counter".
// This will return {0,0} if no mapping could be found.
ProgramLocation GetProgramLocationFromAddress(uint64_t address);
// Returns whether the data is complete. Data can be incomplete if the
// recording size quota is hit.
bool IsDataComplete() const;
private:
using AnalyzerMap =
std::map<ThreadKey, std::unique_ptr<ThreadActivityAnalyzer>>;
struct UserDataSnapshot {
// Complex class needs out-of-line ctor/dtor.
UserDataSnapshot();
UserDataSnapshot(const UserDataSnapshot& rhs);
UserDataSnapshot(UserDataSnapshot&& rhs);
~UserDataSnapshot();
ProcessId process_id;
int64_t create_stamp;
ActivityUserData::Snapshot data;
};
// Finds, creates, and indexes analyzers for all known processes and threads.
void PrepareAllAnalyzers();
// The persistent memory allocator holding all tracking data.
std::unique_ptr<PersistentMemoryAllocator> allocator_;
// The time stamp when analysis began. This is used to prevent looking into
// process IDs that get reused when analyzing a live system.
int64_t analysis_stamp_;
// The iterator for finding tracking information in the allocator.
PersistentMemoryAllocator::Iterator allocator_iterator_;
// A set of all interesting memory references found within the allocator.
std::set<PersistentMemoryAllocator::Reference> memory_references_;
// A set of all process-data memory references found within the allocator.
std::map<ProcessId, UserDataSnapshot> process_data_;
// A set of all process IDs collected during PrepareAllAnalyzers. These are
// popped and returned one-by-one with calls to GetFirst/NextProcess().
std::vector<ProcessId> process_ids_;
// A map, keyed by ThreadKey, of all valid activity analyzers.
AnalyzerMap analyzers_;
// The iterator within the analyzers_ map for returning analyzers through
// first/next iteration.
AnalyzerMap::iterator analyzers_iterator_;
ProcessId analyzers_iterator_pid_;
};
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_ACTIVITY_ANALYZER_H_

@ -1,31 +0,0 @@
// 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 "base/debug/activity_analyzer.h"
#include "base/logging.h"
#include "base/metrics/persistent_memory_allocator.h"
struct Environment {
Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
};
// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static Environment env;
if (size < 64u) { // sizeof(base::PersistentMemoryAllocator::SharedMetadata)
return 0;
}
std::unique_ptr<base::PersistentMemoryAllocator> allocator =
std::make_unique<base::PersistentMemoryAllocator>(
const_cast<uint8_t*>(data), size, /*page_size=*/0, /*id=*/0,
/*name=*/"",
/*read_only=*/true);
base::debug::GlobalActivityAnalyzer gaa(std::move(allocator));
std::ignore = gaa.GetFirstProcess();
return 0;
}

@ -1,13 +0,0 @@
"\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
kTypeIdActivityTracker = "\xB3\x81\x73\x5D"
kTypeIdUserDataRecord = "\xDA\xDD\x5E\x61"
kTypeIdGlobalLogMessage = "\xFA\x34\xF4\x4C"
kTypeIdProcessDataRecord = "\xDA\xDE\x5E\x61"
kTypeIdActivityTrackerFree = "\x4C\x7E\x8C\xA2"
kTypeIdUserDataRecordFree = "\x25\x22\xA1\x9E"
kTypeIdProcessDataRecordFree = "\x25\x21\xA1\x9E"
kGlobalCookie = "\xDC\x05\x83\x40"
kBlockCookieAllocated = "\x69\x92\x79\xC8"

@ -1,562 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/activity_analyzer.h"
#include <atomic>
#include <memory>
#include "base/auto_reset.h"
#include "base/containers/contains.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/pending_task.h"
#include "base/process/process.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/test/spin_wait.h"
#include "base/threading/platform_thread.h"
#include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace debug {
namespace {
class TestActivityTracker {
public:
TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size)
: mem_segment_(std::move(memory)),
tracker_(memset(mem_segment_.get(), 0, mem_size), mem_size) {}
~TestActivityTracker() = default;
ThreadActivityTracker& tracker() { return tracker_; }
private:
std::unique_ptr<char[]> mem_segment_; // Must outlive `tracker_`
ThreadActivityTracker tracker_;
};
} // namespace
class ActivityAnalyzerTest : public testing::Test {
public:
const int kMemorySize = 1 << 20; // 1MiB
const int kStackSize = 1 << 10; // 1KiB
ActivityAnalyzerTest() = default;
~ActivityAnalyzerTest() override {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (global_tracker) {
global_tracker->ReleaseTrackerForCurrentThreadForTesting();
delete global_tracker;
}
}
std::unique_ptr<TestActivityTracker> CreateActivityTracker() {
std::unique_ptr<char[]> memory(new char[kStackSize]);
return std::make_unique<TestActivityTracker>(std::move(memory), kStackSize);
}
template <typename Function>
void AsOtherProcess(ProcessId pid, Function function) {
std::unique_ptr<GlobalActivityTracker> old_global =
GlobalActivityTracker::ReleaseForTesting();
ASSERT_TRUE(old_global);
PersistentMemoryAllocator* old_allocator = old_global->allocator();
std::unique_ptr<PersistentMemoryAllocator> new_allocator(
std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(old_allocator->data()), old_allocator->size(), 0,
0, "", false));
GlobalActivityTracker::CreateWithAllocator(std::move(new_allocator), 3,
pid);
function();
GlobalActivityTracker::ReleaseForTesting();
GlobalActivityTracker::SetForTesting(std::move(old_global));
}
static void DoNothing() {}
};
TEST_F(ActivityAnalyzerTest, ThreadAnalyzerConstruction) {
std::unique_ptr<TestActivityTracker> tracker = CreateActivityTracker();
{
ThreadActivityAnalyzer analyzer(tracker->tracker());
EXPECT_TRUE(analyzer.IsValid());
EXPECT_EQ(PlatformThread::GetName(), analyzer.GetThreadName());
}
// More tests once Analyzer does more.
}
// GlobalActivityAnalyzer tests below.
namespace {
class SimpleActivityThread : public SimpleThread {
public:
SimpleActivityThread(const std::string& name,
const void* source,
Activity::Type activity,
const ActivityData& data)
: SimpleThread(name, Options()),
source_(source),
activity_(activity),
data_(data),
ready_(false),
exit_(false),
exit_condition_(&lock_) {}
SimpleActivityThread(const SimpleActivityThread&) = delete;
SimpleActivityThread& operator=(const SimpleActivityThread&) = delete;
~SimpleActivityThread() override = default;
void Run() override {
ThreadActivityTracker::ActivityId id =
GlobalActivityTracker::Get()
->GetOrCreateTrackerForCurrentThread()
->PushActivity(source_, activity_, data_);
{
AutoLock auto_lock(lock_);
ready_.store(true, std::memory_order_release);
while (!exit_.load(std::memory_order_relaxed))
exit_condition_.Wait();
}
GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id);
}
void Exit() {
AutoLock auto_lock(lock_);
exit_.store(true, std::memory_order_relaxed);
exit_condition_.Signal();
}
void WaitReady() {
SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire));
}
private:
raw_ptr<const void> source_;
Activity::Type activity_;
ActivityData data_;
std::atomic<bool> ready_;
std::atomic<bool> exit_;
Lock lock_;
ConditionVariable exit_condition_;
};
} // namespace
// TODO(1061320): Flaky under tsan.
#if defined(THREAD_SANITIZER)
#define MAYBE_GlobalAnalyzerConstruction DISABLED_GlobalAnalyzerConstruction
#else
#define MAYBE_GlobalAnalyzerConstruction GlobalAnalyzerConstruction
#endif
TEST_F(ActivityAnalyzerTest, MAYBE_GlobalAnalyzerConstruction) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
GlobalActivityTracker::Get()->process_data().SetString("foo", "bar");
PersistentMemoryAllocator* allocator =
GlobalActivityTracker::Get()->allocator();
GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
// The only thread at this point is the test thread of this process.
const ProcessId pid = analyzer.GetFirstProcess();
ASSERT_NE(ProcessId{0}, pid);
ThreadActivityAnalyzer* ta1 = analyzer.GetFirstAnalyzer(pid);
ASSERT_TRUE(ta1);
EXPECT_FALSE(analyzer.GetNextAnalyzer());
ThreadActivityAnalyzer::ThreadKey tk1 = ta1->GetThreadKey();
EXPECT_EQ(ta1, analyzer.GetAnalyzerForThread(tk1));
EXPECT_EQ(ProcessId{0}, analyzer.GetNextProcess());
// Create a second thread that will do something.
SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK,
ActivityData::ForTask(11));
t2.Start();
t2.WaitReady();
// Now there should be two. Calling GetFirstProcess invalidates any
// previously returned analyzer pointers.
ASSERT_EQ(pid, analyzer.GetFirstProcess());
EXPECT_TRUE(analyzer.GetFirstAnalyzer(pid));
EXPECT_TRUE(analyzer.GetNextAnalyzer());
EXPECT_FALSE(analyzer.GetNextAnalyzer());
EXPECT_EQ(ProcessId{0}, analyzer.GetNextProcess());
// Let thread exit.
t2.Exit();
t2.Join();
// Now there should be only one again.
ASSERT_EQ(pid, analyzer.GetFirstProcess());
ThreadActivityAnalyzer* ta2 = analyzer.GetFirstAnalyzer(pid);
ASSERT_TRUE(ta2);
EXPECT_FALSE(analyzer.GetNextAnalyzer());
ThreadActivityAnalyzer::ThreadKey tk2 = ta2->GetThreadKey();
EXPECT_EQ(ta2, analyzer.GetAnalyzerForThread(tk2));
EXPECT_EQ(tk1, tk2);
EXPECT_EQ(ProcessId{0}, analyzer.GetNextProcess());
// Verify that there is process data.
const ActivityUserData::Snapshot& data_snapshot =
analyzer.GetProcessDataSnapshot(pid);
ASSERT_LE(1U, data_snapshot.size());
EXPECT_EQ("bar", data_snapshot.at("foo").GetString());
}
TEST_F(ActivityAnalyzerTest, GlobalAnalyzerFromSharedMemory) {
base::MappedReadOnlyRegion shm =
base::ReadOnlySharedMemoryRegion::Create(kMemorySize);
ASSERT_TRUE(shm.IsValid());
base::WritableSharedMemoryMapping rw_mapping = std::move(shm.mapping);
base::ReadOnlySharedMemoryMapping ro_mapping = shm.region.Map();
ASSERT_TRUE(ro_mapping.IsValid());
GlobalActivityTracker::CreateWithSharedMemory(std::move(rw_mapping), 0, "",
3);
GlobalActivityTracker::Get()->process_data().SetString("foo", "bar");
std::unique_ptr<GlobalActivityAnalyzer> analyzer =
GlobalActivityAnalyzer::CreateWithSharedMemory(std::move(ro_mapping));
const ProcessId pid = analyzer->GetFirstProcess();
ASSERT_NE(ProcessId{0}, pid);
const ActivityUserData::Snapshot& data_snapshot =
analyzer->GetProcessDataSnapshot(pid);
ASSERT_LE(1U, data_snapshot.size());
EXPECT_EQ("bar", data_snapshot.at("foo").GetString());
}
TEST_F(ActivityAnalyzerTest, UserDataSnapshotTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
ThreadActivityAnalyzer::Snapshot tracker_snapshot;
const char string1a[] = "string1a";
const char string1b[] = "string1b";
const char string2a[] = "string2a";
const char string2b[] = "string2b";
PersistentMemoryAllocator* allocator =
GlobalActivityTracker::Get()->allocator();
GlobalActivityAnalyzer global_analyzer(
std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "",
true));
ThreadActivityTracker* tracker =
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
{
ScopedActivity activity1(1, 11, 111);
ActivityUserData& user_data1 = activity1.user_data();
user_data1.Set("raw1", "foo1", 4);
user_data1.SetString("string1", "bar1");
user_data1.SetChar("char1", '1');
user_data1.SetInt("int1", -1111);
user_data1.SetUint("uint1", 1111);
user_data1.SetBool("bool1", true);
user_data1.SetReference("ref1", string1a, sizeof(string1a));
user_data1.SetStringReference("sref1", string1b);
{
ScopedActivity activity2(2, 22, 222);
ActivityUserData& user_data2 = activity2.user_data();
user_data2.Set("raw2", "foo2", 4);
user_data2.SetString("string2", "bar2");
user_data2.SetChar("char2", '2');
user_data2.SetInt("int2", -2222);
user_data2.SetUint("uint2", 2222);
user_data2.SetBool("bool2", false);
user_data2.SetReference("ref2", string2a, sizeof(string2a));
user_data2.SetStringReference("sref2", string2b);
ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot));
ASSERT_EQ(2U, tracker_snapshot.activity_stack.size());
ThreadActivityAnalyzer analyzer(*tracker);
analyzer.AddGlobalInformation(&global_analyzer);
const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot =
analyzer.activity_snapshot();
ASSERT_EQ(2U, analyzer_snapshot.user_data_stack.size());
const ActivityUserData::Snapshot& user_data =
analyzer_snapshot.user_data_stack.at(1);
EXPECT_EQ(8U, user_data.size());
ASSERT_TRUE(Contains(user_data, "raw2"));
EXPECT_EQ("foo2", user_data.at("raw2").Get());
ASSERT_TRUE(Contains(user_data, "string2"));
EXPECT_EQ("bar2", user_data.at("string2").GetString());
ASSERT_TRUE(Contains(user_data, "char2"));
EXPECT_EQ('2', user_data.at("char2").GetChar());
ASSERT_TRUE(Contains(user_data, "int2"));
EXPECT_EQ(-2222, user_data.at("int2").GetInt());
ASSERT_TRUE(Contains(user_data, "uint2"));
EXPECT_EQ(2222U, user_data.at("uint2").GetUint());
ASSERT_TRUE(Contains(user_data, "bool2"));
EXPECT_FALSE(user_data.at("bool2").GetBool());
ASSERT_TRUE(Contains(user_data, "ref2"));
EXPECT_EQ(string2a, user_data.at("ref2").GetReference().data());
EXPECT_EQ(sizeof(string2a), user_data.at("ref2").GetReference().size());
ASSERT_TRUE(Contains(user_data, "sref2"));
EXPECT_EQ(string2b, user_data.at("sref2").GetStringReference().data());
EXPECT_EQ(strlen(string2b),
user_data.at("sref2").GetStringReference().size());
}
ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot));
ASSERT_EQ(1U, tracker_snapshot.activity_stack.size());
ThreadActivityAnalyzer analyzer(*tracker);
analyzer.AddGlobalInformation(&global_analyzer);
const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot =
analyzer.activity_snapshot();
ASSERT_EQ(1U, analyzer_snapshot.user_data_stack.size());
const ActivityUserData::Snapshot& user_data =
analyzer_snapshot.user_data_stack.at(0);
EXPECT_EQ(8U, user_data.size());
EXPECT_EQ("foo1", user_data.at("raw1").Get());
EXPECT_EQ("bar1", user_data.at("string1").GetString());
EXPECT_EQ('1', user_data.at("char1").GetChar());
EXPECT_EQ(-1111, user_data.at("int1").GetInt());
EXPECT_EQ(1111U, user_data.at("uint1").GetUint());
EXPECT_TRUE(user_data.at("bool1").GetBool());
EXPECT_EQ(string1a, user_data.at("ref1").GetReference().data());
EXPECT_EQ(sizeof(string1a), user_data.at("ref1").GetReference().size());
EXPECT_EQ(string1b, user_data.at("sref1").GetStringReference().data());
EXPECT_EQ(strlen(string1b),
user_data.at("sref1").GetStringReference().size());
}
ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot));
ASSERT_EQ(0U, tracker_snapshot.activity_stack.size());
}
TEST_F(ActivityAnalyzerTest, GlobalUserDataTest) {
const ProcessId pid = GetCurrentProcId();
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
const char string1[] = "foo";
const char string2[] = "bar";
PersistentMemoryAllocator* allocator =
GlobalActivityTracker::Get()->allocator();
GlobalActivityAnalyzer global_analyzer(
std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "",
true));
ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
ASSERT_NE(0U, process_data.id());
process_data.Set("raw", "foo", 3);
process_data.SetString("string", "bar");
process_data.SetBool("bool1", false);
process_data.SetBool("bool2", false)->store(true, std::memory_order_relaxed);
process_data.SetChar("char1", '7');
process_data.SetChar("char2", '8')->store('9', std::memory_order_relaxed);
process_data.SetInt("int", -9998)->fetch_sub(1, std::memory_order_relaxed);
process_data.SetUint("uint", 9998)->fetch_add(1, std::memory_order_relaxed);
process_data.SetReference("ref", string1, sizeof(string1));
process_data.SetStringReference("sref", string2);
ProcessId first_pid = global_analyzer.GetFirstProcess();
DCHECK_EQ(pid, first_pid);
const ActivityUserData::Snapshot& snapshot =
global_analyzer.GetProcessDataSnapshot(pid);
ASSERT_TRUE(Contains(snapshot, "raw"));
EXPECT_EQ("foo", snapshot.at("raw").Get());
ASSERT_TRUE(Contains(snapshot, "string"));
EXPECT_EQ("bar", snapshot.at("string").GetString());
ASSERT_TRUE(Contains(snapshot, "bool1"));
EXPECT_FALSE(snapshot.at("bool1").GetBool());
ASSERT_TRUE(Contains(snapshot, "bool2"));
EXPECT_TRUE(snapshot.at("bool2").GetBool());
ASSERT_TRUE(Contains(snapshot, "char1"));
EXPECT_EQ('7', snapshot.at("char1").GetChar());
ASSERT_TRUE(Contains(snapshot, "char2"));
EXPECT_EQ('9', snapshot.at("char2").GetChar());
ASSERT_TRUE(Contains(snapshot, "int"));
EXPECT_EQ(-9999, snapshot.at("int").GetInt());
ASSERT_TRUE(Contains(snapshot, "uint"));
EXPECT_EQ(9999U, snapshot.at("uint").GetUint());
ASSERT_TRUE(Contains(snapshot, "ref"));
EXPECT_EQ(string1, snapshot.at("ref").GetReference().data());
EXPECT_EQ(sizeof(string1), snapshot.at("ref").GetReference().size());
ASSERT_TRUE(Contains(snapshot, "sref"));
EXPECT_EQ(string2, snapshot.at("sref").GetStringReference().data());
EXPECT_EQ(strlen(string2), snapshot.at("sref").GetStringReference().size());
}
TEST_F(ActivityAnalyzerTest, GlobalModulesTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
GlobalActivityTracker* global = GlobalActivityTracker::Get();
PersistentMemoryAllocator* allocator = global->allocator();
GlobalActivityAnalyzer global_analyzer(
std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "",
true));
GlobalActivityTracker::ModuleInfo info1;
info1.is_loaded = true;
info1.address = 0x12345678;
info1.load_time = 1111;
info1.size = 0xABCDEF;
info1.timestamp = 111;
info1.age = 11;
info1.identifier[0] = 1;
info1.file = "anything";
info1.debug_file = "elsewhere";
global->RecordModuleInfo(info1);
std::vector<GlobalActivityTracker::ModuleInfo> modules1;
modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess());
ASSERT_EQ(1U, modules1.size());
GlobalActivityTracker::ModuleInfo& stored1a = modules1[0];
EXPECT_EQ(info1.is_loaded, stored1a.is_loaded);
EXPECT_EQ(info1.address, stored1a.address);
EXPECT_NE(info1.load_time, stored1a.load_time);
EXPECT_EQ(info1.size, stored1a.size);
EXPECT_EQ(info1.timestamp, stored1a.timestamp);
EXPECT_EQ(info1.age, stored1a.age);
EXPECT_EQ(info1.identifier[0], stored1a.identifier[0]);
EXPECT_EQ(info1.file, stored1a.file);
EXPECT_EQ(info1.debug_file, stored1a.debug_file);
info1.is_loaded = false;
global->RecordModuleInfo(info1);
modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess());
ASSERT_EQ(1U, modules1.size());
GlobalActivityTracker::ModuleInfo& stored1b = modules1[0];
EXPECT_EQ(info1.is_loaded, stored1b.is_loaded);
EXPECT_EQ(info1.address, stored1b.address);
EXPECT_NE(info1.load_time, stored1b.load_time);
EXPECT_EQ(info1.size, stored1b.size);
EXPECT_EQ(info1.timestamp, stored1b.timestamp);
EXPECT_EQ(info1.age, stored1b.age);
EXPECT_EQ(info1.identifier[0], stored1b.identifier[0]);
EXPECT_EQ(info1.file, stored1b.file);
EXPECT_EQ(info1.debug_file, stored1b.debug_file);
GlobalActivityTracker::ModuleInfo info2;
info2.is_loaded = true;
info2.address = 0x87654321;
info2.load_time = 2222;
info2.size = 0xFEDCBA;
info2.timestamp = 222;
info2.age = 22;
info2.identifier[0] = 2;
info2.file = "nothing";
info2.debug_file = "farewell";
global->RecordModuleInfo(info2);
std::vector<GlobalActivityTracker::ModuleInfo> modules2;
modules2 = global_analyzer.GetModules(global_analyzer.GetFirstProcess());
ASSERT_EQ(2U, modules2.size());
GlobalActivityTracker::ModuleInfo& stored2 = modules2[1];
EXPECT_EQ(info2.is_loaded, stored2.is_loaded);
EXPECT_EQ(info2.address, stored2.address);
EXPECT_NE(info2.load_time, stored2.load_time);
EXPECT_EQ(info2.size, stored2.size);
EXPECT_EQ(info2.timestamp, stored2.timestamp);
EXPECT_EQ(info2.age, stored2.age);
EXPECT_EQ(info2.identifier[0], stored2.identifier[0]);
EXPECT_EQ(info2.file, stored2.file);
EXPECT_EQ(info2.debug_file, stored2.debug_file);
}
TEST_F(ActivityAnalyzerTest, GlobalLogMessages) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
PersistentMemoryAllocator* allocator =
GlobalActivityTracker::Get()->allocator();
GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
GlobalActivityTracker::Get()->RecordLogMessage("hello world");
GlobalActivityTracker::Get()->RecordLogMessage("foo bar");
std::vector<std::string> messages = analyzer.GetLogMessages();
ASSERT_EQ(2U, messages.size());
EXPECT_EQ("hello world", messages[0]);
EXPECT_EQ("foo bar", messages[1]);
}
TEST_F(ActivityAnalyzerTest, GlobalMultiProcess) {
constexpr ProcessId kProcessIdA = 1001;
constexpr ProcessId kProcessIdB = 2002;
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3,
kProcessIdA);
GlobalActivityTracker* global = GlobalActivityTracker::Get();
PersistentMemoryAllocator* allocator = global->allocator();
EXPECT_EQ(kProcessIdA, global->process_id());
ProcessId process_id;
int64_t create_stamp;
ActivityUserData::GetOwningProcessId(
GlobalActivityTracker::Get()->process_data().GetBaseAddress(),
&process_id, &create_stamp);
ASSERT_EQ(kProcessIdA, process_id);
GlobalActivityTracker::Get()->process_data().SetInt("pid",
global->process_id());
GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
AsOtherProcess(kProcessIdB, [&global, kProcessIdB]() {
ASSERT_NE(global, GlobalActivityTracker::Get());
EXPECT_EQ(kProcessIdB, GlobalActivityTracker::Get()->process_id());
ProcessId process_id;
int64_t create_stamp;
ActivityUserData::GetOwningProcessId(
GlobalActivityTracker::Get()->process_data().GetBaseAddress(),
&process_id, &create_stamp);
ASSERT_EQ(kProcessIdB, process_id);
GlobalActivityTracker::Get()->process_data().SetInt(
"pid", GlobalActivityTracker::Get()->process_id());
});
ASSERT_EQ(global, GlobalActivityTracker::Get());
EXPECT_EQ(kProcessIdA, GlobalActivityTracker::Get()->process_id());
const ProcessId pid1 = analyzer.GetFirstProcess();
ASSERT_EQ(kProcessIdA, pid1);
const ProcessId pid2 = analyzer.GetNextProcess();
ASSERT_EQ(kProcessIdB, pid2);
EXPECT_EQ(ProcessId{0}, analyzer.GetNextProcess());
const ActivityUserData::Snapshot& pdata1 =
analyzer.GetProcessDataSnapshot(pid1);
const ActivityUserData::Snapshot& pdata2 =
analyzer.GetProcessDataSnapshot(pid2);
EXPECT_EQ(kProcessIdA, static_cast<ProcessId>(pdata1.at("pid").GetInt()));
EXPECT_EQ(kProcessIdB, static_cast<ProcessId>(pdata2.at("pid").GetInt()));
}
} // namespace debug
} // namespace base

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,593 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/activity_tracker.h"
#include <memory>
#include <utility>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/pending_task.h"
#include "base/rand_util.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/test/spin_wait.h"
#include "base/threading/platform_thread.h"
#include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace debug {
namespace {
class TestActivityTracker {
public:
TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size)
: mem_segment_(std::move(memory)),
tracker_(memset(mem_segment_.get(), 0, mem_size), mem_size) {}
~TestActivityTracker() = default;
ThreadActivityTracker& tracker() { return tracker_; }
private:
std::unique_ptr<char[]> mem_segment_; // Must outlive `tracker_`
ThreadActivityTracker tracker_;
};
} // namespace
class ActivityTrackerTest : public testing::Test {
public:
const int kMemorySize = 1 << 20; // 1MiB
const int kStackSize = 1 << 10; // 1KiB
using ActivityId = ThreadActivityTracker::ActivityId;
ActivityTrackerTest() = default;
~ActivityTrackerTest() override {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (global_tracker) {
global_tracker->ReleaseTrackerForCurrentThreadForTesting();
delete global_tracker;
}
}
std::unique_ptr<TestActivityTracker> CreateActivityTracker() {
std::unique_ptr<char[]> memory(new char[kStackSize]);
return std::make_unique<TestActivityTracker>(std::move(memory), kStackSize);
}
size_t GetGlobalActiveTrackerCount() {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (!global_tracker)
return 0;
return global_tracker->thread_tracker_count_.load(
std::memory_order_relaxed);
}
size_t GetGlobalInactiveTrackerCount() {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (!global_tracker)
return 0;
AutoLock autolock(global_tracker->thread_tracker_allocator_lock_);
return global_tracker->thread_tracker_allocator_.cache_used();
}
size_t GetGlobalUserDataMemoryCacheUsed() {
AutoLock autolock(GlobalActivityTracker::Get()->user_data_allocator_lock_);
return GlobalActivityTracker::Get()->user_data_allocator_.cache_used();
}
void HandleProcessExit(ProcessId id,
int64_t stamp,
int code,
GlobalActivityTracker::ProcessPhase phase,
std::string&& command,
ActivityUserData::Snapshot&& data) {
exit_id_ = id;
exit_stamp_ = stamp;
exit_code_ = code;
exit_phase_ = phase;
exit_command_ = std::move(command);
exit_data_ = std::move(data);
}
ProcessId exit_id_ = 0;
int64_t exit_stamp_;
int exit_code_;
GlobalActivityTracker::ProcessPhase exit_phase_;
std::string exit_command_;
ActivityUserData::Snapshot exit_data_;
};
TEST_F(ActivityTrackerTest, UserDataTest) {
char buffer[256];
memset(buffer, 0, sizeof(buffer));
ActivityUserData data(buffer, sizeof(buffer));
size_t space = sizeof(buffer) - sizeof(ActivityUserData::MemoryHeader);
ASSERT_EQ(space, data.memory_.size());
data.SetInt("foo", 1);
space -= 24;
ASSERT_EQ(space, data.memory_.size());
data.SetUint("b", 1U); // Small names fit beside header in a word.
space -= 16;
ASSERT_EQ(space, data.memory_.size());
data.Set("c", buffer, 10);
space -= 24;
ASSERT_EQ(space, data.memory_.size());
data.SetString("dear john", "it's been fun");
space -= 32;
ASSERT_EQ(space, data.memory_.size());
data.Set("c", buffer, 20);
ASSERT_EQ(space, data.memory_.size());
data.SetString("dear john", "but we're done together");
ASSERT_EQ(space, data.memory_.size());
data.SetString("dear john", "bye");
ASSERT_EQ(space, data.memory_.size());
data.SetChar("d", 'x');
space -= 8;
ASSERT_EQ(space, data.memory_.size());
data.SetBool("ee", true);
space -= 16;
ASSERT_EQ(space, data.memory_.size());
data.SetString("f", "");
space -= 8;
ASSERT_EQ(space, data.memory_.size());
}
TEST_F(ActivityTrackerTest, PushPopTest) {
std::unique_ptr<TestActivityTracker> tracker = CreateActivityTracker();
ThreadActivityTracker::Snapshot snapshot;
ASSERT_TRUE(tracker->tracker().CreateSnapshot(&snapshot));
ASSERT_EQ(0U, snapshot.activity_stack_depth);
ASSERT_EQ(0U, snapshot.activity_stack.size());
char origin1;
ActivityId id1 = tracker->tracker().PushActivity(&origin1, Activity::ACT_TASK,
ActivityData::ForTask(11));
ASSERT_TRUE(tracker->tracker().CreateSnapshot(&snapshot));
ASSERT_EQ(1U, snapshot.activity_stack_depth);
ASSERT_EQ(1U, snapshot.activity_stack.size());
EXPECT_NE(0, snapshot.activity_stack[0].time_internal);
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1),
snapshot.activity_stack[0].origin_address);
EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id);
char origin2;
char lock2;
ActivityId id2 = tracker->tracker().PushActivity(
&origin2, Activity::ACT_LOCK, ActivityData::ForLock(&lock2));
ASSERT_TRUE(tracker->tracker().CreateSnapshot(&snapshot));
ASSERT_EQ(2U, snapshot.activity_stack_depth);
ASSERT_EQ(2U, snapshot.activity_stack.size());
EXPECT_LE(snapshot.activity_stack[0].time_internal,
snapshot.activity_stack[1].time_internal);
EXPECT_EQ(Activity::ACT_LOCK, snapshot.activity_stack[1].activity_type);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin2),
snapshot.activity_stack[1].origin_address);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2),
snapshot.activity_stack[1].data.lock.lock_address);
tracker->tracker().PopActivity(id2);
ASSERT_TRUE(tracker->tracker().CreateSnapshot(&snapshot));
ASSERT_EQ(1U, snapshot.activity_stack_depth);
ASSERT_EQ(1U, snapshot.activity_stack.size());
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1),
snapshot.activity_stack[0].origin_address);
EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id);
tracker->tracker().PopActivity(id1);
ASSERT_TRUE(tracker->tracker().CreateSnapshot(&snapshot));
ASSERT_EQ(0U, snapshot.activity_stack_depth);
ASSERT_EQ(0U, snapshot.activity_stack.size());
}
TEST_F(ActivityTrackerTest, ScopedTaskTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
ThreadActivityTracker* tracker =
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
ThreadActivityTracker::Snapshot snapshot;
ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
ASSERT_EQ(0U, snapshot.activity_stack_depth);
ASSERT_EQ(0U, snapshot.activity_stack.size());
{
PendingTask task1(FROM_HERE, DoNothing());
ScopedTaskRunActivity activity1(task1);
[[maybe_unused]] ActivityUserData& user_data1 = activity1.user_data();
EXPECT_TRUE(activity1.IsRecorded());
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
ASSERT_EQ(1U, snapshot.activity_stack_depth);
ASSERT_EQ(1U, snapshot.activity_stack.size());
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
{
PendingTask task2(FROM_HERE, DoNothing());
ScopedTaskRunActivity activity2(task2);
[[maybe_unused]] ActivityUserData& user_data2 = activity2.user_data();
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
ASSERT_EQ(2U, snapshot.activity_stack_depth);
ASSERT_EQ(2U, snapshot.activity_stack.size());
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[1].activity_type);
}
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
ASSERT_EQ(1U, snapshot.activity_stack_depth);
ASSERT_EQ(1U, snapshot.activity_stack.size());
EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
}
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
ASSERT_EQ(0U, snapshot.activity_stack_depth);
ASSERT_EQ(0U, snapshot.activity_stack.size());
ASSERT_EQ(2U, GetGlobalUserDataMemoryCacheUsed());
}
namespace {
class SimpleLockThread : public SimpleThread {
public:
SimpleLockThread(const std::string& name, Lock* lock)
: SimpleThread(name, Options()),
lock_(lock),
data_changed_(false),
is_running_(false) {}
SimpleLockThread(const SimpleLockThread&) = delete;
SimpleLockThread& operator=(const SimpleLockThread&) = delete;
~SimpleLockThread() override = default;
void Run() override {
ThreadActivityTracker* tracker =
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
uint32_t pre_version = tracker->GetDataVersionForTesting();
is_running_.store(true, std::memory_order_relaxed);
lock_->Acquire();
data_changed_ = tracker->GetDataVersionForTesting() != pre_version;
lock_->Release();
is_running_.store(false, std::memory_order_relaxed);
}
bool IsRunning() { return is_running_.load(std::memory_order_relaxed); }
bool WasDataChanged() { return data_changed_; }
private:
raw_ptr<Lock> lock_;
bool data_changed_;
std::atomic<bool> is_running_;
};
} // namespace
TEST_F(ActivityTrackerTest, LockTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
ThreadActivityTracker* tracker =
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
ThreadActivityTracker::Snapshot snapshot;
ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
Lock lock;
uint32_t pre_version = tracker->GetDataVersionForTesting();
// Check no activity when only "trying" a lock.
EXPECT_TRUE(lock.Try());
EXPECT_EQ(pre_version, tracker->GetDataVersionForTesting());
lock.AssertAcquired();
lock.Release();
EXPECT_EQ(pre_version, tracker->GetDataVersionForTesting());
// Check no activity when acquiring a free lock.
SimpleLockThread t1("locker1", &lock);
t1.Start();
t1.Join();
EXPECT_FALSE(t1.WasDataChanged());
// Check that activity is recorded when acquring a busy lock.
SimpleLockThread t2("locker2", &lock);
lock.Acquire();
t2.Start();
while (!t2.IsRunning())
PlatformThread::Sleep(Milliseconds(10));
// t2 can't join until the lock is released but have to give time for t2 to
// actually block on the lock before releasing it or the results will not
// be correct.
PlatformThread::Sleep(Milliseconds(200));
lock.Release();
// Now the results will be valid.
t2.Join();
EXPECT_TRUE(t2.WasDataChanged());
}
TEST_F(ActivityTrackerTest, ExceptionTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
GlobalActivityTracker* global = GlobalActivityTracker::Get();
ThreadActivityTracker* tracker =
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
ThreadActivityTracker::Snapshot snapshot;
ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed());
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
ASSERT_EQ(0U, snapshot.last_exception.activity_type);
char origin;
global->RecordException(&origin, 42);
ASSERT_TRUE(tracker->CreateSnapshot(&snapshot));
EXPECT_EQ(Activity::ACT_EXCEPTION, snapshot.last_exception.activity_type);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin),
snapshot.last_exception.origin_address);
EXPECT_EQ(42U, snapshot.last_exception.data.exception.code);
}
TEST_F(ActivityTrackerTest, CreateWithFileTest) {
const char temp_name[] = "CreateWithFileTest";
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
const size_t temp_size = 64 << 10; // 64 KiB
// Create a global tracker on a new file.
ASSERT_FALSE(PathExists(temp_file));
GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3);
GlobalActivityTracker* global = GlobalActivityTracker::Get();
EXPECT_EQ(std::string("foo"), global->allocator()->Name());
global->ReleaseTrackerForCurrentThreadForTesting();
delete global;
// Create a global tracker over an existing file, replacing it. If the
// replacement doesn't work, the name will remain as it was first created.
ASSERT_TRUE(PathExists(temp_file));
GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3);
global = GlobalActivityTracker::Get();
EXPECT_EQ(std::string("bar"), global->allocator()->Name());
global->ReleaseTrackerForCurrentThreadForTesting();
delete global;
}
// GlobalActivityTracker tests below.
TEST_F(ActivityTrackerTest, BasicTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
GlobalActivityTracker* global = GlobalActivityTracker::Get();
// Ensure the data repositories have backing store, indicated by non-zero ID.
EXPECT_NE(0U, global->process_data().id());
}
namespace {
class SimpleActivityThread : public SimpleThread {
public:
SimpleActivityThread(const std::string& name,
const void* origin,
Activity::Type activity,
const ActivityData& data)
: SimpleThread(name, Options()),
origin_(origin),
activity_(activity),
data_(data),
ready_(false),
exit_(false),
exit_condition_(&lock_) {}
SimpleActivityThread(const SimpleActivityThread&) = delete;
SimpleActivityThread& operator=(const SimpleActivityThread&) = delete;
~SimpleActivityThread() override = default;
void Run() override {
ThreadActivityTracker::ActivityId id =
GlobalActivityTracker::Get()
->GetOrCreateTrackerForCurrentThread()
->PushActivity(origin_, activity_, data_);
{
AutoLock auto_lock(lock_);
ready_.store(true, std::memory_order_release);
while (!exit_.load(std::memory_order_relaxed))
exit_condition_.Wait();
}
GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id);
}
void Exit() {
AutoLock auto_lock(lock_);
exit_.store(true, std::memory_order_relaxed);
exit_condition_.Signal();
}
void WaitReady() {
SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire));
}
private:
raw_ptr<const void> origin_;
Activity::Type activity_;
ActivityData data_;
std::atomic<bool> ready_;
std::atomic<bool> exit_;
Lock lock_;
ConditionVariable exit_condition_;
};
} // namespace
TEST_F(ActivityTrackerTest, ThreadDeathTest) {
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
const size_t starting_active = GetGlobalActiveTrackerCount();
const size_t starting_inactive = GetGlobalInactiveTrackerCount();
SimpleActivityThread t1("t1", nullptr, Activity::ACT_TASK,
ActivityData::ForTask(11));
t1.Start();
t1.WaitReady();
EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
t1.Exit();
t1.Join();
EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
// Start another thread and ensure it re-uses the existing memory.
SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK,
ActivityData::ForTask(22));
t2.Start();
t2.WaitReady();
EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount());
EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount());
t2.Exit();
t2.Join();
EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount());
EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount());
}
TEST_F(ActivityTrackerTest, ProcessDeathTest) {
// This doesn't actually create and destroy a process. Instead, it uses for-
// testing interfaces to simulate data created by other processes.
const ProcessId other_process_id = GetCurrentProcId() + 1;
GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
GlobalActivityTracker* global = GlobalActivityTracker::Get();
ThreadActivityTracker* thread = global->GetOrCreateTrackerForCurrentThread();
// Get callbacks for process exit.
global->SetProcessExitCallback(
BindRepeating(&ActivityTrackerTest::HandleProcessExit, Unretained(this)));
// Pretend than another process has started.
global->RecordProcessLaunch(other_process_id, FILE_PATH_LITERAL("foo --bar"));
// Do some activities.
PendingTask task(FROM_HERE, DoNothing());
ScopedTaskRunActivity activity(task);
ActivityUserData& user_data = activity.user_data();
ASSERT_NE(0U, user_data.id());
// Get the memory-allocator references to that data.
PersistentMemoryAllocator::Reference proc_data_ref =
global->allocator()->GetAsReference(
global->process_data().GetBaseAddress(),
GlobalActivityTracker::kTypeIdProcessDataRecord);
ASSERT_TRUE(proc_data_ref);
PersistentMemoryAllocator::Reference tracker_ref =
global->allocator()->GetAsReference(
thread->GetBaseAddress(),
GlobalActivityTracker::kTypeIdActivityTracker);
ASSERT_TRUE(tracker_ref);
PersistentMemoryAllocator::Reference user_data_ref =
global->allocator()->GetAsReference(
user_data.GetBaseAddress(),
GlobalActivityTracker::kTypeIdUserDataRecord);
ASSERT_TRUE(user_data_ref);
// Make a copy of the thread-tracker state so it can be restored later.
const size_t tracker_size = global->allocator()->GetAllocSize(tracker_ref);
std::unique_ptr<char[]> tracker_copy(new char[tracker_size]);
memcpy(tracker_copy.get(), thread->GetBaseAddress(), tracker_size);
// Change the objects to appear to be owned by another process. Use a "past"
// time so that exit-time is always later than create-time.
const int64_t past_stamp = Time::Now().ToInternalValue() - 1;
ProcessId owning_id;
int64_t stamp;
ASSERT_TRUE(ActivityUserData::GetOwningProcessId(
global->process_data().GetBaseAddress(), &owning_id, &stamp));
EXPECT_NE(other_process_id, owning_id);
ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId(
thread->GetBaseAddress(), &owning_id, &stamp));
EXPECT_NE(other_process_id, owning_id);
ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(),
&owning_id, &stamp));
EXPECT_NE(other_process_id, owning_id);
global->process_data().SetOwningProcessIdForTesting(other_process_id,
past_stamp);
thread->SetOwningProcessIdForTesting(other_process_id, past_stamp);
user_data.SetOwningProcessIdForTesting(other_process_id, past_stamp);
ASSERT_TRUE(ActivityUserData::GetOwningProcessId(
global->process_data().GetBaseAddress(), &owning_id, &stamp));
EXPECT_EQ(other_process_id, owning_id);
ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId(
thread->GetBaseAddress(), &owning_id, &stamp));
EXPECT_EQ(other_process_id, owning_id);
ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(),
&owning_id, &stamp));
EXPECT_EQ(other_process_id, owning_id);
// Check that process exit will perform callback and free the allocations.
ASSERT_EQ(ProcessId{0}, exit_id_);
ASSERT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecord,
global->allocator()->GetType(proc_data_ref));
ASSERT_EQ(GlobalActivityTracker::kTypeIdActivityTracker,
global->allocator()->GetType(tracker_ref));
ASSERT_EQ(GlobalActivityTracker::kTypeIdUserDataRecord,
global->allocator()->GetType(user_data_ref));
global->RecordProcessExit(other_process_id, 0);
EXPECT_EQ(other_process_id, exit_id_);
EXPECT_EQ("foo --bar", exit_command_);
EXPECT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecordFree,
global->allocator()->GetType(proc_data_ref));
EXPECT_EQ(GlobalActivityTracker::kTypeIdActivityTrackerFree,
global->allocator()->GetType(tracker_ref));
EXPECT_EQ(GlobalActivityTracker::kTypeIdUserDataRecordFree,
global->allocator()->GetType(user_data_ref));
// Restore memory contents and types so things don't crash when doing real
// process clean-up.
memcpy(const_cast<void*>(thread->GetBaseAddress()), tracker_copy.get(),
tracker_size);
global->allocator()->ChangeType(
proc_data_ref, GlobalActivityTracker::kTypeIdProcessDataRecord,
GlobalActivityTracker::kTypeIdUserDataRecordFree, false);
global->allocator()->ChangeType(
tracker_ref, GlobalActivityTracker::kTypeIdActivityTracker,
GlobalActivityTracker::kTypeIdActivityTrackerFree, false);
global->allocator()->ChangeType(
user_data_ref, GlobalActivityTracker::kTypeIdUserDataRecord,
GlobalActivityTracker::kTypeIdUserDataRecordFree, false);
}
} // namespace debug
} // namespace base

@ -7,7 +7,6 @@
#include <stdlib.h>
#include "base/compiler_specific.h"
#include "base/debug/activity_tracker.h"
#include "base/memory/raw_ptr.h"
#include "base/test/gtest_util.h"
#include "base/threading/platform_thread.h"
@ -154,48 +153,6 @@ TEST(LockTest, TryLock) {
lock.Release();
}
TEST(LockTest, TryTrackedLock) {
// Enable the activity tracker.
debug::GlobalActivityTracker::CreateWithLocalMemory(64 << 10, 0, "", 3, 0);
Lock lock;
ASSERT_TRUE(lock.Try());
lock.AssertAcquired();
// This thread will not be able to get the lock.
{
TryLockTestThread thread(&lock);
PlatformThreadHandle handle;
ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
PlatformThread::Join(handle);
ASSERT_FALSE(thread.got_lock());
}
lock.Release();
// This thread will....
{
TryLockTestThread thread(&lock);
PlatformThreadHandle handle;
ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
PlatformThread::Join(handle);
ASSERT_TRUE(thread.got_lock());
// But it released it....
ASSERT_TRUE(lock.Try());
lock.AssertAcquired();
}
lock.Release();
debug::GlobalActivityTracker::ReleaseForTesting();
}
// Tests that locks actually exclude -------------------------------------------
class MutexLockTestThread : public PlatformThread::Delegate {

@ -54,8 +54,6 @@ chrome/install_static/install_modes.h
chrome/install_static/install_util.h
chrome/install_static/test/scoped_install_details.h
chrome/installer/util/google_update_settings.h
components/browser_watcher/features.h
components/browser_watcher/stability_paths.h
components/cdm/browser/cdm_message_filter_android.h
components/device_event_log/device_event_log_export.h
components/login/login_export.h

@ -207,7 +207,6 @@ if (!is_android && !is_mac) {
"//chrome/install_static:secondary_module",
"//chrome/installer/util:constants",
"//chrome/installer/util:did_run_support",
"//components/browser_watcher:browser_watcher_client",
"//components/crash/core/app",
"//components/crash/core/app:run_as_crashpad_handler",
"//components/crash/core/common",
@ -412,7 +411,6 @@ if (is_win) {
"//chrome/common/profiler",
"//chrome/install_static:install_static_util",
"//chrome/install_static:secondary_module",
"//components/browser_watcher:stability_client",
"//components/crash/core/app",
"//components/policy:generated",
"//content/public/app",

@ -232,7 +232,6 @@ static_library("test_support") {
deps += [
"//chrome/chrome_elf:test_stubs",
"//chrome/install_static:install_static_util",
"//components/browser_watcher:stability_client",
"//sandbox/win:sandbox",
]
}

@ -18,7 +18,6 @@ include_rules = [
"+chromeos/hugepage_text/hugepage_text.h",
"+chromeos/lacros",
"+chromeos/startup",
"+components/browser_watcher",
"+components/component_updater",
"+components/content_settings/core/common/content_settings_pattern.h",
"+components/crash",

@ -110,8 +110,6 @@
#include "chrome/chrome_elf/chrome_elf_main.h"
#include "chrome/common/child_process_logging.h"
#include "chrome/common/win/delay_load_failure_hook.h"
#include "chrome/install_static/install_util.h"
#include "components/browser_watcher/extended_crash_reporting.h"
#include "sandbox/win/src/sandbox.h"
#include "sandbox/win/src/sandbox_factory.h"
#include "ui/base/resource/resource_bundle_win.h"
@ -289,29 +287,6 @@ bool IsSandboxedProcess() {
return is_sandboxed_process_func && is_sandboxed_process_func();
}
void SetUpExtendedCrashReporting(bool is_browser_process) {
browser_watcher::ExtendedCrashReporting* extended_crash_reporting =
browser_watcher::ExtendedCrashReporting::SetUpIfEnabled(
is_browser_process
? browser_watcher::ExtendedCrashReporting::kBrowserProcess
: browser_watcher::ExtendedCrashReporting::kOther);
if (!extended_crash_reporting)
return;
// Record product, version, channel and special build strings.
wchar_t exe_file[MAX_PATH] = {};
CHECK(::GetModuleFileName(nullptr, exe_file, std::size(exe_file)));
std::wstring product_name, version_number, channel_name, special_build;
install_static::GetExecutableVersionDetails(
exe_file, &product_name, &version_number, &special_build, &channel_name);
extended_crash_reporting->SetProductStrings(
base::WideToUTF16(product_name), base::WideToUTF16(version_number),
base::WideToUTF16(channel_name), base::WideToUTF16(special_build));
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
@ -971,7 +946,6 @@ void ChromeMainDelegate::CommonEarlyInitialization() {
}
#if BUILDFLAG(IS_WIN)
SetUpExtendedCrashReporting(is_browser_process);
base::sequence_manager::internal::ThreadControllerPowerMonitor::
InitializeOnMainThread();
base::InitializePlatformThreadFeatures();

@ -5781,8 +5781,6 @@ static_library("browser") {
"//chrome/installer/util:with_no_strings",
"//chrome/notification_helper:constants",
"//chrome/services/util_win/public/mojom",
"//components/browser_watcher:browser_watcher_client",
"//components/browser_watcher:stability_client",
"//components/chrome_cleaner/public/constants",
"//components/crash/core/app",
"//components/crash/core/app:crash_export_thunk_include",

@ -73,7 +73,6 @@ include_rules = [
"+components/browser_ui/site_settings",
"+components/browser_ui/strings",
"+components/browser_ui/styles",
"+components/browser_watcher",
"+components/browsing_data/content",
"+components/browsing_data/core",
"+components/browsing_topics",

@ -600,10 +600,7 @@ test("components_unittests") {
}
if (is_win) {
deps += [
"//components/browser_watcher:unit_tests",
"//components/winhttp:unit_tests",
]
deps += [ "//components/winhttp:unit_tests" ]
}
if (enable_printing) {

@ -1,128 +0,0 @@
# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/protobuf/proto_library.gni")
if (is_win) {
proto_library("activity_report_proto") {
sources = [ "activity_report.proto" ]
}
group("browser_watcher_client") {
deps = [
":activity_report_proto",
":stability_client",
":stability_common",
"//base",
"//components/metrics",
"//third_party/crashpad/crashpad/client",
]
}
# This target links into the crashpad handler which lives in chrome_elf.dll
# on Windows.
static_library("activity_report") {
sources = [
"activity_report_user_stream_data_source.cc",
"activity_report_user_stream_data_source.h",
]
deps = [
":activity_report_proto",
":stability_client",
":stability_common",
"//base",
"//third_party/crashpad/crashpad/client",
"//third_party/crashpad/crashpad/compat",
"//third_party/crashpad/crashpad/handler",
"//third_party/crashpad/crashpad/minidump",
"//third_party/crashpad/crashpad/snapshot",
]
}
static_library("stability_common") {
sources = [
"activity_report_extractor.cc",
"activity_report_extractor.h",
"minidump_user_streams.h",
]
deps = [
":activity_report_proto",
":stability_client",
"//base",
"//components/variations",
"//third_party/crashpad/crashpad/client:client",
"//third_party/crashpad/crashpad/util",
]
}
static_library("stability_client") {
sources = [
"activity_data_names.cc",
"activity_data_names.h",
"activity_tracker_annotation.cc",
"activity_tracker_annotation.h",
"extended_crash_reporting.cc",
"extended_crash_reporting.h",
"extended_crash_reporting_metrics.cc",
"extended_crash_reporting_metrics.h",
"features.cc",
"features.h",
]
deps = [
":activity_report_proto",
"//base",
"//third_party/crashpad/crashpad/client",
"//third_party/crashpad/crashpad/util",
]
# This needs to be a public dep because of base::win::PEImage.
public_deps = [ "//base:base_static" ]
}
source_set("unit_tests") {
testonly = true
sources = [
"activity_report_extractor_unittest.cc",
"activity_tracker_annotation_unittest.cc",
"extended_crash_reporting_win_unittest.cc",
]
deps = [
":activity_report_proto",
":browser_watcher_client",
":stability_client",
":stability_common",
"//base",
"//base/test:test_support",
"//components/crash/core/common:crash_key",
"//components/metrics:metrics",
"//testing/gmock",
"//testing/gtest",
"//third_party/crashpad/crashpad/client",
# TODO(manzagop): remove this lib once Crashpad writes the minidumps.
"//third_party/crashpad/crashpad/minidump",
"//third_party/crashpad/crashpad/snapshot",
"//third_party/crashpad/crashpad/util",
]
}
executable("dump_stability") {
sources = [ "dump_stability_report_main_win.cc" ]
deps = [
":activity_report_proto",
"//base",
]
}
executable("fetch_system_session_events") {
sources = [ "fetch_system_session_events_main_win.cc" ]
deps = [
":activity_report_proto",
":stability_client",
":stability_common",
"//base",
"//components/metrics:metrics",
]
}
}

@ -1,6 +0,0 @@
include_rules = [
"+components/metrics",
"+components/variations",
"+components/crash",
"+third_party/crashpad/crashpad",
]

@ -1,3 +0,0 @@
monorail {
component: "Internals>PlatformIntegration"
}

@ -1,4 +0,0 @@
jessemckenna@google.com
joenotcharles@google.com
chrisha@chromium.org # emeritus

@ -1,24 +0,0 @@
// Copyright 2020 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/browser_watcher/activity_data_names.h"
namespace browser_watcher {
const char kActivityChannel[] = "channel";
const char kActivityExecutionPhase[] = "execution-phase";
const char kActivityKeepAlive[] = "keep-alive";
const char kActivityPlatform[] = "platform";
const char kActivityProcessType[] = "ptype";
const char kActivityProduct[] = "product";
const char kActivityReporterChannel[] = "reporter-channel";
const char kActivityReporterPlatform[] = "reporter-platform";
const char kActivityReporterProduct[] = "reporter-product";
const char kActivityReporterVersion[] = "reporter-version";
const char kActivityRestartAllowed[] = "restart-allowed";
const char kActivitySpecialBuild[] = "special-build";
const char kActivityStartTimestamp[] = "start-timestamp";
const char kActivityVersion[] = "version";
} // namespace browser_watcher

@ -1,28 +0,0 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_BROWSER_WATCHER_ACTIVITY_DATA_NAMES_H_
#define COMPONENTS_BROWSER_WATCHER_ACTIVITY_DATA_NAMES_H_
namespace browser_watcher {
// Alphabetical list of stability data names.
extern const char kActivityChannel[];
extern const char kActivityExecutionPhase[];
extern const char kActivityKeepAlive[];
extern const char kActivityPlatform[];
extern const char kActivityProcessType[];
extern const char kActivityProduct[];
extern const char kActivityReporterChannel[];
extern const char kActivityReporterPlatform[];
extern const char kActivityReporterProduct[];
extern const char kActivityReporterVersion[];
extern const char kActivityRestartAllowed[];
extern const char kActivitySpecialBuild[];
extern const char kActivityStartTimestamp[];
extern const char kActivityVersion[];
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_ACTIVITY_DATA_NAMES_H_

@ -1,275 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package browser_watcher;
// The state of the system on which Chrome is running (shutting down, battery
// level, load, etc.).
// Next id: 2
message SystemState {
// The state of a system session. A system session begins when the system
// starts and ends when it shuts down.
enum SessionState {
UNKNOWN = 0;
CLEAN = 1; // Normal shutdown.
UNCLEAN = 2; // Abnormal shutdown (system crash, power loss).
}
// The state of the system session that contained Chrome's execution.
optional SessionState session_state = 1;
}
// Next id: 10
message CodeModule {
// The base address of this code module as it was loaded by the process.
optional int64 base_address = 1;
// The size of the code module.
optional int64 size = 2;
// The path or file name that the code module was loaded from.
optional string code_file = 3;
// An identifying string used to discriminate between multiple versions and
// builds of the same code module. This may contain a uuid, timestamp,
// version number, or any combination of this or other information, in an
// implementation-defined format.
optional string code_identifier = 4;
// The filename containing debugging information associated with the code
// module. If debugging information is stored in a file separate from the
// code module itself (as is the case when .pdb or .dSYM files are used),
// this will be different from code_file. If debugging information is
// stored in the code module itself (possibly prior to stripping), this
// will be the same as code_file.
optional string debug_file = 5;
// An identifying string similar to code_identifier, but identifies a
// specific version and build of the associated debug file. This may be
// the same as code_identifier when the debug_file and code_file are
// identical or when the same identifier is used to identify distinct
// debug and code files.
optional string debug_identifier = 6;
// A human-readable representation of the code module's version.
optional string version = 7;
optional int64 shrink_down_delta = 8;
// Whether the module was still loaded into memory.
optional bool is_unloaded = 9;
}
// A typed value holds values of interest or references to such values.
// Next id: 9
message TypedValue {
// A reference to a value of interest.
message Reference {
optional uint64 address = 1;
optional int64 size = 2;
}
oneof value {
bytes bytes_value = 1;
Reference bytes_reference = 2;
string string_value = 3;
Reference string_reference = 4;
string char_value = 5;
bool bool_value = 6;
int64 signed_value = 7;
uint64 unsigned_value = 8;
}
}
// An activity represents information about something of interest on a thread.
// Next id: 18
message Activity {
enum Type {
UNKNOWN = 0;
ACT_TASK_RUN = 1;
ACT_LOCK_ACQUIRE = 2;
ACT_EVENT_WAIT = 3;
ACT_THREAD_JOIN = 4;
ACT_PROCESS_WAIT = 5;
ACT_GENERIC = 6;
}
// Identifies the type of the activity (and specifies which fields are
// relevant).
optional Type type = 1;
// Creation time of the activity.
optional int64 time = 2;
// The address that pushed the activity onto the stack.
optional uint64 address = 11;
// The address that is the origin of the activity. This is useful for things
// like tasks that are posted from a completely different thread though most
// activities will leave it null.
optional uint64 origin_address = 3;
// The sequence identifier of the posted task.
// Expected for ACT_TASK_*
optional uint64 task_sequence_id = 4;
// The memory address of the lock object.
optional uint64 lock_address = 5;
// The memory address of the event object.
optional uint64 event_address = 6;
// A unique identifier for a thread within a process.
optional int64 thread_id = 7;
// A unique identifier for a process.
optional int64 process_id = 8;
// An arbitrary identifier used for association.
optional uint32 generic_id = 12;
// An arbitrary value used for information purposes.
optional int32 generic_data = 13;
// Tag ids 10 and 14-17 are reserved for server side augmentation.
// A key-value store.
map<string, TypedValue> user_data = 9;
}
// Details about an exception.
// Next id: 5
message Exception {
optional uint32 code = 1;
optional uint64 program_counter = 2;
optional uint64 exception_address = 3;
optional int64 time = 4;
}
// The state of a thread.
// Next id: 6
message ThreadState {
// The name of the thread, up to a maxiumum length.
optional string thread_name = 1;
// The identifier of the thread.
optional int64 thread_id = 2;
// The activity stack. |activity_count| specifies the number of activities on
// stack and |activities| holds the base of the stack (up to a maximum size).
optional int32 activity_count = 3;
repeated Activity activities = 4;
// The last exception to be successfully captured. Note this exception may
// have been recovered from.
optional Exception exception = 5;
}
// The state of a process.
// Next id: 7
message ProcessState {
enum Type {
UNKNOWN_PROCESS = 0;
BROWSER_PROCESS = 1;
WATCHER_PROCESS = 2;
}
message MemoryState {
message WindowsMemory {
// The private byte usage of the process. Unit is 4K pages.
optional uint32 process_private_usage = 1;
// The peak working set usage of the process. Unit is 4K pages.
optional uint32 process_peak_workingset_size = 2;
// The peak pagefile usage of the process. Unit is 4K pages.
optional uint32 process_peak_pagefile_usage = 3;
// The allocation request that caused OOM, bytes.
optional uint32 process_allocation_attempt = 4;
// The number of opened handles in the process.
optional uint32 process_handle_count = 5;
}
optional WindowsMemory windows_memory = 1;
}
// The identifier of the process.
optional int64 process_id = 3;
optional Type process_type = 5;
// Note: likely only a subset of modules of interest (e.g. Chromium's own
// modules).
repeated CodeModule modules = 1;
repeated ThreadState threads = 2;
optional MemoryState memory_state = 4;
// A key-value store global to the process.
map<string, TypedValue> data = 6;
}
// Description of a field trial or experiment that the user is currently
// enrolled in. This message is an analogue of the UMA proto in
// third_party/metrics_proto/system_profile.proto. For details about generating
// the identifiers from the field trial and group names, see
// variations::MakeActiveGroupId().
// Next id: 3
message FieldTrial {
// A 32-bit identifier for the name of the field trial.
optional fixed32 name_id = 1;
// A 32-bit identifier for the user's group within the field trial.
optional fixed32 group_id = 2;
}
// Records the state of system memory at the time of crash.
// Next id: 2
message SystemMemoryState {
message WindowsMemory {
// The system commit limit. Unit is number of 4K pages.
optional uint32 system_commit_limit = 1;
// The amount of system commit remaining. Unit is number of 4K pages.
optional uint32 system_commit_remaining = 2;
// The current number of open handles.
optional uint32 system_handle_count = 3;
}
optional WindowsMemory windows_memory = 1;
}
// A stability report contains information pertaining to the execution of a
// single logical instance of a "chrome browser". It is comprised of information
// about the system state and about the chrome browser's processes.
// Next id: 9
// This message would more appropriately be named ActivityReport, but since this
// proto is used in the crash backend, renaming it will take some dancing
// around.
message StabilityReport {
// Whether the report is complete. Reports can be incomplete when the
// recording size quota is hit.
optional bool is_complete = 6;
// The process identifier of the crashed process.
optional int64 crashed_process_id = 8;
// State pertaining to the system.
optional SystemState system_state = 1;
// State pertaining to Chrome's processes.
repeated ProcessState process_states = 2;
// Log messages. This is empty on official builds.
// TODO(manzagop): attribute messages to their process.
repeated string log_messages = 3;
// A global key-value store.
map<string, TypedValue> global_data = 4;
// The field trials the user is currently enrolled in.
repeated FieldTrial field_trials = 5;
optional SystemMemoryState system_memory_state = 7;
}

@ -1,307 +0,0 @@
// Copyright 2016 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/browser_watcher/activity_report_extractor.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/debug/activity_analyzer.h"
#include "base/notreached.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/browser_watcher/activity_data_names.h"
#include "third_party/crashpad/crashpad/util/misc/uuid.h"
namespace browser_watcher {
using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot;
using base::PersistentMemoryAllocator;
using base::debug::ActivityUserData;
using base::debug::GlobalActivityAnalyzer;
using base::debug::GlobalActivityTracker;
using base::debug::ThreadActivityAnalyzer;
namespace {
// Collects stability user data from the recorded format to the collected
// format.
void CollectUserData(
const ActivityUserData::Snapshot& recorded_map,
google::protobuf::Map<std::string, TypedValue>* collected_map,
StabilityReport* report) {
DCHECK(collected_map);
for (const auto& name_and_value : recorded_map) {
const std::string& key = name_and_value.first;
const ActivityUserData::TypedValue& recorded_value = name_and_value.second;
TypedValue collected_value;
switch (recorded_value.type()) {
case ActivityUserData::END_OF_VALUES:
NOTREACHED();
break;
case ActivityUserData::RAW_VALUE: {
base::StringPiece raw = recorded_value.Get();
collected_value.set_bytes_value(raw.data(), raw.size());
break;
}
case ActivityUserData::RAW_VALUE_REFERENCE: {
base::StringPiece recorded_ref = recorded_value.GetReference();
TypedValue::Reference* collected_ref =
collected_value.mutable_bytes_reference();
collected_ref->set_address(
reinterpret_cast<uintptr_t>(recorded_ref.data()));
collected_ref->set_size(recorded_ref.size());
break;
}
case ActivityUserData::STRING_VALUE: {
base::StringPiece value = recorded_value.GetString();
collected_value.set_string_value(value.data(), value.size());
// Promote version information to the global key value store.
if (report) {
bool should_promote =
key == kActivityProduct || key == kActivityChannel ||
key == kActivityPlatform || key == kActivityVersion;
if (should_promote) {
// TODO(siggi): Copy the promoted data or perhaps remove this
// promotion once the backend is able to display the per-process
// data.
(*report->mutable_global_data())[key].Swap(&collected_value);
continue;
}
}
break;
}
case ActivityUserData::STRING_VALUE_REFERENCE: {
base::StringPiece recorded_ref = recorded_value.GetStringReference();
TypedValue::Reference* collected_ref =
collected_value.mutable_string_reference();
collected_ref->set_address(
reinterpret_cast<uintptr_t>(recorded_ref.data()));
collected_ref->set_size(recorded_ref.size());
break;
}
case ActivityUserData::CHAR_VALUE: {
char char_value = recorded_value.GetChar();
collected_value.set_char_value(&char_value, 1);
break;
}
case ActivityUserData::BOOL_VALUE:
collected_value.set_bool_value(recorded_value.GetBool());
break;
case ActivityUserData::SIGNED_VALUE:
collected_value.set_signed_value(recorded_value.GetInt());
// Promote the execution timestamp to the global key value store.
if (report && key == kActivityStartTimestamp) {
(*report->mutable_global_data())[key].Swap(&collected_value);
continue;
}
break;
case ActivityUserData::UNSIGNED_VALUE:
collected_value.set_unsigned_value(recorded_value.GetUint());
break;
}
(*collected_map)[key].Swap(&collected_value);
}
}
void CollectModuleInformation(
const std::vector<GlobalActivityTracker::ModuleInfo>& modules,
ProcessState* process_state) {
DCHECK(process_state);
for (const GlobalActivityTracker::ModuleInfo& recorded : modules) {
CodeModule* collected = process_state->add_modules();
collected->set_base_address(recorded.address);
collected->set_size(recorded.size);
collected->set_code_file(recorded.file);
// Compute the code identifier using the required format.
std::string code_identifier =
base::StringPrintf("%08X%zx", recorded.timestamp, recorded.size);
collected->set_code_identifier(code_identifier);
collected->set_debug_file(recorded.debug_file);
// Compute the debug identifier using the required format.
const crashpad::UUID* uuid =
reinterpret_cast<const crashpad::UUID*>(recorded.identifier);
std::string debug_identifier = base::StringPrintf(
"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", uuid->data_1,
uuid->data_2, uuid->data_3, uuid->data_4[0], uuid->data_4[1],
uuid->data_5[0], uuid->data_5[1], uuid->data_5[2], uuid->data_5[3],
uuid->data_5[4], uuid->data_5[5], recorded.age);
collected->set_debug_identifier(debug_identifier);
collected->set_is_unloaded(!recorded.is_loaded);
}
}
void CollectActivity(const base::debug::Activity& recorded,
Activity* collected) {
DCHECK(collected);
collected->set_time(recorded.time_internal);
collected->set_address(recorded.calling_address);
collected->set_origin_address(recorded.origin_address);
// TODO(manzagop): the current collection deals with specific activity types;
// consider modifying it to handle the notion of activity categories.
switch (recorded.activity_type) {
case base::debug::Activity::ACT_TASK_RUN:
collected->set_type(Activity::ACT_TASK_RUN);
collected->set_task_sequence_id(recorded.data.task.sequence_id);
break;
case base::debug::Activity::ACT_LOCK_ACQUIRE:
collected->set_type(Activity::ACT_LOCK_ACQUIRE);
collected->set_lock_address(recorded.data.lock.lock_address);
break;
case base::debug::Activity::ACT_EVENT_WAIT:
collected->set_type(Activity::ACT_EVENT_WAIT);
collected->set_event_address(recorded.data.event.event_address);
break;
case base::debug::Activity::ACT_THREAD_JOIN:
collected->set_type(Activity::ACT_THREAD_JOIN);
collected->set_thread_id(recorded.data.thread.thread_id);
break;
case base::debug::Activity::ACT_PROCESS_WAIT:
collected->set_type(Activity::ACT_PROCESS_WAIT);
collected->set_process_id(recorded.data.process.process_id);
break;
case base::debug::Activity::ACT_GENERIC:
collected->set_type(Activity::ACT_GENERIC);
collected->set_generic_id(recorded.data.generic.id);
collected->set_generic_data(recorded.data.generic.info);
break;
default:
break;
}
}
void CollectException(const base::debug::Activity& recorded,
Exception* collected) {
DCHECK(collected);
collected->set_code(recorded.data.exception.code);
collected->set_program_counter(recorded.calling_address);
collected->set_exception_address(recorded.origin_address);
collected->set_time(recorded.time_internal);
}
void CollectThread(
const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
ThreadState* thread_state) {
DCHECK(thread_state);
thread_state->set_thread_name(snapshot.thread_name);
thread_state->set_thread_id(snapshot.thread_id);
thread_state->set_activity_count(snapshot.activity_stack_depth);
if (snapshot.last_exception.activity_type ==
base::debug::Activity::ACT_EXCEPTION) {
CollectException(snapshot.last_exception,
thread_state->mutable_exception());
}
for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
Activity* collected = thread_state->add_activities();
CollectActivity(snapshot.activity_stack[i], collected);
if (i < snapshot.user_data_stack.size()) {
CollectUserData(snapshot.user_data_stack[i],
collected->mutable_user_data(), nullptr);
}
}
}
bool SetProcessType(ProcessState* process_state) {
DCHECK(process_state);
google::protobuf::Map<std::string, TypedValue>* process_data =
process_state->mutable_data();
const auto it = process_data->find(kActivityProcessType);
if (it == process_data->end())
return false;
const TypedValue& value = it->second;
if (value.value_case() != TypedValue::kSignedValue)
return false;
process_state->set_process_type(
static_cast<browser_watcher::ProcessState_Type>(value.signed_value()));
process_data->erase(it);
return true;
}
void CollectProcess(int64_t pid,
GlobalActivityAnalyzer* analyzer,
StabilityReport* report) {
DCHECK(analyzer);
DCHECK(report);
ProcessState* process_state = report->add_process_states();
// Collect thread activity data.
ThreadActivityAnalyzer* thread_analyzer = analyzer->GetFirstAnalyzer(pid);
for (; thread_analyzer != nullptr;
thread_analyzer = analyzer->GetNextAnalyzer()) {
// Only valid analyzers are expected per contract of GetFirstAnalyzer /
// GetNextAnalyzer.
DCHECK(thread_analyzer->IsValid());
if (!process_state->has_process_id()) {
process_state->set_process_id(
thread_analyzer->activity_snapshot().process_id);
}
DCHECK_EQ(thread_analyzer->activity_snapshot().process_id,
process_state->process_id());
ThreadState* thread_state = process_state->add_threads();
CollectThread(thread_analyzer->activity_snapshot(), thread_state);
}
// Collect global user data.
ActivityUserData::Snapshot process_data_snapshot =
analyzer->GetProcessDataSnapshot(pid);
CollectUserData(process_data_snapshot, process_state->mutable_data(), report);
SetProcessType(process_state);
// Collect module information.
CollectModuleInformation(analyzer->GetModules(pid), process_state);
}
} // namespace
CollectionStatus Extract(
std::unique_ptr<GlobalActivityAnalyzer> global_analyzer,
StabilityReport* report) {
DCHECK(global_analyzer);
DCHECK(report);
report->set_is_complete(global_analyzer->IsDataComplete());
// Collect process data.
for (int64_t pid = global_analyzer->GetFirstProcess(); pid != 0;
pid = global_analyzer->GetNextProcess()) {
CollectProcess(pid, global_analyzer.get(), report);
}
// Collect log messages.
std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
for (const std::string& message : log_messages) {
report->add_log_messages(message);
}
return SUCCESS;
}
} // namespace browser_watcher

@ -1,39 +0,0 @@
// 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.
//
// Implementation of the collection of a stability file to a protocol buffer.
#ifndef COMPONENTS_BROWSER_WATCHER_ACTIVITY_REPORT_EXTRACTOR_H_
#define COMPONENTS_BROWSER_WATCHER_ACTIVITY_REPORT_EXTRACTOR_H_
#include "base/debug/activity_analyzer.h"
#include "components/browser_watcher/activity_report.pb.h"
namespace browser_watcher {
// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
enum CollectionStatus {
NONE = 0,
SUCCESS = 1, // Successfully registered a report with Crashpad.
ANALYZER_CREATION_FAILED = 2,
DEBUG_FILE_NO_DATA = 3,
PREPARE_NEW_CRASH_REPORT_FAILED = 4,
WRITE_TO_MINIDUMP_FAILED = 5,
DEBUG_FILE_DELETION_FAILED = 6,
FINISHED_WRITING_CRASH_REPORT_FAILED = 7,
UNCLEAN_SHUTDOWN = 8,
UNCLEAN_SESSION = 9,
COLLECTION_ATTEMPT = 10,
// New values go here.
COLLECTION_STATUS_MAX = 11
};
// Extracts a stability report from an existing GlobalActivityAnalyzer.
CollectionStatus Extract(
std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer,
StabilityReport* report);
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_ACTIVITY_REPORT_EXTRACTOR_H_

@ -1,446 +0,0 @@
// 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.
#include "components/browser_watcher/activity_report_extractor.h"
#include <memory>
#include <utility>
#include "base/containers/contains.h"
#include "base/debug/activity_analyzer.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
namespace browser_watcher {
using base::File;
using base::FilePath;
using base::FilePersistentMemoryAllocator;
using base::MemoryMappedFile;
using base::PersistentMemoryAllocator;
using base::debug::ActivityData;
using base::debug::ActivityTrackerMemoryAllocator;
using base::debug::ActivityUserData;
using base::debug::GlobalActivityAnalyzer;
using base::debug::GlobalActivityTracker;
using base::debug::ThreadActivityTracker;
namespace {
// The tracker creates some data entries internally.
const size_t kInternalProcessDatums = 1;
// Parameters for the activity tracking.
const size_t kFileSize = 64 << 10; // 64 KiB
const int kStackDepth = 6;
const uint64_t kAllocatorId = 0;
const char kAllocatorName[] = "PostmortemReportCollectorCollectionTest";
const uint64_t kTaskSequenceNum = 42;
const uintptr_t kTaskOrigin = 1000U;
const uintptr_t kLockAddress = 1001U;
const uintptr_t kEventAddress = 1002U;
const int kThreadId = 43;
const int kProcessId = 44;
const int kAnotherThreadId = 45;
const uint32_t kGenericId = 46U;
const int32_t kGenericData = 47;
} // namespace
// Sets up a file backed thread tracker for direct access. A
// GlobalActivityTracker is not created, meaning there is no risk of
// the instrumentation interfering with the file's content.
class StabilityReportExtractorThreadTrackerTest : public testing::Test {
public:
// Create a proper debug file.
void SetUp() override {
testing::Test::SetUp();
// Create a file backed allocator.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug_file.pma");
allocator_ = CreateAllocator();
ASSERT_NE(nullptr, allocator_);
size_t tracker_mem_size =
ThreadActivityTracker::SizeForStackDepth(kStackDepth);
ASSERT_GT(kFileSize, tracker_mem_size);
// Create a tracker.
tracker_ = CreateTracker(allocator_.get(), tracker_mem_size);
ASSERT_NE(nullptr, tracker_);
ASSERT_TRUE(tracker_->IsValid());
}
std::unique_ptr<PersistentMemoryAllocator> CreateAllocator() {
// Create the memory mapped file.
std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
bool success = mmfile->Initialize(
File(debug_file_path_, File::FLAG_CREATE | File::FLAG_READ |
File::FLAG_WRITE |
File::FLAG_WIN_SHARE_DELETE),
{0, static_cast<int64_t>(kFileSize)},
MemoryMappedFile::READ_WRITE_EXTEND);
if (!success || !mmfile->IsValid())
return nullptr;
// Create a persistent memory allocator.
if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true))
return nullptr;
return std::make_unique<FilePersistentMemoryAllocator>(
std::move(mmfile), kFileSize, kAllocatorId, kAllocatorName, false);
}
std::unique_ptr<ThreadActivityTracker> CreateTracker(
PersistentMemoryAllocator* allocator,
size_t tracker_mem_size) {
// Allocate a block of memory for the tracker to use.
PersistentMemoryAllocator::Reference mem_reference = allocator->Allocate(
tracker_mem_size, GlobalActivityTracker::kTypeIdActivityTracker);
if (mem_reference == 0U)
return nullptr;
// Get the memory's base address.
void* mem_base = allocator->GetAsArray<char>(
mem_reference, GlobalActivityTracker::kTypeIdActivityTracker,
PersistentMemoryAllocator::kSizeAny);
if (mem_base == nullptr)
return nullptr;
// Make the allocation iterable so it can be found by other processes.
allocator->MakeIterable(mem_reference);
return std::make_unique<ThreadActivityTracker>(mem_base, tracker_mem_size);
}
void PerformBasicReportValidation(const StabilityReport& report) {
// One report with one thread that has the expected name and id.
ASSERT_EQ(1, report.process_states_size());
const ProcessState& process_state = report.process_states(0);
EXPECT_EQ(base::GetCurrentProcId(), process_state.process_id());
ASSERT_EQ(1, process_state.threads_size());
const ThreadState& thread_state = process_state.threads(0);
EXPECT_EQ(base::PlatformThread::GetName(), thread_state.thread_name());
#if BUILDFLAG(IS_WIN)
EXPECT_EQ(base::PlatformThread::CurrentId(), thread_state.thread_id());
#elif BUILDFLAG(IS_POSIX)
EXPECT_EQ(base::PlatformThread::CurrentHandle().platform_handle(),
thread_state.thread_id());
#endif
}
std::unique_ptr<GlobalActivityAnalyzer> CreateAnalyzer() {
return GlobalActivityAnalyzer::CreateWithFile(debug_file_path());
}
private:
const FilePath& debug_file_path() const { return debug_file_path_; }
protected:
base::ScopedTempDir temp_dir_;
FilePath debug_file_path_;
std::unique_ptr<PersistentMemoryAllocator> allocator_;
std::unique_ptr<ThreadActivityTracker> tracker_;
};
TEST_F(StabilityReportExtractorThreadTrackerTest, CollectSuccess) {
// Create some activity data.
tracker_->PushActivity(reinterpret_cast<void*>(kTaskOrigin),
base::debug::Activity::ACT_TASK_RUN,
ActivityData::ForTask(kTaskSequenceNum));
tracker_->PushActivity(
nullptr, base::debug::Activity::ACT_LOCK_ACQUIRE,
ActivityData::ForLock(reinterpret_cast<void*>(kLockAddress)));
ThreadActivityTracker::ActivityId activity_id = tracker_->PushActivity(
nullptr, base::debug::Activity::ACT_EVENT_WAIT,
ActivityData::ForEvent(reinterpret_cast<void*>(kEventAddress)));
tracker_->PushActivity(nullptr, base::debug::Activity::ACT_THREAD_JOIN,
ActivityData::ForThread(kThreadId));
tracker_->PushActivity(nullptr, base::debug::Activity::ACT_PROCESS_WAIT,
ActivityData::ForProcess(kProcessId));
tracker_->PushActivity(nullptr, base::debug::Activity::ACT_GENERIC,
ActivityData::ForGeneric(kGenericId, kGenericData));
// Note: this exceeds the activity stack's capacity.
tracker_->PushActivity(nullptr, base::debug::Activity::ACT_THREAD_JOIN,
ActivityData::ForThread(kAnotherThreadId));
// Add some user data.
ActivityTrackerMemoryAllocator user_data_allocator(
allocator_.get(), GlobalActivityTracker::kTypeIdUserDataRecord,
GlobalActivityTracker::kTypeIdUserDataRecordFree, 1024U, 10U, false);
std::unique_ptr<ActivityUserData> user_data =
tracker_->GetUserData(activity_id, &user_data_allocator);
user_data->SetInt("some_int", 42);
// Validate collection returns the expected report.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
// Validate the report.
ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
const ThreadState& thread_state = report.process_states(0).threads(0);
EXPECT_EQ(7, thread_state.activity_count());
ASSERT_EQ(6, thread_state.activities_size());
{
const Activity& activity = thread_state.activities(0);
EXPECT_EQ(Activity::ACT_TASK_RUN, activity.type());
EXPECT_EQ(kTaskOrigin, activity.origin_address());
EXPECT_EQ(kTaskSequenceNum, activity.task_sequence_id());
EXPECT_EQ(0U, activity.user_data().size());
}
{
const Activity& activity = thread_state.activities(1);
EXPECT_EQ(Activity::ACT_LOCK_ACQUIRE, activity.type());
EXPECT_EQ(kLockAddress, activity.lock_address());
EXPECT_EQ(0U, activity.user_data().size());
}
{
const Activity& activity = thread_state.activities(2);
EXPECT_EQ(Activity::ACT_EVENT_WAIT, activity.type());
EXPECT_EQ(kEventAddress, activity.event_address());
ASSERT_EQ(1U, activity.user_data().size());
ASSERT_TRUE(base::Contains(activity.user_data(), "some_int"));
EXPECT_EQ(TypedValue::kSignedValue,
activity.user_data().at("some_int").value_case());
EXPECT_EQ(42, activity.user_data().at("some_int").signed_value());
}
{
const Activity& activity = thread_state.activities(3);
EXPECT_EQ(Activity::ACT_THREAD_JOIN, activity.type());
EXPECT_EQ(kThreadId, activity.thread_id());
EXPECT_EQ(0U, activity.user_data().size());
}
{
const Activity& activity = thread_state.activities(4);
EXPECT_EQ(Activity::ACT_PROCESS_WAIT, activity.type());
EXPECT_EQ(kProcessId, activity.process_id());
EXPECT_EQ(0U, activity.user_data().size());
}
{
const Activity& activity = thread_state.activities(5);
EXPECT_EQ(Activity::ACT_GENERIC, activity.type());
EXPECT_EQ(kGenericId, activity.generic_id());
EXPECT_EQ(kGenericData, activity.generic_data());
EXPECT_EQ(0U, activity.user_data().size());
}
}
TEST_F(StabilityReportExtractorThreadTrackerTest, CollectException) {
const void* expected_pc = reinterpret_cast<void*>(0xCAFE);
const void* expected_address = nullptr;
const uint32_t expected_code = 42U;
// Record an exception.
const int64_t timestamp =
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds();
tracker_->RecordExceptionActivity(expected_pc, expected_address,
base::debug::Activity::ACT_EXCEPTION,
ActivityData::ForException(expected_code));
// Collect report and validate.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
// Validate the presence of the exception.
ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
const ThreadState& thread_state = report.process_states(0).threads(0);
ASSERT_TRUE(thread_state.has_exception());
const Exception& exception = thread_state.exception();
EXPECT_EQ(expected_code, exception.code());
EXPECT_EQ(expected_pc, reinterpret_cast<void*>(exception.program_counter()));
EXPECT_EQ(expected_address,
reinterpret_cast<void*>(exception.exception_address()));
const int64_t tolerance_us = 1000ULL;
EXPECT_LE(std::abs(timestamp - exception.time()), tolerance_us);
}
TEST_F(StabilityReportExtractorThreadTrackerTest, CollectNoException) {
// Record something.
tracker_->PushActivity(reinterpret_cast<void*>(kTaskOrigin),
base::debug::Activity::ACT_TASK_RUN,
ActivityData::ForTask(kTaskSequenceNum));
// Collect report and validate there is no exception.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
const ThreadState& thread_state = report.process_states(0).threads(0);
ASSERT_FALSE(thread_state.has_exception());
}
// Tests stability report extraction.
class StabilityReportExtractorTest : public testing::Test {
public:
const int kMemorySize = 1 << 20; // 1MiB
StabilityReportExtractorTest() {}
~StabilityReportExtractorTest() override {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (global_tracker) {
global_tracker->ReleaseTrackerForCurrentThreadForTesting();
delete global_tracker;
}
}
void SetUp() override {
testing::Test::SetUp();
// Set up a debug file path.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
}
std::unique_ptr<GlobalActivityAnalyzer> CreateAnalyzer() {
return GlobalActivityAnalyzer::CreateWithFile(debug_file_path());
}
const FilePath& debug_file_path() { return debug_file_path_; }
protected:
base::ScopedTempDir temp_dir_;
FilePath debug_file_path_;
};
TEST_F(StabilityReportExtractorTest, LogCollection) {
// Record some log messages.
GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
"", 3);
GlobalActivityTracker::Get()->RecordLogMessage("hello world");
GlobalActivityTracker::Get()->RecordLogMessage("foo bar");
// Collect the stability report.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
// Validate the report's log content.
ASSERT_EQ(2, report.log_messages_size());
ASSERT_EQ("hello world", report.log_messages(0));
ASSERT_EQ("foo bar", report.log_messages(1));
}
TEST_F(StabilityReportExtractorTest, ProcessUserDataCollection) {
const char string1[] = "foo";
const char string2[] = "bar";
// Record some process user data.
GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
"", 3);
ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
ActivityUserData::Snapshot snapshot;
ASSERT_TRUE(process_data.CreateSnapshot(&snapshot));
ASSERT_EQ(kInternalProcessDatums, snapshot.size());
process_data.Set("raw", "foo", 3);
process_data.SetString("string", "bar");
process_data.SetChar("char", '9');
process_data.SetInt("int", -9999);
process_data.SetUint("uint", 9999);
process_data.SetBool("bool", true);
process_data.SetReference("ref", string1, strlen(string1));
process_data.SetStringReference("sref", string2);
// Collect the stability report.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
// We expect a single process.
ASSERT_EQ(1, report.process_states_size());
// Validate the report contains the process' data.
const auto& collected_data = report.process_states(0).data();
ASSERT_EQ(kInternalProcessDatums + 8U, collected_data.size());
ASSERT_TRUE(base::Contains(collected_data, "raw"));
EXPECT_EQ(TypedValue::kBytesValue, collected_data.at("raw").value_case());
EXPECT_EQ("foo", collected_data.at("raw").bytes_value());
ASSERT_TRUE(base::Contains(collected_data, "string"));
EXPECT_EQ(TypedValue::kStringValue, collected_data.at("string").value_case());
EXPECT_EQ("bar", collected_data.at("string").string_value());
ASSERT_TRUE(base::Contains(collected_data, "char"));
EXPECT_EQ(TypedValue::kCharValue, collected_data.at("char").value_case());
EXPECT_EQ("9", collected_data.at("char").char_value());
ASSERT_TRUE(base::Contains(collected_data, "int"));
EXPECT_EQ(TypedValue::kSignedValue, collected_data.at("int").value_case());
EXPECT_EQ(-9999, collected_data.at("int").signed_value());
ASSERT_TRUE(base::Contains(collected_data, "uint"));
EXPECT_EQ(TypedValue::kUnsignedValue, collected_data.at("uint").value_case());
EXPECT_EQ(9999U, collected_data.at("uint").unsigned_value());
ASSERT_TRUE(base::Contains(collected_data, "bool"));
EXPECT_EQ(TypedValue::kBoolValue, collected_data.at("bool").value_case());
EXPECT_TRUE(collected_data.at("bool").bool_value());
ASSERT_TRUE(base::Contains(collected_data, "ref"));
EXPECT_EQ(TypedValue::kBytesReference, collected_data.at("ref").value_case());
const TypedValue::Reference& ref = collected_data.at("ref").bytes_reference();
EXPECT_EQ(reinterpret_cast<uintptr_t>(string1), ref.address());
EXPECT_EQ(strlen(string1), static_cast<uint64_t>(ref.size()));
ASSERT_TRUE(base::Contains(collected_data, "sref"));
EXPECT_EQ(TypedValue::kStringReference,
collected_data.at("sref").value_case());
const TypedValue::Reference& sref =
collected_data.at("sref").string_reference();
EXPECT_EQ(reinterpret_cast<uintptr_t>(string2), sref.address());
EXPECT_EQ(strlen(string2), static_cast<uint64_t>(sref.size()));
}
TEST_F(StabilityReportExtractorTest, ModuleCollection) {
// Record some module information.
GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
"", 3);
base::debug::GlobalActivityTracker::ModuleInfo module_info = {};
module_info.is_loaded = true;
module_info.address = 0x123456;
module_info.load_time = 1111LL;
module_info.size = 0x2d000;
module_info.timestamp = 0xCAFECAFE;
module_info.age = 1;
crashpad::UUID debug_uuid;
debug_uuid.InitializeFromString("11223344-5566-7788-abcd-0123456789ab");
memcpy(module_info.identifier, &debug_uuid, sizeof(module_info.identifier));
module_info.file = "foo";
module_info.debug_file = "bar";
GlobalActivityTracker::Get()->RecordModuleInfo(module_info);
// Collect the stability report.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
// Validate the report's modules content.
ASSERT_EQ(1, report.process_states_size());
const ProcessState& process_state = report.process_states(0);
ASSERT_EQ(1, process_state.modules_size());
const CodeModule collected_module = process_state.modules(0);
EXPECT_EQ(module_info.address,
static_cast<uintptr_t>(collected_module.base_address()));
EXPECT_EQ(module_info.size, static_cast<size_t>(collected_module.size()));
EXPECT_EQ(module_info.file, collected_module.code_file());
EXPECT_EQ("CAFECAFE2d000", collected_module.code_identifier());
EXPECT_EQ(module_info.debug_file, collected_module.debug_file());
EXPECT_EQ("1122334455667788ABCD0123456789AB1",
collected_module.debug_identifier());
EXPECT_EQ("", collected_module.version());
EXPECT_EQ(0LL, collected_module.shrink_down_delta());
EXPECT_EQ(!module_info.is_loaded, collected_module.is_unloaded());
}
} // namespace browser_watcher

@ -1,298 +0,0 @@
// 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.
#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

@ -1,42 +0,0 @@
// 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.
#ifndef COMPONENTS_BROWSER_WATCHER_ACTIVITY_REPORT_USER_STREAM_DATA_SOURCE_H_
#define COMPONENTS_BROWSER_WATCHER_ACTIVITY_REPORT_USER_STREAM_DATA_SOURCE_H_
#include <memory>
#include "base/files/file_path.h"
#include "third_party/crashpad/crashpad/handler/user_stream_data_source.h"
namespace crashpad {
class MinidumpUserExtensionStreamDataSource;
class ProcessSnapshot;
} // namespace crashpad
namespace browser_watcher {
// Collects stability instrumentation corresponding to a ProcessSnapshot and
// makes it available to the crash handler.
class ActivityReportUserStreamDataSource
: public crashpad::UserStreamDataSource {
public:
explicit ActivityReportUserStreamDataSource(
const base::FilePath& user_data_dir);
ActivityReportUserStreamDataSource(
const ActivityReportUserStreamDataSource&) = delete;
ActivityReportUserStreamDataSource& operator=(
const ActivityReportUserStreamDataSource&) = delete;
std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override;
private:
base::FilePath user_data_dir_;
};
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_ACTIVITY_REPORT_USER_STREAM_DATA_SOURCE_H_

@ -1,28 +0,0 @@
// Copyright 2020 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/browser_watcher/activity_tracker_annotation.h"
namespace browser_watcher {
const char ActivityTrackerAnnotation::kAnnotationName[] =
"ActivityTrackerLocation";
ActivityTrackerAnnotation::ActivityTrackerAnnotation()
: crashpad::Annotation(kAnnotationType, kAnnotationName, &value_) {}
void ActivityTrackerAnnotation::SetValue(const void* address, size_t size) {
value_.address = reinterpret_cast<uint64_t>(address);
value_.size = size;
SetSize(sizeof(value_));
}
// static
ActivityTrackerAnnotation* ActivityTrackerAnnotation::GetInstance() {
// This object is intentionally leaked.
static ActivityTrackerAnnotation* instance = new ActivityTrackerAnnotation();
return instance;
}
} // namespace browser_watcher

@ -1,39 +0,0 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_BROWSER_WATCHER_ACTIVITY_TRACKER_ANNOTATION_H_
#define COMPONENTS_BROWSER_WATCHER_ACTIVITY_TRACKER_ANNOTATION_H_
#include <stdint.h>
#include "third_party/crashpad/crashpad/client/annotation.h"
namespace browser_watcher {
// A Crashpad annotation to store the location and size of the buffer used
// for activity tracking. This is used to retrieve and record tracked activities
// from the handler at crash time.
class ActivityTrackerAnnotation : public crashpad::Annotation {
public:
struct ValueType {
uint64_t address;
uint64_t size;
};
static constexpr Type kAnnotationType = Annotation::UserDefinedType(0xBAB);
static const char kAnnotationName[];
void SetValue(const void* address, size_t size);
// Returns the sole instance of this class.
static ActivityTrackerAnnotation* GetInstance();
private:
ActivityTrackerAnnotation();
ValueType value_;
};
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_ACTIVITY_TRACKER_ANNOTATION_H_

@ -1,38 +0,0 @@
// Copyright 2019 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/browser_watcher/activity_tracker_annotation.h"
#include "components/crash/core/common/crash_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_watcher {
class ActivityTrackerAnnotationTest : public testing::Test {
public:
void SetUp() override { crash_reporter::InitializeCrashKeysForTesting(); }
void TearDown() override { crash_reporter::ResetCrashKeysForTesting(); }
};
TEST_F(ActivityTrackerAnnotationTest, RegistersOnFirstSet) {
static const char* kBuffer[128];
ActivityTrackerAnnotation* annotation =
ActivityTrackerAnnotation::GetInstance();
// Validate that the annotation doesn't register on construction.
EXPECT_EQ("", crash_reporter::GetCrashKeyValue(
ActivityTrackerAnnotation::kAnnotationName));
annotation->SetValue(&kBuffer, sizeof(kBuffer));
std::string string_value = crash_reporter::GetCrashKeyValue(
ActivityTrackerAnnotation::kAnnotationName);
ASSERT_EQ(sizeof(ActivityTrackerAnnotation::ValueType), string_value.size());
ActivityTrackerAnnotation::ValueType value = {};
memcpy(&value, string_value.data(), sizeof(value));
EXPECT_EQ(value.address, reinterpret_cast<uint64_t>(&kBuffer));
EXPECT_EQ(value.size, sizeof(kBuffer));
}
} // namespace browser_watcher

@ -1,299 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// A utility for printing the contents of a postmortem stability minidump.
#include <windows.h> // NOLINT
#include <dbghelp.h>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "components/browser_watcher/activity_report.pb.h"
namespace {
const char kUsage[] =
"Usage: %ls --minidump=<minidump file>\n"
"\n"
" Dumps the contents of a postmortem minidump in a human readable way.\n";
bool ParseCommandLine(const base::CommandLine* cmd,
base::FilePath* minidump_path) {
*minidump_path = cmd->GetSwitchValuePath("minidump");
if (minidump_path->empty()) {
LOG(ERROR) << "Missing minidump file.\n";
LOG(ERROR) << base::StringPrintf(kUsage, cmd->GetProgram().value().c_str());
return false;
}
return true;
}
void Indent(FILE* out, int indent_level) {
DCHECK(out);
for (int i = 0; i < indent_level; ++i)
fprintf(out, " ");
}
void PrintUserData(
FILE* out,
int indent_level,
const google::protobuf::Map<std::string, browser_watcher::TypedValue>&
user_data) {
DCHECK(out);
Indent(out, indent_level);
fprintf(out, "User data (%zu)\n", user_data.size());
for (const auto& kv : user_data) {
Indent(out, indent_level + 1);
fprintf(out, "%s : ", kv.first.c_str());
const browser_watcher::TypedValue& value = kv.second;
switch (kv.second.value_case()) {
case browser_watcher::TypedValue::kBytesValue: {
const std::string& bytes_value = value.bytes_value();
for (size_t i = 0; i < bytes_value.size(); ++i)
fprintf(out, "%02X ", bytes_value.at(i));
fprintf(out, "\n");
break;
}
case browser_watcher::TypedValue::kBytesReference:
fprintf(out, "bytes reference (address: %llX, size: %llX)\n",
value.bytes_reference().address(),
value.bytes_reference().size());
break;
case browser_watcher::TypedValue::kStringValue:
fprintf(out, "\"%s\"\n", value.string_value().c_str());
break;
case browser_watcher::TypedValue::kStringReference:
fprintf(out, "string reference (address: %llX, size: %llX)\n",
value.string_reference().address(),
value.string_reference().size());
break;
case browser_watcher::TypedValue::kCharValue:
fprintf(out, "'%s'\n", value.char_value().c_str());
break;
case browser_watcher::TypedValue::kBoolValue:
fprintf(out, "%s\n", value.bool_value() ? "true" : "false");
break;
case browser_watcher::TypedValue::kSignedValue:
fprintf(out, "%lld\n", value.signed_value());
break;
case browser_watcher::TypedValue::kUnsignedValue:
fprintf(out, "%llu\n", value.unsigned_value());
break;
case browser_watcher::TypedValue::VALUE_NOT_SET:
fprintf(out, "<not set>\n");
break;
}
}
}
void PrintActivity(FILE* out,
int indent_level,
const browser_watcher::Activity& activity) {
DCHECK(out);
Indent(out, indent_level);
fprintf(out, "Activity\n");
Indent(out, indent_level + 1);
fprintf(out, "type: %d\n", activity.type());
Indent(out, indent_level + 1);
fprintf(out, "time: %lld\n", activity.time());
Indent(out, indent_level + 1);
fprintf(out, "address: %llX\n", activity.address());
switch (activity.type()) {
case browser_watcher::Activity::UNKNOWN:
break;
case browser_watcher::Activity::ACT_TASK_RUN:
Indent(out, indent_level + 1);
fprintf(out, "origin_address: %llX\n", activity.origin_address());
fprintf(out, "task_sequence_id: %lld\n", activity.task_sequence_id());
break;
case browser_watcher::Activity::ACT_LOCK_ACQUIRE:
Indent(out, indent_level + 1);
fprintf(out, "lock_address: %llX\n", activity.lock_address());
break;
case browser_watcher::Activity::ACT_EVENT_WAIT:
Indent(out, indent_level + 1);
fprintf(out, "event_address: %llX\n", activity.event_address());
break;
case browser_watcher::Activity::ACT_THREAD_JOIN:
Indent(out, indent_level + 1);
fprintf(out, "thread_id: %lld\n", activity.thread_id());
break;
case browser_watcher::Activity::ACT_PROCESS_WAIT:
Indent(out, indent_level + 1);
fprintf(out, "process_id: %lld\n", activity.process_id());
break;
case browser_watcher::Activity::ACT_GENERIC:
Indent(out, indent_level + 1);
fprintf(out, "id: %u, data: %d\n", activity.generic_id(),
activity.generic_data());
break;
}
PrintUserData(out, indent_level + 1, activity.user_data());
}
void PrintProcessState(FILE* out,
const browser_watcher::ProcessState& process) {
std::string process_type;
switch (process.process_type()) {
case browser_watcher::ProcessState::UNKNOWN_PROCESS:
process_type = "unknown type";
break;
case browser_watcher::ProcessState::BROWSER_PROCESS:
process_type = "browser";
break;
case browser_watcher::ProcessState::WATCHER_PROCESS:
process_type = "watcher";
break;
default:
base::SStringPrintf(&process_type, "process type %d",
process.process_type());
break;
}
fprintf(out, "Process %lld (%s, %d threads)\n", process.process_id(),
process_type.c_str(), process.threads_size());
if (process.has_memory_state() &&
process.memory_state().has_windows_memory()) {
const auto& windows_memory = process.memory_state().windows_memory();
if (windows_memory.has_process_private_usage()) {
fprintf(out, "process_private_usage: %u pages\n",
windows_memory.process_private_usage());
}
if (windows_memory.has_process_peak_workingset_size()) {
fprintf(out, "process_peak_workingset_size: %u pages\n",
windows_memory.process_peak_workingset_size());
}
if (windows_memory.has_process_peak_pagefile_usage()) {
fprintf(out, "process_peak_pagefile_usage: %u pages\n",
windows_memory.process_peak_pagefile_usage());
}
if (windows_memory.has_process_allocation_attempt()) {
fprintf(out, "process_allocation_attempt: %u bytes\n",
windows_memory.process_allocation_attempt());
}
if (windows_memory.has_process_handle_count()) {
fprintf(out, "process_handle_count: %u handles\n",
windows_memory.process_handle_count());
}
}
for (const browser_watcher::ThreadState& thread : process.threads()) {
fprintf(out, "Thread %lld (%s) : %d activities\n", thread.thread_id(),
thread.thread_name().c_str(), thread.activity_count());
for (const browser_watcher::Activity& activity : thread.activities())
PrintActivity(out, 1, activity);
}
PrintUserData(out, 1, process.data());
}
// TODO(manzagop): flesh out as StabilityReport gets fleshed out.
void PrintReport(FILE* out, const browser_watcher::StabilityReport& report) {
for (std::string message : report.log_messages())
fprintf(out, "log message:\n%s\n", message.c_str());
if (report.has_system_memory_state() &&
report.system_memory_state().has_windows_memory()) {
const auto& windows_memory = report.system_memory_state().windows_memory();
if (windows_memory.has_system_commit_limit()) {
fprintf(out, "system_commit_limit: %u pages\n",
windows_memory.system_commit_limit());
}
if (windows_memory.has_system_commit_remaining()) {
fprintf(out, "system_commit_remaining: %u pages\n",
windows_memory.system_commit_remaining());
}
if (windows_memory.has_system_handle_count()) {
fprintf(out, "system_handle_count: %u handles\n",
windows_memory.system_handle_count());
}
}
PrintUserData(out, 0, report.global_data());
for (int i = 0; i < report.process_states_size(); ++i) {
const browser_watcher::ProcessState process = report.process_states(i);
PrintProcessState(out, process);
}
}
bool GetStabilityStreamRvaAndSize(RVA directory_rva,
ULONG32 stream_count,
FILE* file,
RVA* report_rva,
ULONG32* report_size_bytes) {
std::vector<MINIDUMP_DIRECTORY> directory;
directory.resize(stream_count);
CHECK_EQ(0, fseek(file, directory_rva, SEEK_SET));
CHECK_EQ(stream_count, fread(directory.data(), sizeof(MINIDUMP_DIRECTORY),
stream_count, file));
for (const MINIDUMP_DIRECTORY& entry : directory) {
constexpr ULONG32 kActivityStream = static_cast<ULONG32>(0x4B6B0002);
if (entry.StreamType == kActivityStream) {
*report_rva = entry.Location.Rva;
*report_size_bytes = entry.Location.DataSize;
return true;
}
}
return false;
}
int Main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
// Get the dump.
base::FilePath minidump_path;
if (!ParseCommandLine(base::CommandLine::ForCurrentProcess(), &minidump_path))
return 1;
// Read the minidump to extract the proto.
base::ScopedFILE minidump_file;
minidump_file.reset(base::OpenFile(minidump_path, "rb"));
CHECK(minidump_file.get());
// Read the header.
// TODO(manzagop): leverage Crashpad to do this.
MINIDUMP_HEADER header = {};
CHECK_EQ(1U, fread(&header, sizeof(header), 1U, minidump_file.get()));
CHECK_EQ(static_cast<ULONG32>(MINIDUMP_SIGNATURE), header.Signature);
fprintf(stdout, "Number of streams: %u\n", header.NumberOfStreams);
RVA directory_rva = header.StreamDirectoryRva;
RVA report_rva;
ULONG32 report_size_bytes;
CHECK(GetStabilityStreamRvaAndSize(directory_rva, header.NumberOfStreams,
minidump_file.get(), &report_rva,
&report_size_bytes));
// Read the serialized stability report.
std::string serialized_report;
serialized_report.resize(report_size_bytes);
CHECK_EQ(0, fseek(minidump_file.get(), report_rva, SEEK_SET));
CHECK_EQ(report_size_bytes, fread(&serialized_report.at(0), 1,
report_size_bytes, minidump_file.get()));
browser_watcher::StabilityReport report;
CHECK(report.ParseFromString(serialized_report));
// Note: we can't use the usual protocol buffer human readable API due to
// the use of optimize_for = LITE_RUNTIME.
PrintReport(stdout, report);
return 0;
}
} // namespace
int main(int argc, char** argv) {
return Main(argc, argv);
}

@ -1,243 +0,0 @@
// Copyright 2016 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/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/strings/string_piece.h"
#include "base/time/time.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_metrics.h"
#include "components/browser_watcher/features.h"
#if BUILDFLAG(IS_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 =
std::string(base::StringPiece(pdb_filename, pdb_filename_length));
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 std::u16string& product_name,
const std::u16string& product_version,
const std::u16string& channel_name,
const std::u16string& 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

@ -1,71 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_BROWSER_WATCHER_EXTENDED_CRASH_REPORTING_H_
#define COMPONENTS_BROWSER_WATCHER_EXTENDED_CRASH_REPORTING_H_
#include <stdint.h>
#include "base/memory/raw_ptr.h"
#include "base/strings/string_piece.h"
namespace base {
namespace debug {
class GlobalActivityTracker;
} // namespace debug
} // namespace base
namespace browser_watcher {
class ExtendedCrashReporting {
public:
enum ProcessType { kBrowserProcess, kOther };
~ExtendedCrashReporting();
// Initializes extended crash reporting for this process if enabled.
// Returns nullptr if extended crash reporting is disabled.
// Should only be called once in any one process.
static ExtendedCrashReporting* SetUpIfEnabled(ProcessType process_type);
// Retrieves the extended crash reporting instance for this process if
// it exists, or nullptr if it does not.
static ExtendedCrashReporting* GetInstance();
// Records identifying strings for the product and version for an extended
// crash report. This function is threadsafe.
void SetProductStrings(const std::u16string& product_name,
const std::u16string& product_version,
const std::u16string& channel_name,
const std::u16string& special_build);
// Adds or updates the global extended crash reporting data.
// These functions are threadsafe.
void SetBool(base::StringPiece name, bool value);
void SetInt(base::StringPiece name, int64_t value);
// Adds or updates the global extended crash reporting data, if enabled.
static void SetDataBool(base::StringPiece name, bool value);
static void SetDataInt(base::StringPiece name, int64_t value);
// Allows tests to initialize and teardown the global instance.
static void SetUpForTesting();
static void TearDownForTesting();
private:
explicit ExtendedCrashReporting(base::debug::GlobalActivityTracker* tracker);
static ExtendedCrashReporting* SetUpImpl(ProcessType process_type);
void Initialize(ProcessType process_type);
// Registers a vectored exception handler that stores exception details to the
// activity report on exception - handled or not.
void RegisterVEH();
raw_ptr<void> veh_handle_ = nullptr;
const raw_ptr<base::debug::GlobalActivityTracker> tracker_;
};
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_EXTENDED_CRASH_REPORTING_H_

@ -1,21 +0,0 @@
// 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.
#include "components/browser_watcher/extended_crash_reporting_metrics.h"
#include "base/metrics/histogram_functions.h"
namespace browser_watcher {
void LogCollectOnCrashEvent(CollectOnCrashEvent event) {
base::UmaHistogramEnumeration("ActivityTracker.CollectCrash.Event", event,
CollectOnCrashEvent::kCollectOnCrashEventMax);
}
void LogActivityRecordEvent(ActivityRecordEvent event) {
base::UmaHistogramEnumeration("ActivityTracker.Record.Event", event,
ActivityRecordEvent::kActivityRecordEventMax);
}
} // namespace browser_watcher

@ -1,42 +0,0 @@
// 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.
#ifndef COMPONENTS_BROWSER_WATCHER_EXTENDED_CRASH_REPORTING_METRICS_H_
#define COMPONENTS_BROWSER_WATCHER_EXTENDED_CRASH_REPORTING_METRICS_H_
namespace browser_watcher {
// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
enum class CollectOnCrashEvent {
kCollectAttempt,
kUserDataDirNotEmptyUnused, // No longer used.
kPathExistsUnused, // No longer used.
kReportExtractionSuccess,
kPmaSetDeletedFailedUnused, // No longer used.
kOpenForDeleteFailedUnused, // No longer used.
kSuccess,
kInMemoryAnnotationExists,
// New values go here.
kCollectOnCrashEventMax
};
// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
enum class ActivityRecordEvent {
kRecordAttempt,
kActivityDirectoryExistsUnused, // No longer used.
kGotActivityPathUnused, // No longer used.
kGotTracker,
kMarkDeletedUnused, // No longer used.
kMarkDeletedGotFileUnused, // No longer used.
kOpenForDeleteFailedUnused, // No longer used.
// New values go here.
kActivityRecordEventMax
};
void LogCollectOnCrashEvent(CollectOnCrashEvent event);
void LogActivityRecordEvent(ActivityRecordEvent event);
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_EXTENDED_CRASH_REPORTING_METRICS_H_

@ -1,165 +0,0 @@
// Copyright 2016 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/browser_watcher/extended_crash_reporting.h"
#include <windows.h>
#include "base/command_line.h"
#include "base/debug/activity_analyzer.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/process/process.h"
#include "base/test/multiprocess_test.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/browser_watcher/activity_report.pb.h"
#include "components/browser_watcher/activity_report_extractor.h"
#include "components/browser_watcher/activity_tracker_annotation.h"
#include "components/browser_watcher/features.h"
#include "components/crash/core/common/crash_key.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#include "third_party/crashpad/crashpad/client/annotation_list.h"
namespace browser_watcher {
namespace {
crashpad::Annotation* GetActivtyTrackerAnnotation() {
crashpad::AnnotationList* annotation_list = crashpad::AnnotationList::Get();
for (auto it = annotation_list->begin(); it != annotation_list->end(); ++it) {
if (strcmp(ActivityTrackerAnnotation::kAnnotationName, (*it)->name()) ==
0 &&
ActivityTrackerAnnotation::kAnnotationType == (*it)->type()) {
return *it;
}
}
return nullptr;
}
bool IsNullOrEmpty(crashpad::Annotation* annotation) {
return annotation == nullptr || annotation->size() == 0;
}
bool IsNonEmpty(crashpad::Annotation* annotation) {
return annotation != nullptr && annotation->size() > 0;
}
using base::debug::GlobalActivityAnalyzer;
using base::debug::GlobalActivityTracker;
constexpr uint32_t kExceptionCode = 42U;
constexpr uint32_t kExceptionFlagContinuable = 0U;
} // namespace
class ExtendedCrashReportingTest : public testing::Test {
public:
ExtendedCrashReportingTest() {}
~ExtendedCrashReportingTest() override {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (global_tracker) {
global_tracker->ReleaseTrackerForCurrentThreadForTesting();
delete global_tracker;
}
}
void SetUp() override {
testing::Test::SetUp();
// Initialize the crash keys, which will also reset the activity tracker
// annotation if it's been used in previous tests.
crash_reporter::InitializeCrashKeysForTesting();
}
void TearDown() override {
ExtendedCrashReporting::TearDownForTesting();
crash_reporter::ResetCrashKeysForTesting();
testing::Test::TearDown();
}
std::unique_ptr<GlobalActivityAnalyzer> CreateAnalyzer() {
GlobalActivityTracker* tracker = GlobalActivityTracker::Get();
EXPECT_TRUE(tracker);
base::PersistentMemoryAllocator* tmp = tracker->allocator();
return GlobalActivityAnalyzer::CreateWithAllocator(
std::make_unique<base::PersistentMemoryAllocator>(
const_cast<void*>(tmp->data()), tmp->size(), 0u, 0u, "Copy", true));
}
};
TEST_F(ExtendedCrashReportingTest, DisabledByDefault) {
EXPECT_EQ(nullptr, ExtendedCrashReporting::SetUpIfEnabled(
ExtendedCrashReporting::kBrowserProcess));
EXPECT_EQ(nullptr, ExtendedCrashReporting::GetInstance());
}
TEST_F(ExtendedCrashReportingTest, SetUpIsEnabledByFeatureFlag) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(kExtendedCrashReportingFeature);
ExtendedCrashReporting* reporting = ExtendedCrashReporting::SetUpIfEnabled(
ExtendedCrashReporting::kBrowserProcess);
EXPECT_NE(nullptr, reporting);
EXPECT_EQ(reporting, ExtendedCrashReporting::GetInstance());
}
TEST_F(ExtendedCrashReportingTest, RecordsAnnotation) {
// Make sure the annotation doesn't exist before initialization.
crashpad::Annotation* annotation = GetActivtyTrackerAnnotation();
EXPECT_TRUE(IsNullOrEmpty(annotation));
ExtendedCrashReporting::SetUpForTesting();
EXPECT_NE(nullptr, ExtendedCrashReporting::GetInstance());
EXPECT_TRUE(IsNonEmpty(GetActivtyTrackerAnnotation()));
}
#if defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_WIN)
// The test does not pass under WinASan. See crbug.com/809524.
#define MAYBE_CrashingTest DISABLED_CrashingTest
#else
#define MAYBE_CrashingTest CrashingTest
#endif
TEST_F(ExtendedCrashReportingTest, MAYBE_CrashingTest) {
ExtendedCrashReporting::SetUpForTesting();
ExtendedCrashReporting* extended_crash_reporting =
ExtendedCrashReporting::GetInstance();
ASSERT_NE(nullptr, extended_crash_reporting);
// Raise an exception, then continue.
__try {
::RaiseException(kExceptionCode, kExceptionFlagContinuable, 0U, nullptr);
} __except (EXCEPTION_CONTINUE_EXECUTION) {
}
// Collect the report.
StabilityReport report;
ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
// Validate expectations.
ASSERT_EQ(1, report.process_states_size());
const ProcessState& process_state = report.process_states(0);
ASSERT_EQ(1, process_state.threads_size());
bool thread_found = false;
for (const ThreadState& thread : process_state.threads()) {
if (thread.thread_id() == ::GetCurrentThreadId()) {
thread_found = true;
ASSERT_TRUE(thread.has_exception());
const Exception& exception = thread.exception();
EXPECT_EQ(kExceptionCode, exception.code());
EXPECT_NE(0ULL, exception.program_counter());
EXPECT_NE(0ULL, exception.exception_address());
EXPECT_NE(0LL, exception.time());
}
}
ASSERT_TRUE(thread_found);
}
} // namespace browser_watcher

@ -1,15 +0,0 @@
// Copyright 2016 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/browser_watcher/features.h"
namespace browser_watcher {
BASE_FEATURE(kExtendedCrashReportingFeature,
"ExtendedCrashReporting",
base::FEATURE_DISABLED_BY_DEFAULT);
const char kInMemoryOnlyParam[] = "in_memory_only";
} // namespace browser_watcher

@ -1,22 +0,0 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_BROWSER_WATCHER_FEATURES_H_
#define COMPONENTS_BROWSER_WATCHER_FEATURES_H_
#include "base/feature_list.h"
namespace browser_watcher {
// Enables activity tracking and extending crash reports with structured
// high-level program state.
BASE_DECLARE_FEATURE(kExtendedCrashReportingFeature);
// Name of an experiment parameter that controls whether to record browser
// activity in-memory only.
extern const char kInMemoryOnlyParam[];
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_FEATURES_H_

@ -1,47 +0,0 @@
// 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.
// A utility for testing locally the retrieval of system session events.
#include <iostream>
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "components/metrics/system_session_analyzer/system_session_analyzer_win.h"
namespace {
using metrics::SystemSessionAnalyzer;
class SystemSessionEventFetcher : public SystemSessionAnalyzer {
public:
explicit SystemSessionEventFetcher() : SystemSessionAnalyzer(0) {}
using SystemSessionAnalyzer::FetchEvents;
};
} // namespace
int main(int argc, char** argv) {
SystemSessionEventFetcher fetcher;
std::vector<SystemSessionEventFetcher::EventInfo> events;
// Retrieve events for the last 5 sessions. We expect our own sessions start
// event, and then 2 events per each preceding session for 11 total.
if (!fetcher.FetchEvents(11U, &events)) {
std::cerr << "Failed to fetch events." << std::endl;
return 1;
}
// Print the event ids and times.
for (const SystemSessionEventFetcher::EventInfo& event : events) {
base::Time::Exploded exploded = {};
event.event_time.LocalExplode(&exploded);
std::string time = base::StringPrintf(
"%d/%d/%d %d:%02d:%02d", exploded.month, exploded.day_of_month,
exploded.year, exploded.hour, exploded.minute, exploded.second);
std::cout << "Event: " << event.event_id << " (" << time << ")"
<< std::endl;
}
return 0;
}

@ -1,18 +0,0 @@
// 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.
#ifndef COMPONENTS_BROWSER_WATCHER_MINIDUMP_USER_STREAMS_H_
#define COMPONENTS_BROWSER_WATCHER_MINIDUMP_USER_STREAMS_H_
namespace browser_watcher {
// The stream type assigned to the minidump stream that holds the serialized
// stability report.
// Note: the value was obtained by adding 1 to the stream type used for holding
// the SyzyAsan proto.
constexpr uint32_t kActivityReportStreamType = 0x4B6B0002;
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_MINIDUMP_USER_STREAMS_H_

@ -16,10 +16,6 @@ source_set("keep_alive_registry") {
]
deps = [ "//base" ]
if (is_win) {
deps += [ "//components/browser_watcher:stability_client" ]
}
}
source_set("unit_tests") {

@ -1,3 +0,0 @@
include_rules = [
"+components/browser_watcher",
]

@ -6,15 +6,9 @@
#include "base/logging.h"
#include "base/observer_list.h"
#include "build/build_config.h"
#include "components/keep_alive_registry/keep_alive_state_observer.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#if BUILDFLAG(IS_WIN)
#include "components/browser_watcher/activity_data_names.h"
#include "components/browser_watcher/extended_crash_reporting.h"
#endif
////////////////////////////////////////////////////////////////////////////////
// Public methods
@ -174,10 +168,6 @@ void KeepAliveRegistry::Unregister(KeepAliveOrigin origin,
void KeepAliveRegistry::OnKeepAliveStateChanged(bool new_keeping_alive) {
DVLOG(1) << "Notifying KeepAliveStateObservers: KeepingAlive changed to: "
<< new_keeping_alive;
#if BUILDFLAG(IS_WIN)
browser_watcher::ExtendedCrashReporting::SetDataBool(
browser_watcher::kActivityKeepAlive, new_keeping_alive);
#endif
for (KeepAliveStateObserver& observer : observers_)
observer.OnKeepAliveStateChanged(new_keeping_alive);
}
@ -185,10 +175,6 @@ void KeepAliveRegistry::OnKeepAliveStateChanged(bool new_keeping_alive) {
void KeepAliveRegistry::OnRestartAllowedChanged(bool new_restart_allowed) {
DVLOG(1) << "Notifying KeepAliveStateObservers: Restart changed to: "
<< new_restart_allowed;
#if BUILDFLAG(IS_WIN)
browser_watcher::ExtendedCrashReporting::SetDataBool(
browser_watcher::kActivityRestartAllowed, new_restart_allowed);
#endif
for (KeepAliveStateObserver& observer : observers_)
observer.OnKeepAliveRestartStateChanged(new_restart_allowed);
}

@ -236,7 +236,6 @@ static_library("metrics") {
"system_session_analyzer/system_session_analyzer_win.cc",
"system_session_analyzer/system_session_analyzer_win.h",
]
deps += [ "//components/browser_watcher:stability_client" ]
libs = [ "wevtapi.lib" ]
} else {
sources += [ "machine_id_provider_nonwin.cc" ]

@ -2,7 +2,6 @@
# dependencies to a minimal set.
include_rules = [
"-components",
"+components/browser_watcher",
"+components/component_updater",
"+components/flags_ui",
"+components/metrics",

@ -158,7 +158,6 @@ if (current_cpu == "x86") {
"//chrome/common:constants",
"//chrome/install_static:install_static_util",
"//chrome/installer/util:with_no_strings",
"//components/browser_watcher:browser_watcher_client",
"//components/crash/core/common",
"//components/flags_ui:switches",
"//components/policy:generated",