Import chromium-64.0.3282.140

This commit is contained in:
klzgrad
2018-02-02 05:49:39 -05:00
commit 86b64329f6
19589 changed files with 4029211 additions and 0 deletions

2
base/debug/OWNERS Normal file
View File

@@ -0,0 +1,2 @@
# For activity tracking:
per-file activity_*=bcwhite@chromium.org

View File

@@ -0,0 +1,412 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/activity_analyzer.h"
#include <algorithm>
#include <utility>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
namespace base {
namespace debug {
namespace {
// An empty snapshot that can be returned when there otherwise is none.
LazyInstance<ActivityUserData::Snapshot>::Leaky g_empty_user_data_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) {
UMA_HISTOGRAM_ENUMERATION("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 WrapUnique(new GlobalActivityAnalyzer(std::move(allocator)));
}
#if !defined(OS_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());
mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE);
if (!mmfile->IsValid()) {
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 // !defined(OS_NACL)
// static
std::unique_ptr<GlobalActivityAnalyzer>
GlobalActivityAnalyzer::CreateWithSharedMemory(
std::unique_ptr<SharedMemory> shm) {
if (shm->mapped_size() == 0 ||
!SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) {
return nullptr;
}
return CreateWithAllocator(std::make_unique<SharedPersistentMemoryAllocator>(
std::move(shm), 0, StringPiece(), /*readonly=*/true));
}
// static
std::unique_ptr<GlobalActivityAnalyzer>
GlobalActivityAnalyzer::CreateWithSharedMemoryHandle(
const SharedMemoryHandle& handle,
size_t size) {
std::unique_ptr<SharedMemory> shm(
new SharedMemory(handle, /*readonly=*/true));
if (!shm->Map(size))
return nullptr;
return CreateWithSharedMemory(std::move(shm));
}
int64_t GlobalActivityAnalyzer::GetFirstProcess() {
PrepareAllAnalyzers();
return GetNextProcess();
}
int64_t GlobalActivityAnalyzer::GetNextProcess() {
if (process_ids_.empty())
return 0;
int64_t pid = process_ids_.back();
process_ids_.pop_back();
return pid;
}
ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(int64_t 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(
int64_t 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);
int64_t 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(int64_t pid) {
auto iter = process_data_.find(pid);
if (iter == process_data_.end())
return g_empty_user_data_snapshot.Get();
if (iter->second.create_stamp > analysis_stamp_)
return g_empty_user_data_snapshot.Get();
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(int64_t pid) {
std::vector<GlobalActivityTracker::ModuleInfo> modules;
PersistentMemoryAllocator::Iterator iter(allocator_.get());
const GlobalActivityTracker::ModuleInfoRecord* record;
while (
(record =
iter.GetNextOfObject<GlobalActivityTracker::ModuleInfoRecord>()) !=
nullptr) {
int64_t 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) {
// TODO(bcwhite): Implement this.
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<int64_t> 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.
int64_t 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::ContainsKey(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.
int64_t process_id;
int64_t create_stamp;
ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
DCHECK(!base::ContainsKey(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.
std::reverse(process_ids_.begin(), process_ids_.end());
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,262 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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"
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(int64_t 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:
int64_t 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();
// 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.
int64_t 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;
DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer);
};
// 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();
// Creates a global analyzer using a given persistent-memory |allocator|.
static std::unique_ptr<GlobalActivityAnalyzer> CreateWithAllocator(
std::unique_ptr<PersistentMemoryAllocator> allocator);
#if !defined(OS_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 // !defined(OS_NACL)
// Like above but accesses an allocator in a mapped shared-memory segment.
static std::unique_ptr<GlobalActivityAnalyzer> CreateWithSharedMemory(
std::unique_ptr<SharedMemory> shm);
// Like above but takes a handle to an existing shared memory segment and
// maps it before creating the tracker.
static std::unique_ptr<GlobalActivityAnalyzer> CreateWithSharedMemoryHandle(
const SharedMemoryHandle& handle,
size_t size);
// 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.
int64_t GetFirstProcess();
int64_t 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(int64_t 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(int64_t 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(int64_t 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(int64_t 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();
int64_t 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<int64_t, 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<int64_t> 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_;
int64_t analyzers_iterator_pid_;
DISALLOW_COPY_AND_ASSIGN(GlobalActivityAnalyzer);
};
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_ACTIVITY_ANALYZER_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

23
base/debug/alias.cc Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/alias.h"
#include "build/build_config.h"
namespace base {
namespace debug {
#if defined(COMPILER_MSVC)
#pragma optimize("", off)
#endif
void Alias(const void* var) {
}
#if defined(COMPILER_MSVC)
#pragma optimize("", on)
#endif
} // namespace debug
} // namespace base

37
base/debug/alias.h Normal file
View File

@@ -0,0 +1,37 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_ALIAS_H_
#define BASE_DEBUG_ALIAS_H_
#include "base/base_export.h"
namespace base {
namespace debug {
// Make the optimizer think that var is aliased. This is to prevent it from
// optimizing out local variables that would not otherwise be live at the point
// of a potential crash.
// base::debug::Alias should only be used for local variables, not globals,
// object members, or function return values - these must be copied to locals if
// you want to ensure they are recorded in crash dumps.
// Note that if the local variable is a pointer then its value will be retained
// but the memory that it points to will probably not be saved in the crash
// dump - by default only stack memory is saved. Therefore the aliasing
// technique is usually only worthwhile with non-pointer variables. If you have
// a pointer to an object and you want to retain the object's state you need to
// copy the object or its fields to local variables. Example usage:
// int last_error = err_;
// base::debug::Alias(&last_error);
// char name_copy[16];
// strncpy(name_copy, p->name, sizeof(name_copy) - 1);
// name_copy[sizeof(name_copy) - 1] = '\0';
// base::debug::Alias(name_copy);
// CHECK(false);
void BASE_EXPORT Alias(const void* var);
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_ALIAS_H_

View File

@@ -0,0 +1,107 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/asan_invalid_access.h"
#include <stddef.h>
#include <memory>
#include "base/debug/alias.h"
#include "base/logging.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
namespace base {
namespace debug {
namespace {
#if defined(SYZYASAN) && defined(COMPILER_MSVC)
// Disable warning C4530: "C++ exception handler used, but unwind semantics are
// not enabled". We don't want to change the compilation flags just for this
// test, and no exception should be triggered here, so this warning has no value
// here.
#pragma warning(push)
#pragma warning(disable: 4530)
// Corrupt a memory block and make sure that the corruption gets detected either
// when we free it or when another crash happens (if |induce_crash| is set to
// true).
NOINLINE void CorruptMemoryBlock(bool induce_crash) {
// NOTE(sebmarchand): We intentionally corrupt a memory block here in order to
// trigger an Address Sanitizer (ASAN) error report.
static const int kArraySize = 5;
int* array = new int[kArraySize];
// Encapsulate the invalid memory access into a try-catch statement to prevent
// this function from being instrumented. This way the underflow won't be
// detected but the corruption will (as the allocator will still be hooked).
try {
// Declares the dummy value as volatile to make sure it doesn't get
// optimized away.
int volatile dummy = array[-1]--;
base::debug::Alias(const_cast<int*>(&dummy));
} catch (...) {
}
if (induce_crash)
CHECK(false);
delete[] array;
}
#pragma warning(pop)
#endif // SYZYASAN && COMPILER_MSVC
} // namespace
#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
// NOTE(sebmarchand): We intentionally perform some invalid heap access here in
// order to trigger an AddressSanitizer (ASan) error report.
static const size_t kArraySize = 5;
void AsanHeapOverflow() {
// Declares the array as volatile to make sure it doesn't get optimized away.
std::unique_ptr<volatile int[]> array(
const_cast<volatile int*>(new int[kArraySize]));
int dummy = array[kArraySize];
base::debug::Alias(&dummy);
}
void AsanHeapUnderflow() {
// Declares the array as volatile to make sure it doesn't get optimized away.
std::unique_ptr<volatile int[]> array(
const_cast<volatile int*>(new int[kArraySize]));
// We need to store the underflow address in a temporary variable as trying to
// access array[-1] will trigger a warning C4245: "conversion from 'int' to
// 'size_t', signed/unsigned mismatch".
volatile int* underflow_address = &array[0] - 1;
int dummy = *underflow_address;
base::debug::Alias(&dummy);
}
void AsanHeapUseAfterFree() {
// Declares the array as volatile to make sure it doesn't get optimized away.
std::unique_ptr<volatile int[]> array(
const_cast<volatile int*>(new int[kArraySize]));
volatile int* dangling = array.get();
array.reset();
int dummy = dangling[kArraySize / 2];
base::debug::Alias(&dummy);
}
#endif // ADDRESS_SANITIZER || SYZYASAN
#if defined(SYZYASAN) && defined(COMPILER_MSVC)
void AsanCorruptHeapBlock() {
CorruptMemoryBlock(false);
}
void AsanCorruptHeap() {
CorruptMemoryBlock(true);
}
#endif // SYZYASAN && COMPILER_MSVC
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,47 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Defines some functions that intentionally do an invalid memory access in
// order to trigger an AddressSanitizer (ASan) error report.
#ifndef BASE_DEBUG_ASAN_INVALID_ACCESS_H_
#define BASE_DEBUG_ASAN_INVALID_ACCESS_H_
#include "base/base_export.h"
#include "base/compiler_specific.h"
namespace base {
namespace debug {
#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
// Generates an heap buffer overflow.
BASE_EXPORT NOINLINE void AsanHeapOverflow();
// Generates an heap buffer underflow.
BASE_EXPORT NOINLINE void AsanHeapUnderflow();
// Generates an use after free.
BASE_EXPORT NOINLINE void AsanHeapUseAfterFree();
#endif // ADDRESS_SANITIZER || SYZYASAN
// The "corrupt-block" and "corrupt-heap" classes of bugs is specific to
// SyzyASan.
#if defined(SYZYASAN) && defined(COMPILER_MSVC)
// Corrupts a memory block and makes sure that the corruption gets detected when
// we try to free this block.
BASE_EXPORT NOINLINE void AsanCorruptHeapBlock();
// Corrupts the heap and makes sure that the corruption gets detected when a
// crash occur.
BASE_EXPORT NOINLINE void AsanCorruptHeap();
#endif // SYZYASAN && COMPILER_MSVC
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_ASAN_INVALID_ACCESS_H_

View File

@@ -0,0 +1,260 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/close_handle_hook_win.h"
#include <Windows.h>
#include <psapi.h>
#include <stddef.h>
#include <algorithm>
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/win/iat_patch_function.h"
#include "base/win/pe_image.h"
#include "base/win/scoped_handle.h"
#include "build/build_config.h"
namespace {
typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
HANDLE source_handle,
HANDLE target_process,
HANDLE* target_handle,
DWORD desired_access,
BOOL inherit_handle,
DWORD options);
CloseHandleType g_close_function = NULL;
DuplicateHandleType g_duplicate_function = NULL;
// The entry point for CloseHandle interception. This function notifies the
// verifier about the handle that is being closed, and calls the original
// function.
BOOL WINAPI CloseHandleHook(HANDLE handle) {
base::win::OnHandleBeingClosed(handle);
return g_close_function(handle);
}
BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
HANDLE source_handle,
HANDLE target_process,
HANDLE* target_handle,
DWORD desired_access,
BOOL inherit_handle,
DWORD options) {
if ((options & DUPLICATE_CLOSE_SOURCE) &&
(GetProcessId(source_process) == ::GetCurrentProcessId())) {
base::win::OnHandleBeingClosed(source_handle);
}
return g_duplicate_function(source_process, source_handle, target_process,
target_handle, desired_access, inherit_handle,
options);
}
} // namespace
namespace base {
namespace debug {
namespace {
// Provides a simple way to temporarily change the protection of a memory page.
class AutoProtectMemory {
public:
AutoProtectMemory()
: changed_(false), address_(NULL), bytes_(0), old_protect_(0) {}
~AutoProtectMemory() {
RevertProtection();
}
// Grants write access to a given memory range.
bool ChangeProtection(void* address, size_t bytes);
// Restores the original page protection.
void RevertProtection();
private:
bool changed_;
void* address_;
size_t bytes_;
DWORD old_protect_;
DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
};
bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
DCHECK(!changed_);
DCHECK(address);
// Change the page protection so that we can write.
MEMORY_BASIC_INFORMATION memory_info;
if (!VirtualQuery(address, &memory_info, sizeof(memory_info)))
return false;
DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
memory_info.Protect;
DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
if (!VirtualProtect(address, bytes, protect, &old_protect_))
return false;
changed_ = true;
address_ = address;
bytes_ = bytes;
return true;
}
void AutoProtectMemory::RevertProtection() {
if (!changed_)
return;
DCHECK(address_);
DCHECK(bytes_);
VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
changed_ = false;
address_ = NULL;
bytes_ = 0;
old_protect_ = 0;
}
// Performs an EAT interception.
void EATPatch(HMODULE module, const char* function_name,
void* new_function, void** old_function) {
if (!module)
return;
base::win::PEImage pe(module);
if (!pe.VerifyMagic())
return;
DWORD* eat_entry = pe.GetExportEntry(function_name);
if (!eat_entry)
return;
if (!(*old_function))
*old_function = pe.RVAToAddr(*eat_entry);
AutoProtectMemory memory;
if (!memory.ChangeProtection(eat_entry, sizeof(DWORD)))
return;
// Perform the patch.
#pragma warning(push)
#pragma warning(disable : 4311 4302)
// These casts generate truncation warnings because they are 32 bit specific.
*eat_entry = reinterpret_cast<DWORD>(new_function) -
reinterpret_cast<DWORD>(module);
#pragma warning(pop)
}
// Performs an IAT interception.
base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name,
void* new_function, void** old_function) {
if (!module)
return NULL;
base::win::IATPatchFunction* patch = new base::win::IATPatchFunction;
__try {
// There is no guarantee that |module| is still loaded at this point.
if (patch->PatchFromModule(module, "kernel32.dll", function_name,
new_function)) {
delete patch;
return NULL;
}
} __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Leak the patch.
return NULL;
}
if (!(*old_function)) {
// Things are probably messed up if each intercepted function points to
// a different place, but we need only one function to call.
*old_function = patch->original_function();
}
return patch;
}
// Keeps track of all the hooks needed to intercept functions which could
// possibly close handles.
class HandleHooks {
public:
HandleHooks() {}
~HandleHooks() {}
void AddIATPatch(HMODULE module);
void AddEATPatch();
private:
std::vector<base::win::IATPatchFunction*> hooks_;
DISALLOW_COPY_AND_ASSIGN(HandleHooks);
};
void HandleHooks::AddIATPatch(HMODULE module) {
if (!module)
return;
base::win::IATPatchFunction* patch = NULL;
patch = IATPatch(module, "CloseHandle", &CloseHandleHook,
reinterpret_cast<void**>(&g_close_function));
if (!patch)
return;
hooks_.push_back(patch);
patch = IATPatch(module, "DuplicateHandle", &DuplicateHandleHook,
reinterpret_cast<void**>(&g_duplicate_function));
if (!patch)
return;
hooks_.push_back(patch);
}
void HandleHooks::AddEATPatch() {
// An attempt to restore the entry on the table at destruction is not safe.
EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
&CloseHandleHook, reinterpret_cast<void**>(&g_close_function));
EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
&DuplicateHandleHook,
reinterpret_cast<void**>(&g_duplicate_function));
}
void PatchLoadedModules(HandleHooks* hooks) {
const DWORD kSize = 256;
DWORD returned;
std::unique_ptr<HMODULE[]> modules(new HMODULE[kSize]);
if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
kSize * sizeof(HMODULE), &returned)) {
return;
}
returned /= sizeof(HMODULE);
returned = std::min(kSize, returned);
for (DWORD current = 0; current < returned; current++) {
hooks->AddIATPatch(modules[current]);
}
}
} // namespace
void InstallHandleHooks() {
static HandleHooks* hooks = new HandleHooks();
// Performing EAT interception first is safer in the presence of other
// threads attempting to call CloseHandle.
hooks->AddEATPatch();
PatchLoadedModules(hooks);
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,19 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
#define BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
#include "base/base_export.h"
namespace base {
namespace debug {
// Installs the hooks required to debug use of improper handles.
BASE_EXPORT void InstallHandleHooks();
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_

236
base/debug/crash_logging.cc Normal file
View File

@@ -0,0 +1,236 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/crash_logging.h"
#include <cmath>
#include <unordered_map>
#include "base/debug/stack_trace.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
// Undef the macro so the preprocessor doesn't garble the constructor.
#undef ScopedCrashKey
namespace base {
namespace debug {
namespace {
CrashKeyImplementation* g_crash_key_impl = nullptr;
// Global map of crash key names to registration entries.
typedef std::unordered_map<base::StringPiece, CrashKey, base::StringPieceHash>
CrashKeyMap;
CrashKeyMap* g_crash_keys_ = nullptr;
// The maximum length of a single chunk.
size_t g_chunk_max_length_ = 0;
// String used to format chunked key names.
const char kChunkFormatString[] = "%s-%" PRIuS;
// The functions that are called to actually set the key-value pairs in the
// crash reportng system.
SetCrashKeyValueFuncT g_set_key_func_ = nullptr;
ClearCrashKeyValueFuncT g_clear_key_func_ = nullptr;
// For a given |length|, computes the number of chunks a value of that size
// will occupy.
size_t NumChunksForLength(size_t length) {
// Compute (length / g_chunk_max_length_), rounded up.
return (length + g_chunk_max_length_ - 1) / g_chunk_max_length_;
}
// The longest max_length allowed by the system.
const size_t kLargestValueAllowed = 2048;
} // namespace
CrashKeyString* AllocateCrashKeyString(const char name[],
CrashKeySize value_length) {
if (!g_crash_key_impl)
return nullptr;
return g_crash_key_impl->Allocate(name, value_length);
}
void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) {
if (!g_crash_key_impl || !crash_key)
return;
g_crash_key_impl->Set(crash_key, value);
}
void ClearCrashKeyString(CrashKeyString* crash_key) {
if (!g_crash_key_impl || !crash_key)
return;
g_crash_key_impl->Clear(crash_key);
}
void SetCrashKeyImplementation(std::unique_ptr<CrashKeyImplementation> impl) {
delete g_crash_key_impl;
g_crash_key_impl = impl.release();
}
void SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value) {
if (!g_set_key_func_ || !g_crash_keys_)
return;
const CrashKey* crash_key = LookupCrashKey(key);
DCHECK(crash_key) << "All crash keys must be registered before use "
<< "(key = " << key << ")";
// Handle the un-chunked case.
if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
g_set_key_func_(key, value);
return;
}
// Unset the unused chunks.
std::vector<std::string> chunks =
ChunkCrashKeyValue(*crash_key, value, g_chunk_max_length_);
for (size_t i = chunks.size();
i < NumChunksForLength(crash_key->max_length);
++i) {
g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1));
}
// Set the chunked keys.
for (size_t i = 0; i < chunks.size(); ++i) {
g_set_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1),
chunks[i]);
}
}
void ClearCrashKey(const base::StringPiece& key) {
if (!g_clear_key_func_ || !g_crash_keys_)
return;
const CrashKey* crash_key = LookupCrashKey(key);
// Handle the un-chunked case.
if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
g_clear_key_func_(key);
return;
}
for (size_t i = 0; i < NumChunksForLength(crash_key->max_length); ++i) {
g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1));
}
}
void SetCrashKeyToStackTrace(const base::StringPiece& key,
const StackTrace& trace) {
size_t count = 0;
const void* const* addresses = trace.Addresses(&count);
SetCrashKeyFromAddresses(key, addresses, count);
}
void SetCrashKeyFromAddresses(const base::StringPiece& key,
const void* const* addresses,
size_t count) {
std::string value = "<null>";
if (addresses && count) {
const size_t kBreakpadValueMax = 255;
std::vector<std::string> hex_backtrace;
size_t length = 0;
for (size_t i = 0; i < count; ++i) {
std::string s = base::StringPrintf("%p", addresses[i]);
length += s.length() + 1;
if (length > kBreakpadValueMax)
break;
hex_backtrace.push_back(s);
}
value = base::JoinString(hex_backtrace, " ");
// Warn if this exceeds the breakpad limits.
DCHECK_LE(value.length(), kBreakpadValueMax);
}
SetCrashKeyValue(key, value);
}
ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key,
const base::StringPiece& value)
: key_(key.as_string()) {
SetCrashKeyValue(key, value);
}
ScopedCrashKey::~ScopedCrashKey() {
ClearCrashKey(key_);
}
size_t InitCrashKeys(const CrashKey* const keys, size_t count,
size_t chunk_max_length) {
DCHECK(!g_crash_keys_) << "Crash logging may only be initialized once";
if (!keys) {
delete g_crash_keys_;
g_crash_keys_ = nullptr;
return 0;
}
g_crash_keys_ = new CrashKeyMap;
g_chunk_max_length_ = chunk_max_length;
size_t total_keys = 0;
for (size_t i = 0; i < count; ++i) {
g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i]));
total_keys += NumChunksForLength(keys[i].max_length);
DCHECK_LT(keys[i].max_length, kLargestValueAllowed);
}
DCHECK_EQ(count, g_crash_keys_->size())
<< "Duplicate crash keys were registered";
return total_keys;
}
const CrashKey* LookupCrashKey(const base::StringPiece& key) {
if (!g_crash_keys_)
return nullptr;
CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string());
if (it == g_crash_keys_->end())
return nullptr;
return &(it->second);
}
void SetCrashKeyReportingFunctions(
SetCrashKeyValueFuncT set_key_func,
ClearCrashKeyValueFuncT clear_key_func) {
g_set_key_func_ = set_key_func;
g_clear_key_func_ = clear_key_func;
}
std::vector<std::string> ChunkCrashKeyValue(const CrashKey& crash_key,
const base::StringPiece& value,
size_t chunk_max_length) {
std::string value_string = value.substr(0, crash_key.max_length).as_string();
std::vector<std::string> chunks;
for (size_t offset = 0; offset < value_string.length(); ) {
std::string chunk = value_string.substr(offset, chunk_max_length);
chunks.push_back(chunk);
offset += chunk.length();
}
return chunks;
}
void ResetCrashLoggingForTesting() {
delete g_crash_keys_;
g_crash_keys_ = nullptr;
g_chunk_max_length_ = 0;
g_set_key_func_ = nullptr;
g_clear_key_func_ = nullptr;
}
} // namespace debug
} // namespace base

209
base/debug/crash_logging.h Normal file
View File

@@ -0,0 +1,209 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_CRASH_LOGGING_H_
#define BASE_DEBUG_CRASH_LOGGING_H_
#include <stddef.h>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
namespace base {
namespace debug {
// A crash key is an annotation that is carried along with a crash report, to
// provide additional debugging information beyond a stack trace. Crash keys
// have a name and a string value.
//
// The preferred API is //components/crash/core/common:crash_key, however not
// all clients can hold a direct dependency on that target. The API provided
// in this file indirects the dependency.
//
// Example usage:
// static CrashKeyString* crash_key =
// AllocateCrashKeyString("name", CrashKeySize::Size32);
// SetCrashKeyString(crash_key, "value");
// ClearCrashKeyString(crash_key);
// The maximum length for a crash key's value must be one of the following
// pre-determined values.
enum class CrashKeySize {
Size32 = 32,
Size64 = 64,
Size256 = 256,
};
struct CrashKeyString;
// Allocates a new crash key with the specified |name| with storage for a
// value up to length |size|. This will return null if the crash key system is
// not initialized.
BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[],
CrashKeySize size);
// Stores |value| into the specified |crash_key|. The |crash_key| may be null
// if AllocateCrashKeyString() returned null. If |value| is longer than the
// size with which the key was allocated, it will be truncated.
BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key,
base::StringPiece value);
// Clears any value that was stored in |crash_key|. The |crash_key| may be
// null.
BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key);
////////////////////////////////////////////////////////////////////////////////
// The following declarations are used to initialize the crash key system
// in //base by providing implementations for the above functions.
// The virtual interface that provides the implementation for the crash key
// API. This is implemented by a higher-layer component, and the instance is
// set using the function below.
class CrashKeyImplementation {
public:
virtual ~CrashKeyImplementation() {}
virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0;
virtual void Set(CrashKeyString* crash_key, base::StringPiece value) = 0;
virtual void Clear(CrashKeyString* crash_key) = 0;
};
// Initializes the crash key system in base by replacing the existing
// implementation, if it exists, with |impl|. The |impl| is copied into base.
BASE_EXPORT void SetCrashKeyImplementation(
std::unique_ptr<CrashKeyImplementation> impl);
// The base structure for a crash key, storing the allocation metadata.
struct CrashKeyString {
constexpr CrashKeyString(const char name[], CrashKeySize size)
: name(name), size(size) {}
const char* const name;
const CrashKeySize size;
};
// The API below is deprecated.
////////////////////////////////////////////////////////////////////////////////
class StackTrace;
// Sets or clears a specific key-value pair from the crash metadata. Keys and
// values are terminated at the null byte.
BASE_EXPORT void SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value);
BASE_EXPORT void ClearCrashKey(const base::StringPiece& key);
// Records the given StackTrace into a crash key.
BASE_EXPORT void SetCrashKeyToStackTrace(const base::StringPiece& key,
const StackTrace& trace);
// Formats |count| instruction pointers from |addresses| using %p and
// sets the resulting string as a value for crash key |key|. A maximum of 23
// items will be encoded, since breakpad limits values to 255 bytes.
BASE_EXPORT void SetCrashKeyFromAddresses(const base::StringPiece& key,
const void* const* addresses,
size_t count);
// A scoper that sets the specified key to value for the lifetime of the
// object, and clears it on destruction.
class BASE_EXPORT ScopedCrashKey {
public:
ScopedCrashKey(const base::StringPiece& key, const base::StringPiece& value);
~ScopedCrashKey();
// Helper to force a static_assert when instantiating a ScopedCrashKey
// temporary without a name. The usual idiom is to just #define a macro that
// static_asserts with the message; however, that doesn't work well when the
// type is in a namespace.
//
// Instead, we use a templated helper to trigger the static_assert, observing
// two rules:
// - The static_assert needs to be in a normally uninstantiated template;
// otherwise, it will fail to compile =)
// - Similarly, the static_assert must be dependent on the template argument,
// to prevent it from being evaluated until the template is instantiated.
//
// To prevent this constructor from being accidentally invoked, it takes a
// special enum as an argument.
// Finally, note that this can't just be a template function that takes only
// one parameter, because this ends up triggering the vexing parse issue.
enum ScopedCrashKeyNeedsNameTag {
KEY_NEEDS_NAME,
};
template <typename... Args>
explicit ScopedCrashKey(ScopedCrashKeyNeedsNameTag, const Args&...) {
constexpr bool always_false = sizeof...(Args) == 0 && sizeof...(Args) != 0;
static_assert(
always_false,
"scoped crash key objects should not be unnamed temporaries.");
}
private:
std::string key_;
DISALLOW_COPY_AND_ASSIGN(ScopedCrashKey);
};
// Disallow an instantation of ScopedCrashKey without a name, since this results
// in a temporary that is immediately destroyed. Doing so will trigger the
// static_assert in the templated constructor helper in ScopedCrashKey.
#define ScopedCrashKey(...) \
ScopedCrashKey(base::debug::ScopedCrashKey::KEY_NEEDS_NAME, __VA_ARGS__)
// Before setting values for a key, all the keys must be registered.
struct BASE_EXPORT CrashKey {
// The name of the crash key, used in the above functions.
const char* key_name;
// The maximum length for a value. If the value is longer than this, it will
// be truncated. If the value is larger than the |chunk_max_length| passed to
// InitCrashKeys() but less than this value, it will be split into multiple
// numbered chunks.
size_t max_length;
};
// Before the crash key logging mechanism can be used, all crash keys must be
// registered with this function. The function returns the amount of space
// the crash reporting implementation should allocate space for the registered
// crash keys. |chunk_max_length| is the maximum size that a value in a single
// chunk can be.
BASE_EXPORT size_t InitCrashKeys(const CrashKey* const keys, size_t count,
size_t chunk_max_length);
// Returns the corresponding crash key object or NULL for a given key.
BASE_EXPORT const CrashKey* LookupCrashKey(const base::StringPiece& key);
// In the platform crash reporting implementation, these functions set and
// clear the NUL-terminated key-value pairs.
typedef void (*SetCrashKeyValueFuncT)(const base::StringPiece&,
const base::StringPiece&);
typedef void (*ClearCrashKeyValueFuncT)(const base::StringPiece&);
// Sets the function pointers that are used to integrate with the platform-
// specific crash reporting libraries.
BASE_EXPORT void SetCrashKeyReportingFunctions(
SetCrashKeyValueFuncT set_key_func,
ClearCrashKeyValueFuncT clear_key_func);
// Helper function that breaks up a value according to the parameters
// specified by the crash key object.
BASE_EXPORT std::vector<std::string> ChunkCrashKeyValue(
const CrashKey& crash_key,
const base::StringPiece& value,
size_t chunk_max_length);
// Resets the crash key system so it can be reinitialized. For testing only.
BASE_EXPORT void ResetCrashLoggingForTesting();
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_CRASH_LOGGING_H_

42
base/debug/debugger.cc Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/debugger.h"
#include "base/logging.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
namespace base {
namespace debug {
static bool is_debug_ui_suppressed = false;
bool WaitForDebugger(int wait_seconds, bool silent) {
#if defined(OS_ANDROID)
// The pid from which we know which process to attach to are not output by
// android ddms, so we have to print it out explicitly.
DLOG(INFO) << "DebugUtil::WaitForDebugger(pid=" << static_cast<int>(getpid())
<< ")";
#endif
for (int i = 0; i < wait_seconds * 10; ++i) {
if (BeingDebugged()) {
if (!silent)
BreakDebugger();
return true;
}
PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
}
return false;
}
void SetSuppressDebugUI(bool suppress) {
is_debug_ui_suppressed = suppress;
}
bool IsDebugUISuppressed() {
return is_debug_ui_suppressed;
}
} // namespace debug
} // namespace base

44
base/debug/debugger.h Normal file
View File

@@ -0,0 +1,44 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a cross platform interface for helper functions related to
// debuggers. You should use this to test if you're running under a debugger,
// and if you would like to yield (breakpoint) into the debugger.
#ifndef BASE_DEBUG_DEBUGGER_H_
#define BASE_DEBUG_DEBUGGER_H_
#include "base/base_export.h"
namespace base {
namespace debug {
// Waits wait_seconds seconds for a debugger to attach to the current process.
// When silent is false, an exception is thrown when a debugger is detected.
BASE_EXPORT bool WaitForDebugger(int wait_seconds, bool silent);
// Returns true if the given process is being run under a debugger.
//
// On OS X, the underlying mechanism doesn't work when the sandbox is enabled.
// To get around this, this function caches its value.
//
// WARNING: Because of this, on OS X, a call MUST be made to this function
// BEFORE the sandbox is enabled.
BASE_EXPORT bool BeingDebugged();
// Break into the debugger, assumes a debugger is present.
BASE_EXPORT void BreakDebugger();
// Used in test code, this controls whether showing dialogs and breaking into
// the debugger is suppressed for debug errors, even in debug mode (normally
// release mode doesn't do this stuff -- this is controlled separately).
// Normally UI is not suppressed. This is normally used when running automated
// tests where we want a crash rather than a dialog or a debugger.
BASE_EXPORT void SetSuppressDebugUI(bool suppress);
BASE_EXPORT bool IsDebugUISuppressed();
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_DEBUGGER_H_

View File

@@ -0,0 +1,272 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/debugger.h"
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#if defined(__GLIBCXX__)
#include <cxxabi.h>
#endif
#if defined(OS_MACOSX)
#include <AvailabilityMacros.h>
#endif
#if defined(OS_MACOSX) || defined(OS_BSD)
#include <sys/sysctl.h>
#endif
#if defined(OS_FREEBSD)
#include <sys/user.h>
#endif
#include <ostream>
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_piece.h"
#if defined(USE_SYMBOLIZE)
#include "base/third_party/symbolize/symbolize.h"
#endif
#if defined(OS_ANDROID)
#include "base/threading/platform_thread.h"
#endif
namespace base {
namespace debug {
#if defined(OS_MACOSX) || defined(OS_BSD)
// Based on Apple's recommended method as described in
// http://developer.apple.com/qa/qa2004/qa1361.html
bool BeingDebugged() {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
//
// While some code used below may be async-signal unsafe, note how
// the result is cached (see |is_set| and |being_debugged| static variables
// right below). If this code is properly warmed-up early
// in the start-up process, it should be safe to use later.
// If the process is sandboxed then we can't use the sysctl, so cache the
// value.
static bool is_set = false;
static bool being_debugged = false;
if (is_set)
return being_debugged;
// Initialize mib, which tells sysctl what info we want. In this case,
// we're looking for information about a specific process ID.
int mib[] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid()
#if defined(OS_OPENBSD)
, sizeof(struct kinfo_proc),
0
#endif
};
// Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and
// binary interfaces may change.
struct kinfo_proc info;
size_t info_size = sizeof(info);
#if defined(OS_OPENBSD)
if (sysctl(mib, arraysize(mib), NULL, &info_size, NULL, 0) < 0)
return -1;
mib[5] = (info_size / sizeof(struct kinfo_proc));
#endif
int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
DCHECK_EQ(sysctl_result, 0);
if (sysctl_result != 0) {
is_set = true;
being_debugged = false;
return being_debugged;
}
// This process is being debugged if the P_TRACED flag is set.
is_set = true;
#if defined(OS_FREEBSD)
being_debugged = (info.ki_flag & P_TRACED) != 0;
#elif defined(OS_BSD)
being_debugged = (info.p_flag & P_TRACED) != 0;
#else
being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
#endif
return being_debugged;
}
#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
// We can look in /proc/self/status for TracerPid. We are likely used in crash
// handling, so we are careful not to use the heap or have side effects.
// Another option that is common is to try to ptrace yourself, but then we
// can't detach without forking(), and that's not so great.
// static
bool BeingDebugged() {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
return false;
// We assume our line will be in the first 1024 characters and that we can
// read this much all at once. In practice this will generally be true.
// This simplifies and speeds up things considerably.
char buf[1024];
ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
if (IGNORE_EINTR(close(status_fd)) < 0)
return false;
if (num_read <= 0)
return false;
StringPiece status(buf, num_read);
StringPiece tracer("TracerPid:\t");
StringPiece::size_type pid_index = status.find(tracer);
if (pid_index == StringPiece::npos)
return false;
// Our pid is 0 without a debugger, assume this for any pid starting with 0.
pid_index += tracer.size();
return pid_index < status.size() && status[pid_index] != '0';
}
#elif defined(OS_FUCHSIA)
bool BeingDebugged() {
// TODO(fuchsia): No gdb/gdbserver in the SDK yet.
return false;
}
#else
bool BeingDebugged() {
NOTIMPLEMENTED();
return false;
}
#endif
// We want to break into the debugger in Debug mode, and cause a crash dump in
// Release mode. Breakpad behaves as follows:
//
// +-------+-----------------+-----------------+
// | OS | Dump on SIGTRAP | Dump on SIGABRT |
// +-------+-----------------+-----------------+
// | Linux | N | Y |
// | Mac | Y | N |
// +-------+-----------------+-----------------+
//
// Thus we do the following:
// Linux: Debug mode if a debugger is attached, send SIGTRAP; otherwise send
// SIGABRT
// Mac: Always send SIGTRAP.
#if defined(ARCH_CPU_ARMEL)
#define DEBUG_BREAK_ASM() asm("bkpt 0")
#elif defined(ARCH_CPU_ARM64)
#define DEBUG_BREAK_ASM() asm("brk 0")
#elif defined(ARCH_CPU_MIPS_FAMILY)
#define DEBUG_BREAK_ASM() asm("break 2")
#elif defined(ARCH_CPU_X86_FAMILY)
#define DEBUG_BREAK_ASM() asm("int3")
#endif
#if defined(NDEBUG) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
#define DEBUG_BREAK() abort()
#elif defined(OS_NACL)
// The NaCl verifier doesn't let use use int3. For now, we call abort(). We
// should ask for advice from some NaCl experts about the optimum thing here.
// http://code.google.com/p/nativeclient/issues/detail?id=645
#define DEBUG_BREAK() abort()
#elif !defined(OS_MACOSX)
// Though Android has a "helpful" process called debuggerd to catch native
// signals on the general assumption that they are fatal errors. If no debugger
// is attached, we call abort since Breakpad needs SIGABRT to create a dump.
// When debugger is attached, for ARM platform the bkpt instruction appears
// to cause SIGBUS which is trapped by debuggerd, and we've had great
// difficulty continuing in a debugger once we stop from SIG triggered by native
// code, use GDB to set |go| to 1 to resume execution; for X86 platform, use
// "int3" to setup breakpiont and raise SIGTRAP.
//
// On other POSIX architectures, except Mac OS X, we use the same logic to
// ensure that breakpad creates a dump on crashes while it is still possible to
// use a debugger.
namespace {
void DebugBreak() {
if (!BeingDebugged()) {
abort();
} else {
#if defined(DEBUG_BREAK_ASM)
DEBUG_BREAK_ASM();
#else
volatile int go = 0;
while (!go) {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
}
#endif
}
}
} // namespace
#define DEBUG_BREAK() DebugBreak()
#elif defined(DEBUG_BREAK_ASM)
#define DEBUG_BREAK() DEBUG_BREAK_ASM()
#else
#error "Don't know how to debug break on this architecture/OS"
#endif
void BreakDebugger() {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
// Linker's ICF feature may merge this function with other functions with the
// same definition (e.g. any function whose sole job is to call abort()) and
// it may confuse the crash report processing system. http://crbug.com/508489
static int static_variable_to_make_this_function_unique = 0;
base::debug::Alias(&static_variable_to_make_this_function_unique);
DEBUG_BREAK();
#if defined(OS_ANDROID) && !defined(OFFICIAL_BUILD)
// For Android development we always build release (debug builds are
// unmanageably large), so the unofficial build is used for debugging. It is
// helpful to be able to insert BreakDebugger() statements in the source,
// attach the debugger, inspect the state of the program and then resume it by
// setting the 'go' variable above.
#elif defined(NDEBUG)
// Terminate the program after signaling the debug break.
_exit(1);
#endif
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/debugger.h"
#include <stdlib.h>
#include <windows.h>
namespace base {
namespace debug {
bool BeingDebugged() {
return ::IsDebuggerPresent() != 0;
}
void BreakDebugger() {
if (IsDebugUISuppressed())
_exit(1);
__debugbreak();
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,41 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/dump_without_crashing.h"
#include "base/logging.h"
namespace {
// Pointer to the function that's called by DumpWithoutCrashing() to dump the
// process's memory.
void(CDECL* dump_without_crashing_function_)() = nullptr;
} // namespace
namespace base {
namespace debug {
bool DumpWithoutCrashing() {
if (dump_without_crashing_function_) {
(*dump_without_crashing_function_)();
return true;
}
return false;
}
void SetDumpWithoutCrashingFunction(void (CDECL *function)()) {
#if !defined(COMPONENT_BUILD)
// In component builds, the same base is shared between modules
// so might be initialized several times. However in non-
// component builds this should never happen.
DCHECK(!dump_without_crashing_function_);
#endif
dump_without_crashing_function_ = function;
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,37 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
#define BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "build/build_config.h"
namespace base {
namespace debug {
// Handler to silently dump the current process without crashing.
// Before calling this function, call SetDumpWithoutCrashingFunction to pass a
// function pointer.
// Windows:
// This must be done for each instance of base (i.e. module) and is normally
// chrome_elf!DumpProcessWithoutCrash. See example code in chrome_main.cc that
// does this for chrome.dll and chrome_child.dll. Note: Crashpad sets this up
// for main chrome.exe as part of calling crash_reporter::InitializeCrashpad.
// Mac/Linux:
// Crashpad does this as part of crash_reporter::InitializeCrashpad.
// Returns false if called before SetDumpWithoutCrashingFunction.
BASE_EXPORT bool DumpWithoutCrashing();
// Sets a function that'll be invoked to dump the current process when
// DumpWithoutCrashing() is called.
BASE_EXPORT void SetDumpWithoutCrashingFunction(void (CDECL *function)());
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_

View File

@@ -0,0 +1,141 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/gdi_debug_util_win.h"
#include <algorithm>
#include <cmath>
#include <psapi.h>
#include <stddef.h>
#include <TlHelp32.h>
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
namespace {
void CollectChildGDIUsageAndDie(DWORD parent_pid) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
CHECK_NE(INVALID_HANDLE_VALUE, snapshot);
int total_process_count = 0;
base::debug::Alias(&total_process_count);
int total_peak_gdi_count = 0;
base::debug::Alias(&total_peak_gdi_count);
int total_gdi_count = 0;
base::debug::Alias(&total_gdi_count);
int total_user_count = 0;
base::debug::Alias(&total_user_count);
int child_count = 0;
base::debug::Alias(&child_count);
int peak_gdi_count = 0;
base::debug::Alias(&peak_gdi_count);
int sum_gdi_count = 0;
base::debug::Alias(&sum_gdi_count);
int sum_user_count = 0;
base::debug::Alias(&sum_user_count);
PROCESSENTRY32 proc_entry = {0};
proc_entry.dwSize = sizeof(PROCESSENTRY32);
CHECK(Process32First(snapshot, &proc_entry));
do {
base::win::ScopedHandle process(
OpenProcess(PROCESS_QUERY_INFORMATION,
FALSE,
proc_entry.th32ProcessID));
if (!process.IsValid())
continue;
int num_gdi_handles = GetGuiResources(process.Get(), GR_GDIOBJECTS);
int num_user_handles = GetGuiResources(process.Get(), GR_USEROBJECTS);
// Compute sum and peak counts for all processes.
++total_process_count;
total_user_count += num_user_handles;
total_gdi_count += num_gdi_handles;
total_peak_gdi_count = std::max(total_peak_gdi_count, num_gdi_handles);
if (parent_pid != proc_entry.th32ParentProcessID)
continue;
// Compute sum and peak counts for child processes.
++child_count;
sum_user_count += num_user_handles;
sum_gdi_count += num_gdi_handles;
peak_gdi_count = std::max(peak_gdi_count, num_gdi_handles);
} while (Process32Next(snapshot, &proc_entry));
CloseHandle(snapshot);
CHECK(false);
}
} // namespace
namespace base {
namespace debug {
void CollectGDIUsageAndDie(BITMAPINFOHEADER* header, HANDLE shared_section) {
// Make sure parameters are saved in the minidump.
DWORD last_error = GetLastError();
bool is_gdi_available = base::win::IsUser32AndGdi32Available();
LONG width = header ? header->biWidth : 0;
LONG height = header ? header->biHeight : 0;
base::debug::Alias(&last_error);
base::debug::Alias(&is_gdi_available);
base::debug::Alias(&width);
base::debug::Alias(&height);
base::debug::Alias(&shared_section);
DWORD num_user_handles = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
DWORD num_gdi_handles = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
if (num_gdi_handles == 0) {
DWORD get_gui_resources_error = GetLastError();
base::debug::Alias(&get_gui_resources_error);
CHECK(false);
}
base::debug::Alias(&num_gdi_handles);
base::debug::Alias(&num_user_handles);
const DWORD kLotsOfHandles = 9990;
CHECK_LE(num_gdi_handles, kLotsOfHandles);
PROCESS_MEMORY_COUNTERS_EX pmc;
pmc.cb = sizeof(pmc);
CHECK(GetProcessMemoryInfo(GetCurrentProcess(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
sizeof(pmc)));
const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
CHECK_LE(pmc.PagefileUsage, kLotsOfMemory);
CHECK_LE(pmc.PrivateUsage, kLotsOfMemory);
void* small_data = nullptr;
base::debug::Alias(&small_data);
if (std::abs(height) * width > 100) {
// Huh, that's weird. We don't have crazy handle count, we don't have
// ridiculous memory usage. Try to allocate a small bitmap and see if that
// fails too.
header->biWidth = 5;
header->biHeight = -5;
HBITMAP small_bitmap = CreateDIBSection(
nullptr, reinterpret_cast<BITMAPINFO*>(&header),
0, &small_data, shared_section, 0);
CHECK(small_bitmap != nullptr);
DeleteObject(small_bitmap);
}
// Maybe the child processes are the ones leaking GDI or USER resouces.
CollectChildGDIUsageAndDie(GetCurrentProcessId());
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,25 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
#define BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
#include <windows.h>
#include "base/base_export.h"
namespace base {
namespace debug {
// Crashes the process, using base::debug::Alias to leave valuable debugging
// information in the crash dump. Pass values for |header| and |shared_section|
// in the event of a bitmap allocation failure, to gather information about
// those as well.
void BASE_EXPORT CollectGDIUsageAndDie(BITMAPINFOHEADER* header = nullptr,
HANDLE shared_section = nullptr);
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_
#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
#include "base/macros.h"
#include "build/build_config.h"
// This file defines macros which can be used to annotate intentional memory
// leaks. Support for annotations is implemented in LeakSanitizer. Annotated
// objects will be treated as a source of live pointers, i.e. any heap objects
// reachable by following pointers from an annotated object will not be
// reported as leaks.
//
// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope
// will be annotated as leaks.
// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will
// be annotated as a leak.
#if defined(LEAK_SANITIZER) && !defined(OS_NACL)
#include <sanitizer/lsan_interface.h>
class ScopedLeakSanitizerDisabler {
public:
ScopedLeakSanitizerDisabler() { __lsan_disable(); }
~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
private:
DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler);
};
#define ANNOTATE_SCOPED_MEMORY_LEAK \
ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X);
#else
#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0)
#endif
#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_

140
base/debug/leak_tracker.h Normal file
View File

@@ -0,0 +1,140 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_LEAK_TRACKER_H_
#define BASE_DEBUG_LEAK_TRACKER_H_
#include <stddef.h>
#include "build/build_config.h"
// Only enable leak tracking in non-uClibc debug builds.
#if !defined(NDEBUG) && !defined(__UCLIBC__)
#define ENABLE_LEAK_TRACKER
#endif
#ifdef ENABLE_LEAK_TRACKER
#include "base/containers/linked_list.h"
#include "base/debug/stack_trace.h"
#include "base/logging.h"
#endif // ENABLE_LEAK_TRACKER
// LeakTracker is a helper to verify that all instances of a class
// have been destroyed.
//
// It is particularly useful for classes that are bound to a single thread --
// before destroying that thread, one can check that there are no remaining
// instances of that class.
//
// For example, to enable leak tracking for class net::URLRequest, start by
// adding a member variable of type LeakTracker<net::URLRequest>.
//
// class URLRequest {
// ...
// private:
// base::LeakTracker<URLRequest> leak_tracker_;
// };
//
//
// Next, when we believe all instances of net::URLRequest have been deleted:
//
// LeakTracker<net::URLRequest>::CheckForLeaks();
//
// Should the check fail (because there are live instances of net::URLRequest),
// then the allocation callstack for each leaked instances is dumped to
// the error log.
//
// If ENABLE_LEAK_TRACKER is not defined, then the check has no effect.
namespace base {
namespace debug {
#ifndef ENABLE_LEAK_TRACKER
// If leak tracking is disabled, do nothing.
template<typename T>
class LeakTracker {
public:
~LeakTracker() {}
static void CheckForLeaks() {}
static int NumLiveInstances() { return -1; }
};
#else
// If leak tracking is enabled we track where the object was allocated from.
template<typename T>
class LeakTracker : public LinkNode<LeakTracker<T> > {
public:
LeakTracker() {
instances()->Append(this);
}
~LeakTracker() {
this->RemoveFromList();
}
static void CheckForLeaks() {
// Walk the allocation list and print each entry it contains.
size_t count = 0;
// Copy the first 3 leak allocation callstacks onto the stack.
// This way if we hit the CHECK() in a release build, the leak
// information will be available in mini-dump.
const size_t kMaxStackTracesToCopyOntoStack = 3;
StackTrace stacktraces[kMaxStackTracesToCopyOntoStack];
for (LinkNode<LeakTracker<T> >* node = instances()->head();
node != instances()->end();
node = node->next()) {
StackTrace& allocation_stack = node->value()->allocation_stack_;
if (count < kMaxStackTracesToCopyOntoStack)
stacktraces[count] = allocation_stack;
++count;
if (LOG_IS_ON(ERROR)) {
LOG_STREAM(ERROR) << "Leaked " << node << " which was allocated by:";
allocation_stack.OutputToStream(&LOG_STREAM(ERROR));
}
}
CHECK_EQ(0u, count);
// Hack to keep |stacktraces| and |count| alive (so compiler
// doesn't optimize it out, and it will appear in mini-dumps).
if (count == 0x1234) {
for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i)
stacktraces[i].Print();
}
}
static int NumLiveInstances() {
// Walk the allocation list and count how many entries it has.
int count = 0;
for (LinkNode<LeakTracker<T> >* node = instances()->head();
node != instances()->end();
node = node->next()) {
++count;
}
return count;
}
private:
// Each specialization of LeakTracker gets its own static storage.
static LinkedList<LeakTracker<T> >* instances() {
static LinkedList<LeakTracker<T> > list;
return &list;
}
StackTrace allocation_stack_;
};
#endif // ENABLE_LEAK_TRACKER
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_LEAK_TRACKER_H_

View File

@@ -0,0 +1,169 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/proc_maps_linux.h"
#include <fcntl.h>
#include <stddef.h>
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include <inttypes.h>
#endif
#if defined(OS_ANDROID) && !defined(__LP64__)
// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an
// unsigned long int, which is incompatible with Bionic's stdint.h
// defining uintptr_t as an unsigned int:
// https://code.google.com/p/android/issues/detail?id=57218
#undef SCNxPTR
#define SCNxPTR "x"
#endif
namespace base {
namespace debug {
// Scans |proc_maps| starting from |pos| returning true if the gate VMA was
// found, otherwise returns false.
static bool ContainsGateVMA(std::string* proc_maps, size_t pos) {
#if defined(ARCH_CPU_ARM_FAMILY)
// The gate VMA on ARM kernels is the interrupt vectors page.
return proc_maps->find(" [vectors]\n", pos) != std::string::npos;
#elif defined(ARCH_CPU_X86_64)
// The gate VMA on x86 64-bit kernels is the virtual system call page.
return proc_maps->find(" [vsyscall]\n", pos) != std::string::npos;
#else
// Otherwise assume there is no gate VMA in which case we shouldn't
// get duplicate entires.
return false;
#endif
}
bool ReadProcMaps(std::string* proc_maps) {
// seq_file only writes out a page-sized amount on each call. Refer to header
// file for details.
const long kReadSize = sysconf(_SC_PAGESIZE);
base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY)));
if (!fd.is_valid()) {
DPLOG(ERROR) << "Couldn't open /proc/self/maps";
return false;
}
proc_maps->clear();
while (true) {
// To avoid a copy, resize |proc_maps| so read() can write directly into it.
// Compute |buffer| afterwards since resize() may reallocate.
size_t pos = proc_maps->size();
proc_maps->resize(pos + kReadSize);
void* buffer = &(*proc_maps)[pos];
ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, kReadSize));
if (bytes_read < 0) {
DPLOG(ERROR) << "Couldn't read /proc/self/maps";
proc_maps->clear();
return false;
}
// ... and don't forget to trim off excess bytes.
proc_maps->resize(pos + bytes_read);
if (bytes_read == 0)
break;
// The gate VMA is handled as a special case after seq_file has finished
// iterating through all entries in the virtual memory table.
//
// Unfortunately, if additional entries are added at this point in time
// seq_file gets confused and the next call to read() will return duplicate
// entries including the gate VMA again.
//
// Avoid this by searching for the gate VMA and breaking early.
if (ContainsGateVMA(proc_maps, pos))
break;
}
return true;
}
bool ParseProcMaps(const std::string& input,
std::vector<MappedMemoryRegion>* regions_out) {
CHECK(regions_out);
std::vector<MappedMemoryRegion> regions;
// This isn't async safe nor terribly efficient, but it doesn't need to be at
// this point in time.
std::vector<std::string> lines = SplitString(
input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (size_t i = 0; i < lines.size(); ++i) {
// Due to splitting on '\n' the last line should be empty.
if (i == lines.size() - 1) {
if (!lines[i].empty()) {
DLOG(WARNING) << "Last line not empty";
return false;
}
break;
}
MappedMemoryRegion region;
const char* line = lines[i].c_str();
char permissions[5] = {'\0'}; // Ensure NUL-terminated string.
uint8_t dev_major = 0;
uint8_t dev_minor = 0;
long inode = 0;
int path_index = 0;
// Sample format from man 5 proc:
//
// address perms offset dev inode pathname
// 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
//
// The final %n term captures the offset in the input string, which is used
// to determine the path name. It *does not* increment the return value.
// Refer to man 3 sscanf for details.
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n",
&region.start, &region.end, permissions, &region.offset,
&dev_major, &dev_minor, &inode, &path_index) < 7) {
DPLOG(WARNING) << "sscanf failed for line: " << line;
return false;
}
region.permissions = 0;
if (permissions[0] == 'r')
region.permissions |= MappedMemoryRegion::READ;
else if (permissions[0] != '-')
return false;
if (permissions[1] == 'w')
region.permissions |= MappedMemoryRegion::WRITE;
else if (permissions[1] != '-')
return false;
if (permissions[2] == 'x')
region.permissions |= MappedMemoryRegion::EXECUTE;
else if (permissions[2] != '-')
return false;
if (permissions[3] == 'p')
region.permissions |= MappedMemoryRegion::PRIVATE;
else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory.
return false;
// Pushing then assigning saves us a string copy.
regions.push_back(region);
regions.back().path.assign(line + path_index);
}
regions_out->swap(regions);
return true;
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_PROC_MAPS_LINUX_H_
#define BASE_DEBUG_PROC_MAPS_LINUX_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "base/base_export.h"
namespace base {
namespace debug {
// Describes a region of mapped memory and the path of the file mapped.
struct MappedMemoryRegion {
enum Permission {
READ = 1 << 0,
WRITE = 1 << 1,
EXECUTE = 1 << 2,
PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared.
};
// The address range [start,end) of mapped memory.
uintptr_t start;
uintptr_t end;
// Byte offset into |path| of the range mapped into memory.
unsigned long long offset;
// Image base, if this mapping corresponds to an ELF image.
uintptr_t base;
// Bitmask of read/write/execute/private/shared permissions.
uint8_t permissions;
// Name of the file mapped into memory.
//
// NOTE: path names aren't guaranteed to point at valid files. For example,
// "[heap]" and "[stack]" are used to represent the location of the process'
// heap and stack, respectively.
std::string path;
};
// Reads the data from /proc/self/maps and stores the result in |proc_maps|.
// Returns true if successful, false otherwise.
//
// There is *NO* guarantee that the resulting contents will be free of
// duplicates or even contain valid entries by time the method returns.
//
//
// THE GORY DETAILS
//
// Did you know it's next-to-impossible to atomically read the whole contents
// of /proc/<pid>/maps? You would think that if we passed in a large-enough
// buffer to read() that It Should Just Work(tm), but sadly that's not the case.
//
// Linux's procfs uses seq_file [1] for handling iteration, text formatting,
// and dealing with resulting data that is larger than the size of a page. That
// last bit is especially important because it means that seq_file will never
// return more than the size of a page in a single call to read().
//
// Unfortunately for a program like Chrome the size of /proc/self/maps is
// larger than the size of page so we're forced to call read() multiple times.
// If the virtual memory table changed in any way between calls to read() (e.g.,
// a different thread calling mprotect()), it can make seq_file generate
// duplicate entries or skip entries.
//
// Even if seq_file was changed to keep flushing the contents of its page-sized
// buffer to the usermode buffer inside a single call to read(), it has to
// release its lock on the virtual memory table to handle page faults while
// copying data to usermode. This puts us in the same situation where the table
// can change while we're copying data.
//
// Alternatives such as fork()-and-suspend-the-parent-while-child-reads were
// attempted, but they present more subtle problems than it's worth. Depending
// on your use case your best bet may be to read /proc/<pid>/maps prior to
// starting other threads.
//
// [1] http://kernelnewbies.org/Documents/SeqFileHowTo
BASE_EXPORT bool ReadProcMaps(std::string* proc_maps);
// Parses /proc/<pid>/maps input data and stores in |regions|. Returns true
// and updates |regions| if and only if all of |input| was successfully parsed.
BASE_EXPORT bool ParseProcMaps(const std::string& input,
std::vector<MappedMemoryRegion>* regions);
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_

225
base/debug/profiler.cc Normal file
View File

@@ -0,0 +1,225 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/profiler.h"
#include <string>
#include "base/debug/debugging_flags.h"
#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "base/win/current_module.h"
#include "base/win/pe_image.h"
#endif // defined(OS_WIN)
// TODO(peria): Enable profiling on Windows.
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
#endif
namespace base {
namespace debug {
// TODO(peria): Enable profiling on Windows.
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
static int profile_count = 0;
void StartProfiling(const std::string& name) {
++profile_count;
std::string full_name(name);
std::string pid = IntToString(GetCurrentProcId());
std::string count = IntToString(profile_count);
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
ProfilerStart(full_name.c_str());
}
void StopProfiling() {
ProfilerFlush();
ProfilerStop();
}
void FlushProfiling() {
ProfilerFlush();
}
bool BeingProfiled() {
return ProfilingIsEnabledForAllThreads();
}
void RestartProfilingAfterFork() {
ProfilerRegisterThread();
}
bool IsProfilingSupported() {
return true;
}
#else
void StartProfiling(const std::string& name) {
}
void StopProfiling() {
}
void FlushProfiling() {
}
bool BeingProfiled() {
return false;
}
void RestartProfilingAfterFork() {
}
bool IsProfilingSupported() {
return false;
}
#endif
#if !defined(OS_WIN)
bool IsBinaryInstrumented() {
return false;
}
ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
return nullptr;
}
DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
return nullptr;
}
AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
return nullptr;
}
MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
return nullptr;
}
#else // defined(OS_WIN)
bool IsBinaryInstrumented() {
enum InstrumentationCheckState {
UNINITIALIZED,
INSTRUMENTED_IMAGE,
NON_INSTRUMENTED_IMAGE,
};
static InstrumentationCheckState state = UNINITIALIZED;
if (state == UNINITIALIZED) {
base::win::PEImage image(CURRENT_MODULE());
// Check to be sure our image is structured as we'd expect.
DCHECK(image.VerifyMagic());
// Syzygy-instrumented binaries contain a PE image section named ".thunks",
// and all Syzygy-modified binaries contain the ".syzygy" image section.
// This is a very fast check, as it only looks at the image header.
if ((image.GetImageSectionHeaderByName(".thunks") != NULL) &&
(image.GetImageSectionHeaderByName(".syzygy") != NULL)) {
state = INSTRUMENTED_IMAGE;
} else {
state = NON_INSTRUMENTED_IMAGE;
}
}
DCHECK(state != UNINITIALIZED);
return state == INSTRUMENTED_IMAGE;
}
namespace {
struct FunctionSearchContext {
const char* name;
FARPROC function;
};
// Callback function to PEImage::EnumImportChunks.
bool FindResolutionFunctionInImports(
const base::win::PEImage &image, const char* module_name,
PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
PVOID cookie) {
FunctionSearchContext* context =
reinterpret_cast<FunctionSearchContext*>(cookie);
DCHECK(context);
DCHECK(!context->function);
// Our import address table contains pointers to the functions we import
// at this point. Let's retrieve the first such function and use it to
// find the module this import was resolved to by the loader.
const wchar_t* function_in_module =
reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
// Retrieve the module by a function in the module.
const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
HMODULE module = NULL;
if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
// This can happen if someone IAT patches us to a thunk.
return true;
}
// See whether this module exports the function we're looking for.
FARPROC exported_func = ::GetProcAddress(module, context->name);
if (exported_func != NULL) {
// We found it, return the function and terminate the enumeration.
context->function = exported_func;
return false;
}
// Keep going.
return true;
}
template <typename FunctionType>
FunctionType FindFunctionInImports(const char* function_name) {
if (!IsBinaryInstrumented())
return NULL;
base::win::PEImage image(CURRENT_MODULE());
FunctionSearchContext ctx = { function_name, NULL };
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
return reinterpret_cast<FunctionType>(ctx.function);
}
} // namespace
ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
return FindFunctionInImports<ReturnAddressLocationResolver>(
"ResolveReturnAddressLocation");
}
DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
return FindFunctionInImports<DynamicFunctionEntryHook>(
"OnDynamicFunctionEntry");
}
AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
return FindFunctionInImports<AddDynamicSymbol>(
"AddDynamicSymbol");
}
MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
return FindFunctionInImports<MoveDynamicSymbol>(
"MoveDynamicSymbol");
}
#endif // defined(OS_WIN)
} // namespace debug
} // namespace base

94
base/debug/profiler.h Normal file
View File

@@ -0,0 +1,94 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_PROFILER_H_
#define BASE_DEBUG_PROFILER_H_
#include <stddef.h>
#include <string>
#include "base/base_export.h"
// The Profiler functions allow usage of the underlying sampling based
// profiler. If the application has not been built with the necessary
// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions
// are noops.
namespace base {
namespace debug {
// Start profiling with the supplied name.
// {pid} will be replaced by the process' pid and {count} will be replaced
// by the count of the profile run (starts at 1 with each process).
BASE_EXPORT void StartProfiling(const std::string& name);
// Stop profiling and write out data.
BASE_EXPORT void StopProfiling();
// Force data to be written to file.
BASE_EXPORT void FlushProfiling();
// Returns true if process is being profiled.
BASE_EXPORT bool BeingProfiled();
// Reset profiling after a fork, which disables timers.
BASE_EXPORT void RestartProfilingAfterFork();
// Returns true iff this executable is instrumented with the Syzygy profiler.
BASE_EXPORT bool IsBinaryInstrumented();
// Returns true iff this executable supports profiling.
BASE_EXPORT bool IsProfilingSupported();
// There's a class of profilers that use "return address swizzling" to get a
// hook on function exits. This class of profilers uses some form of entry hook,
// like e.g. binary instrumentation, or a compiler flag, that calls a hook each
// time a function is invoked. The hook then switches the return address on the
// stack for the address of an exit hook function, and pushes the original
// return address to a shadow stack of some type. When in due course the CPU
// executes a return to the exit hook, the exit hook will do whatever work it
// does on function exit, then arrange to return to the original return address.
// This class of profiler does not play well with programs that look at the
// return address, as does e.g. V8. V8 uses the return address to certain
// runtime functions to find the JIT code that called it, and from there finds
// the V8 data structures associated to the JS function involved.
// A return address resolution function is used to fix this. It allows such
// programs to resolve a location on stack where a return address originally
// resided, to the shadow stack location where the profiler stashed it.
typedef uintptr_t (*ReturnAddressLocationResolver)(
uintptr_t return_addr_location);
// This type declaration must match V8's FunctionEntryHook.
typedef void (*DynamicFunctionEntryHook)(uintptr_t function,
uintptr_t return_addr_location);
// The functions below here are to support profiling V8-generated code.
// V8 has provisions for generating a call to an entry hook for newly generated
// JIT code, and it can push symbol information on code generation and advise
// when the garbage collector moves code. The functions declarations below here
// make glue between V8's facilities and a profiler.
// This type declaration must match V8's FunctionEntryHook.
typedef void (*DynamicFunctionEntryHook)(uintptr_t function,
uintptr_t return_addr_location);
typedef void (*AddDynamicSymbol)(const void* address,
size_t length,
const char* name,
size_t name_len);
typedef void (*MoveDynamicSymbol)(const void* address, const void* new_address);
// If this binary is instrumented and the instrumentation supplies a function
// for each of those purposes, find and return the function in question.
// Otherwise returns NULL.
BASE_EXPORT ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc();
BASE_EXPORT DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc();
BASE_EXPORT AddDynamicSymbol GetProfilerAddDynamicSymbolFunc();
BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc();
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_PROFILER_H_

277
base/debug/stack_trace.cc Normal file
View File

@@ -0,0 +1,277 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/stack_trace.h"
#include <string.h>
#include <algorithm>
#include <sstream>
#include "base/logging.h"
#include "base/macros.h"
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include <pthread.h>
#include "base/process/process_handle.h"
#include "base/threading/platform_thread.h"
#endif
#if defined(OS_MACOSX)
#include <pthread.h>
#endif
#if defined(OS_LINUX) && defined(__GLIBC__)
extern "C" void* __libc_stack_end;
#endif
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
namespace base {
namespace debug {
namespace {
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
// GCC and LLVM generate slightly different frames on ARM, see
// https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates
// x86-compatible frame, while GCC needs adjustment.
constexpr size_t kStackFrameAdjustment = sizeof(uintptr_t);
#else
constexpr size_t kStackFrameAdjustment = 0;
#endif
uintptr_t GetNextStackFrame(uintptr_t fp) {
return reinterpret_cast<const uintptr_t*>(fp)[0] - kStackFrameAdjustment;
}
uintptr_t GetStackFramePC(uintptr_t fp) {
return reinterpret_cast<const uintptr_t*>(fp)[1];
}
bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) {
// With the stack growing downwards, older stack frame must be
// at a greater address that the current one.
if (fp <= prev_fp) return false;
// Assume huge stack frames are bogus.
if (fp - prev_fp > 100000) return false;
// Check alignment.
if (fp & (sizeof(uintptr_t) - 1)) return false;
if (stack_end) {
// Both fp[0] and fp[1] must be within the stack.
if (fp > stack_end - 2 * sizeof(uintptr_t)) return false;
// Additional check to filter out false positives.
if (GetStackFramePC(fp) < 32768) return false;
}
return true;
};
// ScanStackForNextFrame() scans the stack for a valid frame to allow unwinding
// past system libraries. Only supported on Linux where system libraries are
// usually in the middle of the trace:
//
// TraceStackFramePointers
// <more frames from Chrome>
// base::WorkSourceDispatch <-- unwinding stops (next frame is invalid),
// g_main_context_dispatch ScanStackForNextFrame() is called
// <more frames from glib>
// g_main_context_iteration
// base::MessagePumpGlib::Run <-- ScanStackForNextFrame() finds valid frame,
// base::RunLoop::Run unwinding resumes
// <more frames from Chrome>
// __libc_start_main
//
// For stack scanning to be efficient it's very important for the thread to
// be started by Chrome. In that case we naturally terminate unwinding once
// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
// not started by Chrome (e.g. Android's main thread), then we end up always
// scanning area at the origin of the stack, wasting time and not finding any
// frames (since Android libraries don't have frame pointers).
//
// ScanStackForNextFrame() returns 0 if it couldn't find a valid frame
// (or if stack scanning is not supported on the current platform).
uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
#if defined(OS_LINUX)
// Enough to resume almost all prematurely terminated traces.
constexpr size_t kMaxStackScanArea = 8192;
if (!stack_end) {
// Too dangerous to scan without knowing where the stack ends.
return 0;
}
fp += sizeof(uintptr_t); // current frame is known to be invalid
uintptr_t last_fp_to_scan = std::min(fp + kMaxStackScanArea, stack_end) -
sizeof(uintptr_t);
for (;fp <= last_fp_to_scan; fp += sizeof(uintptr_t)) {
uintptr_t next_fp = GetNextStackFrame(fp);
if (IsStackFrameValid(next_fp, fp, stack_end)) {
// Check two frames deep. Since stack frame is just a pointer to
// a higher address on the stack, it's relatively easy to find
// something that looks like one. However two linked frames are
// far less likely to be bogus.
uintptr_t next2_fp = GetNextStackFrame(next_fp);
if (IsStackFrameValid(next2_fp, next_fp, stack_end)) {
return fp;
}
}
}
#endif // defined(OS_LINUX)
return 0;
}
// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
// Both frame pointers must come from __builtin_frame_address().
// Returns previous stack frame |fp| was linked to.
void* LinkStackFrames(void* fpp, void* parent_fp) {
uintptr_t fp = reinterpret_cast<uintptr_t>(fpp) - kStackFrameAdjustment;
void* prev_parent_fp = reinterpret_cast<void**>(fp)[0];
reinterpret_cast<void**>(fp)[0] = parent_fp;
return prev_parent_fp;
}
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
} // namespace
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
uintptr_t GetStackEnd() {
#if defined(OS_ANDROID)
// Bionic reads proc/maps on every call to pthread_getattr_np() when called
// from the main thread. So we need to cache end of stack in that case to get
// acceptable performance.
// For all other threads pthread_getattr_np() is fast enough as it just reads
// values from its pthread_t argument.
static uintptr_t main_stack_end = 0;
bool is_main_thread = GetCurrentProcId() == PlatformThread::CurrentId();
if (is_main_thread && main_stack_end) {
return main_stack_end;
}
uintptr_t stack_begin = 0;
size_t stack_size = 0;
pthread_attr_t attributes;
int error = pthread_getattr_np(pthread_self(), &attributes);
if (!error) {
error = pthread_attr_getstack(
&attributes, reinterpret_cast<void**>(&stack_begin), &stack_size);
pthread_attr_destroy(&attributes);
}
DCHECK(!error);
uintptr_t stack_end = stack_begin + stack_size;
if (is_main_thread) {
main_stack_end = stack_end;
}
return stack_end; // 0 in case of error
#elif defined(OS_LINUX) && defined(__GLIBC__)
if (GetCurrentProcId() == PlatformThread::CurrentId()) {
// For the main thread we have a shortcut.
return reinterpret_cast<uintptr_t>(__libc_stack_end);
}
// No easy way to get end of the stack for non-main threads,
// see crbug.com/617730.
#elif defined(OS_MACOSX)
return reinterpret_cast<uintptr_t>(pthread_get_stackaddr_np(pthread_self()));
#endif
// Don't know how to get end of the stack.
return 0;
}
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
StackTrace::StackTrace() : StackTrace(arraysize(trace_)) {}
StackTrace::StackTrace(const void* const* trace, size_t count) {
count = std::min(count, arraysize(trace_));
if (count)
memcpy(trace_, trace, count * sizeof(trace_[0]));
count_ = count;
}
const void *const *StackTrace::Addresses(size_t* count) const {
*count = count_;
if (count_)
return trace_;
return nullptr;
}
std::string StackTrace::ToString() const {
std::stringstream stream;
#if !defined(__UCLIBC__) && !defined(_AIX)
OutputToStream(&stream);
#endif
return stream.str();
}
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial) {
// Usage of __builtin_frame_address() enables frame pointers in this
// function even if they are not enabled globally. So 'fp' will always
// be valid.
uintptr_t fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
kStackFrameAdjustment;
uintptr_t stack_end = GetStackEnd();
size_t depth = 0;
while (depth < max_depth) {
if (skip_initial != 0) {
skip_initial--;
} else {
out_trace[depth++] = reinterpret_cast<const void*>(GetStackFramePC(fp));
}
uintptr_t next_fp = GetNextStackFrame(fp);
if (IsStackFrameValid(next_fp, fp, stack_end)) {
fp = next_fp;
continue;
}
next_fp = ScanStackForNextFrame(fp, stack_end);
if (next_fp) {
fp = next_fp;
continue;
}
// Failed to find next frame.
break;
}
return depth;
}
ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp)
: fp_(fp),
parent_fp_(parent_fp),
original_parent_fp_(LinkStackFrames(fp, parent_fp)) {}
ScopedStackFrameLinker::~ScopedStackFrameLinker() {
void* previous_parent_fp = LinkStackFrames(fp_, original_parent_fp_);
CHECK_EQ(parent_fp_, previous_parent_fp)
<< "Stack frame's parent pointer has changed!";
}
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
} // namespace debug
} // namespace base

197
base/debug/stack_trace.h Normal file
View File

@@ -0,0 +1,197 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_STACK_TRACE_H_
#define BASE_DEBUG_STACK_TRACE_H_
#include <stddef.h>
#include <iosfwd>
#include <string>
#include "base/base_export.h"
#include "base/debug/debugging_flags.h"
#include "base/macros.h"
#include "build/build_config.h"
#if defined(OS_POSIX)
#include <unistd.h>
#endif
#if defined(OS_WIN)
struct _EXCEPTION_POINTERS;
struct _CONTEXT;
#endif
namespace base {
namespace debug {
// Enables stack dump to console output on exception and signals.
// When enabled, the process will quit immediately. This is meant to be used in
// unit_tests only! This is not thread-safe: only call from main thread.
// In sandboxed processes, this has to be called before the sandbox is turned
// on.
// Calling this function on Linux opens /proc/self/maps and caches its
// contents. In non-official builds, this function also opens the object files
// that are loaded in memory and caches their file descriptors (this cannot be
// done in official builds because it has security implications).
BASE_EXPORT bool EnableInProcessStackDumping();
#if defined(OS_POSIX)
BASE_EXPORT void SetStackDumpFirstChanceCallback(bool (*handler)(int,
void*,
void*));
#endif
// Returns end of the stack, or 0 if we couldn't get it.
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
BASE_EXPORT uintptr_t GetStackEnd();
#endif
// A stacktrace can be helpful in debugging. For example, you can include a
// stacktrace member in a object (probably around #ifndef NDEBUG) so that you
// can later see where the given object was created from.
class BASE_EXPORT StackTrace {
public:
// Creates a stacktrace from the current location.
StackTrace();
// Creates a stacktrace from the current location, of up to |count| entries.
// |count| will be limited to at most |kMaxTraces|.
explicit StackTrace(size_t count);
// Creates a stacktrace from an existing array of instruction
// pointers (such as returned by Addresses()). |count| will be
// limited to at most |kMaxTraces|.
StackTrace(const void* const* trace, size_t count);
#if defined(OS_WIN)
// Creates a stacktrace for an exception.
// Note: this function will throw an import not found (StackWalk64) exception
// on system without dbghelp 5.1.
StackTrace(_EXCEPTION_POINTERS* exception_pointers);
StackTrace(const _CONTEXT* context);
#endif
// Copying and assignment are allowed with the default functions.
// Gets an array of instruction pointer values. |*count| will be set to the
// number of elements in the returned array.
const void* const* Addresses(size_t* count) const;
// Prints the stack trace to stderr.
void Print() const;
#if !defined(__UCLIBC__) & !defined(_AIX)
// Resolves backtrace to symbols and write to stream.
void OutputToStream(std::ostream* os) const;
#endif
// Resolves backtrace to symbols and returns as string.
std::string ToString() const;
private:
#if defined(OS_WIN)
void InitTrace(const _CONTEXT* context_record);
#endif
// From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
// the sum of FramesToSkip and FramesToCapture must be less than 63,
// so set it to 62. Even if on POSIX it could be a larger value, it usually
// doesn't give much more information.
static const int kMaxTraces = 62;
void* trace_[kMaxTraces];
// The number of valid frames in |trace_|.
size_t count_;
};
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
// Traces the stack by using frame pointers. This function is faster but less
// reliable than StackTrace. It should work for debug and profiling builds,
// but not for release builds (although there are some exceptions).
//
// Writes at most |max_depth| frames (instruction pointers) into |out_trace|
// after skipping |skip_initial| frames. Note that the function itself is not
// added to the trace so |skip_initial| should be 0 in most cases.
// Returns number of frames written.
BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial);
// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
// Both frame pointers must come from __builtin_frame_address().
// Destructor restores original linkage of |fp| to avoid corrupting caller's
// frame register on return.
//
// This class can be used to repair broken stack frame chain in cases
// when execution flow goes into code built without frame pointers:
//
// void DoWork() {
// Call_SomeLibrary();
// }
// static __thread void* g_saved_fp;
// void Call_SomeLibrary() {
// g_saved_fp = __builtin_frame_address(0);
// some_library_call(...); // indirectly calls SomeLibrary_Callback()
// }
// void SomeLibrary_Callback() {
// ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp);
// ...
// TraceStackFramePointers(...);
// }
//
// This produces the following trace:
//
// #0 SomeLibrary_Callback()
// #1 <address of the code inside SomeLibrary that called #0>
// #2 DoWork()
// ...rest of the trace...
//
// SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback()
// is called, stack frame register contains bogus value that becomes callback'
// parent frame address. Without ScopedStackFrameLinker unwinding would've
// stopped at that bogus frame address yielding just two first frames (#0, #1).
// ScopedStackFrameLinker overwrites callback's parent frame address with
// Call_SomeLibrary's frame, so unwinder produces full trace without even
// noticing that stack frame chain was broken.
class BASE_EXPORT ScopedStackFrameLinker {
public:
ScopedStackFrameLinker(void* fp, void* parent_fp);
~ScopedStackFrameLinker();
private:
void* fp_;
void* parent_fp_;
void* original_parent_fp_;
DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker);
};
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
namespace internal {
#if defined(OS_POSIX) && !defined(OS_ANDROID)
// POSIX doesn't define any async-signal safe function for converting
// an integer to ASCII. We'll have to define our own version.
// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
// conversion was successful or NULL otherwise. It never writes more than "sz"
// bytes. Output will be truncated as needed, and a NUL character is always
// appended.
BASE_EXPORT char *itoa_r(intptr_t i,
char *buf,
size_t sz,
int base,
size_t padding);
#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
} // namespace internal
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_STACK_TRACE_H_

View File

@@ -0,0 +1,134 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/stack_trace.h"
#include <android/log.h>
#include <stddef.h>
#include <unwind.h>
#include <algorithm>
#include <ostream>
#include "base/debug/proc_maps_linux.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#ifdef __LP64__
#define FMT_ADDR "0x%016lx"
#else
#define FMT_ADDR "0x%08x"
#endif
namespace {
struct StackCrawlState {
StackCrawlState(uintptr_t* frames, size_t max_depth)
: frames(frames),
frame_count(0),
max_depth(max_depth),
have_skipped_self(false) {}
uintptr_t* frames;
size_t frame_count;
size_t max_depth;
bool have_skipped_self;
};
_Unwind_Reason_Code TraceStackFrame(_Unwind_Context* context, void* arg) {
StackCrawlState* state = static_cast<StackCrawlState*>(arg);
uintptr_t ip = _Unwind_GetIP(context);
// The first stack frame is this function itself. Skip it.
if (ip != 0 && !state->have_skipped_self) {
state->have_skipped_self = true;
return _URC_NO_REASON;
}
state->frames[state->frame_count++] = ip;
if (state->frame_count >= state->max_depth)
return _URC_END_OF_STACK;
return _URC_NO_REASON;
}
} // namespace
namespace base {
namespace debug {
bool EnableInProcessStackDumping() {
// When running in an application, our code typically expects SIGPIPE
// to be ignored. Therefore, when testing that same code, it should run
// with SIGPIPE ignored as well.
// TODO(phajdan.jr): De-duplicate this SIGPIPE code.
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
return (sigaction(SIGPIPE, &action, NULL) == 0);
}
StackTrace::StackTrace(size_t count) {
count = std::min(arraysize(trace_), count);
StackCrawlState state(reinterpret_cast<uintptr_t*>(trace_), count);
_Unwind_Backtrace(&TraceStackFrame, &state);
count_ = state.frame_count;
}
void StackTrace::Print() const {
std::string backtrace = ToString();
__android_log_write(ANDROID_LOG_ERROR, "chromium", backtrace.c_str());
}
// NOTE: Native libraries in APKs are stripped before installing. Print out the
// relocatable address and library names so host computers can use tools to
// symbolize and demangle (e.g., addr2line, c++filt).
void StackTrace::OutputToStream(std::ostream* os) const {
std::string proc_maps;
std::vector<MappedMemoryRegion> regions;
// Allow IO to read /proc/self/maps. Reading this file doesn't hit the disk
// since it lives in procfs, and this is currently used to print a stack trace
// on fatal log messages in debug builds only. If the restriction is enabled
// then it will recursively trigger fatal failures when this enters on the
// UI thread.
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!ReadProcMaps(&proc_maps)) {
__android_log_write(
ANDROID_LOG_ERROR, "chromium", "Failed to read /proc/self/maps");
} else if (!ParseProcMaps(proc_maps, &regions)) {
__android_log_write(
ANDROID_LOG_ERROR, "chromium", "Failed to parse /proc/self/maps");
}
for (size_t i = 0; i < count_; ++i) {
// Subtract one as return address of function may be in the next
// function when a function is annotated as noreturn.
uintptr_t address = reinterpret_cast<uintptr_t>(trace_[i]) - 1;
std::vector<MappedMemoryRegion>::iterator iter = regions.begin();
while (iter != regions.end()) {
if (address >= iter->start && address < iter->end &&
!iter->path.empty()) {
break;
}
++iter;
}
*os << base::StringPrintf("#%02zd " FMT_ADDR " ", i, address);
if (iter != regions.end()) {
uintptr_t rel_pc = address - iter->start + iter->offset;
const char* path = iter->path.c_str();
*os << base::StringPrintf("%s+" FMT_ADDR, path, rel_pc);
} else {
*os << "<unknown>";
}
*os << "\n";
}
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,212 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/stack_trace.h"
#include <link.h>
#include <stddef.h>
#include <string.h>
#include <threads.h>
#include <unwind.h>
#include <zircon/crashlogger.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/definitions.h>
#include <zircon/syscalls/port.h>
#include <zircon/types.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include "base/logging.h"
namespace base {
namespace debug {
namespace {
const char kProcessNamePrefix[] = "app:";
const size_t kProcessNamePrefixLen = arraysize(kProcessNamePrefix) - 1;
struct BacktraceData {
void** trace_array;
size_t* count;
size_t max;
};
_Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context,
void* user_data) {
BacktraceData* data = reinterpret_cast<BacktraceData*>(user_data);
uintptr_t pc = _Unwind_GetIP(context);
data->trace_array[*data->count] = reinterpret_cast<void*>(pc);
*data->count += 1;
if (*data->count == data->max)
return _URC_END_OF_STACK;
return _URC_NO_REASON;
}
// Stores and queries debugging symbol map info for the current process.
class SymbolMap {
public:
struct Entry {
void* addr;
char name[ZX_MAX_NAME_LEN + kProcessNamePrefixLen];
};
SymbolMap();
~SymbolMap() = default;
// Gets the symbol map entry for |address|. Returns null if no entry could be
// found for the address, or if the symbol map could not be queried.
Entry* GetForAddress(void* address);
private:
static const size_t kMaxMapEntries = 64;
void Populate();
// Sorted in descending order by address, for lookup purposes.
Entry entries_[kMaxMapEntries];
size_t count_ = 0;
bool valid_ = false;
DISALLOW_COPY_AND_ASSIGN(SymbolMap);
};
SymbolMap::SymbolMap() {
Populate();
}
SymbolMap::Entry* SymbolMap::GetForAddress(void* address) {
if (!valid_) {
return nullptr;
}
// Working backwards in the address space, return the first map entry whose
// address comes before |address| (thereby enclosing it.)
for (size_t i = 0; i < count_; ++i) {
if (address >= entries_[i].addr) {
return &entries_[i];
}
}
return nullptr;
}
void SymbolMap::Populate() {
zx_handle_t process = zx_process_self();
// Try to fetch the name of the process' main executable, which was set as the
// name of the |process| kernel object.
// TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so
// if we keep hitting problems with truncation, find a way to plumb argv[0]
// through to here instead, e.g. using CommandLine::GetProgramName().
char app_name[arraysize(SymbolMap::Entry::name)];
strcpy(app_name, kProcessNamePrefix);
zx_status_t status = zx_object_get_property(
process, ZX_PROP_NAME, app_name + kProcessNamePrefixLen,
sizeof(app_name) - kProcessNamePrefixLen);
if (status != ZX_OK) {
DPLOG(WARNING)
<< "Couldn't get name, falling back to 'app' for program name: "
<< status;
strlcat(app_name, "app", sizeof(app_name));
}
// Retrieve the debug info struct.
constexpr size_t map_capacity = sizeof(entries_);
uintptr_t debug_addr;
status = zx_object_get_property(process, ZX_PROP_PROCESS_DEBUG_ADDR,
&debug_addr, sizeof(debug_addr));
if (status != ZX_OK) {
DPLOG(ERROR) << "Couldn't get symbol map for process: " << status;
return;
}
r_debug* debug_info = reinterpret_cast<r_debug*>(debug_addr);
// Get the link map from the debug info struct.
link_map* lmap = reinterpret_cast<link_map*>(debug_info->r_map);
if (!lmap) {
DPLOG(ERROR) << "Null link_map for process.";
return;
}
// Copy the contents of the link map linked list to |entries_|.
while (lmap != nullptr) {
if (count_ == map_capacity) {
break;
}
SymbolMap::Entry* next_entry = &entries_[count_];
count_++;
next_entry->addr = reinterpret_cast<void*>(lmap->l_addr);
char* name_to_use = lmap->l_name[0] ? lmap->l_name : app_name;
strlcpy(next_entry->name, name_to_use, sizeof(next_entry->name));
lmap = lmap->l_next;
}
std::sort(
&entries_[0], &entries_[count_ - 1],
[](const Entry& a, const Entry& b) -> bool { return a.addr >= b.addr; });
valid_ = true;
}
} // namespace
// static
bool EnableInProcessStackDumping() {
// StackTrace works to capture the current stack (e.g. for diagnostics added
// to code), but for local capture and print of backtraces, we just let the
// system crashlogger take over. It handles printing out a nicely formatted
// backtrace with dso information, relative offsets, etc. that we can then
// filter with addr2line in the run script to get file/line info.
return true;
}
StackTrace::StackTrace(size_t count) : count_(0) {
BacktraceData data = {&trace_[0], &count_,
std::min(count, static_cast<size_t>(kMaxTraces))};
_Unwind_Backtrace(&UnwindStore, &data);
}
void StackTrace::Print() const {
OutputToStream(&std::cerr);
}
// Sample stack trace output is designed to be similar to Fuchsia's crashlogger:
// bt#00: pc 0x1527a058aa00 (app:/system/base_unittests,0x18bda00)
// bt#01: pc 0x1527a0254b5c (app:/system/base_unittests,0x1587b5c)
// bt#02: pc 0x15279f446ece (app:/system/base_unittests,0x779ece)
// ...
// bt#21: pc 0x1527a05b51b4 (app:/system/base_unittests,0x18e81b4)
// bt#22: pc 0x54fdbf3593de (libc.so,0x1c3de)
// bt#23: end
void StackTrace::OutputToStream(std::ostream* os) const {
SymbolMap map;
size_t i = 0;
for (; (i < count_) && os->good(); ++i) {
SymbolMap::Entry* entry = map.GetForAddress(trace_[i]);
if (entry) {
size_t offset = reinterpret_cast<uintptr_t>(trace_[i]) -
reinterpret_cast<uintptr_t>(entry->addr);
*os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0)
<< ": pc " << trace_[i] << " (" << entry->name << ",0x" << std::hex
<< offset << std::dec << std::setw(0) << ")\n";
} else {
// Fallback if the DSO map isn't available.
// Logged PC values are absolute memory addresses, and the shared object
// name is not emitted.
*os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0)
<< ": pc " << trace_[i] << "\n";
}
}
(*os) << "bt#" << std::setw(2) << i << ": end\n";
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,891 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/stack_trace.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#if !defined(USE_SYMBOLIZE)
#include <cxxabi.h>
#endif
#if !defined(__UCLIBC__) && !defined(_AIX)
#include <execinfo.h>
#endif
#if defined(OS_MACOSX)
#include <AvailabilityMacros.h>
#endif
#if defined(OS_LINUX)
#include "base/debug/proc_maps_linux.h"
#endif
#include "base/cfi_flags.h"
#include "base/debug/debugger.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/free_deleter.h"
#include "base/memory/singleton.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#if defined(USE_SYMBOLIZE)
#include "base/third_party/symbolize/symbolize.h"
#endif
namespace base {
namespace debug {
namespace {
volatile sig_atomic_t in_signal_handler = 0;
bool (*try_handle_signal)(int, void*, void*) = nullptr;
#if !defined(USE_SYMBOLIZE)
// The prefix used for mangled symbols, per the Itanium C++ ABI:
// http://www.codesourcery.com/cxx-abi/abi.html#mangling
const char kMangledSymbolPrefix[] = "_Z";
// Characters that can be used for symbols, generated by Ruby:
// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
const char kSymbolCharacters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
#endif // !defined(USE_SYMBOLIZE)
#if !defined(USE_SYMBOLIZE)
// Demangles C++ symbols in the given text. Example:
//
// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
// =>
// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
void DemangleSymbols(std::string* text) {
// Note: code in this function is NOT async-signal safe (std::string uses
// malloc internally).
#if !defined(__UCLIBC__) && !defined(_AIX)
std::string::size_type search_from = 0;
while (search_from < text->size()) {
// Look for the start of a mangled symbol, from search_from.
std::string::size_type mangled_start =
text->find(kMangledSymbolPrefix, search_from);
if (mangled_start == std::string::npos) {
break; // Mangled symbol not found.
}
// Look for the end of the mangled symbol.
std::string::size_type mangled_end =
text->find_first_not_of(kSymbolCharacters, mangled_start);
if (mangled_end == std::string::npos) {
mangled_end = text->size();
}
std::string mangled_symbol =
text->substr(mangled_start, mangled_end - mangled_start);
// Try to demangle the mangled symbol candidate.
int status = 0;
std::unique_ptr<char, base::FreeDeleter> demangled_symbol(
abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, 0, &status));
if (status == 0) { // Demangling is successful.
// Remove the mangled symbol.
text->erase(mangled_start, mangled_end - mangled_start);
// Insert the demangled symbol.
text->insert(mangled_start, demangled_symbol.get());
// Next time, we'll start right after the demangled symbol we inserted.
search_from = mangled_start + strlen(demangled_symbol.get());
} else {
// Failed to demangle. Retry after the "_Z" we just found.
search_from = mangled_start + 2;
}
}
#endif // !defined(__UCLIBC__) && !defined(_AIX)
}
#endif // !defined(USE_SYMBOLIZE)
class BacktraceOutputHandler {
public:
virtual void HandleOutput(const char* output) = 0;
protected:
virtual ~BacktraceOutputHandler() = default;
};
#if !defined(__UCLIBC__) && !defined(_AIX)
void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
// This should be more than enough to store a 64-bit number in hex:
// 16 hex digits + 1 for null-terminator.
char buf[17] = { '\0' };
handler->HandleOutput("0x");
internal::itoa_r(reinterpret_cast<intptr_t>(pointer),
buf, sizeof(buf), 16, 12);
handler->HandleOutput(buf);
}
#if defined(USE_SYMBOLIZE)
void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) {
// Max unsigned 64-bit number in decimal has 20 digits (18446744073709551615).
// Hence, 30 digits should be more than enough to represent it in decimal
// (including the null-terminator).
char buf[30] = { '\0' };
handler->HandleOutput("#");
internal::itoa_r(frame_id, buf, sizeof(buf), 10, 1);
handler->HandleOutput(buf);
}
#endif // defined(USE_SYMBOLIZE)
void ProcessBacktrace(void *const *trace,
size_t size,
BacktraceOutputHandler* handler) {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
#if defined(USE_SYMBOLIZE)
for (size_t i = 0; i < size; ++i) {
OutputFrameId(i, handler);
handler->HandleOutput(" ");
OutputPointer(trace[i], handler);
handler->HandleOutput(" ");
char buf[1024] = { '\0' };
// Subtract by one as return address of function may be in the next
// function when a function is annotated as noreturn.
void* address = static_cast<char*>(trace[i]) - 1;
if (google::Symbolize(address, buf, sizeof(buf)))
handler->HandleOutput(buf);
else
handler->HandleOutput("<unknown>");
handler->HandleOutput("\n");
}
#else
bool printed = false;
// Below part is async-signal unsafe (uses malloc), so execute it only
// when we are not executing the signal handler.
if (in_signal_handler == 0) {
std::unique_ptr<char*, FreeDeleter> trace_symbols(
backtrace_symbols(trace, size));
if (trace_symbols.get()) {
for (size_t i = 0; i < size; ++i) {
std::string trace_symbol = trace_symbols.get()[i];
DemangleSymbols(&trace_symbol);
handler->HandleOutput(trace_symbol.c_str());
handler->HandleOutput("\n");
}
printed = true;
}
}
if (!printed) {
for (size_t i = 0; i < size; ++i) {
handler->HandleOutput(" [");
OutputPointer(trace[i], handler);
handler->HandleOutput("]\n");
}
}
#endif // defined(USE_SYMBOLIZE)
}
#endif // !defined(__UCLIBC__) && !defined(_AIX)
void PrintToStderr(const char* output) {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
ignore_result(HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output))));
}
void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
// NOTE: This code MUST be async-signal safe.
// NO malloc or stdio is allowed here.
// Give a registered callback a chance to recover from this signal
//
// V8 uses guard regions to guarantee memory safety in WebAssembly. This means
// some signals might be expected if they originate from Wasm code while
// accessing the guard region. We give V8 the chance to handle and recover
// from these signals first.
if (try_handle_signal != nullptr &&
try_handle_signal(signal, info, void_context)) {
// The first chance handler took care of this. The SA_RESETHAND flag
// replaced this signal handler upon entry, but we want to stay
// installed. Thus, we reinstall ourselves before returning.
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_RESETHAND | SA_SIGINFO;
action.sa_sigaction = &StackDumpSignalHandler;
sigemptyset(&action.sa_mask);
sigaction(signal, &action, nullptr);
return;
}
// Record the fact that we are in the signal handler now, so that the rest
// of StackTrace can behave in an async-signal-safe manner.
in_signal_handler = 1;
if (BeingDebugged())
BreakDebugger();
PrintToStderr("Received signal ");
char buf[1024] = { 0 };
internal::itoa_r(signal, buf, sizeof(buf), 10, 0);
PrintToStderr(buf);
if (signal == SIGBUS) {
if (info->si_code == BUS_ADRALN)
PrintToStderr(" BUS_ADRALN ");
else if (info->si_code == BUS_ADRERR)
PrintToStderr(" BUS_ADRERR ");
else if (info->si_code == BUS_OBJERR)
PrintToStderr(" BUS_OBJERR ");
else
PrintToStderr(" <unknown> ");
} else if (signal == SIGFPE) {
if (info->si_code == FPE_FLTDIV)
PrintToStderr(" FPE_FLTDIV ");
else if (info->si_code == FPE_FLTINV)
PrintToStderr(" FPE_FLTINV ");
else if (info->si_code == FPE_FLTOVF)
PrintToStderr(" FPE_FLTOVF ");
else if (info->si_code == FPE_FLTRES)
PrintToStderr(" FPE_FLTRES ");
else if (info->si_code == FPE_FLTSUB)
PrintToStderr(" FPE_FLTSUB ");
else if (info->si_code == FPE_FLTUND)
PrintToStderr(" FPE_FLTUND ");
else if (info->si_code == FPE_INTDIV)
PrintToStderr(" FPE_INTDIV ");
else if (info->si_code == FPE_INTOVF)
PrintToStderr(" FPE_INTOVF ");
else
PrintToStderr(" <unknown> ");
} else if (signal == SIGILL) {
if (info->si_code == ILL_BADSTK)
PrintToStderr(" ILL_BADSTK ");
else if (info->si_code == ILL_COPROC)
PrintToStderr(" ILL_COPROC ");
else if (info->si_code == ILL_ILLOPN)
PrintToStderr(" ILL_ILLOPN ");
else if (info->si_code == ILL_ILLADR)
PrintToStderr(" ILL_ILLADR ");
else if (info->si_code == ILL_ILLTRP)
PrintToStderr(" ILL_ILLTRP ");
else if (info->si_code == ILL_PRVOPC)
PrintToStderr(" ILL_PRVOPC ");
else if (info->si_code == ILL_PRVREG)
PrintToStderr(" ILL_PRVREG ");
else
PrintToStderr(" <unknown> ");
} else if (signal == SIGSEGV) {
if (info->si_code == SEGV_MAPERR)
PrintToStderr(" SEGV_MAPERR ");
else if (info->si_code == SEGV_ACCERR)
PrintToStderr(" SEGV_ACCERR ");
else
PrintToStderr(" <unknown> ");
}
if (signal == SIGBUS || signal == SIGFPE ||
signal == SIGILL || signal == SIGSEGV) {
internal::itoa_r(reinterpret_cast<intptr_t>(info->si_addr),
buf, sizeof(buf), 16, 12);
PrintToStderr(buf);
}
PrintToStderr("\n");
#if BUILDFLAG(CFI_ENFORCEMENT_TRAP)
if (signal == SIGILL && info->si_code == ILL_ILLOPN) {
PrintToStderr(
"CFI: Most likely a control flow integrity violation; for more "
"information see:\n");
PrintToStderr(
"https://www.chromium.org/developers/testing/control-flow-integrity\n");
}
#endif // BUILDFLAG(CFI_ENFORCEMENT_TRAP)
debug::StackTrace().Print();
#if defined(OS_LINUX)
#if ARCH_CPU_X86_FAMILY
ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context);
const struct {
const char* label;
greg_t value;
} registers[] = {
#if ARCH_CPU_32_BITS
{ " gs: ", context->uc_mcontext.gregs[REG_GS] },
{ " fs: ", context->uc_mcontext.gregs[REG_FS] },
{ " es: ", context->uc_mcontext.gregs[REG_ES] },
{ " ds: ", context->uc_mcontext.gregs[REG_DS] },
{ " edi: ", context->uc_mcontext.gregs[REG_EDI] },
{ " esi: ", context->uc_mcontext.gregs[REG_ESI] },
{ " ebp: ", context->uc_mcontext.gregs[REG_EBP] },
{ " esp: ", context->uc_mcontext.gregs[REG_ESP] },
{ " ebx: ", context->uc_mcontext.gregs[REG_EBX] },
{ " edx: ", context->uc_mcontext.gregs[REG_EDX] },
{ " ecx: ", context->uc_mcontext.gregs[REG_ECX] },
{ " eax: ", context->uc_mcontext.gregs[REG_EAX] },
{ " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] },
{ " err: ", context->uc_mcontext.gregs[REG_ERR] },
{ " ip: ", context->uc_mcontext.gregs[REG_EIP] },
{ " cs: ", context->uc_mcontext.gregs[REG_CS] },
{ " efl: ", context->uc_mcontext.gregs[REG_EFL] },
{ " usp: ", context->uc_mcontext.gregs[REG_UESP] },
{ " ss: ", context->uc_mcontext.gregs[REG_SS] },
#elif ARCH_CPU_64_BITS
{ " r8: ", context->uc_mcontext.gregs[REG_R8] },
{ " r9: ", context->uc_mcontext.gregs[REG_R9] },
{ " r10: ", context->uc_mcontext.gregs[REG_R10] },
{ " r11: ", context->uc_mcontext.gregs[REG_R11] },
{ " r12: ", context->uc_mcontext.gregs[REG_R12] },
{ " r13: ", context->uc_mcontext.gregs[REG_R13] },
{ " r14: ", context->uc_mcontext.gregs[REG_R14] },
{ " r15: ", context->uc_mcontext.gregs[REG_R15] },
{ " di: ", context->uc_mcontext.gregs[REG_RDI] },
{ " si: ", context->uc_mcontext.gregs[REG_RSI] },
{ " bp: ", context->uc_mcontext.gregs[REG_RBP] },
{ " bx: ", context->uc_mcontext.gregs[REG_RBX] },
{ " dx: ", context->uc_mcontext.gregs[REG_RDX] },
{ " ax: ", context->uc_mcontext.gregs[REG_RAX] },
{ " cx: ", context->uc_mcontext.gregs[REG_RCX] },
{ " sp: ", context->uc_mcontext.gregs[REG_RSP] },
{ " ip: ", context->uc_mcontext.gregs[REG_RIP] },
{ " efl: ", context->uc_mcontext.gregs[REG_EFL] },
{ " cgf: ", context->uc_mcontext.gregs[REG_CSGSFS] },
{ " erf: ", context->uc_mcontext.gregs[REG_ERR] },
{ " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] },
{ " msk: ", context->uc_mcontext.gregs[REG_OLDMASK] },
{ " cr2: ", context->uc_mcontext.gregs[REG_CR2] },
#endif // ARCH_CPU_32_BITS
};
#if ARCH_CPU_32_BITS
const int kRegisterPadding = 8;
#elif ARCH_CPU_64_BITS
const int kRegisterPadding = 16;
#endif
for (size_t i = 0; i < arraysize(registers); i++) {
PrintToStderr(registers[i].label);
internal::itoa_r(registers[i].value, buf, sizeof(buf),
16, kRegisterPadding);
PrintToStderr(buf);
if ((i + 1) % 4 == 0)
PrintToStderr("\n");
}
PrintToStderr("\n");
#endif // ARCH_CPU_X86_FAMILY
#endif // defined(OS_LINUX)
PrintToStderr("[end of stack trace]\n");
#if defined(OS_MACOSX) && !defined(OS_IOS)
if (::signal(signal, SIG_DFL) == SIG_ERR)
_exit(1);
#else
// Non-Mac OSes should probably reraise the signal as well, but the Linux
// sandbox tests break on CrOS devices.
// https://code.google.com/p/chromium/issues/detail?id=551681
PrintToStderr("Calling _exit(1). Core file will not be generated.\n");
_exit(1);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
}
class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
public:
PrintBacktraceOutputHandler() = default;
void HandleOutput(const char* output) override {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
PrintToStderr(output);
}
private:
DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler);
};
class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
public:
explicit StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {
}
void HandleOutput(const char* output) override { (*os_) << output; }
private:
std::ostream* os_;
DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler);
};
void WarmUpBacktrace() {
// Warm up stack trace infrastructure. It turns out that on the first
// call glibc initializes some internal data structures using pthread_once,
// and even backtrace() can call malloc(), leading to hangs.
//
// Example stack trace snippet (with tcmalloc):
//
// #8 0x0000000000a173b5 in tc_malloc
// at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161
// #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517
// #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262
// #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
// #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1")
// at dl-open.c:639
// #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89
// #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
// #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48
// #16 __GI___libc_dlopen_mode at dl-libc.c:165
// #17 0x00007ffff61ef8f5 in init
// at ../sysdeps/x86_64/../ia64/backtrace.c:53
// #18 0x00007ffff6aad400 in pthread_once
// at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104
// #19 0x00007ffff61efa14 in __GI___backtrace
// at ../sysdeps/x86_64/../ia64/backtrace.c:104
// #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace
// at base/debug/stack_trace_posix.cc:175
// #21 0x00000000007a4ae5 in
// base::(anonymous namespace)::StackDumpSignalHandler
// at base/process_util_posix.cc:172
// #22 <signal handler called>
StackTrace stack_trace;
}
#if defined(USE_SYMBOLIZE)
// class SandboxSymbolizeHelper.
//
// The purpose of this class is to prepare and install a "file open" callback
// needed by the stack trace symbolization code
// (base/third_party/symbolize/symbolize.h) so that it can function properly
// in a sandboxed process. The caveat is that this class must be instantiated
// before the sandboxing is enabled so that it can get the chance to open all
// the object files that are loaded in the virtual address space of the current
// process.
class SandboxSymbolizeHelper {
public:
// Returns the singleton instance.
static SandboxSymbolizeHelper* GetInstance() {
return Singleton<SandboxSymbolizeHelper,
LeakySingletonTraits<SandboxSymbolizeHelper>>::get();
}
private:
friend struct DefaultSingletonTraits<SandboxSymbolizeHelper>;
SandboxSymbolizeHelper()
: is_initialized_(false) {
Init();
}
~SandboxSymbolizeHelper() {
UnregisterCallback();
CloseObjectFiles();
}
// Returns a O_RDONLY file descriptor for |file_path| if it was opened
// successfully during the initialization. The file is repositioned at
// offset 0.
// IMPORTANT: This function must be async-signal-safe because it can be
// called from a signal handler (symbolizing stack frames for a crash).
int GetFileDescriptor(const char* file_path) {
int fd = -1;
#if !defined(OFFICIAL_BUILD)
if (file_path) {
// The assumption here is that iterating over std::map<std::string, int>
// using a const_iterator does not allocate dynamic memory, hense it is
// async-signal-safe.
std::map<std::string, int>::const_iterator it;
for (it = modules_.begin(); it != modules_.end(); ++it) {
if (strcmp((it->first).c_str(), file_path) == 0) {
// POSIX.1-2004 requires an implementation to guarantee that dup()
// is async-signal-safe.
fd = HANDLE_EINTR(dup(it->second));
break;
}
}
// POSIX.1-2004 requires an implementation to guarantee that lseek()
// is async-signal-safe.
if (fd >= 0 && lseek(fd, 0, SEEK_SET) < 0) {
// Failed to seek.
fd = -1;
}
}
#endif // !defined(OFFICIAL_BUILD)
return fd;
}
// Searches for the object file (from /proc/self/maps) that contains
// the specified pc. If found, sets |start_address| to the start address
// of where this object file is mapped in memory, sets the module base
// address into |base_address|, copies the object file name into
// |out_file_name|, and attempts to open the object file. If the object
// file is opened successfully, returns the file descriptor. Otherwise,
// returns -1. |out_file_name_size| is the size of the file name buffer
// (including the null terminator).
// IMPORTANT: This function must be async-signal-safe because it can be
// called from a signal handler (symbolizing stack frames for a crash).
static int OpenObjectFileContainingPc(uint64_t pc, uint64_t& start_address,
uint64_t& base_address, char* file_path,
int file_path_size) {
// This method can only be called after the singleton is instantiated.
// This is ensured by the following facts:
// * This is the only static method in this class, it is private, and
// the class has no friends (except for the DefaultSingletonTraits).
// The compiler guarantees that it can only be called after the
// singleton is instantiated.
// * This method is used as a callback for the stack tracing code and
// the callback registration is done in the constructor, so logically
// it cannot be called before the singleton is created.
SandboxSymbolizeHelper* instance = GetInstance();
// The assumption here is that iterating over
// std::vector<MappedMemoryRegion> using a const_iterator does not allocate
// dynamic memory, hence it is async-signal-safe.
for (const MappedMemoryRegion& region : instance->regions_) {
if (region.start <= pc && pc < region.end) {
start_address = region.start;
base_address = region.base;
if (file_path && file_path_size > 0) {
strncpy(file_path, region.path.c_str(), file_path_size);
// Ensure null termination.
file_path[file_path_size - 1] = '\0';
}
return instance->GetFileDescriptor(region.path.c_str());
}
}
return -1;
}
// Set the base address for each memory region by reading ELF headers in
// process memory.
void SetBaseAddressesForMemoryRegions() {
base::ScopedFD mem_fd(
HANDLE_EINTR(open("/proc/self/mem", O_RDONLY | O_CLOEXEC)));
if (!mem_fd.is_valid())
return;
auto safe_memcpy = [&mem_fd](void* dst, uintptr_t src, size_t size) {
return HANDLE_EINTR(pread(mem_fd.get(), dst, size, src)) == ssize_t(size);
};
uintptr_t cur_base = 0;
for (auto& r : regions_) {
ElfW(Ehdr) ehdr;
static_assert(SELFMAG <= sizeof(ElfW(Ehdr)), "SELFMAG too large");
if ((r.permissions & MappedMemoryRegion::READ) &&
safe_memcpy(&ehdr, r.start, sizeof(ElfW(Ehdr))) &&
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
switch (ehdr.e_type) {
case ET_EXEC:
cur_base = 0;
break;
case ET_DYN:
// Find the segment containing file offset 0. This will correspond
// to the ELF header that we just read. Normally this will have
// virtual address 0, but this is not guaranteed. We must subtract
// the virtual address from the address where the ELF header was
// mapped to get the base address.
//
// If we fail to find a segment for file offset 0, use the address
// of the ELF header as the base address.
cur_base = r.start;
for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
ElfW(Phdr) phdr;
if (safe_memcpy(&phdr, r.start + ehdr.e_phoff + i * sizeof(phdr),
sizeof(phdr)) &&
phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
cur_base = r.start - phdr.p_vaddr;
break;
}
}
break;
default:
// ET_REL or ET_CORE. These aren't directly executable, so they
// don't affect the base address.
break;
}
}
r.base = cur_base;
}
}
// Parses /proc/self/maps in order to compile a list of all object file names
// for the modules that are loaded in the current process.
// Returns true on success.
bool CacheMemoryRegions() {
// Reads /proc/self/maps.
std::string contents;
if (!ReadProcMaps(&contents)) {
LOG(ERROR) << "Failed to read /proc/self/maps";
return false;
}
// Parses /proc/self/maps.
if (!ParseProcMaps(contents, &regions_)) {
LOG(ERROR) << "Failed to parse the contents of /proc/self/maps";
return false;
}
SetBaseAddressesForMemoryRegions();
is_initialized_ = true;
return true;
}
// Opens all object files and caches their file descriptors.
void OpenSymbolFiles() {
// Pre-opening and caching the file descriptors of all loaded modules is
// not safe for production builds. Hence it is only done in non-official
// builds. For more details, take a look at: http://crbug.com/341966.
#if !defined(OFFICIAL_BUILD)
// Open the object files for all read-only executable regions and cache
// their file descriptors.
std::vector<MappedMemoryRegion>::const_iterator it;
for (it = regions_.begin(); it != regions_.end(); ++it) {
const MappedMemoryRegion& region = *it;
// Only interesed in read-only executable regions.
if ((region.permissions & MappedMemoryRegion::READ) ==
MappedMemoryRegion::READ &&
(region.permissions & MappedMemoryRegion::WRITE) == 0 &&
(region.permissions & MappedMemoryRegion::EXECUTE) ==
MappedMemoryRegion::EXECUTE) {
if (region.path.empty()) {
// Skip regions with empty file names.
continue;
}
if (region.path[0] == '[') {
// Skip pseudo-paths, like [stack], [vdso], [heap], etc ...
continue;
}
// Avoid duplicates.
if (modules_.find(region.path) == modules_.end()) {
int fd = open(region.path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
modules_.insert(std::make_pair(region.path, fd));
} else {
LOG(WARNING) << "Failed to open file: " << region.path
<< "\n Error: " << strerror(errno);
}
}
}
}
#endif // !defined(OFFICIAL_BUILD)
}
// Initializes and installs the symbolization callback.
void Init() {
if (CacheMemoryRegions()) {
OpenSymbolFiles();
google::InstallSymbolizeOpenObjectFileCallback(
&OpenObjectFileContainingPc);
}
}
// Unregister symbolization callback.
void UnregisterCallback() {
if (is_initialized_) {
google::InstallSymbolizeOpenObjectFileCallback(nullptr);
is_initialized_ = false;
}
}
// Closes all file descriptors owned by this instance.
void CloseObjectFiles() {
#if !defined(OFFICIAL_BUILD)
std::map<std::string, int>::iterator it;
for (it = modules_.begin(); it != modules_.end(); ++it) {
int ret = IGNORE_EINTR(close(it->second));
DCHECK(!ret);
it->second = -1;
}
modules_.clear();
#endif // !defined(OFFICIAL_BUILD)
}
// Set to true upon successful initialization.
bool is_initialized_;
#if !defined(OFFICIAL_BUILD)
// Mapping from file name to file descriptor. Includes file descriptors
// for all successfully opened object files and the file descriptor for
// /proc/self/maps. This code is not safe for production builds.
std::map<std::string, int> modules_;
#endif // !defined(OFFICIAL_BUILD)
// Cache for the process memory regions. Produced by parsing the contents
// of /proc/self/maps cache.
std::vector<MappedMemoryRegion> regions_;
DISALLOW_COPY_AND_ASSIGN(SandboxSymbolizeHelper);
};
#endif // USE_SYMBOLIZE
} // namespace
bool EnableInProcessStackDumping() {
#if defined(USE_SYMBOLIZE)
SandboxSymbolizeHelper::GetInstance();
#endif // USE_SYMBOLIZE
// When running in an application, our code typically expects SIGPIPE
// to be ignored. Therefore, when testing that same code, it should run
// with SIGPIPE ignored as well.
struct sigaction sigpipe_action;
memset(&sigpipe_action, 0, sizeof(sigpipe_action));
sigpipe_action.sa_handler = SIG_IGN;
sigemptyset(&sigpipe_action.sa_mask);
bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0);
// Avoid hangs during backtrace initialization, see above.
WarmUpBacktrace();
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_RESETHAND | SA_SIGINFO;
action.sa_sigaction = &StackDumpSignalHandler;
sigemptyset(&action.sa_mask);
success &= (sigaction(SIGILL, &action, nullptr) == 0);
success &= (sigaction(SIGABRT, &action, nullptr) == 0);
success &= (sigaction(SIGFPE, &action, nullptr) == 0);
success &= (sigaction(SIGBUS, &action, nullptr) == 0);
success &= (sigaction(SIGSEGV, &action, nullptr) == 0);
// On Linux, SIGSYS is reserved by the kernel for seccomp-bpf sandboxing.
#if !defined(OS_LINUX)
success &= (sigaction(SIGSYS, &action, nullptr) == 0);
#endif // !defined(OS_LINUX)
return success;
}
void SetStackDumpFirstChanceCallback(bool (*handler)(int, void*, void*)) {
DCHECK(try_handle_signal == nullptr || handler == nullptr);
try_handle_signal = handler;
}
StackTrace::StackTrace(size_t count) {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
#if !defined(__UCLIBC__) && !defined(_AIX)
count = std::min(arraysize(trace_), count);
// Though the backtrace API man page does not list any possible negative
// return values, we take no chance.
count_ = base::saturated_cast<size_t>(backtrace(trace_, count));
#else
count_ = 0;
#endif
}
void StackTrace::Print() const {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
#if !defined(__UCLIBC__) && !defined(_AIX)
PrintBacktraceOutputHandler handler;
ProcessBacktrace(trace_, count_, &handler);
#endif
}
#if !defined(__UCLIBC__) && !defined(_AIX)
void StackTrace::OutputToStream(std::ostream* os) const {
StreamBacktraceOutputHandler handler(os);
ProcessBacktrace(trace_, count_, &handler);
}
#endif
namespace internal {
// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding) {
// Make sure we can write at least one NUL byte.
size_t n = 1;
if (n > sz)
return nullptr;
if (base < 2 || base > 16) {
buf[0] = '\000';
return nullptr;
}
char* start = buf;
uintptr_t j = i;
// Handle negative numbers (only for base 10).
if (i < 0 && base == 10) {
// This does "j = -i" while avoiding integer overflow.
j = static_cast<uintptr_t>(-(i + 1)) + 1;
// Make sure we can write the '-' character.
if (++n > sz) {
buf[0] = '\000';
return nullptr;
}
*start++ = '-';
}
// Loop until we have converted the entire number. Output at least one
// character (i.e. '0').
char* ptr = start;
do {
// Make sure there is still enough space left in our output buffer.
if (++n > sz) {
buf[0] = '\000';
return nullptr;
}
// Output the next digit.
*ptr++ = "0123456789abcdef"[j % base];
j /= base;
if (padding > 0)
padding--;
} while (j > 0 || padding > 0);
// Terminate the output with a NUL character.
*ptr = '\000';
// Conversion to ASCII actually resulted in the digits being in reverse
// order. We can't easily generate them in forward order, as we can't tell
// the number of characters needed until we are done converting.
// So, now, we reverse the string (except for the possible "-" sign).
while (--ptr > start) {
char ch = *ptr;
*ptr = *start;
*start++ = ch;
}
return buf;
}
} // namespace internal
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,365 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/stack_trace.h"
#include <windows.h>
#include <dbghelp.h>
#include <stddef.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
namespace base {
namespace debug {
namespace {
// Previous unhandled filter. Will be called if not NULL when we intercept an
// exception. Only used in unit tests.
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
bool g_initialized_symbols = false;
DWORD g_init_error = ERROR_SUCCESS;
// Prints the exception call stack.
// This is the unit tests exception filter.
long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
DWORD exc_code = info->ExceptionRecord->ExceptionCode;
std::cerr << "Received fatal exception ";
switch (exc_code) {
case EXCEPTION_ACCESS_VIOLATION:
std::cerr << "EXCEPTION_ACCESS_VIOLATION";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
std::cerr << "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
break;
case EXCEPTION_BREAKPOINT:
std::cerr << "EXCEPTION_BREAKPOINT";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
std::cerr << "EXCEPTION_DATATYPE_MISALIGNMENT";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
std::cerr << "EXCEPTION_FLT_DENORMAL_OPERAND";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
std::cerr << "EXCEPTION_FLT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
std::cerr << "EXCEPTION_FLT_INEXACT_RESULT";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
std::cerr << "EXCEPTION_FLT_INVALID_OPERATION";
break;
case EXCEPTION_FLT_OVERFLOW:
std::cerr << "EXCEPTION_FLT_OVERFLOW";
break;
case EXCEPTION_FLT_STACK_CHECK:
std::cerr << "EXCEPTION_FLT_STACK_CHECK";
break;
case EXCEPTION_FLT_UNDERFLOW:
std::cerr << "EXCEPTION_FLT_UNDERFLOW";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
std::cerr << "EXCEPTION_ILLEGAL_INSTRUCTION";
break;
case EXCEPTION_IN_PAGE_ERROR:
std::cerr << "EXCEPTION_IN_PAGE_ERROR";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
std::cerr << "EXCEPTION_INT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_INT_OVERFLOW:
std::cerr << "EXCEPTION_INT_OVERFLOW";
break;
case EXCEPTION_INVALID_DISPOSITION:
std::cerr << "EXCEPTION_INVALID_DISPOSITION";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
std::cerr << "EXCEPTION_NONCONTINUABLE_EXCEPTION";
break;
case EXCEPTION_PRIV_INSTRUCTION:
std::cerr << "EXCEPTION_PRIV_INSTRUCTION";
break;
case EXCEPTION_SINGLE_STEP:
std::cerr << "EXCEPTION_SINGLE_STEP";
break;
case EXCEPTION_STACK_OVERFLOW:
std::cerr << "EXCEPTION_STACK_OVERFLOW";
break;
default:
std::cerr << "0x" << std::hex << exc_code;
break;
}
std::cerr << "\n";
debug::StackTrace(info).Print();
if (g_previous_filter)
return g_previous_filter(info);
return EXCEPTION_CONTINUE_SEARCH;
}
FilePath GetExePath() {
wchar_t system_buffer[MAX_PATH];
GetModuleFileName(NULL, system_buffer, MAX_PATH);
system_buffer[MAX_PATH - 1] = L'\0';
return FilePath(system_buffer);
}
bool InitializeSymbols() {
if (g_initialized_symbols)
return g_init_error == ERROR_SUCCESS;
g_initialized_symbols = true;
// Defer symbol load until they're needed, use undecorated names, and get line
// numbers.
SymSetOptions(SYMOPT_DEFERRED_LOADS |
SYMOPT_UNDNAME |
SYMOPT_LOAD_LINES);
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) {
g_init_error = GetLastError();
// TODO(awong): Handle error: SymInitialize can fail with
// ERROR_INVALID_PARAMETER.
// When it fails, we should not call debugbreak since it kills the current
// process (prevents future tests from running or kills the browser
// process).
DLOG(ERROR) << "SymInitialize failed: " << g_init_error;
return false;
}
// When transferring the binaries e.g. between bots, path put
// into the executable will get off. To still retrieve symbols correctly,
// add the directory of the executable to symbol search path.
// All following errors are non-fatal.
const size_t kSymbolsArraySize = 1024;
std::unique_ptr<wchar_t[]> symbols_path(new wchar_t[kSymbolsArraySize]);
// Note: The below function takes buffer size as number of characters,
// not number of bytes!
if (!SymGetSearchPathW(GetCurrentProcess(),
symbols_path.get(),
kSymbolsArraySize)) {
g_init_error = GetLastError();
DLOG(WARNING) << "SymGetSearchPath failed: " << g_init_error;
return false;
}
std::wstring new_path(std::wstring(symbols_path.get()) +
L";" + GetExePath().DirName().value());
if (!SymSetSearchPathW(GetCurrentProcess(), new_path.c_str())) {
g_init_error = GetLastError();
DLOG(WARNING) << "SymSetSearchPath failed." << g_init_error;
return false;
}
g_init_error = ERROR_SUCCESS;
return true;
}
// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
// of functions. The Sym* family of functions may only be invoked by one
// thread at a time. SymbolContext code may access a symbol server over the
// network while holding the lock for this singleton. In the case of high
// latency, this code will adversely affect performance.
//
// There is also a known issue where this backtrace code can interact
// badly with breakpad if breakpad is invoked in a separate thread while
// we are using the Sym* functions. This is because breakpad does now
// share a lock with this function. See this related bug:
//
// https://crbug.com/google-breakpad/311
//
// This is a very unlikely edge case, and the current solution is to
// just ignore it.
class SymbolContext {
public:
static SymbolContext* GetInstance() {
// We use a leaky singleton because code may call this during process
// termination.
return
Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
}
// For the given trace, attempts to resolve the symbols, and output a trace
// to the ostream os. The format for each line of the backtrace is:
//
// <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
//
// This function should only be called if Init() has been called. We do not
// LOG(FATAL) here because this code is called might be triggered by a
// LOG(FATAL) itself. Also, it should not be calling complex code that is
// extensible like PathService since that can in turn fire CHECKs.
void OutputTraceToStream(const void* const* trace,
size_t count,
std::ostream* os) {
base::AutoLock lock(lock_);
for (size_t i = 0; (i < count) && os->good(); ++i) {
const int kMaxNameLength = 256;
DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
// Code adapted from MSDN example:
// http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
ULONG64 buffer[
(sizeof(SYMBOL_INFO) +
kMaxNameLength * sizeof(wchar_t) +
sizeof(ULONG64) - 1) /
sizeof(ULONG64)];
memset(buffer, 0, sizeof(buffer));
// Initialize symbol information retrieval structures.
DWORD64 sym_displacement = 0;
PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = kMaxNameLength - 1;
BOOL has_symbol = SymFromAddr(GetCurrentProcess(), frame,
&sym_displacement, symbol);
// Attempt to retrieve line number information.
DWORD line_displacement = 0;
IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
&line_displacement, &line);
// Output the backtrace line.
(*os) << "\t";
if (has_symbol) {
(*os) << symbol->Name << " [0x" << trace[i] << "+"
<< sym_displacement << "]";
} else {
// If there is no symbol information, add a spacer.
(*os) << "(No symbol) [0x" << trace[i] << "]";
}
if (has_line) {
(*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
}
(*os) << "\n";
}
}
private:
friend struct DefaultSingletonTraits<SymbolContext>;
SymbolContext() {
InitializeSymbols();
}
base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(SymbolContext);
};
} // namespace
bool EnableInProcessStackDumping() {
// Add stack dumping support on exception on windows. Similar to OS_POSIX
// signal() handling in process_util_posix.cc.
g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
// Need to initialize symbols early in the process or else this fails on
// swarming (since symbols are in different directory than in the exes) and
// also release x64.
return InitializeSymbols();
}
// Disable optimizations for the StackTrace::StackTrace function. It is
// important to disable at least frame pointer optimization ("y"), since
// that breaks CaptureStackBackTrace() and prevents StackTrace from working
// in Release builds (it may still be janky if other frames are using FPO,
// but at least it will make it further).
#if defined(COMPILER_MSVC)
#pragma optimize("", off)
#endif
StackTrace::StackTrace(size_t count) {
count = std::min(arraysize(trace_), count);
// When walking our own stack, use CaptureStackBackTrace().
count_ = CaptureStackBackTrace(0, count, trace_, NULL);
}
#if defined(COMPILER_MSVC)
#pragma optimize("", on)
#endif
StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
InitTrace(exception_pointers->ContextRecord);
}
StackTrace::StackTrace(const CONTEXT* context) {
InitTrace(context);
}
void StackTrace::InitTrace(const CONTEXT* context_record) {
// StackWalk64 modifies the register context in place, so we have to copy it
// so that downstream exception handlers get the right context. The incoming
// context may have had more register state (YMM, etc) than we need to unwind
// the stack. Typically StackWalk64 only needs integer and control registers.
CONTEXT context_copy;
memcpy(&context_copy, context_record, sizeof(context_copy));
context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
// When walking an exception stack, we need to use StackWalk64().
count_ = 0;
// Initialize stack walking.
STACKFRAME64 stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
#if defined(_WIN64)
int machine_type = IMAGE_FILE_MACHINE_AMD64;
stack_frame.AddrPC.Offset = context_record->Rip;
stack_frame.AddrFrame.Offset = context_record->Rbp;
stack_frame.AddrStack.Offset = context_record->Rsp;
#else
int machine_type = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = context_record->Eip;
stack_frame.AddrFrame.Offset = context_record->Ebp;
stack_frame.AddrStack.Offset = context_record->Esp;
#endif
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Mode = AddrModeFlat;
while (StackWalk64(machine_type,
GetCurrentProcess(),
GetCurrentThread(),
&stack_frame,
&context_copy,
NULL,
&SymFunctionTableAccess64,
&SymGetModuleBase64,
NULL) &&
count_ < arraysize(trace_)) {
trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
}
for (size_t i = count_; i < arraysize(trace_); ++i)
trace_[i] = NULL;
}
void StackTrace::Print() const {
OutputToStream(&std::cerr);
}
void StackTrace::OutputToStream(std::ostream* os) const {
SymbolContext* context = SymbolContext::GetInstance();
if (g_init_error != ERROR_SUCCESS) {
(*os) << "Error initializing symbols (" << g_init_error
<< "). Dumping unresolved backtrace:\n";
for (size_t i = 0; (i < count_) && os->good(); ++i) {
(*os) << "\t" << trace_[i] << "\n";
}
} else {
(*os) << "Backtrace:\n";
context->OutputTraceToStream(trace_, count_, os);
}
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,65 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/task_annotator.h"
#include <array>
#include "base/debug/activity_tracker.h"
#include "base/debug/alias.h"
#include "base/pending_task.h"
#include "base/trace_event/trace_event.h"
namespace base {
namespace debug {
TaskAnnotator::TaskAnnotator() = default;
TaskAnnotator::~TaskAnnotator() = default;
void TaskAnnotator::DidQueueTask(const char* queue_function,
const PendingTask& pending_task) {
TRACE_EVENT_WITH_FLOW0(
TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), queue_function,
TRACE_ID_MANGLE(GetTaskTraceID(pending_task)), TRACE_EVENT_FLAG_FLOW_OUT);
}
void TaskAnnotator::RunTask(const char* queue_function,
PendingTask* pending_task) {
ScopedTaskRunActivity task_activity(*pending_task);
TRACE_EVENT_WITH_FLOW0(
TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), queue_function,
TRACE_ID_MANGLE(GetTaskTraceID(*pending_task)), TRACE_EVENT_FLAG_FLOW_IN);
// Before running the task, store the task backtrace with the chain of
// PostTasks that resulted in this call and deliberately alias it to ensure
// it is on the stack if the task crashes. Be careful not to assume that the
// variable itself will have the expected value when displayed by the
// optimizer in an optimized build. Look at a memory dump of the stack.
static constexpr int kStackTaskTraceSnapshotSize =
std::tuple_size<decltype(pending_task->task_backtrace)>::value + 3;
std::array<const void*, kStackTaskTraceSnapshotSize> task_backtrace;
// Store a marker to locate |task_backtrace| content easily on a memory
// dump.
task_backtrace.front() = reinterpret_cast<void*>(0xefefefefefefefef);
task_backtrace.back() = reinterpret_cast<void*>(0xfefefefefefefefe);
task_backtrace[1] = pending_task->posted_from.program_counter();
std::copy(pending_task->task_backtrace.begin(),
pending_task->task_backtrace.end(), task_backtrace.begin() + 2);
debug::Alias(&task_backtrace);
std::move(pending_task->task).Run();
}
uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
return (static_cast<uint64_t>(task.sequence_num) << 32) |
((static_cast<uint64_t>(reinterpret_cast<intptr_t>(this)) << 32) >>
32);
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,45 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_TASK_ANNOTATOR_H_
#define BASE_DEBUG_TASK_ANNOTATOR_H_
#include <stdint.h>
#include "base/base_export.h"
#include "base/macros.h"
namespace base {
struct PendingTask;
namespace debug {
// Implements common debug annotations for posted tasks. This includes data
// such as task origins, queueing durations and memory usage.
class BASE_EXPORT TaskAnnotator {
public:
TaskAnnotator();
~TaskAnnotator();
// Called to indicate that a task has been queued to run in the future.
// |queue_function| is used as the trace flow event name.
void DidQueueTask(const char* queue_function,
const PendingTask& pending_task);
// Run a previously queued task. |queue_function| should match what was
// passed into |DidQueueTask| for this task.
void RunTask(const char* queue_function, PendingTask* pending_task);
private:
// Creates a process-wide unique ID to represent this task in trace events.
// This will be mangled with a Process ID hash to reduce the likelyhood of
// colliding with TaskAnnotator pointers on other processes.
uint64_t GetTaskTraceID(const PendingTask& task) const;
DISALLOW_COPY_AND_ASSIGN(TaskAnnotator);
};
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_TASK_ANNOTATOR_H_

View File

@@ -0,0 +1,340 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/thread_heap_usage_tracker.h"
#include <stdint.h>
#include <algorithm>
#include <limits>
#include <new>
#include <type_traits>
#include "base/allocator/allocator_shim.h"
#include "base/allocator/features.h"
#include "base/logging.h"
#include "base/threading/thread_local_storage.h"
#include "build/build_config.h"
#if defined(OS_MACOSX) || defined(OS_IOS)
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
namespace base {
namespace debug {
namespace {
using base::allocator::AllocatorDispatch;
ThreadLocalStorage::StaticSlot g_thread_allocator_usage = TLS_INITIALIZER;
const uintptr_t kSentinelMask = std::numeric_limits<uintptr_t>::max() - 1;
ThreadHeapUsage* const kInitializationSentinel =
reinterpret_cast<ThreadHeapUsage*>(kSentinelMask);
ThreadHeapUsage* const kTeardownSentinel =
reinterpret_cast<ThreadHeapUsage*>(kSentinelMask | 1);
bool g_heap_tracking_enabled = false;
// Forward declared as it needs to delegate memory allocation to the next
// lower shim.
ThreadHeapUsage* GetOrCreateThreadUsage();
size_t GetAllocSizeEstimate(const AllocatorDispatch* next,
void* ptr,
void* context) {
if (ptr == nullptr)
return 0U;
return next->get_size_estimate_function(next, ptr, context);
}
void RecordAlloc(const AllocatorDispatch* next,
void* ptr,
size_t size,
void* context) {
ThreadHeapUsage* usage = GetOrCreateThreadUsage();
if (usage == nullptr)
return;
usage->alloc_ops++;
size_t estimate = GetAllocSizeEstimate(next, ptr, context);
if (size && estimate) {
// Only keep track of the net number of bytes allocated in the scope if the
// size estimate function returns sane values, e.g. non-zero.
usage->alloc_bytes += estimate;
usage->alloc_overhead_bytes += estimate - size;
// Record the max outstanding number of bytes, but only if the difference
// is net positive (e.g. more bytes allocated than freed in the scope).
if (usage->alloc_bytes > usage->free_bytes) {
uint64_t allocated_bytes = usage->alloc_bytes - usage->free_bytes;
if (allocated_bytes > usage->max_allocated_bytes)
usage->max_allocated_bytes = allocated_bytes;
}
} else {
usage->alloc_bytes += size;
}
}
void RecordFree(const AllocatorDispatch* next, void* ptr, void* context) {
ThreadHeapUsage* usage = GetOrCreateThreadUsage();
if (usage == nullptr)
return;
size_t estimate = GetAllocSizeEstimate(next, ptr, context);
usage->free_ops++;
usage->free_bytes += estimate;
}
void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) {
void* ret = self->next->alloc_function(self->next, size, context);
if (ret != nullptr)
RecordAlloc(self->next, ret, size, context);
return ret;
}
void* AllocZeroInitializedFn(const AllocatorDispatch* self,
size_t n,
size_t size,
void* context) {
void* ret =
self->next->alloc_zero_initialized_function(self->next, n, size, context);
if (ret != nullptr)
RecordAlloc(self->next, ret, size, context);
return ret;
}
void* AllocAlignedFn(const AllocatorDispatch* self,
size_t alignment,
size_t size,
void* context) {
void* ret =
self->next->alloc_aligned_function(self->next, alignment, size, context);
if (ret != nullptr)
RecordAlloc(self->next, ret, size, context);
return ret;
}
void* ReallocFn(const AllocatorDispatch* self,
void* address,
size_t size,
void* context) {
if (address != nullptr)
RecordFree(self->next, address, context);
void* ret = self->next->realloc_function(self->next, address, size, context);
if (ret != nullptr && size != 0)
RecordAlloc(self->next, ret, size, context);
return ret;
}
void FreeFn(const AllocatorDispatch* self, void* address, void* context) {
if (address != nullptr)
RecordFree(self->next, address, context);
self->next->free_function(self->next, address, context);
}
size_t GetSizeEstimateFn(const AllocatorDispatch* self,
void* address,
void* context) {
return self->next->get_size_estimate_function(self->next, address, context);
}
unsigned BatchMallocFn(const AllocatorDispatch* self,
size_t size,
void** results,
unsigned num_requested,
void* context) {
unsigned count = self->next->batch_malloc_function(self->next, size, results,
num_requested, context);
for (unsigned i = 0; i < count; ++i) {
RecordAlloc(self->next, results[i], size, context);
}
return count;
}
void BatchFreeFn(const AllocatorDispatch* self,
void** to_be_freed,
unsigned num_to_be_freed,
void* context) {
for (unsigned i = 0; i < num_to_be_freed; ++i) {
if (to_be_freed[i] != nullptr) {
RecordFree(self->next, to_be_freed[i], context);
}
}
self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed,
context);
}
void FreeDefiniteSizeFn(const AllocatorDispatch* self,
void* ptr,
size_t size,
void* context) {
if (ptr != nullptr)
RecordFree(self->next, ptr, context);
self->next->free_definite_size_function(self->next, ptr, size, context);
}
// The allocator dispatch used to intercept heap operations.
AllocatorDispatch allocator_dispatch = {&AllocFn,
&AllocZeroInitializedFn,
&AllocAlignedFn,
&ReallocFn,
&FreeFn,
&GetSizeEstimateFn,
&BatchMallocFn,
&BatchFreeFn,
&FreeDefiniteSizeFn,
nullptr};
ThreadHeapUsage* GetOrCreateThreadUsage() {
auto tls_ptr = reinterpret_cast<uintptr_t>(g_thread_allocator_usage.Get());
if ((tls_ptr & kSentinelMask) == kSentinelMask)
return nullptr; // Re-entrancy case.
auto* allocator_usage = reinterpret_cast<ThreadHeapUsage*>(tls_ptr);
if (allocator_usage == nullptr) {
// Prevent reentrancy due to the allocation below.
g_thread_allocator_usage.Set(kInitializationSentinel);
allocator_usage = new ThreadHeapUsage();
static_assert(std::is_pod<ThreadHeapUsage>::value,
"AllocatorDispatch must be POD");
memset(allocator_usage, 0, sizeof(*allocator_usage));
g_thread_allocator_usage.Set(allocator_usage);
}
return allocator_usage;
}
} // namespace
ThreadHeapUsageTracker::ThreadHeapUsageTracker() : thread_usage_(nullptr) {
static_assert(std::is_pod<ThreadHeapUsage>::value, "Must be POD.");
}
ThreadHeapUsageTracker::~ThreadHeapUsageTracker() {
DCHECK(thread_checker_.CalledOnValidThread());
if (thread_usage_ != nullptr) {
// If this tracker wasn't stopped, make it inclusive so that the
// usage isn't lost.
Stop(false);
}
}
void ThreadHeapUsageTracker::Start() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(g_thread_allocator_usage.initialized());
thread_usage_ = GetOrCreateThreadUsage();
usage_ = *thread_usage_;
// Reset the stats for our current scope.
// The per-thread usage instance now tracks this scope's usage, while this
// instance persists the outer scope's usage stats. On destruction, this
// instance will restore the outer scope's usage stats with this scope's
// usage added.
memset(thread_usage_, 0, sizeof(*thread_usage_));
}
void ThreadHeapUsageTracker::Stop(bool usage_is_exclusive) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_NE(nullptr, thread_usage_);
ThreadHeapUsage current = *thread_usage_;
if (usage_is_exclusive) {
// Restore the outer scope.
*thread_usage_ = usage_;
} else {
// Update the outer scope with the accrued inner usage.
if (thread_usage_->max_allocated_bytes) {
uint64_t outer_net_alloc_bytes = usage_.alloc_bytes - usage_.free_bytes;
thread_usage_->max_allocated_bytes =
std::max(usage_.max_allocated_bytes,
outer_net_alloc_bytes + thread_usage_->max_allocated_bytes);
}
thread_usage_->alloc_ops += usage_.alloc_ops;
thread_usage_->alloc_bytes += usage_.alloc_bytes;
thread_usage_->alloc_overhead_bytes += usage_.alloc_overhead_bytes;
thread_usage_->free_ops += usage_.free_ops;
thread_usage_->free_bytes += usage_.free_bytes;
}
thread_usage_ = nullptr;
usage_ = current;
}
ThreadHeapUsage ThreadHeapUsageTracker::GetUsageSnapshot() {
DCHECK(g_thread_allocator_usage.initialized());
ThreadHeapUsage* usage = GetOrCreateThreadUsage();
DCHECK_NE(nullptr, usage);
return *usage;
}
void ThreadHeapUsageTracker::EnableHeapTracking() {
EnsureTLSInitialized();
CHECK_EQ(false, g_heap_tracking_enabled) << "No double-enabling.";
g_heap_tracking_enabled = true;
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
base::allocator::InsertAllocatorDispatch(&allocator_dispatch);
#else
CHECK(false) << "Can't enable heap tracking without the shim.";
#endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
}
bool ThreadHeapUsageTracker::IsHeapTrackingEnabled() {
return g_heap_tracking_enabled;
}
void ThreadHeapUsageTracker::DisableHeapTrackingForTesting() {
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
base::allocator::RemoveAllocatorDispatchForTesting(&allocator_dispatch);
#else
CHECK(false) << "Can't disable heap tracking without the shim.";
#endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
DCHECK_EQ(true, g_heap_tracking_enabled) << "Heap tracking not enabled.";
g_heap_tracking_enabled = false;
}
base::allocator::AllocatorDispatch*
ThreadHeapUsageTracker::GetDispatchForTesting() {
return &allocator_dispatch;
}
void ThreadHeapUsageTracker::EnsureTLSInitialized() {
if (!g_thread_allocator_usage.initialized()) {
g_thread_allocator_usage.Initialize([](void* thread_heap_usage) {
// This destructor will be called twice. Once to destroy the actual
// ThreadHeapUsage instance and a second time, immediately after, for the
// sentinel. Re-setting the TLS slow (below) does re-initialize the TLS
// slot. The ThreadLocalStorage code is designed to deal with this use
// case (see comments in ThreadHeapUsageTracker::EnsureTLSInitialized) and
// will re-call the destructor with the kTeardownSentinel as arg.
if (thread_heap_usage == kTeardownSentinel)
return;
DCHECK(thread_heap_usage != kInitializationSentinel);
// Deleting the ThreadHeapUsage TLS object will re-enter the shim and hit
// RecordFree() above. The sentinel prevents RecordFree() from re-creating
// another ThreadHeapUsage object.
g_thread_allocator_usage.Set(kTeardownSentinel);
delete static_cast<ThreadHeapUsage*>(thread_heap_usage);
});
}
}
} // namespace debug
} // namespace base

View File

@@ -0,0 +1,117 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_
#define BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_
#include <stdint.h>
#include "base/allocator/features.h"
#include "base/base_export.h"
#include "base/threading/thread_checker.h"
namespace base {
namespace allocator {
struct AllocatorDispatch;
} // namespace allocator
namespace debug {
// Used to store the heap allocator usage in a scope.
struct ThreadHeapUsage {
// The cumulative number of allocation operations.
uint64_t alloc_ops;
// The cumulative number of allocated bytes. Where available, this is
// inclusive heap padding and estimated or actual heap overhead.
uint64_t alloc_bytes;
// Where available, cumulative number of heap padding and overhead bytes.
uint64_t alloc_overhead_bytes;
// The cumulative number of free operations.
uint64_t free_ops;
// The cumulative number of bytes freed.
// Only recorded if the underlying heap shim can return the size of an
// allocation.
uint64_t free_bytes;
// The maximal value of |alloc_bytes| - |free_bytes| seen for this thread.
// Only recorded if the underlying heap shim supports returning the size of
// an allocation.
uint64_t max_allocated_bytes;
};
// By keeping a tally on heap operations, it's possible to track:
// - the number of alloc/free operations, where a realloc is zero or one
// of each, depending on the input parameters (see man realloc).
// - the number of bytes allocated/freed.
// - the number of estimated bytes of heap overhead used.
// - the high-watermark amount of bytes allocated in the scope.
// This in turn allows measuring the memory usage and memory usage churn over
// a scope. Scopes must be cleanly nested, and each scope must be
// destroyed on the thread where it's created.
//
// Note that this depends on the capabilities of the underlying heap shim. If
// that shim can not yield a size estimate for an allocation, it's not possible
// to keep track of overhead, freed bytes and the allocation high water mark.
class BASE_EXPORT ThreadHeapUsageTracker {
public:
ThreadHeapUsageTracker();
~ThreadHeapUsageTracker();
// Start tracking heap usage on this thread.
// This may only be called on the thread where the instance is created.
// Note IsHeapTrackingEnabled() must be true.
void Start();
// Stop tracking heap usage on this thread and store the usage tallied.
// If |usage_is_exclusive| is true, the usage tallied won't be added to the
// outer scope's usage. If |usage_is_exclusive| is false, the usage tallied
// in this scope will also tally to any outer scope.
// This may only be called on the thread where the instance is created.
void Stop(bool usage_is_exclusive);
// After Stop() returns the usage tallied from Start() to Stop().
const ThreadHeapUsage& usage() const { return usage_; }
// Returns this thread's heap usage from the start of the innermost
// enclosing ThreadHeapUsageTracker instance, if any.
static ThreadHeapUsage GetUsageSnapshot();
// Enables the heap intercept. May only be called once, and only if the heap
// shim is available, e.g. if BUILDFLAG(USE_ALLOCATOR_SHIM) is
// true.
static void EnableHeapTracking();
// Returns true iff heap tracking is enabled.
static bool IsHeapTrackingEnabled();
protected:
// Exposed for testing only - note that it's safe to re-EnableHeapTracking()
// after calling this function in tests.
static void DisableHeapTrackingForTesting();
// Exposed for testing only.
static void EnsureTLSInitialized();
// Exposed to allow testing the shim without inserting it in the allocator
// shim chain.
static base::allocator::AllocatorDispatch* GetDispatchForTesting();
private:
ThreadChecker thread_checker_;
// The heap usage at Start(), or the difference from Start() to Stop().
ThreadHeapUsage usage_;
// This thread's heap usage, non-null from Start() to Stop().
ThreadHeapUsage* thread_usage_;
};
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_