Remove base/profiler, base/debug and base/third_party/symbolize Change-Id: Ia46b0aea972a4931f8e45c8ee36fe1d784de47d3 Reviewed-on: https://gn-review.googlesource.com/1541 Commit-Queue: Scott Graham <scottmg@chromium.org> Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/base/debug/OWNERS b/base/debug/OWNERS deleted file mode 100644 index 6150257..0000000 --- a/base/debug/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -# For activity tracking: -per-file activity_*=bcwhite@chromium.org
diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc deleted file mode 100644 index d787829..0000000 --- a/base/debug/activity_analyzer.cc +++ /dev/null
@@ -1,412 +0,0 @@ -// 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
diff --git a/base/debug/activity_analyzer.h b/base/debug/activity_analyzer.h deleted file mode 100644 index 9add85a..0000000 --- a/base/debug/activity_analyzer.h +++ /dev/null
@@ -1,262 +0,0 @@ -// 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_
diff --git a/base/debug/alias.cc b/base/debug/alias.cc deleted file mode 100644 index d93d495..0000000 --- a/base/debug/alias.cc +++ /dev/null
@@ -1,23 +0,0 @@ -// 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_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
diff --git a/base/debug/alias.h b/base/debug/alias.h deleted file mode 100644 index 128fdaa..0000000 --- a/base/debug/alias.h +++ /dev/null
@@ -1,43 +0,0 @@ -// 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" -#include "base/strings/string_util.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); -// DEBUG_ALIAS_FOR_CSTR(name_copy, p->name, 16); -// CHECK(false); -void BASE_EXPORT Alias(const void* var); - -} // namespace debug -} // namespace base - -// Convenience macro that copies the null-terminated string from |c_str| into a -// stack-allocated char array named |var_name| that holds up to |char_count| -// characters and should be preserved in memory dumps. -#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count) \ - char var_name[char_count]; \ - ::base::strlcpy(var_name, (c_str), arraysize(var_name)); \ - ::base::debug::Alias(var_name); - -#endif // BASE_DEBUG_ALIAS_H_
diff --git a/base/debug/asan_invalid_access.cc b/base/debug/asan_invalid_access.cc deleted file mode 100644 index d5d43d5..0000000 --- a/base/debug/asan_invalid_access.cc +++ /dev/null
@@ -1,101 +0,0 @@ -// 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_config.h" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -namespace base { -namespace debug { - -namespace { - -#if defined(OS_WIN) && defined(ADDRESS_SANITIZER) -// 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; - LONG* array = new LONG[kArraySize]; - - // Explicitly call out to a kernel32 function to perform the memory access. - // This way the underflow won't be detected but the corruption will (as the - // allocator will still be hooked). - auto InterlockedIncrementFn = - reinterpret_cast<LONG (*)(LONG volatile * addend)>( - GetProcAddress(GetModuleHandle(L"kernel32"), "InterlockedIncrement")); - CHECK(InterlockedIncrementFn); - - LONG volatile dummy = InterlockedIncrementFn(array - 1); - base::debug::Alias(const_cast<LONG*>(&dummy)); - - if (induce_crash) - CHECK(false); - delete[] array; -} -#endif // OS_WIN && ADDRESS_SANITIZER - -} // namespace - -#if defined(ADDRESS_SANITIZER) -// 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); -} - -#if defined(OS_WIN) -void AsanCorruptHeapBlock() { - CorruptMemoryBlock(false); -} - -void AsanCorruptHeap() { - CorruptMemoryBlock(true); -} -#endif // OS_WIN -#endif // ADDRESS_SANITIZER - -} // namespace debug -} // namespace base
diff --git a/base/debug/asan_invalid_access.h b/base/debug/asan_invalid_access.h deleted file mode 100644 index f8b078a..0000000 --- a/base/debug/asan_invalid_access.h +++ /dev/null
@@ -1,46 +0,0 @@ -// 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" -#include "build_config.h" - -namespace base { -namespace debug { - -#if defined(ADDRESS_SANITIZER) - -// 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(); - -// The "corrupt-block" and "corrupt-heap" classes of bugs is specific to -// Windows. -#if defined(OS_WIN) -// 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 // OS_WIN -#endif // ADDRESS_SANITIZER - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_ASAN_INVALID_ACCESS_H_
diff --git a/base/debug/close_handle_hook_win.cc b/base/debug/close_handle_hook_win.cc deleted file mode 100644 index 35afdf5..0000000 --- a/base/debug/close_handle_hook_win.cc +++ /dev/null
@@ -1,263 +0,0 @@ -// 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_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", reinterpret_cast<void*>(&CloseHandleHook), - reinterpret_cast<void**>(&g_close_function)); - if (!patch) - return; - hooks_.push_back(patch); - - patch = IATPatch(module, "DuplicateHandle", - reinterpret_cast<void*>(&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", - reinterpret_cast<void*>(&CloseHandleHook), - reinterpret_cast<void**>(&g_close_function)); - EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle", - reinterpret_cast<void*>(&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
diff --git a/base/debug/close_handle_hook_win.h b/base/debug/close_handle_hook_win.h deleted file mode 100644 index c775d75..0000000 --- a/base/debug/close_handle_hook_win.h +++ /dev/null
@@ -1,19 +0,0 @@ -// 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_
diff --git a/base/debug/crash_logging.cc b/base/debug/crash_logging.cc deleted file mode 100644 index 1dabb6b..0000000 --- a/base/debug/crash_logging.cc +++ /dev/null
@@ -1,54 +0,0 @@ -// 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" - -namespace base { -namespace debug { - -namespace { - -CrashKeyImplementation* g_crash_key_impl = nullptr; - -} // 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); -} - -ScopedCrashKeyString::ScopedCrashKeyString(CrashKeyString* crash_key, - base::StringPiece value) - : crash_key_(crash_key) { - SetCrashKeyString(crash_key_, value); -} - -ScopedCrashKeyString::~ScopedCrashKeyString() { - ClearCrashKeyString(crash_key_); -} - -void SetCrashKeyImplementation(std::unique_ptr<CrashKeyImplementation> impl) { - delete g_crash_key_impl; - g_crash_key_impl = impl.release(); -} - -} // namespace debug -} // namespace base
diff --git a/base/debug/crash_logging.h b/base/debug/crash_logging.h deleted file mode 100644 index 9c6cd75..0000000 --- a/base/debug/crash_logging.h +++ /dev/null
@@ -1,104 +0,0 @@ -// 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 "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); - -// A scoper that sets the specified key to value for the lifetime of the -// object, and clears it on destruction. -class BASE_EXPORT ScopedCrashKeyString { - public: - ScopedCrashKeyString(CrashKeyString* crash_key, base::StringPiece value); - ~ScopedCrashKeyString(); - - private: - CrashKeyString* const crash_key_; - - DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString); -}; - -//////////////////////////////////////////////////////////////////////////////// -// 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() = default; - - 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; -}; - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_CRASH_LOGGING_H_
diff --git a/base/debug/debugger.cc b/base/debug/debugger.cc deleted file mode 100644 index 025bc54..0000000 --- a/base/debug/debugger.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// 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_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
diff --git a/base/debug/debugger.h b/base/debug/debugger.h deleted file mode 100644 index 8680e28..0000000 --- a/base/debug/debugger.h +++ /dev/null
@@ -1,44 +0,0 @@ -// 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_
diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc deleted file mode 100644 index 63d9d52..0000000 --- a/base/debug/debugger_posix.cc +++ /dev/null
@@ -1,272 +0,0 @@ -// 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_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
diff --git a/base/debug/debugger_win.cc b/base/debug/debugger_win.cc deleted file mode 100644 index a1d86e4..0000000 --- a/base/debug/debugger_win.cc +++ /dev/null
@@ -1,25 +0,0 @@ -// 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
diff --git a/base/debug/dump_without_crashing.cc b/base/debug/dump_without_crashing.cc deleted file mode 100644 index 1ab8c9c..0000000 --- a/base/debug/dump_without_crashing.cc +++ /dev/null
@@ -1,41 +0,0 @@ -// 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
diff --git a/base/debug/dump_without_crashing.h b/base/debug/dump_without_crashing.h deleted file mode 100644 index c36973f..0000000 --- a/base/debug/dump_without_crashing.h +++ /dev/null
@@ -1,37 +0,0 @@ -// 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_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_
diff --git a/base/debug/elf_reader_linux.cc b/base/debug/elf_reader_linux.cc deleted file mode 100644 index cdf8193..0000000 --- a/base/debug/elf_reader_linux.cc +++ /dev/null
@@ -1,132 +0,0 @@ -// Copyright 2018 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/elf_reader_linux.h" - -#include <arpa/inet.h> -#include <elf.h> - -#include <vector> - -#include "base/bits.h" -#include "base/containers/span.h" -#include "base/sha1.h" -#include "base/strings/stringprintf.h" - -namespace base { -namespace debug { - -namespace { - -#if __SIZEOF_POINTER__ == 4 -using Ehdr = Elf32_Ehdr; -using Dyn = Elf32_Dyn; -using Half = Elf32_Half; -using Nhdr = Elf32_Nhdr; -using Phdr = Elf32_Phdr; -using Word = Elf32_Word; -#else -using Ehdr = Elf64_Ehdr; -using Dyn = Elf64_Dyn; -using Half = Elf64_Half; -using Nhdr = Elf64_Nhdr; -using Phdr = Elf64_Phdr; -using Word = Elf64_Word; -#endif - -using ElfSegment = span<const char>; - -Optional<std::string> ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) { - const void* section_end = segment.data() + segment.size_bytes(); - const Nhdr* note_header = reinterpret_cast<const Nhdr*>(segment.data()); - while (note_header < section_end) { - if (note_header->n_type == NT_GNU_BUILD_ID) - break; - note_header = reinterpret_cast<const Nhdr*>( - reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + - bits::Align(note_header->n_namesz, 4) + - bits::Align(note_header->n_descsz, 4)); - } - - if (note_header >= section_end || note_header->n_descsz != kSHA1Length) - return nullopt; - - const uint8_t* guid = reinterpret_cast<const uint8_t*>(note_header) + - sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4); - - uint32_t dword = htonl(*reinterpret_cast<const int32_t*>(guid)); - uint16_t word1 = htons(*reinterpret_cast<const int16_t*>(guid + 4)); - uint16_t word2 = htons(*reinterpret_cast<const int16_t*>(guid + 6)); - std::string identifier; - identifier.reserve(kSHA1Length * 2); // as hex string - SStringPrintf(&identifier, "%08X%04X%04X", dword, word1, word2); - for (size_t i = 8; i < note_header->n_descsz; ++i) - StringAppendF(&identifier, "%02X", guid[i]); - - return identifier; -} - -std::vector<ElfSegment> FindElfSegments(const void* elf_mapped_base, - uint32_t segment_type) { - const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base); - if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) - return std::vector<ElfSegment>(); - - const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); - const Phdr* phdrs = - reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff); - std::vector<ElfSegment> segments; - for (Half i = 0; i < elf_header->e_phnum; ++i) { - if (phdrs[i].p_type == segment_type) - segments.push_back({elf_base + phdrs[i].p_offset, phdrs[i].p_filesz}); - } - return segments; -} - -} // namespace - -Optional<std::string> ReadElfBuildId(const void* elf_base) { - // Elf program headers can have multiple PT_NOTE arrays. - std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_NOTE); - if (segs.empty()) - return nullopt; - Optional<std::string> id; - for (const ElfSegment& seg : segs) { - id = ElfSegmentBuildIDNoteAsString(seg); - if (id) - return id; - } - - return nullopt; -} - -Optional<std::string> ReadElfLibraryName(const void* elf_base) { - std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_DYNAMIC); - if (segs.empty()) - return nullopt; - DCHECK_EQ(1u, segs.size()); - - const ElfSegment& dynamic_seg = segs.front(); - const Dyn* dynamic_start = reinterpret_cast<const Dyn*>(dynamic_seg.data()); - const Dyn* dynamic_end = reinterpret_cast<const Dyn*>( - dynamic_seg.data() + dynamic_seg.size_bytes()); - Optional<std::string> soname; - Word soname_strtab_offset = 0; - const char* strtab_addr = 0; - for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end; - ++dynamic_iter) { - if (dynamic_iter->d_tag == DT_STRTAB) { - strtab_addr = - dynamic_iter->d_un.d_ptr + reinterpret_cast<const char*>(elf_base); - } else if (dynamic_iter->d_tag == DT_SONAME) { - soname_strtab_offset = dynamic_iter->d_un.d_val; - } - } - if (soname_strtab_offset && strtab_addr) - return std::string(strtab_addr + soname_strtab_offset); - return nullopt; -} - -} // namespace debug -} // namespace base
diff --git a/base/debug/elf_reader_linux.h b/base/debug/elf_reader_linux.h deleted file mode 100644 index 4086dfb..0000000 --- a/base/debug/elf_reader_linux.h +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2018 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_ELF_READER_LINUX_H_ -#define BASE_DEBUG_ELF_READER_LINUX_H_ - -#include <string> - -#include "base/base_export.h" -#include "base/optional.h" - -namespace base { -namespace debug { - -// Returns the ELF section .note.gnu.build-id from the ELF file mapped at -// |elf_base|, if present. The caller must ensure that the file is fully mapped -// in memory. -Optional<std::string> BASE_EXPORT ReadElfBuildId(const void* elf_base); - -// Returns the library name from the ELF file mapped at |elf_base|, if present. -// The caller must ensure that the file is fully mapped in memory. -Optional<std::string> BASE_EXPORT ReadElfLibraryName(const void* elf_base); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_ELF_READER_LINUX_H_
diff --git a/base/debug/gdi_debug_util_win.cc b/base/debug/gdi_debug_util_win.cc deleted file mode 100644 index bf9827c..0000000 --- a/base/debug/gdi_debug_util_win.cc +++ /dev/null
@@ -1,141 +0,0 @@ -// 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
diff --git a/base/debug/gdi_debug_util_win.h b/base/debug/gdi_debug_util_win.h deleted file mode 100644 index 3383a4d..0000000 --- a/base/debug/gdi_debug_util_win.h +++ /dev/null
@@ -1,25 +0,0 @@ -// 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_
diff --git a/base/debug/leak_annotations.h b/base/debug/leak_annotations.h deleted file mode 100644 index f1a2d07..0000000 --- a/base/debug/leak_annotations.h +++ /dev/null
@@ -1,46 +0,0 @@ -// 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_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_
diff --git a/base/debug/leak_tracker.h b/base/debug/leak_tracker.h deleted file mode 100644 index 43f2102..0000000 --- a/base/debug/leak_tracker.h +++ /dev/null
@@ -1,142 +0,0 @@ -// 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_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: - // This destructor suppresses warnings about instances of this class not being - // used. - ~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_
diff --git a/base/debug/proc_maps_linux.cc b/base/debug/proc_maps_linux.cc deleted file mode 100644 index 1a9476f..0000000 --- a/base/debug/proc_maps_linux.cc +++ /dev/null
@@ -1,169 +0,0 @@ -// 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_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", - ®ion.start, ®ion.end, permissions, ®ion.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
diff --git a/base/debug/proc_maps_linux.h b/base/debug/proc_maps_linux.h deleted file mode 100644 index f5f8a59..0000000 --- a/base/debug/proc_maps_linux.h +++ /dev/null
@@ -1,94 +0,0 @@ -// 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_
diff --git a/base/debug/profiler.cc b/base/debug/profiler.cc deleted file mode 100644 index 82e5229..0000000 --- a/base/debug/profiler.cc +++ /dev/null
@@ -1,142 +0,0 @@ -// 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/process/process_handle.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "build_config.h" - -#if defined(OS_WIN) -#include "base/win/current_module.h" -#include "base/win/pe_image.h" -#endif // defined(OS_WIN) - -namespace base { -namespace debug { - -void StartProfiling(const std::string& name) { -} - -void StopProfiling() { -} - -void FlushProfiling() { -} - -bool BeingProfiled() { - return false; -} - -void RestartProfilingAfterFork() { -} - -bool IsProfilingSupported() { - return false; -} - -#if !defined(OS_WIN) - -ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { - return nullptr; -} - -DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { - return nullptr; -} - -AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { - return nullptr; -} - -MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { - return nullptr; -} - -#else // defined(OS_WIN) - -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) { - 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
diff --git a/base/debug/profiler.h b/base/debug/profiler.h deleted file mode 100644 index f706a1a..0000000 --- a/base/debug/profiler.h +++ /dev/null
@@ -1,91 +0,0 @@ -// 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 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_
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc deleted file mode 100644 index 3a63dae..0000000 --- a/base/debug/stack_trace.cc +++ /dev/null
@@ -1,43 +0,0 @@ -// 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" - -namespace base { -namespace debug { - -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(); -} - -} // namespace debug -} // namespace base
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h deleted file mode 100644 index 33023fe..0000000 --- a/base/debug/stack_trace.h +++ /dev/null
@@ -1,126 +0,0 @@ -// 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/macros.h" -#include "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 - -// 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_; -}; - -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_
diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc deleted file mode 100644 index c67fd62..0000000 --- a/base/debug/stack_trace_posix.cc +++ /dev/null
@@ -1,887 +0,0 @@ -// 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/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_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; - } - -// Do not take the "in signal handler" code path on Mac in a DCHECK-enabled -// build, as this prevents seeing a useful (symbolized) stack trace on a crash -// or DCHECK() failure. While it may not be fully safe to run the stack symbol -// printing code, in practice it's better to provide meaningful stack traces - -// and the risk is low given we're likely crashing already. -#if !defined(OS_MACOSX) || !DCHECK_IS_ON() - // 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; -#endif - - 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"); - - 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, ®ions_)) { - 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
diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc deleted file mode 100644 index 1ef2a06..0000000 --- a/base/debug/stack_trace_win.cc +++ /dev/null
@@ -1,365 +0,0 @@ -// 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
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc deleted file mode 100644 index 69b9c04..0000000 --- a/base/debug/task_annotator.cc +++ /dev/null
@@ -1,106 +0,0 @@ -// 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/alias.h" -#include "base/no_destructor.h" -#include "base/pending_task.h" -#include "base/threading/thread_local.h" - -namespace base { -namespace debug { - -namespace { - -TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr; - -// Returns the TLS slot that stores the PendingTask currently in progress on -// each thread. Used to allow creating a breadcrumb of program counters on the -// stack to help identify a task's origin in crashes. -ThreadLocalPointer<const PendingTask>* GetTLSForCurrentPendingTask() { - static NoDestructor<ThreadLocalPointer<const PendingTask>> - tls_for_current_pending_task; - return tls_for_current_pending_task.get(); -} - -} // namespace - -TaskAnnotator::TaskAnnotator() = default; - -TaskAnnotator::~TaskAnnotator() = default; - -void TaskAnnotator::DidQueueTask(const char* queue_function, - const PendingTask& pending_task) { - // TODO(https://crbug.com/826902): Fix callers that invoke DidQueueTask() - // twice for the same PendingTask. - // DCHECK(!pending_task.task_backtrace[0]) - // << "Task backtrace was already set, task posted twice??"; - if (!pending_task.task_backtrace[0]) { - const PendingTask* parent_task = GetTLSForCurrentPendingTask()->Get(); - if (parent_task) { - pending_task.task_backtrace[0] = - parent_task->posted_from.program_counter(); - std::copy(parent_task->task_backtrace.begin(), - parent_task->task_backtrace.end() - 1, - pending_task.task_backtrace.begin() + 1); - } - } -} - -void TaskAnnotator::RunTask(const char* queue_function, - PendingTask* pending_task) { - // 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); - - ThreadLocalPointer<const PendingTask>* tls_for_current_pending_task = - GetTLSForCurrentPendingTask(); - const PendingTask* previous_pending_task = - tls_for_current_pending_task->Get(); - tls_for_current_pending_task->Set(pending_task); - - if (g_task_annotator_observer) - g_task_annotator_observer->BeforeRunTask(pending_task); - std::move(pending_task->task).Run(); - - tls_for_current_pending_task->Set(previous_pending_task); -} - -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); -} - -// static -void TaskAnnotator::RegisterObserverForTesting(ObserverForTesting* observer) { - DCHECK(!g_task_annotator_observer); - g_task_annotator_observer = observer; -} - -// static -void TaskAnnotator::ClearObserverForTesting() { - g_task_annotator_observer = nullptr; -} - -} // namespace debug -} // namespace base
diff --git a/base/debug/task_annotator.h b/base/debug/task_annotator.h deleted file mode 100644 index f53d02c..0000000 --- a/base/debug/task_annotator.h +++ /dev/null
@@ -1,64 +0,0 @@ -// 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: - class ObserverForTesting { - public: - // Invoked just before RunTask() in the scope in which the task is about to - // be executed. - virtual void BeforeRunTask(const PendingTask* pending_task) = 0; - }; - - 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. |queue_function| can - // be null if the caller doesn't want trace flow events logged to - // toplevel.flow. - 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); - - // 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. Callers may use - // this when generating their own flow events (i.e. when passing - // |queue_function == nullptr| in above methods). - uint64_t GetTaskTraceID(const PendingTask& task) const; - - private: - friend class TaskAnnotatorBacktraceIntegrationTest; - - // Registers an ObserverForTesting that will be invoked by all TaskAnnotators' - // RunTask(). This registration and the implementation of BeforeRunTask() are - // responsible to ensure thread-safety. - static void RegisterObserverForTesting(ObserverForTesting* observer); - static void ClearObserverForTesting(); - - DISALLOW_COPY_AND_ASSIGN(TaskAnnotator); -}; - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_TASK_ANNOTATOR_H_
diff --git a/base/lazy_instance.h b/base/lazy_instance.h index 36d3158..456b431 100644 --- a/base/lazy_instance.h +++ b/base/lazy_instance.h
@@ -48,7 +48,6 @@ #include <new> // For placement new. #include "base/atomicops.h" -#include "base/debug/leak_annotations.h" #include "base/lazy_instance_helpers.h" #include "base/logging.h" #include "base/threading/thread_restrictions.h" @@ -113,7 +112,6 @@ #endif static Type* New(void* instance) { - ANNOTATE_SCOPED_MEMORY_LEAK; return LazyInstanceTraitsBase<Type>::New(instance); } static void Delete(Type* instance) {
diff --git a/base/logging.cc b/base/logging.cc index 72ca87a..78f1aa0 100644 --- a/base/logging.cc +++ b/base/logging.cc
@@ -88,9 +88,6 @@ #include "base/callback.h" #include "base/command_line.h" #include "base/containers/stack.h" -#include "base/debug/alias.h" -#include "base/debug/debugger.h" -#include "base/debug/stack_trace.h" #include "base/lazy_instance.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_piece.h" @@ -586,11 +583,8 @@ size_t stack_start = stream_.tellp(); #if !defined(OFFICIAL_BUILD) && !defined(OS_NACL) && !defined(__UCLIBC__) && \ !defined(OS_AIX) - if (severity_ == LOG_FATAL && !base::debug::BeingDebugged()) { - // Include a stack trace on a fatal, unless a debugger is attached. - base::debug::StackTrace trace; + if (severity_ == LOG_FATAL) { stream_ << std::endl; // Newline to separate from log message. - trace.OutputToStream(&stream_); } #endif stream_ << std::endl; @@ -815,10 +809,6 @@ } if (severity_ == LOG_FATAL) { - // Ensure the first characters of the string are on the stack so they - // are contained in minidumps for diagnostic purposes. - DEBUG_ALIAS_FOR_CSTR(str_stack, str_newline.c_str(), 1024); - if (log_assert_handler_stack.IsCreated() && !log_assert_handler_stack.Get().empty()) { LogAssertHandlerFunction log_assert_handler = @@ -838,14 +828,12 @@ // information, and displaying message boxes when the application is // hosed can cause additional problems. #ifndef NDEBUG - if (!base::debug::BeingDebugged()) { - // Displaying a dialog is unnecessary when debugging and can complicate - // debugging. - DisplayDebugMessageInDialog(stream_.str()); - } + // Displaying a dialog is unnecessary when debugging and can complicate + // debugging. + DisplayDebugMessageInDialog(stream_.str()); #endif // Crash the process to generate a dump. - base::debug::BreakDebugger(); + abort(); } } } @@ -959,10 +947,6 @@ Win32ErrorLogMessage::~Win32ErrorLogMessage() { stream() << ": " << SystemErrorCodeToString(err_); - // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a - // field) and use Alias in hopes that it makes it into crash dumps. - DWORD last_error = err_; - base::debug::Alias(&last_error); } #elif defined(OS_POSIX) || defined(OS_FUCHSIA) ErrnoLogMessage::ErrnoLogMessage(const char* file, @@ -975,10 +959,6 @@ ErrnoLogMessage::~ErrnoLogMessage() { stream() << ": " << SystemErrorCodeToString(err_); - // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a - // field) and use Alias in hopes that it makes it into crash dumps. - int last_error = err_; - base::debug::Alias(&last_error); } #endif // defined(OS_WIN) @@ -1017,7 +997,7 @@ } if (level == LOG_FATAL) - base::debug::BreakDebugger(); + abort(); } // This was defined at the beginning of this file.
diff --git a/base/logging.h b/base/logging.h index 39b17ff..59e2fcf 100644 --- a/base/logging.h +++ b/base/logging.h
@@ -17,7 +17,6 @@ #include "base/base_export.h" #include "base/callback_forward.h" #include "base/compiler_specific.h" -#include "base/debug/debugger.h" #include "base/macros.h" #include "base/strings/string_piece_forward.h" #include "base/template_util.h"
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc index 0dd1272..7a43e92 100644 --- a/base/message_loop/incoming_task_queue.cc +++ b/base/message_loop/incoming_task_queue.cc
@@ -121,7 +121,7 @@ void IncomingTaskQueue::RunTask(PendingTask* pending_task) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - task_annotator_.RunTask("MessageLoop::PostTask", pending_task); + std::move(pending_task->task).Run(); } IncomingTaskQueue::TriageQueue::TriageQueue(IncomingTaskQueue* outer) @@ -329,8 +329,6 @@ // delayed_run_time value) and for identifying the task in about:tracing. pending_task->sequence_num = next_sequence_num_++; - task_annotator_.DidQueueTask("MessageLoop::PostTask", *pending_task); - bool was_empty = incoming_queue_.empty(); incoming_queue_.push(std::move(*pending_task));
diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h index f158d2a..b4d8185 100644 --- a/base/message_loop/incoming_task_queue.h +++ b/base/message_loop/incoming_task_queue.h
@@ -7,7 +7,6 @@ #include "base/base_export.h" #include "base/callback.h" -#include "base/debug/task_annotator.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/pending_task.h" @@ -206,8 +205,6 @@ // Checks calls made only on the MessageLoop thread. SEQUENCE_CHECKER(sequence_checker_); - debug::TaskAnnotator task_annotator_; - // True if we always need to call ScheduleWork when receiving a new task, even // if the incoming queue was not empty. const bool always_schedule_work_;
diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc index 47cf477..af0c7db 100644 --- a/base/message_loop/message_pump_win.cc +++ b/base/message_loop/message_pump_win.cc
@@ -9,7 +9,6 @@ #include <limits> -#include "base/debug/alias.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" @@ -209,8 +208,6 @@ delay = INFINITE; // Tell the optimizer to retain these values to simplify analyzing hangs. - base::debug::Alias(&delay); - base::debug::Alias(&wait_flags); DWORD result = MsgWaitForMultipleObjectsEx(0, nullptr, delay, QS_ALLINPUT, wait_flags); @@ -313,8 +310,6 @@ if (delay_msec < USER_TIMER_MINIMUM) delay_msec = USER_TIMER_MINIMUM; - // Tell the optimizer to retain these values to simplify analyzing hangs. - base::debug::Alias(&delay_msec); // Create a WM_TIMER event that will wake us up to check for any pending // timers (in case we are running within a nested, external sub-pump). UINT_PTR ret = SetTimer(message_window_.hwnd(), 0, delay_msec, nullptr); @@ -510,8 +505,6 @@ if (timeout < 0) // Negative value means no timers waiting. timeout = INFINITE; - // Tell the optimizer to retain these values to simplify analyzing hangs. - base::debug::Alias(&timeout); WaitForIOCompletion(timeout, nullptr); }
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc index 1004012..fc9d188 100644 --- a/base/process/launch_posix.cc +++ b/base/process/launch_posix.cc
@@ -27,8 +27,6 @@ #include "base/command_line.h" #include "base/compiler_specific.h" -#include "base/debug/debugger.h" -#include "base/debug/stack_trace.h" #include "base/files/dir_reader_posix.h" #include "base/files/file_util.h" #include "base/files/scoped_file.h"
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc index 0ae6820..3ee81b1 100644 --- a/base/process/launch_win.cc +++ b/base/process/launch_win.cc
@@ -17,8 +17,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" -#include "base/debug/activity_tracker.h" -#include "base/debug/stack_trace.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/process/kill.h"
diff --git a/base/process/memory.cc b/base/process/memory.cc index a98e309..84305e0 100644 --- a/base/process/memory.cc +++ b/base/process/memory.cc
@@ -2,7 +2,6 @@ // 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 "base/logging.h" #include "base/process/memory.h" #include "build_config.h" @@ -17,9 +16,7 @@ // Breakpad server classifies base::`anonymous namespace'::OnNoMemory as // out-of-memory crash. NOINLINE void OnNoMemory(size_t size) { - size_t tmp_size = size; - base::debug::Alias(&tmp_size); - LOG(FATAL) << "Out of memory. size=" << tmp_size; + LOG(FATAL) << "Out of memory. size=" << size; } } // namespace
diff --git a/base/process/process_win.cc b/base/process/process_win.cc index a2e614c..2ef4daf 100644 --- a/base/process/process_win.cc +++ b/base/process/process_win.cc
@@ -4,7 +4,6 @@ #include "base/process/process.h" -#include "base/debug/activity_tracker.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/process/kill.h"
diff --git a/base/profiler/OWNERS b/base/profiler/OWNERS deleted file mode 100644 index 81ff9fa..0000000 --- a/base/profiler/OWNERS +++ /dev/null
@@ -1,5 +0,0 @@ -# Stack sampling profiler -per-file native_stack_sampler*=wittman@chromium.org -per-file stack_sampling_profiler*=wittman@chromium.org -per-file test_support_library*=wittman@chromium.org -per-file win32_stack_frame_unwinder*=wittman@chromium.org
diff --git a/base/profiler/native_stack_sampler.cc b/base/profiler/native_stack_sampler.cc deleted file mode 100644 index 6eed54f..0000000 --- a/base/profiler/native_stack_sampler.cc +++ /dev/null
@@ -1,34 +0,0 @@ -// 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/profiler/native_stack_sampler.h" - -#include "base/memory/ptr_util.h" - -namespace base { - -NativeStackSampler::StackBuffer::StackBuffer(size_t buffer_size) - : buffer_(new uintptr_t[(buffer_size + sizeof(uintptr_t) - 1) / - sizeof(uintptr_t)]), - size_(buffer_size) {} - -NativeStackSampler::StackBuffer::~StackBuffer() = default; - -NativeStackSampler::NativeStackSampler() = default; - -NativeStackSampler::~NativeStackSampler() = default; - -std::unique_ptr<NativeStackSampler::StackBuffer> -NativeStackSampler::CreateStackBuffer() { - size_t size = GetStackBufferSize(); - if (size == 0) - return nullptr; - return std::make_unique<StackBuffer>(size); -} - -NativeStackSamplerTestDelegate::~NativeStackSamplerTestDelegate() = default; - -NativeStackSamplerTestDelegate::NativeStackSamplerTestDelegate() = default; - -} // namespace base
diff --git a/base/profiler/native_stack_sampler.h b/base/profiler/native_stack_sampler.h deleted file mode 100644 index ebd7c3c..0000000 --- a/base/profiler/native_stack_sampler.h +++ /dev/null
@@ -1,112 +0,0 @@ -// 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_PROFILER_NATIVE_STACK_SAMPLER_H_ -#define BASE_PROFILER_NATIVE_STACK_SAMPLER_H_ - -#include <memory> - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/profiler/stack_sampling_profiler.h" -#include "base/threading/platform_thread.h" - -namespace base { - -class NativeStackSamplerTestDelegate; - -// NativeStackSampler is an implementation detail of StackSamplingProfiler. It -// abstracts the native implementation required to record a stack sample for a -// given thread. -class NativeStackSampler { - public: - // This class contains a buffer for stack copies that can be shared across - // multiple instances of NativeStackSampler. - class StackBuffer { - public: - StackBuffer(size_t buffer_size); - ~StackBuffer(); - - void* buffer() const { return buffer_.get(); } - size_t size() const { return size_; } - - private: - // The word-aligned buffer. - const std::unique_ptr<uintptr_t[]> buffer_; - - // The size of the buffer. - const size_t size_; - - DISALLOW_COPY_AND_ASSIGN(StackBuffer); - }; - - // The callback type used to add annotations to a sample during collection. - // This is passed to the native sampler to be applied at the most appropriate - // time. It is a simple function-pointer because the generated code must be - // completely predictable and do nothing that could acquire a mutex; a - // Callback object is code outside the control of this object and could, - // for example, acquire a mutex as part of allocating memory for a LOG - // message. - using AnnotateCallback = void (*)(StackSamplingProfiler::Sample*); - - virtual ~NativeStackSampler(); - - // Creates a stack sampler that records samples for |thread_handle|. Returns - // null if this platform does not support stack sampling. - static std::unique_ptr<NativeStackSampler> Create( - PlatformThreadId thread_id, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate); - - // Gets the required size of the stack buffer. - static size_t GetStackBufferSize(); - - // Creates an instance of the a stack buffer that can be used for calls to - // any NativeStackSampler object. - static std::unique_ptr<StackBuffer> CreateStackBuffer(); - - // The following functions are all called on the SamplingThread (not the - // thread being sampled). - - // Notifies the sampler that we're starting to record a new profile. Modules - // shared across samples in the profile should be recorded in |modules|. - virtual void ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) = 0; - - // Records a stack sample to |sample|. - virtual void RecordStackSample(StackBuffer* stackbuffer, - StackSamplingProfiler::Sample* sample) = 0; - - // Notifies the sampler that we've stopped recording the current - // profile. - virtual void ProfileRecordingStopped(StackBuffer* stackbuffer) = 0; - - protected: - NativeStackSampler(); - - private: - DISALLOW_COPY_AND_ASSIGN(NativeStackSampler); -}; - -// NativeStackSamplerTestDelegate provides seams for test code to execute during -// stack collection. -class BASE_EXPORT NativeStackSamplerTestDelegate { - public: - virtual ~NativeStackSamplerTestDelegate(); - - // Called after copying the stack and resuming the target thread, but prior to - // walking the stack. Invoked on the SamplingThread. - virtual void OnPreStackWalk() = 0; - - protected: - NativeStackSamplerTestDelegate(); - - private: - DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerTestDelegate); -}; - -} // namespace base - -#endif // BASE_PROFILER_NATIVE_STACK_SAMPLER_H_ -
diff --git a/base/profiler/native_stack_sampler_mac.cc b/base/profiler/native_stack_sampler_mac.cc deleted file mode 100644 index a161173..0000000 --- a/base/profiler/native_stack_sampler_mac.cc +++ /dev/null
@@ -1,666 +0,0 @@ -// 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/profiler/native_stack_sampler.h" - -#include <dlfcn.h> -#include <libkern/OSByteOrder.h> -#include <libunwind.h> -#include <mach-o/compact_unwind_encoding.h> -#include <mach-o/getsect.h> -#include <mach-o/swap.h> -#include <mach/kern_return.h> -#include <mach/mach.h> -#include <mach/thread_act.h> -#include <mach/vm_map.h> -#include <pthread.h> -#include <sys/resource.h> -#include <sys/syslimits.h> - -#include <algorithm> -#include <map> -#include <memory> - -#include "base/logging.h" -#include "base/mac/mach_logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string_number_conversions.h" - -extern "C" { -void _sigtramp(int, int, struct sigset*); -} - -namespace base { - -namespace { - -// Maps a module's address range (half-open) in memory to an index in a separate -// data structure. -struct ModuleIndex { - ModuleIndex(uintptr_t start, uintptr_t end, size_t idx) - : base_address(start), end_address(end), index(idx){}; - // Base address of the represented module. - uintptr_t base_address; - // First address off the end of the represented module. - uintptr_t end_address; - // An index to the represented module in a separate container. - size_t index; -}; - -// Module identifiers --------------------------------------------------------- - -// Returns the unique build ID for a module loaded at |module_addr|. Returns the -// empty string if the function fails to get the build ID. -// -// Build IDs are created by the concatenation of the module's GUID (Windows) / -// UUID (Mac) and an "age" field that indicates how many times that GUID/UUID -// has been reused. In Windows binaries, the "age" field is present in the -// module header, but on the Mac, UUIDs are never reused and so the "age" value -// appended to the UUID is always 0. -std::string GetUniqueId(const void* module_addr) { - const mach_header_64* mach_header = - reinterpret_cast<const mach_header_64*>(module_addr); - DCHECK_EQ(MH_MAGIC_64, mach_header->magic); - - size_t offset = sizeof(mach_header_64); - size_t offset_limit = sizeof(mach_header_64) + mach_header->sizeofcmds; - for (uint32_t i = 0; (i < mach_header->ncmds) && - (offset + sizeof(load_command) < offset_limit); - ++i) { - const load_command* current_cmd = reinterpret_cast<const load_command*>( - reinterpret_cast<const uint8_t*>(mach_header) + offset); - - if (offset + current_cmd->cmdsize > offset_limit) { - // This command runs off the end of the command list. This is malformed. - return std::string(); - } - - if (current_cmd->cmd == LC_UUID) { - if (current_cmd->cmdsize < sizeof(uuid_command)) { - // This "UUID command" is too small. This is malformed. - return std::string(); - } - - const uuid_command* uuid_cmd = - reinterpret_cast<const uuid_command*>(current_cmd); - static_assert(sizeof(uuid_cmd->uuid) == sizeof(uuid_t), - "UUID field of UUID command should be 16 bytes."); - // The ID is comprised of the UUID concatenated with the Mac's "age" value - // which is always 0. - return HexEncode(&uuid_cmd->uuid, sizeof(uuid_cmd->uuid)) + "0"; - } - offset += current_cmd->cmdsize; - } - return std::string(); -} - -// Returns the size of the _TEXT segment of the module loaded at |module_addr|. -size_t GetModuleTextSize(const void* module_addr) { - const mach_header_64* mach_header = - reinterpret_cast<const mach_header_64*>(module_addr); - DCHECK_EQ(MH_MAGIC_64, mach_header->magic); - - unsigned long module_size; - getsegmentdata(mach_header, SEG_TEXT, &module_size); - - return module_size; -} - -// Gets the index for the Module containing |instruction_pointer| in -// |modules|, adding it if it's not already present. Returns -// StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be -// determined for |module|. -size_t GetModuleIndex(const uintptr_t instruction_pointer, - std::vector<StackSamplingProfiler::Module>* modules, - std::vector<ModuleIndex>* profile_module_index) { - // Check if |instruction_pointer| is in the address range of a module we've - // already seen. - auto module_index = - std::find_if(profile_module_index->begin(), profile_module_index->end(), - [instruction_pointer](const ModuleIndex& index) { - return instruction_pointer >= index.base_address && - instruction_pointer < index.end_address; - }); - if (module_index != profile_module_index->end()) { - return module_index->index; - } - Dl_info inf; - if (!dladdr(reinterpret_cast<const void*>(instruction_pointer), &inf)) - return StackSamplingProfiler::Frame::kUnknownModuleIndex; - - StackSamplingProfiler::Module module( - reinterpret_cast<uintptr_t>(inf.dli_fbase), GetUniqueId(inf.dli_fbase), - base::FilePath(inf.dli_fname)); - modules->push_back(module); - - uintptr_t base_module_address = reinterpret_cast<uintptr_t>(inf.dli_fbase); - size_t index = modules->size() - 1; - profile_module_index->emplace_back( - base_module_address, - base_module_address + GetModuleTextSize(inf.dli_fbase), index); - return index; -} - -// Stack walking -------------------------------------------------------------- - -// Fills |state| with |target_thread|'s context. -// -// Note that this is called while a thread is suspended. Make very very sure -// that no shared resources (e.g. memory allocators) are used for the duration -// of this function. -bool GetThreadState(thread_act_t target_thread, x86_thread_state64_t* state) { - mach_msg_type_number_t count = - static_cast<mach_msg_type_number_t>(x86_THREAD_STATE64_COUNT); - return thread_get_state(target_thread, x86_THREAD_STATE64, - reinterpret_cast<thread_state_t>(state), - &count) == KERN_SUCCESS; -} - -// If the value at |pointer| points to the original stack, rewrites it to point -// to the corresponding location in the copied stack. -// -// Note that this is called while a thread is suspended. Make very very sure -// that no shared resources (e.g. memory allocators) are used for the duration -// of this function. -uintptr_t RewritePointerIfInOriginalStack( - const uintptr_t* original_stack_bottom, - const uintptr_t* original_stack_top, - uintptr_t* stack_copy_bottom, - uintptr_t pointer) { - uintptr_t original_stack_bottom_int = - reinterpret_cast<uintptr_t>(original_stack_bottom); - uintptr_t original_stack_top_int = - reinterpret_cast<uintptr_t>(original_stack_top); - uintptr_t stack_copy_bottom_int = - reinterpret_cast<uintptr_t>(stack_copy_bottom); - - if ((pointer < original_stack_bottom_int) || - (pointer >= original_stack_top_int)) { - return pointer; - } - - return stack_copy_bottom_int + (pointer - original_stack_bottom_int); -} - -// Copies the stack to a buffer while rewriting possible pointers to locations -// within the stack to point to the corresponding locations in the copy. This is -// necessary to handle stack frames with dynamic stack allocation, where a -// pointer to the beginning of the dynamic allocation area is stored on the -// stack and/or in a non-volatile register. -// -// Eager rewriting of anything that looks like a pointer to the stack, as done -// in this function, does not adversely affect the stack unwinding. The only -// other values on the stack the unwinding depends on are return addresses, -// which should not point within the stack memory. The rewriting is guaranteed -// to catch all pointers because the stacks are guaranteed by the ABI to be -// sizeof(void*) aligned. -// -// Note that this is called while a thread is suspended. Make very very sure -// that no shared resources (e.g. memory allocators) are used for the duration -// of this function. -void CopyStackAndRewritePointers(uintptr_t* stack_copy_bottom, - const uintptr_t* original_stack_bottom, - const uintptr_t* original_stack_top, - x86_thread_state64_t* thread_state) - NO_SANITIZE("address") { - size_t count = original_stack_top - original_stack_bottom; - for (size_t pos = 0; pos < count; ++pos) { - stack_copy_bottom[pos] = RewritePointerIfInOriginalStack( - original_stack_bottom, original_stack_top, stack_copy_bottom, - original_stack_bottom[pos]); - } - - uint64_t* rewrite_registers[] = {&thread_state->__rbx, &thread_state->__rbp, - &thread_state->__rsp, &thread_state->__r12, - &thread_state->__r13, &thread_state->__r14, - &thread_state->__r15}; - for (auto* reg : rewrite_registers) { - *reg = RewritePointerIfInOriginalStack( - original_stack_bottom, original_stack_top, stack_copy_bottom, *reg); - } -} - -// Extracts the "frame offset" for a given frame from the compact unwind info. -// A frame offset indicates the location of saved non-volatile registers in -// relation to the frame pointer. See |mach-o/compact_unwind_encoding.h| for -// details. -uint32_t GetFrameOffset(int compact_unwind_info) { - // The frame offset lives in bytes 16-23. This shifts it down by the number of - // leading zeroes in the mask, then masks with (1 << number of one bits in the - // mask) - 1, turning 0x00FF0000 into 0x000000FF. Adapted from |EXTRACT_BITS| - // in libunwind's CompactUnwinder.hpp. - return ( - (compact_unwind_info >> __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET)) & - (((1 << __builtin_popcount(UNWIND_X86_64_RBP_FRAME_OFFSET))) - 1)); -} - -// True if the unwind from |leaf_frame_rip| may trigger a crash bug in -// unw_init_local. If so, the stack walk should be aborted at the leaf frame. -bool MayTriggerUnwInitLocalCrash(uint64_t leaf_frame_rip) { - // The issue here is a bug in unw_init_local that, in some unwinds, results in - // attempts to access memory at the address immediately following the address - // range of the library. When the library is the last of the mapped libraries - // that address is in a different memory region. Starting with 10.13.4 beta - // releases it appears that this region is sometimes either unmapped or mapped - // without read access, resulting in crashes on the attempted access. It's not - // clear what circumstances result in this situation; attempts to reproduce on - // a 10.13.4 beta did not trigger the issue. - // - // The workaround is to check if the memory address that would be accessed is - // readable, and if not, abort the stack walk before calling unw_init_local. - // As of 2018/03/19 about 0.1% of non-idle stacks on the UI and GPU main - // threads have a leaf frame in the last library. Since the issue appears to - // only occur some of the time it's expected that the quantity of lost samples - // will be lower than 0.1%, possibly significantly lower. - // - // TODO(lgrey): Add references above to LLVM/Radar bugs on unw_init_local once - // filed. - Dl_info info; - if (dladdr(reinterpret_cast<const void*>(leaf_frame_rip), &info) == 0) - return false; - uint64_t unused; - vm_size_t size = sizeof(unused); - return vm_read_overwrite(current_task(), - reinterpret_cast<vm_address_t>(info.dli_fbase) + - GetModuleTextSize(info.dli_fbase), - sizeof(unused), - reinterpret_cast<vm_address_t>(&unused), &size) != 0; -} - -// Check if the cursor contains a valid-looking frame pointer for frame pointer -// unwinds. If the stack frame has a frame pointer, stepping the cursor will -// involve indexing memory access off of that pointer. In that case, -// sanity-check the frame pointer register to ensure it's within bounds. -// -// Additionally, the stack frame might be in a prologue or epilogue, which can -// cause a crash when the unwinder attempts to access non-volatile registers -// that have not yet been pushed, or have already been popped from the -// stack. libwunwind will try to restore those registers using an offset from -// the frame pointer. However, since we copy the stack from RSP up, any -// locations below the stack pointer are before the beginning of the stack -// buffer. Account for this by checking that the expected location is above the -// stack pointer, and rejecting the sample if it isn't. -bool HasValidRbp(unw_cursor_t* unwind_cursor, uintptr_t stack_top) { - unw_proc_info_t proc_info; - unw_get_proc_info(unwind_cursor, &proc_info); - if ((proc_info.format & UNWIND_X86_64_MODE_MASK) == - UNWIND_X86_64_MODE_RBP_FRAME) { - unw_word_t rsp, rbp; - unw_get_reg(unwind_cursor, UNW_X86_64_RSP, &rsp); - unw_get_reg(unwind_cursor, UNW_X86_64_RBP, &rbp); - uint32_t offset = GetFrameOffset(proc_info.format) * sizeof(unw_word_t); - if (rbp < offset || (rbp - offset) < rsp || rbp > stack_top) { - return false; - } - } - return true; -} - -// Walks the stack represented by |unwind_context|, calling back to the provided -// lambda for each frame. Returns false if an error occurred, otherwise returns -// true. -template <typename StackFrameCallback, typename ContinueUnwindPredicate> -bool WalkStackFromContext( - unw_context_t* unwind_context, - size_t* frame_count, - std::vector<StackSamplingProfiler::Module>* current_modules, - std::vector<ModuleIndex>* profile_module_index, - const StackFrameCallback& callback, - const ContinueUnwindPredicate& continue_unwind) { - unw_cursor_t unwind_cursor; - unw_init_local(&unwind_cursor, unwind_context); - - int step_result; - unw_word_t rip; - do { - ++(*frame_count); - unw_get_reg(&unwind_cursor, UNW_REG_IP, &rip); - - // Ensure IP is in a module. - // - // Frameless unwinding (non-DWARF) works by fetching the function's - // stack size from the unwind encoding or stack, and adding it to the - // stack pointer to determine the function's return address. - // - // If we're in a function prologue or epilogue, the actual stack size - // may be smaller than it will be during the normal course of execution. - // When libunwind adds the expected stack size, it will look for the - // return address in the wrong place. This check should ensure that we - // bail before trying to deref a bad IP obtained this way in the previous - // frame. - size_t module_index = - GetModuleIndex(rip, current_modules, profile_module_index); - if (module_index == StackSamplingProfiler::Frame::kUnknownModuleIndex) { - return false; - } - - callback(static_cast<uintptr_t>(rip), module_index); - - if (!continue_unwind(&unwind_cursor)) - return false; - - step_result = unw_step(&unwind_cursor); - } while (step_result > 0); - - if (step_result != 0) - return false; - - return true; -} - -const char* LibSystemKernelName() { - static char path[PATH_MAX]; - static char* name = nullptr; - if (name) - return name; - - Dl_info info; - dladdr(reinterpret_cast<void*>(_exit), &info); - strlcpy(path, info.dli_fname, PATH_MAX); - name = path; - -#if !defined(ADDRESS_SANITIZER) - DCHECK_EQ(std::string(name), - std::string("/usr/lib/system/libsystem_kernel.dylib")); -#endif - return name; -} - -void GetSigtrampRange(uintptr_t* start, uintptr_t* end) { - uintptr_t address = reinterpret_cast<uintptr_t>(&_sigtramp); - DCHECK(address != 0); - - *start = address; - - unw_context_t context; - unw_cursor_t cursor; - unw_proc_info_t info; - - unw_getcontext(&context); - // Set the context's RIP to the beginning of sigtramp, - // +1 byte to work around a bug in 10.11 (crbug.com/764468). - context.data[16] = address + 1; - unw_init_local(&cursor, &context); - unw_get_proc_info(&cursor, &info); - - DCHECK_EQ(info.start_ip, address); - *end = info.end_ip; -} - -// Walks the stack represented by |thread_state|, calling back to the provided -// lambda for each frame. -template <typename StackFrameCallback, typename ContinueUnwindPredicate> -void WalkStack(const x86_thread_state64_t& thread_state, - std::vector<StackSamplingProfiler::Module>* current_modules, - std::vector<ModuleIndex>* profile_module_index, - const StackFrameCallback& callback, - const ContinueUnwindPredicate& continue_unwind) { - size_t frame_count = 0; - // This uses libunwind to walk the stack. libunwind is designed to be used for - // a thread to walk its own stack. This creates two problems. - - // Problem 1: There is no official way to create a unw_context other than to - // create it from the current state of the current thread's stack. To get - // around this, forge a context. A unw_context is just a copy of the 16 main - // registers followed by the instruction pointer, nothing more. - // Coincidentally, the first 17 items of the x86_thread_state64_t type are - // exactly those registers in exactly the same order, so just bulk copy them - // over. - unw_context_t unwind_context; - memcpy(&unwind_context, &thread_state, sizeof(uintptr_t) * 17); - bool result = - WalkStackFromContext(&unwind_context, &frame_count, current_modules, - profile_module_index, callback, continue_unwind); - - if (!result) - return; - - if (frame_count == 1) { - // Problem 2: Because libunwind is designed to be triggered by user code on - // their own thread, if it hits a library that has no unwind info for the - // function that is being executed, it just stops. This isn't a problem in - // the normal case, but in this case, it's quite possible that the stack - // being walked is stopped in a function that bridges to the kernel and thus - // is missing the unwind info. - - // For now, just unwind the single case where the thread is stopped in a - // function in libsystem_kernel. - uint64_t& rsp = unwind_context.data[7]; - uint64_t& rip = unwind_context.data[16]; - Dl_info info; - if (dladdr(reinterpret_cast<void*>(rip), &info) != 0 && - strcmp(info.dli_fname, LibSystemKernelName()) == 0) { - rip = *reinterpret_cast<uint64_t*>(rsp); - rsp += 8; - WalkStackFromContext(&unwind_context, &frame_count, current_modules, - profile_module_index, callback, continue_unwind); - } - } -} - -// ScopedSuspendThread -------------------------------------------------------- - -// Suspends a thread for the lifetime of the object. -class ScopedSuspendThread { - public: - explicit ScopedSuspendThread(mach_port_t thread_port) - : thread_port_(thread_suspend(thread_port) == KERN_SUCCESS - ? thread_port - : MACH_PORT_NULL) {} - - ~ScopedSuspendThread() { - if (!was_successful()) - return; - - kern_return_t kr = thread_resume(thread_port_); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_resume"; - } - - bool was_successful() const { return thread_port_ != MACH_PORT_NULL; } - - private: - mach_port_t thread_port_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSuspendThread); -}; - -// NativeStackSamplerMac ------------------------------------------------------ - -class NativeStackSamplerMac : public NativeStackSampler { - public: - NativeStackSamplerMac(mach_port_t thread_port, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate); - ~NativeStackSamplerMac() override; - - // StackSamplingProfiler::NativeStackSampler: - void ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) override; - void RecordStackSample(StackBuffer* stack_buffer, - StackSamplingProfiler::Sample* sample) override; - void ProfileRecordingStopped(StackBuffer* stack_buffer) override; - - private: - // Suspends the thread with |thread_port_|, copies its stack and resumes the - // thread, then records the stack frames and associated modules into |sample|. - void SuspendThreadAndRecordStack(StackBuffer* stack_buffer, - StackSamplingProfiler::Sample* sample); - - // Weak reference: Mach port for thread being profiled. - mach_port_t thread_port_; - - const AnnotateCallback annotator_; - - NativeStackSamplerTestDelegate* const test_delegate_; - - // The stack base address corresponding to |thread_handle_|. - const void* const thread_stack_base_address_; - - // Weak. Points to the modules associated with the profile being recorded - // between ProfileRecordingStarting() and ProfileRecordingStopped(). - std::vector<StackSamplingProfiler::Module>* current_modules_ = nullptr; - - // Maps a module's address range to the corresponding Module's index within - // current_modules_. - std::vector<ModuleIndex> profile_module_index_; - - // The address range of |_sigtramp|, the signal trampoline function. - uintptr_t sigtramp_start_; - uintptr_t sigtramp_end_; - - DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerMac); -}; - -NativeStackSamplerMac::NativeStackSamplerMac( - mach_port_t thread_port, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate) - : thread_port_(thread_port), - annotator_(annotator), - test_delegate_(test_delegate), - thread_stack_base_address_( - pthread_get_stackaddr_np(pthread_from_mach_thread_np(thread_port))) { - DCHECK(annotator_); - - GetSigtrampRange(&sigtramp_start_, &sigtramp_end_); - // This class suspends threads, and those threads might be suspended in dyld. - // Therefore, for all the system functions that might be linked in dynamically - // that are used while threads are suspended, make calls to them to make sure - // that they are linked up. - x86_thread_state64_t thread_state; - GetThreadState(thread_port_, &thread_state); -} - -NativeStackSamplerMac::~NativeStackSamplerMac() {} - -void NativeStackSamplerMac::ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) { - current_modules_ = modules; - profile_module_index_.clear(); -} - -void NativeStackSamplerMac::RecordStackSample( - StackBuffer* stack_buffer, - StackSamplingProfiler::Sample* sample) { - DCHECK(current_modules_); - - SuspendThreadAndRecordStack(stack_buffer, sample); -} - -void NativeStackSamplerMac::ProfileRecordingStopped(StackBuffer* stack_buffer) { - current_modules_ = nullptr; -} - -void NativeStackSamplerMac::SuspendThreadAndRecordStack( - StackBuffer* stack_buffer, - StackSamplingProfiler::Sample* sample) { - x86_thread_state64_t thread_state; - - // Copy the stack. - - uintptr_t new_stack_top = 0; - { - // IMPORTANT NOTE: Do not do ANYTHING in this in this scope that might - // allocate memory, including indirectly via use of DCHECK/CHECK or other - // logging statements. Otherwise this code can deadlock on heap locks in the - // default heap acquired by the target thread before it was suspended. - ScopedSuspendThread suspend_thread(thread_port_); - if (!suspend_thread.was_successful()) - return; - - if (!GetThreadState(thread_port_, &thread_state)) - return; - uintptr_t stack_top = - reinterpret_cast<uintptr_t>(thread_stack_base_address_); - uintptr_t stack_bottom = thread_state.__rsp; - if (stack_bottom >= stack_top) - return; - uintptr_t stack_size = stack_top - stack_bottom; - - if (stack_size > stack_buffer->size()) - return; - - (*annotator_)(sample); - - CopyStackAndRewritePointers( - reinterpret_cast<uintptr_t*>(stack_buffer->buffer()), - reinterpret_cast<uintptr_t*>(stack_bottom), - reinterpret_cast<uintptr_t*>(stack_top), &thread_state); - - new_stack_top = - reinterpret_cast<uintptr_t>(stack_buffer->buffer()) + stack_size; - } // ScopedSuspendThread - - if (test_delegate_) - test_delegate_->OnPreStackWalk(); - - // Walk the stack and record it. - - // Reserve enough memory for most stacks, to avoid repeated allocations. - // Approximately 99.9% of recorded stacks are 128 frames or fewer. - sample->frames.reserve(128); - - auto* current_modules = current_modules_; - auto* profile_module_index = &profile_module_index_; - - // Avoid an out-of-bounds read bug in libunwind that can crash us in some - // circumstances. If we're subject to that case, just record the first frame - // and bail. See MayTriggerUnwInitLocalCrash for details. - uintptr_t rip = thread_state.__rip; - if (MayTriggerUnwInitLocalCrash(rip)) { - sample->frames.emplace_back( - rip, GetModuleIndex(rip, current_modules, profile_module_index)); - return; - } - - const auto continue_predicate = [this, - new_stack_top](unw_cursor_t* unwind_cursor) { - // Don't continue if we're in sigtramp. Unwinding this from another thread - // is very fragile. It's a complex DWARF unwind that needs to restore the - // entire thread context which was saved by the kernel when the interrupt - // occurred. - unw_word_t rip; - unw_get_reg(unwind_cursor, UNW_REG_IP, &rip); - if (rip >= sigtramp_start_ && rip < sigtramp_end_) - return false; - - // Don't continue if rbp appears to be invalid (due to a previous bad - // unwind). - return HasValidRbp(unwind_cursor, new_stack_top); - }; - - WalkStack(thread_state, current_modules, profile_module_index, - [sample, current_modules, profile_module_index]( - uintptr_t frame_ip, size_t module_index) { - sample->frames.emplace_back(frame_ip, module_index); - }, - continue_predicate); -} - -} // namespace - -std::unique_ptr<NativeStackSampler> NativeStackSampler::Create( - PlatformThreadId thread_id, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate) { - return std::make_unique<NativeStackSamplerMac>(thread_id, annotator, - test_delegate); -} - -size_t NativeStackSampler::GetStackBufferSize() { - // In platform_thread_mac's GetDefaultThreadStackSize(), RLIMIT_STACK is used - // for all stacks, not just the main thread's, so it is good for use here. - struct rlimit stack_rlimit; - if (getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 && - stack_rlimit.rlim_cur != RLIM_INFINITY) { - return stack_rlimit.rlim_cur; - } - - // If getrlimit somehow fails, return the default macOS main thread stack size - // of 8 MB (DFLSSIZ in <i386/vmparam.h>) with extra wiggle room. - return 12 * 1024 * 1024; -} - -} // namespace base
diff --git a/base/profiler/native_stack_sampler_posix.cc b/base/profiler/native_stack_sampler_posix.cc deleted file mode 100644 index 1055d44..0000000 --- a/base/profiler/native_stack_sampler_posix.cc +++ /dev/null
@@ -1,20 +0,0 @@ -// 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/profiler/native_stack_sampler.h" - -namespace base { - -std::unique_ptr<NativeStackSampler> NativeStackSampler::Create( - PlatformThreadId thread_id, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate) { - return std::unique_ptr<NativeStackSampler>(); -} - -size_t NativeStackSampler::GetStackBufferSize() { - return 0; -} - -} // namespace base
diff --git a/base/profiler/native_stack_sampler_win.cc b/base/profiler/native_stack_sampler_win.cc deleted file mode 100644 index b53197d..0000000 --- a/base/profiler/native_stack_sampler_win.cc +++ /dev/null
@@ -1,562 +0,0 @@ -// 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/profiler/native_stack_sampler.h" - -#include <objbase.h> -#include <windows.h> -#include <stddef.h> -#include <winternl.h> - -#include <cstdlib> -#include <map> -#include <memory> -#include <utility> -#include <vector> - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/profiler/win32_stack_frame_unwinder.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "base/win/pe_image.h" -#include "base/win/scoped_handle.h" - -namespace base { - -// Stack recording functions -------------------------------------------------- - -namespace { - -// The thread environment block internal type. -struct TEB { - NT_TIB Tib; - // Rest of struct is ignored. -}; - -// Returns the thread environment block pointer for |thread_handle|. -const TEB* GetThreadEnvironmentBlock(HANDLE thread_handle) { - // Define the internal types we need to invoke NtQueryInformationThread. - enum THREAD_INFORMATION_CLASS { ThreadBasicInformation }; - - struct CLIENT_ID { - HANDLE UniqueProcess; - HANDLE UniqueThread; - }; - - struct THREAD_BASIC_INFORMATION { - NTSTATUS ExitStatus; - TEB* Teb; - CLIENT_ID ClientId; - KAFFINITY AffinityMask; - LONG Priority; - LONG BasePriority; - }; - - using NtQueryInformationThreadFunction = - NTSTATUS (WINAPI*)(HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG, - PULONG); - - const NtQueryInformationThreadFunction nt_query_information_thread = - reinterpret_cast<NtQueryInformationThreadFunction>( - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), - "NtQueryInformationThread")); - if (!nt_query_information_thread) - return nullptr; - - THREAD_BASIC_INFORMATION basic_info = {0}; - NTSTATUS status = - nt_query_information_thread(thread_handle, ThreadBasicInformation, - &basic_info, sizeof(THREAD_BASIC_INFORMATION), - nullptr); - if (status != 0) - return nullptr; - - return basic_info.Teb; -} - -#if defined(_WIN64) -// If the value at |pointer| points to the original stack, rewrite it to point -// to the corresponding location in the copied stack. -void RewritePointerIfInOriginalStack(uintptr_t top, uintptr_t bottom, - void* stack_copy, const void** pointer) { - const uintptr_t value = reinterpret_cast<uintptr_t>(*pointer); - if (value >= bottom && value < top) { - *pointer = reinterpret_cast<const void*>( - static_cast<unsigned char*>(stack_copy) + (value - bottom)); - } -} -#endif - -void CopyMemoryFromStack(void* to, const void* from, size_t length) - NO_SANITIZE("address") { -#if defined(ADDRESS_SANITIZER) - // The following loop is an inlined version of memcpy. The code must be - // inlined to avoid instrumentation when using ASAN (memory sanitizer). The - // stack profiler is generating false positive when walking the stack. - for (size_t pos = 0; pos < length; ++pos) - reinterpret_cast<char*>(to)[pos] = reinterpret_cast<const char*>(from)[pos]; -#else - std::memcpy(to, from, length); -#endif -} - -// Rewrites possible pointers to locations within the stack to point to the -// corresponding locations in the copy, and rewrites the non-volatile registers -// in |context| likewise. This is necessary to handle stack frames with dynamic -// stack allocation, where a pointer to the beginning of the dynamic allocation -// area is stored on the stack and/or in a non-volatile register. -// -// Eager rewriting of anything that looks like a pointer to the stack, as done -// in this function, does not adversely affect the stack unwinding. The only -// other values on the stack the unwinding depends on are return addresses, -// which should not point within the stack memory. The rewriting is guaranteed -// to catch all pointers because the stacks are guaranteed by the ABI to be -// sizeof(void*) aligned. -// -// Note: this function must not access memory in the original stack as it may -// have been changed or deallocated by this point. This is why |top| and -// |bottom| are passed as uintptr_t. -void RewritePointersToStackMemory(uintptr_t top, uintptr_t bottom, - CONTEXT* context, void* stack_copy) { -#if defined(_WIN64) - DWORD64 CONTEXT::* const nonvolatile_registers[] = { - &CONTEXT::R12, - &CONTEXT::R13, - &CONTEXT::R14, - &CONTEXT::R15, - &CONTEXT::Rdi, - &CONTEXT::Rsi, - &CONTEXT::Rbx, - &CONTEXT::Rbp, - &CONTEXT::Rsp - }; - - // Rewrite pointers in the context. - for (size_t i = 0; i < arraysize(nonvolatile_registers); ++i) { - DWORD64* const reg = &(context->*nonvolatile_registers[i]); - RewritePointerIfInOriginalStack(top, bottom, stack_copy, - reinterpret_cast<const void**>(reg)); - } - - // Rewrite pointers on the stack. - const void** start = reinterpret_cast<const void**>(stack_copy); - const void** end = reinterpret_cast<const void**>( - reinterpret_cast<char*>(stack_copy) + (top - bottom)); - for (const void** loc = start; loc < end; ++loc) - RewritePointerIfInOriginalStack(top, bottom, stack_copy, loc); -#endif -} - -// Movable type representing a recorded stack frame. -struct RecordedFrame { - RecordedFrame() {} - - RecordedFrame(RecordedFrame&& other) - : instruction_pointer(other.instruction_pointer), - module(std::move(other.module)) { - } - - RecordedFrame& operator=(RecordedFrame&& other) { - instruction_pointer = other.instruction_pointer; - module = std::move(other.module); - return *this; - } - - const void* instruction_pointer; - ScopedModuleHandle module; - - private: - DISALLOW_COPY_AND_ASSIGN(RecordedFrame); -}; - -// Walks the stack represented by |context| from the current frame downwards, -// recording the instruction pointer and associated module for each frame in -// |stack|. -void RecordStack(CONTEXT* context, std::vector<RecordedFrame>* stack) { -#ifdef _WIN64 - DCHECK(stack->empty()); - - // Reserve enough memory for most stacks, to avoid repeated - // allocations. Approximately 99.9% of recorded stacks are 128 frames or - // fewer. - stack->reserve(128); - - Win32StackFrameUnwinder frame_unwinder; - while (context->Rip) { - const void* instruction_pointer = - reinterpret_cast<const void*>(context->Rip); - ScopedModuleHandle module; - if (!frame_unwinder.TryUnwind(context, &module)) - return; - RecordedFrame frame; - frame.instruction_pointer = instruction_pointer; - frame.module = std::move(module); - stack->push_back(std::move(frame)); - } -#endif -} - -// Gets the unique build ID for a module. Windows build IDs are created by a -// concatenation of a GUID and AGE fields found in the headers of a module. The -// GUID is stored in the first 16 bytes and the AGE is stored in the last 4 -// bytes. Returns the empty string if the function fails to get the build ID. -// -// Example: -// dumpbin chrome.exe /headers | find "Format:" -// ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ... -// -// The resulting buildID string of this instance of chrome.exe is -// "16B2A4281DED442E9A36FCE8CBD2972610". -// -// Note that the AGE field is encoded in decimal, not hex. -std::string GetBuildIDForModule(HMODULE module_handle) { - GUID guid; - DWORD age; - win::PEImage(module_handle).GetDebugId(&guid, &age, /* pdb_file= */ nullptr); - const int kGUIDSize = 39; - std::wstring build_id; - int result = - ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); - if (result != kGUIDSize) - return std::string(); - RemoveChars(build_id, L"{}-", &build_id); - build_id += StringPrintf(L"%d", age); - return WideToUTF8(build_id); -} - -// ScopedDisablePriorityBoost ------------------------------------------------- - -// Disables priority boost on a thread for the lifetime of the object. -class ScopedDisablePriorityBoost { - public: - ScopedDisablePriorityBoost(HANDLE thread_handle); - ~ScopedDisablePriorityBoost(); - - private: - HANDLE thread_handle_; - BOOL got_previous_boost_state_; - BOOL boost_state_was_disabled_; - - DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost); -}; - -ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle) - : thread_handle_(thread_handle), - got_previous_boost_state_(false), - boost_state_was_disabled_(false) { - got_previous_boost_state_ = - ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_); - if (got_previous_boost_state_) { - // Confusingly, TRUE disables priority boost. - ::SetThreadPriorityBoost(thread_handle_, TRUE); - } -} - -ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() { - if (got_previous_boost_state_) - ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_); -} - -// ScopedSuspendThread -------------------------------------------------------- - -// Suspends a thread for the lifetime of the object. -class ScopedSuspendThread { - public: - ScopedSuspendThread(HANDLE thread_handle); - ~ScopedSuspendThread(); - - bool was_successful() const { return was_successful_; } - - private: - HANDLE thread_handle_; - bool was_successful_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSuspendThread); -}; - -ScopedSuspendThread::ScopedSuspendThread(HANDLE thread_handle) - : thread_handle_(thread_handle), - was_successful_(::SuspendThread(thread_handle) != - static_cast<DWORD>(-1)) {} - -ScopedSuspendThread::~ScopedSuspendThread() { - if (!was_successful_) - return; - - // Disable the priority boost that the thread would otherwise receive on - // resume. We do this to avoid artificially altering the dynamics of the - // executing application any more than we already are by suspending and - // resuming the thread. - // - // Note that this can racily disable a priority boost that otherwise would - // have been given to the thread, if the thread is waiting on other wait - // conditions at the time of SuspendThread and those conditions are satisfied - // before priority boost is reenabled. The measured length of this window is - // ~100us, so this should occur fairly rarely. - ScopedDisablePriorityBoost disable_priority_boost(thread_handle_); - bool resume_thread_succeeded = - ::ResumeThread(thread_handle_) != static_cast<DWORD>(-1); - CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); -} - -// Tests whether |stack_pointer| points to a location in the guard page. -// -// IMPORTANT NOTE: This function is invoked while the target thread is -// suspended so it must not do any allocation from the default heap, including -// indirectly via use of DCHECK/CHECK or other logging statements. Otherwise -// this code can deadlock on heap locks in the default heap acquired by the -// target thread before it was suspended. -bool PointsToGuardPage(uintptr_t stack_pointer) { - MEMORY_BASIC_INFORMATION memory_info; - SIZE_T result = ::VirtualQuery(reinterpret_cast<LPCVOID>(stack_pointer), - &memory_info, - sizeof(memory_info)); - return result != 0 && (memory_info.Protect & PAGE_GUARD); -} - -// Suspends the thread with |thread_handle|, copies its stack and resumes the -// thread, then records the stack frames and associated modules into |stack|. -// -// IMPORTANT NOTE: No allocations from the default heap may occur in the -// ScopedSuspendThread scope, including indirectly via use of DCHECK/CHECK or -// other logging statements. Otherwise this code can deadlock on heap locks in -// the default heap acquired by the target thread before it was suspended. -void SuspendThreadAndRecordStack( - HANDLE thread_handle, - const void* base_address, - void* stack_copy_buffer, - size_t stack_copy_buffer_size, - std::vector<RecordedFrame>* stack, - NativeStackSampler::AnnotateCallback annotator, - StackSamplingProfiler::Sample* sample, - NativeStackSamplerTestDelegate* test_delegate) { - DCHECK(stack->empty()); - - CONTEXT thread_context = {0}; - thread_context.ContextFlags = CONTEXT_FULL; - // The stack bounds are saved to uintptr_ts for use outside - // ScopedSuspendThread, as the thread's memory is not safe to dereference - // beyond that point. - const uintptr_t top = reinterpret_cast<uintptr_t>(base_address); - uintptr_t bottom = 0u; - - { - ScopedSuspendThread suspend_thread(thread_handle); - - if (!suspend_thread.was_successful()) - return; - - if (!::GetThreadContext(thread_handle, &thread_context)) - return; -#if defined(_WIN64) - bottom = thread_context.Rsp; -#else - bottom = thread_context.Esp; -#endif - - if ((top - bottom) > stack_copy_buffer_size) - return; - - // Dereferencing a pointer in the guard page in a thread that doesn't own - // the stack results in a STATUS_GUARD_PAGE_VIOLATION exception and a crash. - // This occurs very rarely, but reliably over the population. - if (PointsToGuardPage(bottom)) - return; - - (*annotator)(sample); - - CopyMemoryFromStack(stack_copy_buffer, - reinterpret_cast<const void*>(bottom), top - bottom); - } - - if (test_delegate) - test_delegate->OnPreStackWalk(); - - RewritePointersToStackMemory(top, bottom, &thread_context, stack_copy_buffer); - - RecordStack(&thread_context, stack); -} - -// NativeStackSamplerWin ------------------------------------------------------ - -class NativeStackSamplerWin : public NativeStackSampler { - public: - NativeStackSamplerWin(win::ScopedHandle thread_handle, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate); - ~NativeStackSamplerWin() override; - - // StackSamplingProfiler::NativeStackSampler: - void ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) override; - void RecordStackSample(StackBuffer* stack_buffer, - StackSamplingProfiler::Sample* sample) override; - void ProfileRecordingStopped(StackBuffer* stack_buffer) override; - - private: - // Attempts to query the module filename, base address, and id for - // |module_handle|, and store them in |module|. Returns true if it succeeded. - static bool GetModuleForHandle(HMODULE module_handle, - StackSamplingProfiler::Module* module); - - // Gets the index for the Module corresponding to |module_handle| in - // |modules|, adding it if it's not already present. Returns - // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be - // determined for |module|. - size_t GetModuleIndex(HMODULE module_handle, - std::vector<StackSamplingProfiler::Module>* modules); - - // Copies the information represented by |stack| into |sample| and |modules|. - void CopyToSample(const std::vector<RecordedFrame>& stack, - StackSamplingProfiler::Sample* sample, - std::vector<StackSamplingProfiler::Module>* modules); - - win::ScopedHandle thread_handle_; - - const AnnotateCallback annotator_; - - NativeStackSamplerTestDelegate* const test_delegate_; - - // The stack base address corresponding to |thread_handle_|. - const void* const thread_stack_base_address_; - - // Weak. Points to the modules associated with the profile being recorded - // between ProfileRecordingStarting() and ProfileRecordingStopped(). - std::vector<StackSamplingProfiler::Module>* current_modules_; - - // Maps a module handle to the corresponding Module's index within - // current_modules_. - std::map<HMODULE, size_t> profile_module_index_; - - DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin); -}; - -NativeStackSamplerWin::NativeStackSamplerWin( - win::ScopedHandle thread_handle, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate) - : thread_handle_(thread_handle.Take()), - annotator_(annotator), - test_delegate_(test_delegate), - thread_stack_base_address_( - GetThreadEnvironmentBlock(thread_handle_.Get())->Tib.StackBase) { - DCHECK(annotator_); -} - -NativeStackSamplerWin::~NativeStackSamplerWin() { -} - -void NativeStackSamplerWin::ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) { - current_modules_ = modules; - profile_module_index_.clear(); -} - -void NativeStackSamplerWin::RecordStackSample( - StackBuffer* stack_buffer, - StackSamplingProfiler::Sample* sample) { - DCHECK(stack_buffer); - DCHECK(current_modules_); - - std::vector<RecordedFrame> stack; - SuspendThreadAndRecordStack(thread_handle_.Get(), thread_stack_base_address_, - stack_buffer->buffer(), stack_buffer->size(), - &stack, annotator_, sample, test_delegate_); - CopyToSample(stack, sample, current_modules_); -} - -void NativeStackSamplerWin::ProfileRecordingStopped(StackBuffer* stack_buffer) { - current_modules_ = nullptr; -} - -// static -bool NativeStackSamplerWin::GetModuleForHandle( - HMODULE module_handle, - StackSamplingProfiler::Module* module) { - wchar_t module_name[MAX_PATH]; - DWORD result_length = - GetModuleFileName(module_handle, module_name, arraysize(module_name)); - if (result_length == 0) - return false; - - module->filename = base::FilePath(module_name); - - module->base_address = reinterpret_cast<uintptr_t>(module_handle); - - module->id = GetBuildIDForModule(module_handle); - if (module->id.empty()) - return false; - - return true; -} - -size_t NativeStackSamplerWin::GetModuleIndex( - HMODULE module_handle, - std::vector<StackSamplingProfiler::Module>* modules) { - if (!module_handle) - return StackSamplingProfiler::Frame::kUnknownModuleIndex; - - auto loc = profile_module_index_.find(module_handle); - if (loc == profile_module_index_.end()) { - StackSamplingProfiler::Module module; - if (!GetModuleForHandle(module_handle, &module)) - return StackSamplingProfiler::Frame::kUnknownModuleIndex; - modules->push_back(module); - loc = profile_module_index_.insert(std::make_pair( - module_handle, modules->size() - 1)).first; - } - - return loc->second; -} - -void NativeStackSamplerWin::CopyToSample( - const std::vector<RecordedFrame>& stack, - StackSamplingProfiler::Sample* sample, - std::vector<StackSamplingProfiler::Module>* modules) { - sample->frames.clear(); - sample->frames.reserve(stack.size()); - - for (const RecordedFrame& frame : stack) { - sample->frames.push_back(StackSamplingProfiler::Frame( - reinterpret_cast<uintptr_t>(frame.instruction_pointer), - GetModuleIndex(frame.module.Get(), modules))); - } -} - -} // namespace - -std::unique_ptr<NativeStackSampler> NativeStackSampler::Create( - PlatformThreadId thread_id, - AnnotateCallback annotator, - NativeStackSamplerTestDelegate* test_delegate) { -#if _WIN64 - // Get the thread's handle. - HANDLE thread_handle = ::OpenThread( - THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, - FALSE, - thread_id); - - if (thread_handle) { - return std::unique_ptr<NativeStackSampler>(new NativeStackSamplerWin( - win::ScopedHandle(thread_handle), annotator, test_delegate)); - } -#endif - return std::unique_ptr<NativeStackSampler>(); -} - -size_t NativeStackSampler::GetStackBufferSize() { - // The default Win32 reserved stack size is 1 MB and Chrome Windows threads - // currently always use the default, but this allows for expansion if it - // occurs. The size beyond the actual stack size consists of unallocated - // virtual memory pages so carries little cost (just a bit of wasted address - // space). - return 2 << 20; // 2 MiB -} - -} // namespace base
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc deleted file mode 100644 index a8cddf0..0000000 --- a/base/profiler/stack_sampling_profiler.cc +++ /dev/null
@@ -1,890 +0,0 @@ -// 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/profiler/stack_sampling_profiler.h" - -#include <algorithm> -#include <map> -#include <utility> - -#include "base/atomic_sequence_num.h" -#include "base/atomicops.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/lazy_instance.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/singleton.h" -#include "base/profiler/native_stack_sampler.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread.h" -#include "base/threading/thread_restrictions.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/timer/elapsed_timer.h" - -namespace base { - -namespace { - -// This value is used to initialize the WaitableEvent object. This MUST BE set -// to MANUAL for correct operation of the IsSignaled() call in Start(). See the -// comment there for why. -constexpr WaitableEvent::ResetPolicy kResetPolicy = - WaitableEvent::ResetPolicy::MANUAL; - -// This value is used when there is no collection in progress and thus no ID -// for referencing the active collection to the SamplingThread. -const int NULL_PROFILER_ID = -1; - -void ChangeAtomicFlags(subtle::Atomic32* flags, - subtle::Atomic32 set, - subtle::Atomic32 clear) { - DCHECK(set != 0 || clear != 0); - DCHECK_EQ(0, set & clear); - - subtle::Atomic32 bits = subtle::NoBarrier_Load(flags); - while (true) { - subtle::Atomic32 existing = - subtle::NoBarrier_CompareAndSwap(flags, bits, (bits | set) & ~clear); - if (existing == bits) - break; - bits = existing; - } -} - -} // namespace - -// StackSamplingProfiler::Module ---------------------------------------------- - -StackSamplingProfiler::Module::Module() : base_address(0u) {} -StackSamplingProfiler::Module::Module(uintptr_t base_address, - const std::string& id, - const FilePath& filename) - : base_address(base_address), id(id), filename(filename) {} - -StackSamplingProfiler::Module::~Module() = default; - -// StackSamplingProfiler::Frame ----------------------------------------------- - -StackSamplingProfiler::Frame::Frame(uintptr_t instruction_pointer, - size_t module_index) - : instruction_pointer(instruction_pointer), module_index(module_index) {} - -StackSamplingProfiler::Frame::~Frame() = default; - -StackSamplingProfiler::Frame::Frame() - : instruction_pointer(0), module_index(kUnknownModuleIndex) { -} - -// StackSamplingProfiler::Sample ---------------------------------------------- - -StackSamplingProfiler::Sample::Sample() = default; - -StackSamplingProfiler::Sample::Sample(const Sample& sample) = default; - -StackSamplingProfiler::Sample::~Sample() = default; - -StackSamplingProfiler::Sample::Sample(const Frame& frame) { - frames.push_back(std::move(frame)); -} - -StackSamplingProfiler::Sample::Sample(const std::vector<Frame>& frames) - : frames(frames) {} - -// StackSamplingProfiler::CallStackProfile ------------------------------------ - -StackSamplingProfiler::CallStackProfile::CallStackProfile() = default; - -StackSamplingProfiler::CallStackProfile::CallStackProfile( - CallStackProfile&& other) = default; - -StackSamplingProfiler::CallStackProfile::~CallStackProfile() = default; - -StackSamplingProfiler::CallStackProfile& -StackSamplingProfiler::CallStackProfile::operator=(CallStackProfile&& other) = - default; - -StackSamplingProfiler::CallStackProfile -StackSamplingProfiler::CallStackProfile::CopyForTesting() const { - return CallStackProfile(*this); -} - -StackSamplingProfiler::CallStackProfile::CallStackProfile( - const CallStackProfile& other) = default; - -// StackSamplingProfiler::SamplingThread -------------------------------------- - -class StackSamplingProfiler::SamplingThread : public Thread { - public: - class TestAPI { - public: - // Reset the existing sampler. This will unfortunately create the object - // unnecessarily if it doesn't already exist but there's no way around that. - static void Reset(); - - // Disables inherent idle-shutdown behavior. - static void DisableIdleShutdown(); - - // Begins an idle shutdown as if the idle-timer had expired and wait for - // it to execute. Since the timer would have only been started at a time - // when the sampling thread actually was idle, this must be called only - // when it is known that there are no active sampling threads. If - // |simulate_intervening_add| is true then, when executed, the shutdown - // task will believe that a new collection has been added since it was - // posted. - static void ShutdownAssumingIdle(bool simulate_intervening_add); - - private: - // Calls the sampling threads ShutdownTask and then signals an event. - static void ShutdownTaskAndSignalEvent(SamplingThread* sampler, - int add_events, - WaitableEvent* event); - }; - - struct CollectionContext { - CollectionContext(int profiler_id, - PlatformThreadId target, - const SamplingParams& params, - const CompletedCallback& callback, - WaitableEvent* finished, - std::unique_ptr<NativeStackSampler> sampler) - : profiler_id(profiler_id), - target(target), - params(params), - callback(callback), - finished(finished), - native_sampler(std::move(sampler)) {} - ~CollectionContext() = default; - - // An identifier for the profiler associated with this collection, used to - // uniquely identify the collection to outside interests. - const int profiler_id; - - const PlatformThreadId target; // ID of The thread being sampled. - const SamplingParams params; // Information about how to sample. - const CompletedCallback callback; // Callback made when sampling complete. - WaitableEvent* const finished; // Signaled when all sampling complete. - - // Platform-specific module that does the actual sampling. - std::unique_ptr<NativeStackSampler> native_sampler; - - // The absolute time for the next sample. - Time next_sample_time; - - // The time that a profile was started, for calculating the total duration. - Time profile_start_time; - - // Counters that indicate the current position along the acquisition. - int burst = 0; - int sample = 0; - - // The collected stack samples. The active profile is always at the back(). - CallStackProfiles profiles; - - // Sequence number for generating new profiler ids. - static AtomicSequenceNumber next_profiler_id; - }; - - // Gets the single instance of this class. - static SamplingThread* GetInstance(); - - // Adds a new CollectionContext to the thread. This can be called externally - // from any thread. This returns an ID that can later be used to stop - // the sampling. - int Add(std::unique_ptr<CollectionContext> collection); - - // Removes an active collection based on its ID, forcing it to run its - // callback if any data has been collected. This can be called externally - // from any thread. - void Remove(int id); - - private: - friend class TestAPI; - friend struct DefaultSingletonTraits<SamplingThread>; - - // The different states in which the sampling-thread can be. - enum ThreadExecutionState { - // The thread is not running because it has never been started. It will be - // started when a sampling request is received. - NOT_STARTED, - - // The thread is running and processing tasks. This is the state when any - // sampling requests are active and during the "idle" period afterward - // before the thread is stopped. - RUNNING, - - // Once all sampling requests have finished and the "idle" period has - // expired, the thread will be set to this state and its shutdown - // initiated. A call to Stop() must be made to ensure the previous thread - // has completely exited before calling Start() and moving back to the - // RUNNING state. - EXITING, - }; - - SamplingThread(); - ~SamplingThread() override; - - // Get task runner that is usable from the outside. - scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd(); - scoped_refptr<SingleThreadTaskRunner> GetTaskRunner( - ThreadExecutionState* out_state); - - // Get task runner that is usable from the sampling thread itself. - scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread(); - - // Finishes a collection and reports collected data via callback. The - // collection's |finished| waitable event will be signalled. The |collection| - // should already have been removed from |active_collections_| by the caller, - // as this is needed to avoid flakyness in unit tests. - void FinishCollection(CollectionContext* collection); - - // Records a single sample of a collection. - void RecordSample(CollectionContext* collection); - - // Check if the sampling thread is idle and begin a shutdown if it is. - void ScheduleShutdownIfIdle(); - - // These methods are tasks that get posted to the internal message queue. - void AddCollectionTask(std::unique_ptr<CollectionContext> collection); - void RemoveCollectionTask(int id); - void PerformCollectionTask(int id); - void ShutdownTask(int add_events); - - // Updates the |next_sample_time| time based on configured parameters. - // Returns true if there is a next sample or false if sampling is complete. - bool UpdateNextSampleTime(CollectionContext* collection); - - // Thread: - void CleanUp() override; - - // A stack-buffer used by the native sampler for its work. This buffer can - // be re-used for multiple native sampler objects so long as the API calls - // that take it are not called concurrently. - std::unique_ptr<NativeStackSampler::StackBuffer> stack_buffer_; - - // A map of IDs to collection contexts. Because this class is a singleton - // that is never destroyed, context objects will never be destructed except - // by explicit action. Thus, it's acceptable to pass unretained pointers - // to these objects when posting tasks. - std::map<int, std::unique_ptr<CollectionContext>> active_collections_; - - // State maintained about the current execution (or non-execution) of - // the thread. This state must always be accessed while holding the - // lock. A copy of the task-runner is maintained here for use by any - // calling thread; this is necessary because Thread's accessor for it is - // not itself thread-safe. The lock is also used to order calls to the - // Thread API (Start, Stop, StopSoon, & DetachFromSequence) so that - // multiple threads may make those calls. - Lock thread_execution_state_lock_; // Protects all thread_execution_state_* - ThreadExecutionState thread_execution_state_ = NOT_STARTED; - scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_; - bool thread_execution_state_disable_idle_shutdown_for_testing_ = false; - - // A counter that notes adds of new collection requests. It is incremented - // when changes occur so that delayed shutdown tasks are able to detect if - // samething new has happened while it was waiting. Like all "execution_state" - // vars, this must be accessed while holding |thread_execution_state_lock_|. - int thread_execution_state_add_events_ = 0; - - DISALLOW_COPY_AND_ASSIGN(SamplingThread); -}; - -// static -void StackSamplingProfiler::SamplingThread::TestAPI::Reset() { - SamplingThread* sampler = SamplingThread::GetInstance(); - - ThreadExecutionState state; - { - AutoLock lock(sampler->thread_execution_state_lock_); - state = sampler->thread_execution_state_; - DCHECK(sampler->active_collections_.empty()); - } - - // Stop the thread and wait for it to exit. This has to be done through by - // the thread itself because it has taken ownership of its own lifetime. - if (state == RUNNING) { - ShutdownAssumingIdle(false); - state = EXITING; - } - // Make sure thread is cleaned up since state will be reset to NOT_STARTED. - if (state == EXITING) - sampler->Stop(); - - // Reset internal variables to the just-initialized state. - { - AutoLock lock(sampler->thread_execution_state_lock_); - sampler->thread_execution_state_ = NOT_STARTED; - sampler->thread_execution_state_task_runner_ = nullptr; - sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = false; - sampler->thread_execution_state_add_events_ = 0; - } -} - -// static -void StackSamplingProfiler::SamplingThread::TestAPI::DisableIdleShutdown() { - SamplingThread* sampler = SamplingThread::GetInstance(); - - { - AutoLock lock(sampler->thread_execution_state_lock_); - sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = true; - } -} - -// static -void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownAssumingIdle( - bool simulate_intervening_add) { - SamplingThread* sampler = SamplingThread::GetInstance(); - - ThreadExecutionState state; - scoped_refptr<SingleThreadTaskRunner> task_runner = - sampler->GetTaskRunner(&state); - DCHECK_EQ(RUNNING, state); - DCHECK(task_runner); - - int add_events; - { - AutoLock lock(sampler->thread_execution_state_lock_); - add_events = sampler->thread_execution_state_add_events_; - if (simulate_intervening_add) - ++sampler->thread_execution_state_add_events_; - } - - WaitableEvent executed(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - // PostTaskAndReply won't work because thread and associated message-loop may - // be shut down. - task_runner->PostTask( - FROM_HERE, BindOnce(&ShutdownTaskAndSignalEvent, Unretained(sampler), - add_events, Unretained(&executed))); - executed.Wait(); -} - -// static -void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownTaskAndSignalEvent( - SamplingThread* sampler, - int add_events, - WaitableEvent* event) { - sampler->ShutdownTask(add_events); - event->Signal(); -} - -AtomicSequenceNumber - StackSamplingProfiler::SamplingThread::CollectionContext::next_profiler_id; - -StackSamplingProfiler::SamplingThread::SamplingThread() - : Thread("StackSamplingProfiler") {} - -StackSamplingProfiler::SamplingThread::~SamplingThread() = default; - -StackSamplingProfiler::SamplingThread* -StackSamplingProfiler::SamplingThread::GetInstance() { - return Singleton<SamplingThread, LeakySingletonTraits<SamplingThread>>::get(); -} - -int StackSamplingProfiler::SamplingThread::Add( - std::unique_ptr<CollectionContext> collection) { - // This is not to be run on the sampling thread. - - int id = collection->profiler_id; - scoped_refptr<SingleThreadTaskRunner> task_runner = - GetOrCreateTaskRunnerForAdd(); - - task_runner->PostTask( - FROM_HERE, BindOnce(&SamplingThread::AddCollectionTask, Unretained(this), - std::move(collection))); - - return id; -} - -void StackSamplingProfiler::SamplingThread::Remove(int id) { - // This is not to be run on the sampling thread. - - ThreadExecutionState state; - scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state); - if (state != RUNNING) - return; - DCHECK(task_runner); - - // This can fail if the thread were to exit between acquisition of the task - // runner above and the call below. In that case, however, everything has - // stopped so there's no need to try to stop it. - task_runner->PostTask( - FROM_HERE, - BindOnce(&SamplingThread::RemoveCollectionTask, Unretained(this), id)); -} - -scoped_refptr<SingleThreadTaskRunner> -StackSamplingProfiler::SamplingThread::GetOrCreateTaskRunnerForAdd() { - AutoLock lock(thread_execution_state_lock_); - - // The increment of the "add events" count is why this method is to be only - // called from "add". - ++thread_execution_state_add_events_; - - if (thread_execution_state_ == RUNNING) { - DCHECK(thread_execution_state_task_runner_); - // This shouldn't be called from the sampling thread as it's inefficient. - // Use GetTaskRunnerOnSamplingThread() instead. - DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); - return thread_execution_state_task_runner_; - } - - if (thread_execution_state_ == EXITING) { - // StopSoon() was previously called to shut down the thread - // asynchonously. Stop() must now be called before calling Start() again to - // reset the thread state. - // - // We must allow blocking here to satisfy the Thread implementation, but in - // practice the Stop() call is unlikely to actually block. For this to - // happen a new profiling request would have to be made within the narrow - // window between StopSoon() and thread exit following the end of the 60 - // second idle period. - ScopedAllowBlocking allow_blocking; - Stop(); - } - - DCHECK(!stack_buffer_); - stack_buffer_ = NativeStackSampler::CreateStackBuffer(); - - // The thread is not running. Start it and get associated runner. The task- - // runner has to be saved for future use because though it can be used from - // any thread, it can be acquired via task_runner() only on the created - // thread and the thread that creates it (i.e. this thread) for thread-safety - // reasons which are alleviated in SamplingThread by gating access to it with - // the |thread_execution_state_lock_|. - Start(); - thread_execution_state_ = RUNNING; - thread_execution_state_task_runner_ = Thread::task_runner(); - - // Detach the sampling thread from the "sequence" (i.e. thread) that - // started it so that it can be self-managed or stopped by another thread. - DetachFromSequence(); - - return thread_execution_state_task_runner_; -} - -scoped_refptr<SingleThreadTaskRunner> -StackSamplingProfiler::SamplingThread::GetTaskRunner( - ThreadExecutionState* out_state) { - AutoLock lock(thread_execution_state_lock_); - if (out_state) - *out_state = thread_execution_state_; - if (thread_execution_state_ == RUNNING) { - // This shouldn't be called from the sampling thread as it's inefficient. - // Use GetTaskRunnerOnSamplingThread() instead. - DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); - DCHECK(thread_execution_state_task_runner_); - } else { - DCHECK(!thread_execution_state_task_runner_); - } - - return thread_execution_state_task_runner_; -} - -scoped_refptr<SingleThreadTaskRunner> -StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() { - // This should be called only from the sampling thread as it has limited - // accessibility. - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - return Thread::task_runner(); -} - -void StackSamplingProfiler::SamplingThread::FinishCollection( - CollectionContext* collection) { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - DCHECK_EQ(0u, active_collections_.count(collection->profiler_id)); - - // If there is no duration for the final profile (because it was stopped), - // calculate it now. - if (!collection->profiles.empty() && - collection->profiles.back().profile_duration == TimeDelta()) { - collection->profiles.back().profile_duration = - Time::Now() - collection->profile_start_time + - collection->params.sampling_interval; - } - - // Extract some information so callback and event-signalling can still be - // done after the collection has been removed from the list of "active" ones. - // This allows the the controlling object (and tests using it) to be confident - // that collection is fully finished when those things occur. - const CompletedCallback callback = collection->callback; - CallStackProfiles profiles = std::move(collection->profiles); - WaitableEvent* finished = collection->finished; - - // Run the associated callback, passing the collected profiles. - callback.Run(std::move(profiles)); - - // Signal that this collection is finished. - finished->Signal(); -} - -void StackSamplingProfiler::SamplingThread::RecordSample( - CollectionContext* collection) { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - DCHECK(collection->native_sampler); - - // If this is the first sample of a burst, a new Profile needs to be created - // and filled. - if (collection->sample == 0) { - collection->profiles.push_back(CallStackProfile()); - CallStackProfile& profile = collection->profiles.back(); - profile.sampling_period = collection->params.sampling_interval; - collection->profile_start_time = Time::Now(); - collection->native_sampler->ProfileRecordingStarting(&profile.modules); - } - - // The currently active profile being captured. - CallStackProfile& profile = collection->profiles.back(); - - // Record a single sample. - profile.samples.push_back(Sample()); - collection->native_sampler->RecordStackSample(stack_buffer_.get(), - &profile.samples.back()); - - // If this is the last sample of a burst, record the total time. - if (collection->sample == collection->params.samples_per_burst - 1) { - profile.profile_duration = Time::Now() - collection->profile_start_time + - collection->params.sampling_interval; - collection->native_sampler->ProfileRecordingStopped(stack_buffer_.get()); - } -} - -void StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle() { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - if (!active_collections_.empty()) - return; - - int add_events; - { - AutoLock lock(thread_execution_state_lock_); - if (thread_execution_state_disable_idle_shutdown_for_testing_) - return; - add_events = thread_execution_state_add_events_; - } - - GetTaskRunnerOnSamplingThread()->PostDelayedTask( - FROM_HERE, - BindOnce(&SamplingThread::ShutdownTask, Unretained(this), add_events), - TimeDelta::FromSeconds(60)); -} - -void StackSamplingProfiler::SamplingThread::AddCollectionTask( - std::unique_ptr<CollectionContext> collection) { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - const int profiler_id = collection->profiler_id; - const TimeDelta initial_delay = collection->params.initial_delay; - - active_collections_.insert( - std::make_pair(profiler_id, std::move(collection))); - - GetTaskRunnerOnSamplingThread()->PostDelayedTask( - FROM_HERE, - BindOnce(&SamplingThread::PerformCollectionTask, Unretained(this), - profiler_id), - initial_delay); - - // Another increment of "add events" serves to invalidate any pending - // shutdown tasks that may have been initiated between the Add() and this - // task running. - { - AutoLock lock(thread_execution_state_lock_); - ++thread_execution_state_add_events_; - } -} - -void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - auto found = active_collections_.find(id); - if (found == active_collections_.end()) - return; - - // Remove |collection| from |active_collections_|. - std::unique_ptr<CollectionContext> collection = std::move(found->second); - size_t count = active_collections_.erase(id); - DCHECK_EQ(1U, count); - - FinishCollection(collection.get()); - ScheduleShutdownIfIdle(); -} - -void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - auto found = active_collections_.find(id); - - // The task won't be found if it has been stopped. - if (found == active_collections_.end()) - return; - - CollectionContext* collection = found->second.get(); - - // Handle first-run with no "next time". - if (collection->next_sample_time == Time()) - collection->next_sample_time = Time::Now(); - - // Do the collection of a single sample. - RecordSample(collection); - - // Update the time of the next sample recording. - const bool collection_finished = !UpdateNextSampleTime(collection); - if (!collection_finished) { - bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask( - FROM_HERE, - BindOnce(&SamplingThread::PerformCollectionTask, Unretained(this), id), - std::max(collection->next_sample_time - Time::Now(), TimeDelta())); - DCHECK(success); - return; - } - - // Take ownership of |collection| and remove it from the map. If collection is - // to be restarted, a new collection task will be added below. - std::unique_ptr<CollectionContext> owned_collection = - std::move(found->second); - size_t count = active_collections_.erase(id); - DCHECK_EQ(1U, count); - - // All capturing has completed so finish the collection. - FinishCollection(collection); - ScheduleShutdownIfIdle(); -} - -void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - // Holding this lock ensures that any attempt to start another job will - // get postponed until |thread_execution_state_| is updated, thus eliminating - // the race in starting a new thread while the previous one is exiting. - AutoLock lock(thread_execution_state_lock_); - - // If the current count of creation requests doesn't match the passed count - // then other tasks have been created since this was posted. Abort shutdown. - if (thread_execution_state_add_events_ != add_events) - return; - - // There can be no new AddCollectionTasks at this point because creating - // those always increments "add events". There may be other requests, like - // Remove, but it's okay to schedule the thread to stop once they've been - // executed (i.e. "soon"). - DCHECK(active_collections_.empty()); - StopSoon(); - - // StopSoon will have set the owning sequence (again) so it must be detached - // (again) in order for Stop/Start to be called (again) should more work - // come in. Holding the |thread_execution_state_lock_| ensures the necessary - // happens-after with regard to this detach and future Thread API calls. - DetachFromSequence(); - - // Set the thread_state variable so the thread will be restarted when new - // work comes in. Remove the |thread_execution_state_task_runner_| to avoid - // confusion. - thread_execution_state_ = EXITING; - thread_execution_state_task_runner_ = nullptr; - stack_buffer_.reset(); -} - -bool StackSamplingProfiler::SamplingThread::UpdateNextSampleTime( - CollectionContext* collection) { - // This will keep a consistent average interval between samples but will - // result in constant series of acquisitions, thus nearly locking out the - // target thread, if the interval is smaller than the time it takes to - // actually acquire the sample. Anything sampling that quickly is going - // to be a problem anyway so don't worry about it. - if (++collection->sample < collection->params.samples_per_burst) { - collection->next_sample_time += collection->params.sampling_interval; - return true; - } - - if (++collection->burst < collection->params.bursts) { - collection->sample = 0; - collection->next_sample_time += collection->params.burst_interval; - return true; - } - - return false; -} - -void StackSamplingProfiler::SamplingThread::CleanUp() { - DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); - - // There should be no collections remaining when the thread stops. - DCHECK(active_collections_.empty()); - - // Let the parent clean up. - Thread::CleanUp(); -} - -// StackSamplingProfiler ------------------------------------------------------ - -// static -void StackSamplingProfiler::TestAPI::Reset() { - SamplingThread::TestAPI::Reset(); - ResetAnnotations(); -} - -// static -void StackSamplingProfiler::TestAPI::ResetAnnotations() { - subtle::NoBarrier_Store(&process_milestones_, 0u); -} - -// static -bool StackSamplingProfiler::TestAPI::IsSamplingThreadRunning() { - return SamplingThread::GetInstance()->IsRunning(); -} - -// static -void StackSamplingProfiler::TestAPI::DisableIdleShutdown() { - SamplingThread::TestAPI::DisableIdleShutdown(); -} - -// static -void StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown( - bool simulate_intervening_start) { - SamplingThread::TestAPI::ShutdownAssumingIdle(simulate_intervening_start); -} - -subtle::Atomic32 StackSamplingProfiler::process_milestones_ = 0; - -StackSamplingProfiler::StackSamplingProfiler( - const SamplingParams& params, - const CompletedCallback& callback, - NativeStackSamplerTestDelegate* test_delegate) - : StackSamplingProfiler(base::PlatformThread::CurrentId(), - params, - callback, - test_delegate) {} - -StackSamplingProfiler::StackSamplingProfiler( - PlatformThreadId thread_id, - const SamplingParams& params, - const CompletedCallback& callback, - NativeStackSamplerTestDelegate* test_delegate) - : thread_id_(thread_id), - params_(params), - completed_callback_(callback), - // The event starts "signaled" so code knows it's safe to start thread - // and "manual" so that it can be waited in multiple places. - profiling_inactive_(kResetPolicy, WaitableEvent::InitialState::SIGNALED), - profiler_id_(NULL_PROFILER_ID), - test_delegate_(test_delegate) {} - -StackSamplingProfiler::~StackSamplingProfiler() { - // Stop returns immediately but the shutdown runs asynchronously. There is a - // non-zero probability that one more sample will be taken after this call - // returns. - Stop(); - - // The behavior of sampling a thread that has exited is undefined and could - // cause Bad Things(tm) to occur. The safety model provided by this class is - // that an instance of this object is expected to live at least as long as - // the thread it is sampling. However, because the sampling is performed - // asynchronously by the SamplingThread, there is no way to guarantee this - // is true without waiting for it to signal that it has finished. - // - // The wait time should, at most, be only as long as it takes to collect one - // sample (~200us) or none at all if sampling has already completed. - ThreadRestrictions::ScopedAllowWait allow_wait; - profiling_inactive_.Wait(); -} - -void StackSamplingProfiler::Start() { - if (completed_callback_.is_null()) - return; - - std::unique_ptr<NativeStackSampler> native_sampler = - NativeStackSampler::Create(thread_id_, &RecordAnnotations, - test_delegate_); - - if (!native_sampler) - return; - - // The IsSignaled() check below requires that the WaitableEvent be manually - // reset, to avoid signaling the event in IsSignaled() itself. - static_assert(kResetPolicy == WaitableEvent::ResetPolicy::MANUAL, - "The reset policy must be set to MANUAL"); - - // If a previous profiling phase is still winding down, wait for it to - // complete. We can't use task posting for this coordination because the - // thread owning the profiler may not have a message loop. - if (!profiling_inactive_.IsSignaled()) - profiling_inactive_.Wait(); - profiling_inactive_.Reset(); - - DCHECK_EQ(NULL_PROFILER_ID, profiler_id_); - profiler_id_ = SamplingThread::GetInstance()->Add( - std::make_unique<SamplingThread::CollectionContext>( - SamplingThread::CollectionContext::next_profiler_id.GetNext(), - thread_id_, params_, completed_callback_, &profiling_inactive_, - std::move(native_sampler))); - DCHECK_NE(NULL_PROFILER_ID, profiler_id_); -} - -void StackSamplingProfiler::Stop() { - SamplingThread::GetInstance()->Remove(profiler_id_); - profiler_id_ = NULL_PROFILER_ID; -} - -// static -void StackSamplingProfiler::SetProcessMilestone(int milestone) { - DCHECK_LE(0, milestone); - DCHECK_GT(static_cast<int>(sizeof(process_milestones_) * 8), milestone); - DCHECK_EQ(0, subtle::NoBarrier_Load(&process_milestones_) & (1 << milestone)); - ChangeAtomicFlags(&process_milestones_, 1 << milestone, 0); -} - -// static -void StackSamplingProfiler::RecordAnnotations(Sample* sample) { - // The code inside this method must not do anything that could acquire a - // mutex, including allocating memory (which includes LOG messages) because - // that mutex could be held by a stopped thread, thus resulting in deadlock. - sample->process_milestones = subtle::NoBarrier_Load(&process_milestones_); -} - -// StackSamplingProfiler::Frame global functions ------------------------------ - -bool operator==(const StackSamplingProfiler::Module& a, - const StackSamplingProfiler::Module& b) { - return a.base_address == b.base_address && a.id == b.id && - a.filename == b.filename; -} - -bool operator==(const StackSamplingProfiler::Sample& a, - const StackSamplingProfiler::Sample& b) { - return a.process_milestones == b.process_milestones && a.frames == b.frames; -} - -bool operator!=(const StackSamplingProfiler::Sample& a, - const StackSamplingProfiler::Sample& b) { - return !(a == b); -} - -bool operator<(const StackSamplingProfiler::Sample& a, - const StackSamplingProfiler::Sample& b) { - if (a.process_milestones < b.process_milestones) - return true; - if (a.process_milestones > b.process_milestones) - return false; - - return a.frames < b.frames; -} - -bool operator==(const StackSamplingProfiler::Frame &a, - const StackSamplingProfiler::Frame &b) { - return a.instruction_pointer == b.instruction_pointer && - a.module_index == b.module_index; -} - -bool operator<(const StackSamplingProfiler::Frame &a, - const StackSamplingProfiler::Frame &b) { - return (a.module_index < b.module_index) || - (a.module_index == b.module_index && - a.instruction_pointer < b.instruction_pointer); -} - -} // namespace base
diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h deleted file mode 100644 index 2f9ade5..0000000 --- a/base/profiler/stack_sampling_profiler.h +++ /dev/null
@@ -1,331 +0,0 @@ -// 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_PROFILER_STACK_SAMPLING_PROFILER_H_ -#define BASE_PROFILER_STACK_SAMPLING_PROFILER_H_ - -#include <stddef.h> - -#include <memory> -#include <string> -#include <vector> - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/callback.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" - -namespace base { - -class NativeStackSampler; -class NativeStackSamplerTestDelegate; - -// StackSamplingProfiler periodically stops a thread to sample its stack, for -// the purpose of collecting information about which code paths are -// executing. This information is used in aggregate by UMA to identify hot -// and/or janky code paths. -// -// Sample StackSamplingProfiler usage: -// -// // Create and customize params as desired. -// base::StackStackSamplingProfiler::SamplingParams params; -// // Any thread's ID may be passed as the target. -// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), -// params); -// -// // Or, to process the profiles within Chrome rather than via UMA, use a -// // custom completed callback: -// base::StackStackSamplingProfiler::CompletedCallback -// thread_safe_callback = ...; -// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), -// params, thread_safe_callback); -// -// profiler.Start(); -// // ... work being done on the target thread here ... -// profiler.Stop(); // optional, stops collection before complete per params -// -// The default SamplingParams causes stacks to be recorded in a single burst at -// a 10Hz interval for a total of 30 seconds. All of these parameters may be -// altered as desired. -// -// When all call stack profiles are complete, or the profiler is stopped, the -// completed callback is called from a thread created by the profiler with the -// collected profiles. -// -// The results of the profiling are passed to the completed callback and consist -// of a vector of CallStackProfiles. Each CallStackProfile corresponds to a -// burst as specified in SamplingParams and contains a set of Samples and -// Modules. One Sample corresponds to a single recorded stack, and the Modules -// record those modules associated with the recorded stack frames. -class BASE_EXPORT StackSamplingProfiler { - public: - // Module represents the module (DLL or exe) corresponding to a stack frame. - struct BASE_EXPORT Module { - Module(); - Module(uintptr_t base_address, - const std::string& id, - const FilePath& filename); - ~Module(); - - // Points to the base address of the module. - uintptr_t base_address; - - // An opaque binary string that uniquely identifies a particular program - // version with high probability. This is parsed from headers of the loaded - // module. - // For binaries generated by GNU tools: - // Contents of the .note.gnu.build-id field. - // On Windows: - // GUID + AGE in the debug image headers of a module. - std::string id; - - // The filename of the module. - FilePath filename; - }; - - // Frame represents an individual sampled stack frame with module information. - struct BASE_EXPORT Frame { - // Identifies an unknown module. - static const size_t kUnknownModuleIndex = static_cast<size_t>(-1); - - Frame(uintptr_t instruction_pointer, size_t module_index); - ~Frame(); - - // Default constructor to satisfy IPC macros. Do not use explicitly. - Frame(); - - // The sampled instruction pointer within the function. - uintptr_t instruction_pointer; - - // Index of the module in CallStackProfile::modules. We don't represent - // module state directly here to save space. - size_t module_index; - }; - - // Sample represents a set of stack frames with some extra information. - struct BASE_EXPORT Sample { - Sample(); - Sample(const Sample& sample); - ~Sample(); - - // These constructors are used only during testing. - Sample(const Frame& frame); - Sample(const std::vector<Frame>& frames); - - // The entire stack frame when the sample is taken. - std::vector<Frame> frames; - - // A bit-field indicating which process milestones have passed. This can be - // used to tell where in the process lifetime the samples are taken. Just - // as a "lifetime" can only move forward, these bits mark the milestones of - // the processes life as they occur. Bits can be set but never reset. The - // actual definition of the individual bits is left to the user of this - // module. - uint32_t process_milestones = 0; - }; - - // CallStackProfile represents a set of samples. - struct BASE_EXPORT CallStackProfile { - CallStackProfile(); - CallStackProfile(CallStackProfile&& other); - ~CallStackProfile(); - - CallStackProfile& operator=(CallStackProfile&& other); - - CallStackProfile CopyForTesting() const; - - std::vector<Module> modules; - std::vector<Sample> samples; - - // Duration of this profile. - TimeDelta profile_duration; - - // Time between samples. - TimeDelta sampling_period; - - private: - // Copying is possible but expensive so disallow it except for internal use - // (i.e. CopyForTesting); use std::move instead. - CallStackProfile(const CallStackProfile& other); - - DISALLOW_ASSIGN(CallStackProfile); - }; - - using CallStackProfiles = std::vector<CallStackProfile>; - - // Represents parameters that configure the sampling. - struct BASE_EXPORT SamplingParams { - // Time to delay before first samples are taken. - TimeDelta initial_delay = TimeDelta::FromMilliseconds(0); - - // Number of sampling bursts to perform. - int bursts = 1; - - // Interval between sampling bursts. This is the desired duration from the - // start of one burst to the start of the next burst. - TimeDelta burst_interval = TimeDelta::FromSeconds(10); - - // Number of samples to record per burst. - int samples_per_burst = 300; - - // Interval between samples during a sampling burst. This is the desired - // duration from the start of one sample to the start of the next sample. - TimeDelta sampling_interval = TimeDelta::FromMilliseconds(100); - }; - - // Testing support. These methods are static beause they interact with the - // sampling thread, a singleton used by all StackSamplingProfiler objects. - // These methods can only be called by the same thread that started the - // sampling. - class BASE_EXPORT TestAPI { - public: - // Resets the internal state to that of a fresh start. This is necessary - // so that tests don't inherit state from previous tests. - static void Reset(); - - // Resets internal annotations (like process phase) to initial values. - static void ResetAnnotations(); - - // Returns whether the sampling thread is currently running or not. - static bool IsSamplingThreadRunning(); - - // Disables inherent idle-shutdown behavior. - static void DisableIdleShutdown(); - - // Initiates an idle shutdown task, as though the idle timer had expired, - // causing the thread to exit. There is no "idle" check so this must be - // called only when all sampling tasks have completed. This blocks until - // the task has been executed, though the actual stopping of the thread - // still happens asynchronously. Watch IsSamplingThreadRunning() to know - // when the thread has exited. If |simulate_intervening_start| is true then - // this method will make it appear to the shutdown task that a new profiler - // was started between when the idle-shutdown was initiated and when it - // runs. - static void PerformSamplingThreadIdleShutdown( - bool simulate_intervening_start); - }; - - // The callback type used to collect completed profiles. The passed |profiles| - // are move-only. Other threads, including the UI thread, may block on - // callback completion so this should run as quickly as possible. - // - // IMPORTANT NOTE: The callback is invoked on a thread the profiler - // constructs, rather than on the thread used to construct the profiler and - // set the callback, and thus the callback must be callable on any thread. For - // threads with message loops that create StackSamplingProfilers, posting a - // task to the message loop with the moved (i.e. std::move) profiles is the - // thread-safe callback implementation. - using CompletedCallback = Callback<void(CallStackProfiles)>; - - // Creates a profiler for the CURRENT thread that sends completed profiles - // to |callback|. An optional |test_delegate| can be supplied by tests. - // The caller must ensure that this object gets destroyed before the current - // thread exits. - StackSamplingProfiler( - const SamplingParams& params, - const CompletedCallback& callback, - NativeStackSamplerTestDelegate* test_delegate = nullptr); - - // Creates a profiler for ANOTHER thread that sends completed profiles to - // |callback|. An optional |test_delegate| can be supplied by tests. - // - // IMPORTANT: The caller must ensure that the thread being sampled does not - // exit before this object gets destructed or Bad Things(tm) may occur. - StackSamplingProfiler( - PlatformThreadId thread_id, - const SamplingParams& params, - const CompletedCallback& callback, - NativeStackSamplerTestDelegate* test_delegate = nullptr); - - // Stops any profiling currently taking place before destroying the profiler. - // This will block until the callback has been run if profiling has started - // but not already finished. - ~StackSamplingProfiler(); - - // Initializes the profiler and starts sampling. Might block on a - // WaitableEvent if this StackSamplingProfiler was previously started and - // recently stopped, while the previous profiling phase winds down. - void Start(); - - // Stops the profiler and any ongoing sampling. This method will return - // immediately with the callback being run asynchronously. At most one - // more stack sample will be taken after this method returns. Calling this - // function is optional; if not invoked profiling terminates when all the - // profiling bursts specified in the SamplingParams are completed or the - // profiler object is destroyed, whichever occurs first. - void Stop(); - - // Set the current system state that is recorded with each captured stack - // frame. This is thread-safe so can be called from anywhere. The parameter - // value should be from an enumeration of the appropriate type with values - // ranging from 0 to 31, inclusive. This sets bits within Sample field of - // |process_milestones|. The actual meanings of these bits are defined - // (globally) by the caller(s). - static void SetProcessMilestone(int milestone); - - private: - friend class TestAPI; - - // SamplingThread is a separate thread used to suspend and sample stacks from - // the target thread. - class SamplingThread; - - // Adds annotations to a Sample. - static void RecordAnnotations(Sample* sample); - - // This global variables holds the current system state and is recorded with - // every captured sample, done on a separate thread which is why updates to - // this must be atomic. A PostTask to move the the updates to that thread - // would skew the timing and a lock could result in deadlock if the thread - // making a change was also being profiled and got stopped. - static subtle::Atomic32 process_milestones_; - - // The thread whose stack will be sampled. - PlatformThreadId thread_id_; - - const SamplingParams params_; - - const CompletedCallback completed_callback_; - - // This starts "signaled", is reset when sampling begins, and is signaled - // when that sampling is complete and the callback done. - WaitableEvent profiling_inactive_; - - // Object that does the native sampling. This is created during construction - // and later passed to the sampling thread when profiling is started. - std::unique_ptr<NativeStackSampler> native_sampler_; - - // An ID uniquely identifying this profiler to the sampling thread. This - // will be an internal "null" value when no collection has been started. - int profiler_id_; - - // Stored until it can be passed to the NativeStackSampler created in Start(). - NativeStackSamplerTestDelegate* const test_delegate_; - - DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler); -}; - -// These operators permit types to be compared and used in a map of Samples, as -// done in tests and by the metrics provider code. -BASE_EXPORT bool operator==(const StackSamplingProfiler::Module& a, - const StackSamplingProfiler::Module& b); -BASE_EXPORT bool operator==(const StackSamplingProfiler::Sample& a, - const StackSamplingProfiler::Sample& b); -BASE_EXPORT bool operator!=(const StackSamplingProfiler::Sample& a, - const StackSamplingProfiler::Sample& b); -BASE_EXPORT bool operator<(const StackSamplingProfiler::Sample& a, - const StackSamplingProfiler::Sample& b); -BASE_EXPORT bool operator==(const StackSamplingProfiler::Frame& a, - const StackSamplingProfiler::Frame& b); -BASE_EXPORT bool operator<(const StackSamplingProfiler::Frame& a, - const StackSamplingProfiler::Frame& b); - -} // namespace base - -#endif // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
diff --git a/base/profiler/test_support_library.cc b/base/profiler/test_support_library.cc deleted file mode 100644 index 035f8f7..0000000 --- a/base/profiler/test_support_library.cc +++ /dev/null
@@ -1,30 +0,0 @@ -// 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. - -// Note: there is intentionally no header file associated with this library so -// we don't risk implicitly demand loading it by accessing a symbol. - -#if defined(WIN32) -#define BASE_PROFILER_TEST_SUPPORT_LIBRARY_EXPORT __declspec(dllexport) -#else // defined(WIN32) -#define BASE_PROFILER_TEST_SUPPORT_LIBRARY_EXPORT __attribute__((visibility("default"))) -#endif - -namespace base { - -// Must be defined in an extern "C" block so we can look up the unmangled name. -extern "C" { - -BASE_PROFILER_TEST_SUPPORT_LIBRARY_EXPORT void InvokeCallbackFunction( - void (*function)(void*), - void* arg) { - function(arg); - // Prevent tail call. - volatile int i = 0; - i = 1; -} - -} // extern "C" - -} // namespace base
diff --git a/base/profiler/win32_stack_frame_unwinder.cc b/base/profiler/win32_stack_frame_unwinder.cc deleted file mode 100644 index 9e6ab39..0000000 --- a/base/profiler/win32_stack_frame_unwinder.cc +++ /dev/null
@@ -1,186 +0,0 @@ -// 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/profiler/win32_stack_frame_unwinder.h" - -#include <windows.h> - -#include <utility> - -#include "base/macros.h" -#include "base/memory/ptr_util.h" - -namespace base { - -// Win32UnwindFunctions ------------------------------------------------------- - -const HMODULE ModuleHandleTraits::kNonNullModuleForTesting = - reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1)); - -// static -bool ModuleHandleTraits::CloseHandle(HMODULE handle) { - if (handle == kNonNullModuleForTesting) - return true; - - return ::FreeLibrary(handle) != 0; -} - -// static -bool ModuleHandleTraits::IsHandleValid(HMODULE handle) { - return handle != nullptr; -} - -// static -HMODULE ModuleHandleTraits::NullHandle() { - return nullptr; -} - -namespace { - -// Implements the UnwindFunctions interface for the corresponding Win32 -// functions. -class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions { -public: - Win32UnwindFunctions(); - ~Win32UnwindFunctions() override; - - PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter, - PDWORD64 image_base) override; - - void VirtualUnwind(DWORD64 image_base, - DWORD64 program_counter, - PRUNTIME_FUNCTION runtime_function, - CONTEXT* context) override; - - ScopedModuleHandle GetModuleForProgramCounter( - DWORD64 program_counter) override; - -private: - DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions); -}; - -Win32UnwindFunctions::Win32UnwindFunctions() {} -Win32UnwindFunctions::~Win32UnwindFunctions() {} - -PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry( - DWORD64 program_counter, - PDWORD64 image_base) { -#ifdef _WIN64 - return RtlLookupFunctionEntry(program_counter, image_base, nullptr); -#else - NOTREACHED(); - return nullptr; -#endif -} - -void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base, - DWORD64 program_counter, - PRUNTIME_FUNCTION runtime_function, - CONTEXT* context) { -#ifdef _WIN64 - void* handler_data; - ULONG64 establisher_frame; - KNONVOLATILE_CONTEXT_POINTERS nvcontext = {}; - RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter, - runtime_function, context, &handler_data, - &establisher_frame, &nvcontext); -#else - NOTREACHED(); -#endif -} - -ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter( - DWORD64 program_counter) { - HMODULE module_handle = nullptr; - // GetModuleHandleEx() increments the module reference count, which is then - // managed and ultimately decremented by ScopedModuleHandle. - if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast<LPCTSTR>(program_counter), - &module_handle)) { - const DWORD error = ::GetLastError(); - DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error)); - } - return ScopedModuleHandle(module_handle); -} - -} // namespace - -// Win32StackFrameUnwinder ---------------------------------------------------- - -Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {} -Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {} - -Win32StackFrameUnwinder::Win32StackFrameUnwinder() - : Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {} - -Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {} - -bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, - ScopedModuleHandle* module) { -#ifdef _WIN64 - ScopedModuleHandle frame_module = - unwind_functions_->GetModuleForProgramCounter(context->Rip); - if (!frame_module.IsValid()) { - // There's no loaded module containing the instruction pointer. This can be - // due to executing code that is not in a module. In particular, - // runtime-generated code associated with third-party injected DLLs - // typically is not in a module. It can also be due to the the module having - // been unloaded since we recorded the stack. In the latter case the - // function unwind information was part of the unloaded module, so it's not - // possible to unwind further. - // - // If a module was found, it's still theoretically possible for the detected - // module module to be different than the one that was loaded when the stack - // was copied (i.e. if the module was unloaded and a different module loaded - // in overlapping memory). This likely would cause a crash, but has not been - // observed in practice. - return false; - } - - ULONG64 image_base; - // Try to look up unwind metadata for the current function. - PRUNTIME_FUNCTION runtime_function = - unwind_functions_->LookupFunctionEntry(context->Rip, &image_base); - - if (runtime_function) { - unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function, - context); - at_top_frame_ = false; - } else { - if (at_top_frame_) { - at_top_frame_ = false; - - // This is a leaf function (i.e. a function that neither calls a function, - // nor allocates any stack space itself) so the return address is at RSP. - context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp); - context->Rsp += 8; - } else { - // In theory we shouldn't get here, as it means we've encountered a - // function without unwind information below the top of the stack, which - // is forbidden by the Microsoft x64 calling convention. - // - // The one known case in Chrome code that executes this path occurs - // because of BoringSSL unwind information inconsistent with the actual - // function code. See https://crbug.com/542919. - // - // Note that dodgy third-party generated code that otherwise would enter - // this path should be caught by the module check above, since the code - // typically is located outside of a module. - return false; - } - } - - module->Set(frame_module.Take()); - return true; -#else - NOTREACHED(); - return false; -#endif -} - -Win32StackFrameUnwinder::Win32StackFrameUnwinder( - std::unique_ptr<UnwindFunctions> unwind_functions) - : at_top_frame_(true), unwind_functions_(std::move(unwind_functions)) {} - -} // namespace base
diff --git a/base/profiler/win32_stack_frame_unwinder.h b/base/profiler/win32_stack_frame_unwinder.h deleted file mode 100644 index c92d50c..0000000 --- a/base/profiler/win32_stack_frame_unwinder.h +++ /dev/null
@@ -1,102 +0,0 @@ -// 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_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_ -#define BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_ - -#include <windows.h> - -#include <memory> - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/win/scoped_handle.h" - -namespace base { - -#if !defined(_WIN64) -// Allows code to compile for x86. Actual support for x86 will require either -// refactoring these interfaces or separate architecture-specific interfaces. -struct RUNTIME_FUNCTION { - DWORD BeginAddress; - DWORD EndAddress; -}; -using PRUNTIME_FUNCTION = RUNTIME_FUNCTION*; -#endif // !defined(_WIN64) - -// Traits class to adapt GenericScopedHandle for HMODULES. -class ModuleHandleTraits : public win::HandleTraits { - public: - using Handle = HMODULE; - - static bool BASE_EXPORT CloseHandle(HMODULE handle); - static bool BASE_EXPORT IsHandleValid(HMODULE handle); - static HMODULE BASE_EXPORT NullHandle(); - - BASE_EXPORT static const HMODULE kNonNullModuleForTesting; - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(ModuleHandleTraits); -}; - -// HMODULE is not really a handle, and has reference count semantics, so the -// standard VerifierTraits does not apply. -using ScopedModuleHandle = - win::GenericScopedHandle<ModuleHandleTraits, win::DummyVerifierTraits>; - -// Instances of this class are expected to be created and destroyed for each -// stack unwinding. This class is not used while the target thread is suspended, -// so may allocate from the default heap. -class BASE_EXPORT Win32StackFrameUnwinder { - public: - // Interface for Win32 unwind-related functionality this class depends - // on. Provides a seam for testing. - class BASE_EXPORT UnwindFunctions { - public: - virtual ~UnwindFunctions(); - - virtual PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter, - PDWORD64 image_base) = 0; - virtual void VirtualUnwind(DWORD64 image_base, - DWORD64 program_counter, - PRUNTIME_FUNCTION runtime_function, - CONTEXT* context) = 0; - - // Returns the module containing |program_counter|. Can return null if the - // module has been unloaded. - virtual ScopedModuleHandle GetModuleForProgramCounter( - DWORD64 program_counter) = 0; - - protected: - UnwindFunctions(); - - private: - DISALLOW_COPY_AND_ASSIGN(UnwindFunctions); - }; - - Win32StackFrameUnwinder(); - ~Win32StackFrameUnwinder(); - - // Attempts to unwind the frame represented by the stack and instruction - // pointers in |context|. If successful, updates |context| and provides the - // module associated with the frame in |module|. - bool TryUnwind(CONTEXT* context, ScopedModuleHandle* module); - - private: - // This function is for internal and test purposes only. - Win32StackFrameUnwinder(std::unique_ptr<UnwindFunctions> unwind_functions); - friend class Win32StackFrameUnwinderTest; - - // State associated with each stack unwinding. - bool at_top_frame_; - bool unwind_info_present_for_all_frames_; - - std::unique_ptr<UnwindFunctions> unwind_functions_; - - DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinder); -}; - -} // namespace base - -#endif // BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
diff --git a/base/synchronization/lock_impl_win.cc b/base/synchronization/lock_impl_win.cc index e0c4e9d..fe9c664 100644 --- a/base/synchronization/lock_impl_win.cc +++ b/base/synchronization/lock_impl_win.cc
@@ -4,8 +4,6 @@ #include "base/synchronization/lock_impl.h" -#include "base/debug/activity_tracker.h" - #include <windows.h> namespace base {
diff --git a/base/synchronization/waitable_event_win.cc b/base/synchronization/waitable_event_win.cc index d04a5a6..60126f2 100644 --- a/base/synchronization/waitable_event_win.cc +++ b/base/synchronization/waitable_event_win.cc
@@ -10,7 +10,6 @@ #include <algorithm> #include <utility> -#include "base/debug/activity_tracker.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/threading/scoped_blocking_call.h"
diff --git a/base/task_scheduler/scheduler_worker.cc b/base/task_scheduler/scheduler_worker.cc index 33b400a..32042e4 100644 --- a/base/task_scheduler/scheduler_worker.cc +++ b/base/task_scheduler/scheduler_worker.cc
@@ -9,7 +9,6 @@ #include <utility> #include "base/compiler_specific.h" -#include "base/debug/alias.h" #include "base/logging.h" #include "base/task_scheduler/scheduler_worker_observer.h" #include "base/task_scheduler/task_tracker.h" @@ -212,62 +211,52 @@ NOINLINE void SchedulerWorker::RunPooledWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunBackgroundPooledWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunSharedWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunBackgroundSharedWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunDedicatedWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunBackgroundDedicatedWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } #if defined(OS_WIN) NOINLINE void SchedulerWorker::RunSharedCOMWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunBackgroundSharedCOMWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunDedicatedCOMWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } NOINLINE void SchedulerWorker::RunBackgroundDedicatedCOMWorker() { const int line_number = __LINE__; RunWorker(); - base::debug::Alias(&line_number); } #endif // defined(OS_WIN)
diff --git a/base/task_scheduler/service_thread.cc b/base/task_scheduler/service_thread.cc index ce8bf4d..9ee0986 100644 --- a/base/task_scheduler/service_thread.cc +++ b/base/task_scheduler/service_thread.cc
@@ -4,7 +4,6 @@ #include "base/task_scheduler/service_thread.h" -#include "base/debug/alias.h" #include "base/task_scheduler/post_task.h" #include "base/task_scheduler/task_tracker.h" #include "base/task_scheduler/task_traits.h" @@ -20,7 +19,6 @@ NOINLINE void ServiceThread::Run(RunLoop* run_loop) { const int line_number = __LINE__; Thread::Run(run_loop); - base::debug::Alias(&line_number); } } // namespace internal
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc index 33424bb..704aadd 100644 --- a/base/task_scheduler/task_tracker.cc +++ b/base/task_scheduler/task_tracker.cc
@@ -245,8 +245,6 @@ if (task.delayed_run_time.is_null()) subtle::NoBarrier_AtomicIncrement(&num_incomplete_undelayed_tasks_, 1); - task_annotator_.DidQueueTask(nullptr, task); - return true; } @@ -374,7 +372,7 @@ } if (can_run_task) { - task_annotator_.RunTask(nullptr, &task); + std::move(task.task).Run(); } // Make sure the arguments bound to the callback are deleted within the
diff --git a/base/task_scheduler/task_tracker.h b/base/task_scheduler/task_tracker.h index ae484ce..3240e3a 100644 --- a/base/task_scheduler/task_tracker.h +++ b/base/task_scheduler/task_tracker.h
@@ -12,7 +12,6 @@ #include "base/atomicops.h" #include "base/base_export.h" #include "base/callback_forward.h" -#include "base/debug/task_annotator.h" #include "base/logging.h" #include "base/macros.h" #include "base/strings/string_piece.h" @@ -258,8 +257,6 @@ // manner. void CallFlushCallbackForTesting(); - debug::TaskAnnotator task_annotator_; - // Number of tasks blocking shutdown and boolean indicating whether shutdown // has started. const std::unique_ptr<State> state_;
diff --git a/base/third_party/symbolize/DEPS b/base/third_party/symbolize/DEPS deleted file mode 100644 index 73eab50..0000000 --- a/base/third_party/symbolize/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+glog", -]
diff --git a/base/third_party/symbolize/LICENSE b/base/third_party/symbolize/LICENSE deleted file mode 100644 index 433a3d1..0000000 --- a/base/third_party/symbolize/LICENSE +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/base/third_party/symbolize/README.chromium b/base/third_party/symbolize/README.chromium deleted file mode 100644 index ff78e0e..0000000 --- a/base/third_party/symbolize/README.chromium +++ /dev/null
@@ -1,24 +0,0 @@ -Name: google-glog's symbolization library -URL: https://github.com/google/glog -License: BSD - -The following files are copied AS-IS from: -http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r141) -https://github.com/google/glog/tree/a5ffa884137f7687d0393ccba22557d583654a25 - -- demangle.cc -- demangle.h -- symbolize.cc -- symbolize.h - -Cherry picked upstream changes: -https://github.com/google/glog/pull/115 -https://github.com/google/glog/pull/261 -to fix symbolization issues when using lld. - -The following files are minimal stubs created for use in Chromium: - -- config.h -- glog/logging.h -- glog/raw_logging.h -- utilities.h
diff --git a/base/third_party/symbolize/config.h b/base/third_party/symbolize/config.h deleted file mode 100644 index 945f5a6..0000000 --- a/base/third_party/symbolize/config.h +++ /dev/null
@@ -1,7 +0,0 @@ -// 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. - -#define GOOGLE_NAMESPACE google -#define _END_GOOGLE_NAMESPACE_ } -#define _START_GOOGLE_NAMESPACE_ namespace google {
diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc deleted file mode 100644 index e858181..0000000 --- a/base/third_party/symbolize/demangle.cc +++ /dev/null
@@ -1,1304 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: Satoru Takabayashi -// -// For reference check out: -// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling -// -// Note that we only have partial C++0x support yet. - -#include <stdio.h> // for NULL -#include "demangle.h" - -_START_GOOGLE_NAMESPACE_ - -typedef struct { - const char *abbrev; - const char *real_name; -} AbbrevPair; - -// List of operators from Itanium C++ ABI. -static const AbbrevPair kOperatorList[] = { - { "nw", "new" }, - { "na", "new[]" }, - { "dl", "delete" }, - { "da", "delete[]" }, - { "ps", "+" }, - { "ng", "-" }, - { "ad", "&" }, - { "de", "*" }, - { "co", "~" }, - { "pl", "+" }, - { "mi", "-" }, - { "ml", "*" }, - { "dv", "/" }, - { "rm", "%" }, - { "an", "&" }, - { "or", "|" }, - { "eo", "^" }, - { "aS", "=" }, - { "pL", "+=" }, - { "mI", "-=" }, - { "mL", "*=" }, - { "dV", "/=" }, - { "rM", "%=" }, - { "aN", "&=" }, - { "oR", "|=" }, - { "eO", "^=" }, - { "ls", "<<" }, - { "rs", ">>" }, - { "lS", "<<=" }, - { "rS", ">>=" }, - { "eq", "==" }, - { "ne", "!=" }, - { "lt", "<" }, - { "gt", ">" }, - { "le", "<=" }, - { "ge", ">=" }, - { "nt", "!" }, - { "aa", "&&" }, - { "oo", "||" }, - { "pp", "++" }, - { "mm", "--" }, - { "cm", "," }, - { "pm", "->*" }, - { "pt", "->" }, - { "cl", "()" }, - { "ix", "[]" }, - { "qu", "?" }, - { "st", "sizeof" }, - { "sz", "sizeof" }, - { NULL, NULL }, -}; - -// List of builtin types from Itanium C++ ABI. -static const AbbrevPair kBuiltinTypeList[] = { - { "v", "void" }, - { "w", "wchar_t" }, - { "b", "bool" }, - { "c", "char" }, - { "a", "signed char" }, - { "h", "unsigned char" }, - { "s", "short" }, - { "t", "unsigned short" }, - { "i", "int" }, - { "j", "unsigned int" }, - { "l", "long" }, - { "m", "unsigned long" }, - { "x", "long long" }, - { "y", "unsigned long long" }, - { "n", "__int128" }, - { "o", "unsigned __int128" }, - { "f", "float" }, - { "d", "double" }, - { "e", "long double" }, - { "g", "__float128" }, - { "z", "ellipsis" }, - { NULL, NULL } -}; - -// List of substitutions Itanium C++ ABI. -static const AbbrevPair kSubstitutionList[] = { - { "St", "" }, - { "Sa", "allocator" }, - { "Sb", "basic_string" }, - // std::basic_string<char, std::char_traits<char>,std::allocator<char> > - { "Ss", "string"}, - // std::basic_istream<char, std::char_traits<char> > - { "Si", "istream" }, - // std::basic_ostream<char, std::char_traits<char> > - { "So", "ostream" }, - // std::basic_iostream<char, std::char_traits<char> > - { "Sd", "iostream" }, - { NULL, NULL } -}; - -// State needed for demangling. -typedef struct { - const char *mangled_cur; // Cursor of mangled name. - char *out_cur; // Cursor of output string. - const char *out_begin; // Beginning of output string. - const char *out_end; // End of output string. - const char *prev_name; // For constructors/destructors. - int prev_name_length; // For constructors/destructors. - short nest_level; // For nested names. - bool append; // Append flag. - bool overflowed; // True if output gets overflowed. -} State; - -// We don't use strlen() in libc since it's not guaranteed to be async -// signal safe. -static size_t StrLen(const char *str) { - size_t len = 0; - while (*str != '\0') { - ++str; - ++len; - } - return len; -} - -// Returns true if "str" has at least "n" characters remaining. -static bool AtLeastNumCharsRemaining(const char *str, int n) { - for (int i = 0; i < n; ++i) { - if (str[i] == '\0') { - return false; - } - } - return true; -} - -// Returns true if "str" has "prefix" as a prefix. -static bool StrPrefix(const char *str, const char *prefix) { - size_t i = 0; - while (str[i] != '\0' && prefix[i] != '\0' && - str[i] == prefix[i]) { - ++i; - } - return prefix[i] == '\0'; // Consumed everything in "prefix". -} - -static void InitState(State *state, const char *mangled, - char *out, int out_size) { - state->mangled_cur = mangled; - state->out_cur = out; - state->out_begin = out; - state->out_end = out + out_size; - state->prev_name = NULL; - state->prev_name_length = -1; - state->nest_level = -1; - state->append = true; - state->overflowed = false; -} - -// Returns true and advances "mangled_cur" if we find "one_char_token" -// at "mangled_cur" position. It is assumed that "one_char_token" does -// not contain '\0'. -static bool ParseOneCharToken(State *state, const char one_char_token) { - if (state->mangled_cur[0] == one_char_token) { - ++state->mangled_cur; - return true; - } - return false; -} - -// Returns true and advances "mangled_cur" if we find "two_char_token" -// at "mangled_cur" position. It is assumed that "two_char_token" does -// not contain '\0'. -static bool ParseTwoCharToken(State *state, const char *two_char_token) { - if (state->mangled_cur[0] == two_char_token[0] && - state->mangled_cur[1] == two_char_token[1]) { - state->mangled_cur += 2; - return true; - } - return false; -} - -// Returns true and advances "mangled_cur" if we find any character in -// "char_class" at "mangled_cur" position. -static bool ParseCharClass(State *state, const char *char_class) { - const char *p = char_class; - for (; *p != '\0'; ++p) { - if (state->mangled_cur[0] == *p) { - ++state->mangled_cur; - return true; - } - } - return false; -} - -// This function is used for handling an optional non-terminal. -static bool Optional(bool) { - return true; -} - -// This function is used for handling <non-terminal>+ syntax. -typedef bool (*ParseFunc)(State *); -static bool OneOrMore(ParseFunc parse_func, State *state) { - if (parse_func(state)) { - while (parse_func(state)) { - } - return true; - } - return false; -} - -// This function is used for handling <non-terminal>* syntax. The function -// always returns true and must be followed by a termination token or a -// terminating sequence not handled by parse_func (e.g. -// ParseOneCharToken(state, 'E')). -static bool ZeroOrMore(ParseFunc parse_func, State *state) { - while (parse_func(state)) { - } - return true; -} - -// Append "str" at "out_cur". If there is an overflow, "overflowed" -// is set to true for later use. The output string is ensured to -// always terminate with '\0' as long as there is no overflow. -static void Append(State *state, const char * const str, const int length) { - int i; - for (i = 0; i < length; ++i) { - if (state->out_cur + 1 < state->out_end) { // +1 for '\0' - *state->out_cur = str[i]; - ++state->out_cur; - } else { - state->overflowed = true; - break; - } - } - if (!state->overflowed) { - *state->out_cur = '\0'; // Terminate it with '\0' - } -} - -// We don't use equivalents in libc to avoid locale issues. -static bool IsLower(char c) { - return c >= 'a' && c <= 'z'; -} - -static bool IsAlpha(char c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); -} - -static bool IsDigit(char c) { - return c >= '0' && c <= '9'; -} - -// Returns true if "str" is a function clone suffix. These suffixes are used -// by GCC 4.5.x and later versions to indicate functions which have been -// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as -// a function clone suffix. -static bool IsFunctionCloneSuffix(const char *str) { - size_t i = 0; - while (str[i] != '\0') { - // Consume a single .<alpha>+.<digit>+ sequence. - if (str[i] != '.' || !IsAlpha(str[i + 1])) { - return false; - } - i += 2; - while (IsAlpha(str[i])) { - ++i; - } - if (str[i] != '.' || !IsDigit(str[i + 1])) { - return false; - } - i += 2; - while (IsDigit(str[i])) { - ++i; - } - } - return true; // Consumed everything in "str". -} - -// Append "str" with some tweaks, iff "append" state is true. -// Returns true so that it can be placed in "if" conditions. -static void MaybeAppendWithLength(State *state, const char * const str, - const int length) { - if (state->append && length > 0) { - // Append a space if the output buffer ends with '<' and "str" - // starts with '<' to avoid <<<. - if (str[0] == '<' && state->out_begin < state->out_cur && - state->out_cur[-1] == '<') { - Append(state, " ", 1); - } - // Remember the last identifier name for ctors/dtors. - if (IsAlpha(str[0]) || str[0] == '_') { - state->prev_name = state->out_cur; - state->prev_name_length = length; - } - Append(state, str, length); - } -} - -// A convenient wrapper arount MaybeAppendWithLength(). -static bool MaybeAppend(State *state, const char * const str) { - if (state->append) { - int length = StrLen(str); - MaybeAppendWithLength(state, str, length); - } - return true; -} - -// This function is used for handling nested names. -static bool EnterNestedName(State *state) { - state->nest_level = 0; - return true; -} - -// This function is used for handling nested names. -static bool LeaveNestedName(State *state, short prev_value) { - state->nest_level = prev_value; - return true; -} - -// Disable the append mode not to print function parameters, etc. -static bool DisableAppend(State *state) { - state->append = false; - return true; -} - -// Restore the append mode to the previous state. -static bool RestoreAppend(State *state, bool prev_value) { - state->append = prev_value; - return true; -} - -// Increase the nest level for nested names. -static void MaybeIncreaseNestLevel(State *state) { - if (state->nest_level > -1) { - ++state->nest_level; - } -} - -// Appends :: for nested names if necessary. -static void MaybeAppendSeparator(State *state) { - if (state->nest_level >= 1) { - MaybeAppend(state, "::"); - } -} - -// Cancel the last separator if necessary. -static void MaybeCancelLastSeparator(State *state) { - if (state->nest_level >= 1 && state->append && - state->out_begin <= state->out_cur - 2) { - state->out_cur -= 2; - *state->out_cur = '\0'; - } -} - -// Returns true if the identifier of the given length pointed to by -// "mangled_cur" is anonymous namespace. -static bool IdentifierIsAnonymousNamespace(State *state, int length) { - static const char anon_prefix[] = "_GLOBAL__N_"; - return (length > (int)sizeof(anon_prefix) - 1 && // Should be longer. - StrPrefix(state->mangled_cur, anon_prefix)); -} - -// Forward declarations of our parsing functions. -static bool ParseMangledName(State *state); -static bool ParseEncoding(State *state); -static bool ParseName(State *state); -static bool ParseUnscopedName(State *state); -static bool ParseUnscopedTemplateName(State *state); -static bool ParseNestedName(State *state); -static bool ParsePrefix(State *state); -static bool ParseUnqualifiedName(State *state); -static bool ParseSourceName(State *state); -static bool ParseLocalSourceName(State *state); -static bool ParseNumber(State *state, int *number_out); -static bool ParseFloatNumber(State *state); -static bool ParseSeqId(State *state); -static bool ParseIdentifier(State *state, int length); -static bool ParseOperatorName(State *state); -static bool ParseSpecialName(State *state); -static bool ParseCallOffset(State *state); -static bool ParseNVOffset(State *state); -static bool ParseVOffset(State *state); -static bool ParseCtorDtorName(State *state); -static bool ParseType(State *state); -static bool ParseCVQualifiers(State *state); -static bool ParseBuiltinType(State *state); -static bool ParseFunctionType(State *state); -static bool ParseBareFunctionType(State *state); -static bool ParseClassEnumType(State *state); -static bool ParseArrayType(State *state); -static bool ParsePointerToMemberType(State *state); -static bool ParseTemplateParam(State *state); -static bool ParseTemplateTemplateParam(State *state); -static bool ParseTemplateArgs(State *state); -static bool ParseTemplateArg(State *state); -static bool ParseExpression(State *state); -static bool ParseExprPrimary(State *state); -static bool ParseLocalName(State *state); -static bool ParseDiscriminator(State *state); -static bool ParseSubstitution(State *state); - -// Implementation note: the following code is a straightforward -// translation of the Itanium C++ ABI defined in BNF with a couple of -// exceptions. -// -// - Support GNU extensions not defined in the Itanium C++ ABI -// - <prefix> and <template-prefix> are combined to avoid infinite loop -// - Reorder patterns to shorten the code -// - Reorder patterns to give greedier functions precedence -// We'll mark "Less greedy than" for these cases in the code -// -// Each parsing function changes the state and returns true on -// success. Otherwise, don't change the state and returns false. To -// ensure that the state isn't changed in the latter case, we save the -// original state before we call more than one parsing functions -// consecutively with &&, and restore the state if unsuccessful. See -// ParseEncoding() as an example of this convention. We follow the -// convention throughout the code. -// -// Originally we tried to do demangling without following the full ABI -// syntax but it turned out we needed to follow the full syntax to -// parse complicated cases like nested template arguments. Note that -// implementing a full-fledged demangler isn't trivial (libiberty's -// cp-demangle.c has +4300 lines). -// -// Note that (foo) in <(foo) ...> is a modifier to be ignored. -// -// Reference: -// - Itanium C++ ABI -// <http://www.codesourcery.com/cxx-abi/abi.html#mangling> - -// <mangled-name> ::= _Z <encoding> -static bool ParseMangledName(State *state) { - return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); -} - -// <encoding> ::= <(function) name> <bare-function-type> -// ::= <(data) name> -// ::= <special-name> -static bool ParseEncoding(State *state) { - State copy = *state; - if (ParseName(state) && ParseBareFunctionType(state)) { - return true; - } - *state = copy; - - if (ParseName(state) || ParseSpecialName(state)) { - return true; - } - return false; -} - -// <name> ::= <nested-name> -// ::= <unscoped-template-name> <template-args> -// ::= <unscoped-name> -// ::= <local-name> -static bool ParseName(State *state) { - if (ParseNestedName(state) || ParseLocalName(state)) { - return true; - } - - State copy = *state; - if (ParseUnscopedTemplateName(state) && - ParseTemplateArgs(state)) { - return true; - } - *state = copy; - - // Less greedy than <unscoped-template-name> <template-args>. - if (ParseUnscopedName(state)) { - return true; - } - return false; -} - -// <unscoped-name> ::= <unqualified-name> -// ::= St <unqualified-name> -static bool ParseUnscopedName(State *state) { - if (ParseUnqualifiedName(state)) { - return true; - } - - State copy = *state; - if (ParseTwoCharToken(state, "St") && - MaybeAppend(state, "std::") && - ParseUnqualifiedName(state)) { - return true; - } - *state = copy; - return false; -} - -// <unscoped-template-name> ::= <unscoped-name> -// ::= <substitution> -static bool ParseUnscopedTemplateName(State *state) { - return ParseUnscopedName(state) || ParseSubstitution(state); -} - -// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E -// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E -static bool ParseNestedName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'N') && - EnterNestedName(state) && - Optional(ParseCVQualifiers(state)) && - ParsePrefix(state) && - LeaveNestedName(state, copy.nest_level) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - return false; -} - -// This part is tricky. If we literally translate them to code, we'll -// end up infinite loop. Hence we merge them to avoid the case. -// -// <prefix> ::= <prefix> <unqualified-name> -// ::= <template-prefix> <template-args> -// ::= <template-param> -// ::= <substitution> -// ::= # empty -// <template-prefix> ::= <prefix> <(template) unqualified-name> -// ::= <template-param> -// ::= <substitution> -static bool ParsePrefix(State *state) { - bool has_something = false; - while (true) { - MaybeAppendSeparator(state); - if (ParseTemplateParam(state) || - ParseSubstitution(state) || - ParseUnscopedName(state)) { - has_something = true; - MaybeIncreaseNestLevel(state); - continue; - } - MaybeCancelLastSeparator(state); - if (has_something && ParseTemplateArgs(state)) { - return ParsePrefix(state); - } else { - break; - } - } - return true; -} - -// <unqualified-name> ::= <operator-name> -// ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <local-source-name> -static bool ParseUnqualifiedName(State *state) { - return (ParseOperatorName(state) || - ParseCtorDtorName(state) || - ParseSourceName(state) || - ParseLocalSourceName(state)); -} - -// <source-name> ::= <positive length number> <identifier> -static bool ParseSourceName(State *state) { - State copy = *state; - int length = -1; - if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { - return true; - } - *state = copy; - return false; -} - -// <local-source-name> ::= L <source-name> [<discriminator>] -// -// References: -// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 -// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 -static bool ParseLocalSourceName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && - Optional(ParseDiscriminator(state))) { - return true; - } - *state = copy; - return false; -} - -// <number> ::= [n] <non-negative decimal integer> -// If "number_out" is non-null, then *number_out is set to the value of the -// parsed number on success. -static bool ParseNumber(State *state, int *number_out) { - int sign = 1; - if (ParseOneCharToken(state, 'n')) { - sign = -1; - } - const char *p = state->mangled_cur; - int number = 0; - for (;*p != '\0'; ++p) { - if (IsDigit(*p)) { - number = number * 10 + (*p - '0'); - } else { - break; - } - } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; - if (number_out != NULL) { - *number_out = number * sign; - } - return true; - } - return false; -} - -// Floating-point literals are encoded using a fixed-length lowercase -// hexadecimal string. -static bool ParseFloatNumber(State *state) { - const char *p = state->mangled_cur; - for (;*p != '\0'; ++p) { - if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { - break; - } - } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; - return true; - } - return false; -} - -// The <seq-id> is a sequence number in base 36, -// using digits and upper case letters -static bool ParseSeqId(State *state) { - const char *p = state->mangled_cur; - for (;*p != '\0'; ++p) { - if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { - break; - } - } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; - return true; - } - return false; -} - -// <identifier> ::= <unqualified source code identifier> (of given length) -static bool ParseIdentifier(State *state, int length) { - if (length == -1 || - !AtLeastNumCharsRemaining(state->mangled_cur, length)) { - return false; - } - if (IdentifierIsAnonymousNamespace(state, length)) { - MaybeAppend(state, "(anonymous namespace)"); - } else { - MaybeAppendWithLength(state, state->mangled_cur, length); - } - state->mangled_cur += length; - return true; -} - -// <operator-name> ::= nw, and other two letters cases -// ::= cv <type> # (cast) -// ::= v <digit> <source-name> # vendor extended operator -static bool ParseOperatorName(State *state) { - if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { - return false; - } - // First check with "cv" (cast) case. - State copy = *state; - if (ParseTwoCharToken(state, "cv") && - MaybeAppend(state, "operator ") && - EnterNestedName(state) && - ParseType(state) && - LeaveNestedName(state, copy.nest_level)) { - return true; - } - *state = copy; - - // Then vendor extended operators. - if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && - ParseSourceName(state)) { - return true; - } - *state = copy; - - // Other operator names should start with a lower alphabet followed - // by a lower/upper alphabet. - if (!(IsLower(state->mangled_cur[0]) && - IsAlpha(state->mangled_cur[1]))) { - return false; - } - // We may want to perform a binary search if we really need speed. - const AbbrevPair *p; - for (p = kOperatorList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[0] && - state->mangled_cur[1] == p->abbrev[1]) { - MaybeAppend(state, "operator"); - if (IsLower(*p->real_name)) { // new, delete, etc. - MaybeAppend(state, " "); - } - MaybeAppend(state, p->real_name); - state->mangled_cur += 2; - return true; - } - } - return false; -} - -// <special-name> ::= TV <type> -// ::= TT <type> -// ::= TI <type> -// ::= TS <type> -// ::= Tc <call-offset> <call-offset> <(base) encoding> -// ::= GV <(object) name> -// ::= T <call-offset> <(base) encoding> -// G++ extensions: -// ::= TC <type> <(offset) number> _ <(base) type> -// ::= TF <type> -// ::= TJ <type> -// ::= GR <name> -// ::= GA <encoding> -// ::= Th <call-offset> <(base) encoding> -// ::= Tv <call-offset> <(base) encoding> -// -// Note: we don't care much about them since they don't appear in -// stack traces. The are special data. -static bool ParseSpecialName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'T') && - ParseCharClass(state, "VTIS") && - ParseType(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && - ParseCallOffset(state) && ParseEncoding(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "GV") && - ParseName(state)) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && - ParseEncoding(state)) { - return true; - } - *state = copy; - - // G++ extensions - if (ParseTwoCharToken(state, "TC") && ParseType(state) && - ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && - DisableAppend(state) && - ParseType(state)) { - RestoreAppend(state, copy.append); - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && - ParseType(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "GR") && ParseName(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && - ParseCallOffset(state) && ParseEncoding(state)) { - return true; - } - *state = copy; - return false; -} - -// <call-offset> ::= h <nv-offset> _ -// ::= v <v-offset> _ -static bool ParseCallOffset(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'h') && - ParseNVOffset(state) && ParseOneCharToken(state, '_')) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'v') && - ParseVOffset(state) && ParseOneCharToken(state, '_')) { - return true; - } - *state = copy; - - return false; -} - -// <nv-offset> ::= <(offset) number> -static bool ParseNVOffset(State *state) { - return ParseNumber(state, NULL); -} - -// <v-offset> ::= <(offset) number> _ <(virtual offset) number> -static bool ParseVOffset(State *state) { - State copy = *state; - if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && - ParseNumber(state, NULL)) { - return true; - } - *state = copy; - return false; -} - -// <ctor-dtor-name> ::= C1 | C2 | C3 -// ::= D0 | D1 | D2 -static bool ParseCtorDtorName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'C') && - ParseCharClass(state, "123")) { - const char * const prev_name = state->prev_name; - const int prev_name_length = state->prev_name_length; - MaybeAppendWithLength(state, prev_name, prev_name_length); - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'D') && - ParseCharClass(state, "012")) { - const char * const prev_name = state->prev_name; - const int prev_name_length = state->prev_name_length; - MaybeAppend(state, "~"); - MaybeAppendWithLength(state, prev_name, prev_name_length); - return true; - } - *state = copy; - return false; -} - -// <type> ::= <CV-qualifiers> <type> -// ::= P <type> # pointer-to -// ::= R <type> # reference-to -// ::= O <type> # rvalue reference-to (C++0x) -// ::= C <type> # complex pair (C 2000) -// ::= G <type> # imaginary (C 2000) -// ::= U <source-name> <type> # vendor extended type qualifier -// ::= <builtin-type> -// ::= <function-type> -// ::= <class-enum-type> -// ::= <array-type> -// ::= <pointer-to-member-type> -// ::= <template-template-param> <template-args> -// ::= <template-param> -// ::= <substitution> -// ::= Dp <type> # pack expansion of (C++0x) -// ::= Dt <expression> E # decltype of an id-expression or class -// # member access (C++0x) -// ::= DT <expression> E # decltype of an expression (C++0x) -// -static bool ParseType(State *state) { - // We should check CV-qualifers, and PRGC things first. - State copy = *state; - if (ParseCVQualifiers(state) && ParseType(state)) { - return true; - } - *state = copy; - - if (ParseCharClass(state, "OPRCG") && ParseType(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && - ParseExpression(state) && ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && - ParseType(state)) { - return true; - } - *state = copy; - - if (ParseBuiltinType(state) || - ParseFunctionType(state) || - ParseClassEnumType(state) || - ParseArrayType(state) || - ParsePointerToMemberType(state) || - ParseSubstitution(state)) { - return true; - } - - if (ParseTemplateTemplateParam(state) && - ParseTemplateArgs(state)) { - return true; - } - *state = copy; - - // Less greedy than <template-template-param> <template-args>. - if (ParseTemplateParam(state)) { - return true; - } - - return false; -} - -// <CV-qualifiers> ::= [r] [V] [K] -// We don't allow empty <CV-qualifiers> to avoid infinite loop in -// ParseType(). -static bool ParseCVQualifiers(State *state) { - int num_cv_qualifiers = 0; - num_cv_qualifiers += ParseOneCharToken(state, 'r'); - num_cv_qualifiers += ParseOneCharToken(state, 'V'); - num_cv_qualifiers += ParseOneCharToken(state, 'K'); - return num_cv_qualifiers > 0; -} - -// <builtin-type> ::= v, etc. -// ::= u <source-name> -static bool ParseBuiltinType(State *state) { - const AbbrevPair *p; - for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[0]) { - MaybeAppend(state, p->real_name); - ++state->mangled_cur; - return true; - } - } - - State copy = *state; - if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { - return true; - } - *state = copy; - return false; -} - -// <function-type> ::= F [Y] <bare-function-type> E -static bool ParseFunctionType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'F') && - Optional(ParseOneCharToken(state, 'Y')) && - ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - return false; -} - -// <bare-function-type> ::= <(signature) type>+ -static bool ParseBareFunctionType(State *state) { - State copy = *state; - DisableAppend(state); - if (OneOrMore(ParseType, state)) { - RestoreAppend(state, copy.append); - MaybeAppend(state, "()"); - return true; - } - *state = copy; - return false; -} - -// <class-enum-type> ::= <name> -static bool ParseClassEnumType(State *state) { - return ParseName(state); -} - -// <array-type> ::= A <(positive dimension) number> _ <(element) type> -// ::= A [<(dimension) expression>] _ <(element) type> -static bool ParseArrayType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && - ParseOneCharToken(state, '_') && ParseType(state)) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && - ParseOneCharToken(state, '_') && ParseType(state)) { - return true; - } - *state = copy; - return false; -} - -// <pointer-to-member-type> ::= M <(class) type> <(member) type> -static bool ParsePointerToMemberType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'M') && ParseType(state) && - ParseType(state)) { - return true; - } - *state = copy; - return false; -} - -// <template-param> ::= T_ -// ::= T <parameter-2 non-negative number> _ -static bool ParseTemplateParam(State *state) { - if (ParseTwoCharToken(state, "T_")) { - MaybeAppend(state, "?"); // We don't support template substitutions. - return true; - } - - State copy = *state; - if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && - ParseOneCharToken(state, '_')) { - MaybeAppend(state, "?"); // We don't support template substitutions. - return true; - } - *state = copy; - return false; -} - - -// <template-template-param> ::= <template-param> -// ::= <substitution> -static bool ParseTemplateTemplateParam(State *state) { - return (ParseTemplateParam(state) || - ParseSubstitution(state)); -} - -// <template-args> ::= I <template-arg>+ E -static bool ParseTemplateArgs(State *state) { - State copy = *state; - DisableAppend(state); - if (ParseOneCharToken(state, 'I') && - OneOrMore(ParseTemplateArg, state) && - ParseOneCharToken(state, 'E')) { - RestoreAppend(state, copy.append); - MaybeAppend(state, "<>"); - return true; - } - *state = copy; - return false; -} - -// <template-arg> ::= <type> -// ::= <expr-primary> -// ::= I <template-arg>* E # argument pack -// ::= X <expression> E -static bool ParseTemplateArg(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'I') && - ZeroOrMore(ParseTemplateArg, state) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - - if (ParseType(state) || - ParseExprPrimary(state)) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'X') && ParseExpression(state) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - return false; -} - -// <expression> ::= <template-param> -// ::= <expr-primary> -// ::= <unary operator-name> <expression> -// ::= <binary operator-name> <expression> <expression> -// ::= <trinary operator-name> <expression> <expression> -// <expression> -// ::= st <type> -// ::= sr <type> <unqualified-name> <template-args> -// ::= sr <type> <unqualified-name> -static bool ParseExpression(State *state) { - if (ParseTemplateParam(state) || ParseExprPrimary(state)) { - return true; - } - - State copy = *state; - if (ParseOperatorName(state) && - ParseExpression(state) && - ParseExpression(state) && - ParseExpression(state)) { - return true; - } - *state = copy; - - if (ParseOperatorName(state) && - ParseExpression(state) && - ParseExpression(state)) { - return true; - } - *state = copy; - - if (ParseOperatorName(state) && - ParseExpression(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "st") && ParseType(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "sr") && ParseType(state) && - ParseUnqualifiedName(state) && - ParseTemplateArgs(state)) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "sr") && ParseType(state) && - ParseUnqualifiedName(state)) { - return true; - } - *state = copy; - return false; -} - -// <expr-primary> ::= L <type> <(value) number> E -// ::= L <type> <(value) float> E -// ::= L <mangled-name> E -// // A bug in g++'s C++ ABI version 2 (-fabi-version=2). -// ::= LZ <encoding> E -static bool ParseExprPrimary(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseNumber(state, NULL) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseFloatNumber(state) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - - if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; - - return false; -} - -// <local-name> := Z <(function) encoding> E <(entity) name> -// [<discriminator>] -// := Z <(function) encoding> E s [<discriminator>] -static bool ParseLocalName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && - ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && - ParseName(state) && Optional(ParseDiscriminator(state))) { - return true; - } - *state = copy; - - if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && - ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { - return true; - } - *state = copy; - return false; -} - -// <discriminator> := _ <(non-negative) number> -static bool ParseDiscriminator(State *state) { - State copy = *state; - if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { - return true; - } - *state = copy; - return false; -} - -// <substitution> ::= S_ -// ::= S <seq-id> _ -// ::= St, etc. -static bool ParseSubstitution(State *state) { - if (ParseTwoCharToken(state, "S_")) { - MaybeAppend(state, "?"); // We don't support substitutions. - return true; - } - - State copy = *state; - if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && - ParseOneCharToken(state, '_')) { - MaybeAppend(state, "?"); // We don't support substitutions. - return true; - } - *state = copy; - - // Expand abbreviations like "St" => "std". - if (ParseOneCharToken(state, 'S')) { - const AbbrevPair *p; - for (p = kSubstitutionList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[1]) { - MaybeAppend(state, "std"); - if (p->real_name[0] != '\0') { - MaybeAppend(state, "::"); - MaybeAppend(state, p->real_name); - } - ++state->mangled_cur; - return true; - } - } - } - *state = copy; - return false; -} - -// Parse <mangled-name>, optionally followed by either a function-clone suffix -// or version suffix. Returns true only if all of "mangled_cur" was consumed. -static bool ParseTopLevelMangledName(State *state) { - if (ParseMangledName(state)) { - if (state->mangled_cur[0] != '\0') { - // Drop trailing function clone suffix, if any. - if (IsFunctionCloneSuffix(state->mangled_cur)) { - return true; - } - // Append trailing version suffix if any. - // ex. _Z3foo@@GLIBCXX_3.4 - if (state->mangled_cur[0] == '@') { - MaybeAppend(state, state->mangled_cur); - return true; - } - return false; // Unconsumed suffix. - } - return true; - } - return false; -} - -// The demangler entry point. -bool Demangle(const char *mangled, char *out, int out_size) { - State state; - InitState(&state, mangled, out, out_size); - return ParseTopLevelMangledName(&state) && !state.overflowed; -} - -_END_GOOGLE_NAMESPACE_
diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h deleted file mode 100644 index 9c75915..0000000 --- a/base/third_party/symbolize/demangle.h +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: Satoru Takabayashi -// -// An async-signal-safe and thread-safe demangler for Itanium C++ ABI -// (aka G++ V3 ABI). - -// The demangler is implemented to be used in async signal handlers to -// symbolize stack traces. We cannot use libstdc++'s -// abi::__cxa_demangle() in such signal handlers since it's not async -// signal safe (it uses malloc() internally). -// -// Note that this demangler doesn't support full demangling. More -// specifically, it doesn't print types of function parameters and -// types of template arguments. It just skips them. However, it's -// still very useful to extract basic information such as class, -// function, constructor, destructor, and operator names. -// -// See the implementation note in demangle.cc if you are interested. -// -// Example: -// -// | Mangled Name | The Demangler | abi::__cxa_demangle() -// |---------------|---------------|----------------------- -// | _Z1fv | f() | f() -// | _Z1fi | f() | f(int) -// | _Z3foo3bar | foo() | foo(bar) -// | _Z1fIiEvi | f<>() | void f<int>(int) -// | _ZN1N1fE | N::f | N::f -// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar() -// | _Zrm1XS_" | operator%() | operator%(X, X) -// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo() -// | _Z1fSs | f() | f(std::basic_string<char, -// | | | std::char_traits<char>, -// | | | std::allocator<char> >) -// -// See the unit test for more examples. -// -// Note: we might want to write demanglers for ABIs other than Itanium -// C++ ABI in the future. -// - -#ifndef BASE_DEMANGLE_H_ -#define BASE_DEMANGLE_H_ - -#include "config.h" - -_START_GOOGLE_NAMESPACE_ - -// Demangle "mangled". On success, return true and write the -// demangled symbol name to "out". Otherwise, return false. -// "out" is modified even if demangling is unsuccessful. -bool Demangle(const char *mangled, char *out, int out_size); - -_END_GOOGLE_NAMESPACE_ - -#endif // BASE_DEMANGLE_H_
diff --git a/base/third_party/symbolize/glog/logging.h b/base/third_party/symbolize/glog/logging.h deleted file mode 100644 index a42c306..0000000 --- a/base/third_party/symbolize/glog/logging.h +++ /dev/null
@@ -1,5 +0,0 @@ -// 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. - -// Empty.
diff --git a/base/third_party/symbolize/glog/raw_logging.h b/base/third_party/symbolize/glog/raw_logging.h deleted file mode 100644 index f5515c4..0000000 --- a/base/third_party/symbolize/glog/raw_logging.h +++ /dev/null
@@ -1,6 +0,0 @@ -// 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. - -#define WARNING 1; -#define RAW_LOG(severity, ...); // Do nothing.
diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc deleted file mode 100644 index e6fbb84..0000000 --- a/base/third_party/symbolize/symbolize.cc +++ /dev/null
@@ -1,883 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: Satoru Takabayashi -// Stack-footprint reduction work done by Raksit Ashok -// -// Implementation note: -// -// We don't use heaps but only use stacks. We want to reduce the -// stack consumption so that the symbolizer can run on small stacks. -// -// Here are some numbers collected with GCC 4.1.0 on x86: -// - sizeof(Elf32_Sym) = 16 -// - sizeof(Elf32_Shdr) = 40 -// - sizeof(Elf64_Sym) = 24 -// - sizeof(Elf64_Shdr) = 64 -// -// This implementation is intended to be async-signal-safe but uses -// some functions which are not guaranteed to be so, such as memchr() -// and memmove(). We assume they are async-signal-safe. -// -// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE -// macro to add platform specific defines (e.g. OS_OPENBSD). - -#ifdef GLOG_BUILD_CONFIG_INCLUDE -#include GLOG_BUILD_CONFIG_INCLUDE -#endif // GLOG_BUILD_CONFIG_INCLUDE - -#include "utilities.h" - -#if defined(HAVE_SYMBOLIZE) - -#include <string.h> - -#include <algorithm> -#include <limits> - -#include "symbolize.h" -#include "demangle.h" - -_START_GOOGLE_NAMESPACE_ - -// We don't use assert() since it's not guaranteed to be -// async-signal-safe. Instead we define a minimal assertion -// macro. So far, we don't need pretty printing for __FILE__, etc. - -// A wrapper for abort() to make it callable in ? :. -static int AssertFail() { - abort(); - return 0; // Should not reach. -} - -#define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail()) - -static SymbolizeCallback g_symbolize_callback = NULL; -void InstallSymbolizeCallback(SymbolizeCallback callback) { - g_symbolize_callback = callback; -} - -static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = - NULL; -void InstallSymbolizeOpenObjectFileCallback( - SymbolizeOpenObjectFileCallback callback) { - g_symbolize_open_object_file_callback = callback; -} - -// This function wraps the Demangle function to provide an interface -// where the input symbol is demangled in-place. -// To keep stack consumption low, we would like this function to not -// get inlined. -static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) { - char demangled[256]; // Big enough for sane demangled symbols. - if (Demangle(out, demangled, sizeof(demangled))) { - // Demangling succeeded. Copy to out if the space allows. - size_t len = strlen(demangled); - if (len + 1 <= (size_t)out_size) { // +1 for '\0'. - SAFE_ASSERT(len < sizeof(demangled)); - memmove(out, demangled, len + 1); - } - } -} - -_END_GOOGLE_NAMESPACE_ - -#if defined(__ELF__) - -#include <dlfcn.h> -#if defined(OS_OPENBSD) -#include <sys/exec_elf.h> -#else -#include <elf.h> -#endif -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "symbolize.h" -#include "config.h" -#include "glog/raw_logging.h" - -// Re-runs fn until it doesn't cause EINTR. -#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR) - -_START_GOOGLE_NAMESPACE_ - -// Read up to "count" bytes from file descriptor "fd" into the buffer -// starting at "buf" while handling short reads and EINTR. On -// success, return the number of bytes read. Otherwise, return -1. -static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) { - SAFE_ASSERT(fd >= 0); - SAFE_ASSERT(count <= std::numeric_limits<ssize_t>::max()); - char *buf0 = reinterpret_cast<char *>(buf); - ssize_t num_bytes = 0; - while (num_bytes < count) { - ssize_t len; - NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes)); - if (len < 0) { // There was an error other than EINTR. - return -1; - } - if (len == 0) { // Reached EOF. - break; - } - num_bytes += len; - } - SAFE_ASSERT(num_bytes <= count); - return num_bytes; -} - -// Read up to "count" bytes from "offset" in the file pointed by file -// descriptor "fd" into the buffer starting at "buf". On success, -// return the number of bytes read. Otherwise, return -1. -static ssize_t ReadFromOffset(const int fd, void *buf, - const size_t count, const off_t offset) { - off_t off = lseek(fd, offset, SEEK_SET); - if (off == (off_t)-1) { - return -1; - } - return ReadPersistent(fd, buf, count); -} - -// Try reading exactly "count" bytes from "offset" bytes in a file -// pointed by "fd" into the buffer starting at "buf" while handling -// short reads and EINTR. On success, return true. Otherwise, return -// false. -static bool ReadFromOffsetExact(const int fd, void *buf, - const size_t count, const off_t offset) { - ssize_t len = ReadFromOffset(fd, buf, count, offset); - return len == count; -} - -// Returns elf_header.e_type if the file pointed by fd is an ELF binary. -static int FileGetElfType(const int fd) { - ElfW(Ehdr) elf_header; - if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { - return -1; - } - if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { - return -1; - } - return elf_header.e_type; -} - -// Read the section headers in the given ELF binary, and if a section -// of the specified type is found, set the output to this section header -// and return true. Otherwise, return false. -// To keep stack consumption low, we would like this function to not get -// inlined. -static ATTRIBUTE_NOINLINE bool -GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const off_t sh_offset, - ElfW(Word) type, ElfW(Shdr) *out) { - // Read at most 16 section headers at a time to save read calls. - ElfW(Shdr) buf[16]; - for (int i = 0; i < sh_num;) { - const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); - const ssize_t num_bytes_to_read = - (sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf); - const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, - sh_offset + i * sizeof(buf[0])); - SAFE_ASSERT(len % sizeof(buf[0]) == 0); - const ssize_t num_headers_in_buf = len / sizeof(buf[0]); - SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0])); - for (int j = 0; j < num_headers_in_buf; ++j) { - if (buf[j].sh_type == type) { - *out = buf[j]; - return true; - } - } - i += num_headers_in_buf; - } - return false; -} - -// There is no particular reason to limit section name to 63 characters, -// but there has (as yet) been no need for anything longer either. -const int kMaxSectionNameLen = 64; - -// name_len should include terminating '\0'. -bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, - ElfW(Shdr) *out) { - ElfW(Ehdr) elf_header; - if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { - return false; - } - - ElfW(Shdr) shstrtab; - off_t shstrtab_offset = (elf_header.e_shoff + - elf_header.e_shentsize * elf_header.e_shstrndx); - if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { - return false; - } - - for (int i = 0; i < elf_header.e_shnum; ++i) { - off_t section_header_offset = (elf_header.e_shoff + - elf_header.e_shentsize * i); - if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { - return false; - } - char header_name[kMaxSectionNameLen]; - if (sizeof(header_name) < name_len) { - RAW_LOG(WARNING, "Section name '%s' is too long (%" PRIuS "); " - "section will not be found (even if present).", name, name_len); - // No point in even trying. - return false; - } - off_t name_offset = shstrtab.sh_offset + out->sh_name; - ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); - if (n_read == -1) { - return false; - } else if (n_read != name_len) { - // Short read -- name could be at end of file. - continue; - } - if (memcmp(header_name, name, name_len) == 0) { - return true; - } - } - return false; -} - -// Read a symbol table and look for the symbol containing the -// pc. Iterate over symbols in a symbol table and look for the symbol -// containing "pc". On success, return true and write the symbol name -// to out. Otherwise, return false. -// To keep stack consumption low, we would like this function to not get -// inlined. -static ATTRIBUTE_NOINLINE bool -FindSymbol(uint64_t pc, const int fd, char *out, int out_size, - uint64_t symbol_offset, const ElfW(Shdr) *strtab, - const ElfW(Shdr) *symtab) { - if (symtab == NULL) { - return false; - } - const int num_symbols = symtab->sh_size / symtab->sh_entsize; - for (int i = 0; i < num_symbols;) { - off_t offset = symtab->sh_offset + i * symtab->sh_entsize; - - // If we are reading Elf64_Sym's, we want to limit this array to - // 32 elements (to keep stack consumption low), otherwise we can - // have a 64 element Elf32_Sym array. -#if __WORDSIZE == 64 -#define NUM_SYMBOLS 32 -#else -#define NUM_SYMBOLS 64 -#endif - - // Read at most NUM_SYMBOLS symbols at once to save read() calls. - ElfW(Sym) buf[NUM_SYMBOLS]; - int num_symbols_to_read = std::min(NUM_SYMBOLS, num_symbols - i); - const ssize_t len = - ReadFromOffset(fd, &buf, sizeof(buf[0]) * num_symbols_to_read, offset); - SAFE_ASSERT(len % sizeof(buf[0]) == 0); - const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); - SAFE_ASSERT(num_symbols_in_buf <= num_symbols_to_read); - for (int j = 0; j < num_symbols_in_buf; ++j) { - const ElfW(Sym)& symbol = buf[j]; - uint64_t start_address = symbol.st_value; - start_address += symbol_offset; - uint64_t end_address = start_address + symbol.st_size; - if (symbol.st_value != 0 && // Skip null value symbols. - symbol.st_shndx != 0 && // Skip undefined symbols. - start_address <= pc && pc < end_address) { - ssize_t len1 = ReadFromOffset(fd, out, out_size, - strtab->sh_offset + symbol.st_name); - if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) { - return false; - } - return true; // Obtained the symbol name. - } - } - i += num_symbols_in_buf; - } - return false; -} - -// Get the symbol name of "pc" from the file pointed by "fd". Process -// both regular and dynamic symbol tables if necessary. On success, -// write the symbol name to "out" and return true. Otherwise, return -// false. -static bool GetSymbolFromObjectFile(const int fd, - uint64_t pc, - char* out, - int out_size, - uint64_t base_address) { - // Read the ELF header. - ElfW(Ehdr) elf_header; - if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { - return false; - } - - ElfW(Shdr) symtab, strtab; - - // Consult a regular symbol table first. - if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, - SHT_SYMTAB, &symtab)) { - if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff + - symtab.sh_link * sizeof(symtab))) { - return false; - } - if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) { - return true; // Found the symbol in a regular symbol table. - } - } - - // If the symbol is not found, then consult a dynamic symbol table. - if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, - SHT_DYNSYM, &symtab)) { - if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff + - symtab.sh_link * sizeof(symtab))) { - return false; - } - if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) { - return true; // Found the symbol in a dynamic symbol table. - } - } - - return false; -} - -namespace { -// Thin wrapper around a file descriptor so that the file descriptor -// gets closed for sure. -struct FileDescriptor { - const int fd_; - explicit FileDescriptor(int fd) : fd_(fd) {} - ~FileDescriptor() { - if (fd_ >= 0) { - NO_INTR(close(fd_)); - } - } - int get() { return fd_; } - - private: - explicit FileDescriptor(const FileDescriptor&); - void operator=(const FileDescriptor&); -}; - -// Helper class for reading lines from file. -// -// Note: we don't use ProcMapsIterator since the object is big (it has -// a 5k array member) and uses async-unsafe functions such as sscanf() -// and snprintf(). -class LineReader { - public: - explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd), - buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) { - } - - // Read '\n'-terminated line from file. On success, modify "bol" - // and "eol", then return true. Otherwise, return false. - // - // Note: if the last line doesn't end with '\n', the line will be - // dropped. It's an intentional behavior to make the code simple. - bool ReadLine(const char **bol, const char **eol) { - if (BufferIsEmpty()) { // First time. - const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); - if (num_bytes <= 0) { // EOF or error. - return false; - } - eod_ = buf_ + num_bytes; - bol_ = buf_; - } else { - bol_ = eol_ + 1; // Advance to the next line in the buffer. - SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". - if (!HasCompleteLine()) { - const int incomplete_line_length = eod_ - bol_; - // Move the trailing incomplete line to the beginning. - memmove(buf_, bol_, incomplete_line_length); - // Read text from file and append it. - char * const append_pos = buf_ + incomplete_line_length; - const int capacity_left = buf_len_ - incomplete_line_length; - const ssize_t num_bytes = ReadPersistent(fd_, append_pos, - capacity_left); - if (num_bytes <= 0) { // EOF or error. - return false; - } - eod_ = append_pos + num_bytes; - bol_ = buf_; - } - } - eol_ = FindLineFeed(); - if (eol_ == NULL) { // '\n' not found. Malformed line. - return false; - } - *eol_ = '\0'; // Replace '\n' with '\0'. - - *bol = bol_; - *eol = eol_; - return true; - } - - // Beginning of line. - const char *bol() { - return bol_; - } - - // End of line. - const char *eol() { - return eol_; - } - - private: - explicit LineReader(const LineReader&); - void operator=(const LineReader&); - - char *FindLineFeed() { - return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_)); - } - - bool BufferIsEmpty() { - return buf_ == eod_; - } - - bool HasCompleteLine() { - return !BufferIsEmpty() && FindLineFeed() != NULL; - } - - const int fd_; - char * const buf_; - const int buf_len_; - char *bol_; - char *eol_; - const char *eod_; // End of data in "buf_". -}; -} // namespace - -// Place the hex number read from "start" into "*hex". The pointer to -// the first non-hex character or "end" is returned. -static char *GetHex(const char *start, const char *end, uint64_t *hex) { - *hex = 0; - const char *p; - for (p = start; p < end; ++p) { - int ch = *p; - if ((ch >= '0' && ch <= '9') || - (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { - *hex = (*hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9); - } else { // Encountered the first non-hex character. - break; - } - } - SAFE_ASSERT(p <= end); - return const_cast<char *>(p); -} - -// 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). -static ATTRIBUTE_NOINLINE int -OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, - uint64_t &start_address, - uint64_t &base_address, - char *out_file_name, - int out_file_name_size) { - int object_fd; - - int maps_fd; - NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY)); - FileDescriptor wrapped_maps_fd(maps_fd); - if (wrapped_maps_fd.get() < 0) { - return -1; - } - - int mem_fd; - NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY)); - FileDescriptor wrapped_mem_fd(mem_fd); - if (wrapped_mem_fd.get() < 0) { - return -1; - } - - // Iterate over maps and look for the map containing the pc. Then - // look into the symbol tables inside. - char buf[1024]; // Big enough for line of sane /proc/self/maps - int num_maps = 0; - LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); - while (true) { - num_maps++; - const char *cursor; - const char *eol; - if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. - return -1; - } - - // Start parsing line in /proc/self/maps. Here is an example: - // - // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat - // - // We want start address (08048000), end address (0804c000), flags - // (r-xp) and file name (/bin/cat). - - // Read start address. - cursor = GetHex(cursor, eol, &start_address); - if (cursor == eol || *cursor != '-') { - return -1; // Malformed line. - } - ++cursor; // Skip '-'. - - // Read end address. - uint64_t end_address; - cursor = GetHex(cursor, eol, &end_address); - if (cursor == eol || *cursor != ' ') { - return -1; // Malformed line. - } - ++cursor; // Skip ' '. - - // Read flags. Skip flags until we encounter a space or eol. - const char * const flags_start = cursor; - while (cursor < eol && *cursor != ' ') { - ++cursor; - } - // We expect at least four letters for flags (ex. "r-xp"). - if (cursor == eol || cursor < flags_start + 4) { - return -1; // Malformed line. - } - - // Determine the base address by reading ELF headers in process memory. - ElfW(Ehdr) ehdr; - if (flags_start[0] == 'r' && - ReadFromOffsetExact(mem_fd, &ehdr, sizeof(ElfW(Ehdr)), start_address) && - memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) { - switch (ehdr.e_type) { - case ET_EXEC: - base_address = 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. - base_address = start_address; - for (unsigned i = 0; i != ehdr.e_phnum; ++i) { - ElfW(Phdr) phdr; - if (ReadFromOffsetExact( - mem_fd, &phdr, sizeof(phdr), - start_address + ehdr.e_phoff + i * sizeof(phdr)) && - phdr.p_type == PT_LOAD && phdr.p_offset == 0) { - base_address = start_address - 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; - } - } - - // Check start and end addresses. - if (!(start_address <= pc && pc < end_address)) { - continue; // We skip this map. PC isn't in this map. - } - - // Check flags. We are only interested in "r-x" maps. - if (memcmp(flags_start, "r-x", 3) != 0) { // Not a "r-x" map. - continue; // We skip this map. - } - ++cursor; // Skip ' '. - - // Read file offset. - uint64_t file_offset; - cursor = GetHex(cursor, eol, &file_offset); - if (cursor == eol || *cursor != ' ') { - return -1; // Malformed line. - } - ++cursor; // Skip ' '. - - // Skip to file name. "cursor" now points to dev. We need to - // skip at least two spaces for dev and inode. - int num_spaces = 0; - while (cursor < eol) { - if (*cursor == ' ') { - ++num_spaces; - } else if (num_spaces >= 2) { - // The first non-space character after skipping two spaces - // is the beginning of the file name. - break; - } - ++cursor; - } - if (cursor == eol) { - return -1; // Malformed line. - } - - // Finally, "cursor" now points to file name of our interest. - NO_INTR(object_fd = open(cursor, O_RDONLY)); - if (object_fd < 0) { - // Failed to open object file. Copy the object file name to - // |out_file_name|. - strncpy(out_file_name, cursor, out_file_name_size); - // Making sure |out_file_name| is always null-terminated. - out_file_name[out_file_name_size - 1] = '\0'; - return -1; - } - return object_fd; - } -} - -// 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. -// 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 NULL; - - if (base < 2 || base > 16) { - buf[0] = '\000'; - return NULL; - } - - 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 NULL; - } - *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 NULL; - } - - // 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; -} - -// Safely appends string |source| to string |dest|. Never writes past the -// buffer size |dest_size| and guarantees that |dest| is null-terminated. -void SafeAppendString(const char* source, char* dest, int dest_size) { - int dest_string_length = strlen(dest); - SAFE_ASSERT(dest_string_length < dest_size); - dest += dest_string_length; - dest_size -= dest_string_length; - strncpy(dest, source, dest_size); - // Making sure |dest| is always null-terminated. - dest[dest_size - 1] = '\0'; -} - -// Converts a 64-bit value into a hex string, and safely appends it to |dest|. -// Never writes past the buffer size |dest_size| and guarantees that |dest| is -// null-terminated. -void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) { - // 64-bit numbers in hex can have up to 16 digits. - char buf[17] = {'\0'}; - SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size); -} - -// The implementation of our symbolization routine. If it -// successfully finds the symbol containing "pc" and obtains the -// symbol name, returns true and write the symbol name to "out". -// Otherwise, returns false. If Callback function is installed via -// InstallSymbolizeCallback(), the function is also called in this function, -// and "out" is used as its output. -// To keep stack consumption low, we would like this function to not -// get inlined. -static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, - int out_size) { - uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); - uint64_t start_address = 0; - uint64_t base_address = 0; - int object_fd = -1; - - if (out_size < 1) { - return false; - } - out[0] = '\0'; - SafeAppendString("(", out, out_size); - - if (g_symbolize_open_object_file_callback) { - object_fd = g_symbolize_open_object_file_callback(pc0, start_address, - base_address, out + 1, - out_size - 1); - } else { - object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address, - base_address, - out + 1, - out_size - 1); - } - - // Check whether a file name was returned. -#if !defined(PRINT_UNSYMBOLIZED_STACK_TRACES) - if (object_fd < 0) { -#endif - if (out[1]) { - // The object file containing PC was determined successfully however the - // object file was not opened successfully. This is still considered - // success because the object file name and offset are known and tools - // like asan_symbolize.py can be used for the symbolization. - out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. - SafeAppendString("+0x", out, out_size); - SafeAppendHexNumber(pc0 - base_address, out, out_size); - SafeAppendString(")", out, out_size); - return true; - } - // Failed to determine the object file containing PC. Bail out. - return false; -#if !defined(PRINT_UNSYMBOLIZED_STACK_TRACES) - } -#endif - FileDescriptor wrapped_object_fd(object_fd); - int elf_type = FileGetElfType(wrapped_object_fd.get()); - if (elf_type == -1) { - return false; - } - if (g_symbolize_callback) { - // Run the call back if it's installed. - // Note: relocation (and much of the rest of this code) will be - // wrong for prelinked shared libraries and PIE executables. - uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0; - int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(), - pc, out, out_size, - relocation); - if (num_bytes_written > 0) { - out += num_bytes_written; - out_size -= num_bytes_written; - } - } - if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0, - out, out_size, base_address)) { - return false; - } - - // Symbolization succeeded. Now we try to demangle the symbol. - DemangleInplace(out, out_size); - return true; -} - -_END_GOOGLE_NAMESPACE_ - -#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) - -#include <dlfcn.h> -#include <string.h> - -_START_GOOGLE_NAMESPACE_ - -static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, - int out_size) { - Dl_info info; - if (dladdr(pc, &info)) { - if ((int)strlen(info.dli_sname) < out_size) { - strcpy(out, info.dli_sname); - // Symbolization succeeded. Now we try to demangle the symbol. - DemangleInplace(out, out_size); - return true; - } - } - return false; -} - -_END_GOOGLE_NAMESPACE_ - -#else -# error BUG: HAVE_SYMBOLIZE was wrongly set -#endif - -_START_GOOGLE_NAMESPACE_ - -bool Symbolize(void *pc, char *out, int out_size) { - SAFE_ASSERT(out_size >= 0); - return SymbolizeAndDemangle(pc, out, out_size); -} - -_END_GOOGLE_NAMESPACE_ - -#else /* HAVE_SYMBOLIZE */ - -#include <assert.h> - -#include "config.h" - -_START_GOOGLE_NAMESPACE_ - -// TODO: Support other environments. -bool Symbolize(void *pc, char *out, int out_size) { - assert(0); - return false; -} - -_END_GOOGLE_NAMESPACE_ - -#endif
diff --git a/base/third_party/symbolize/symbolize.h b/base/third_party/symbolize/symbolize.h deleted file mode 100644 index aeb2fe3..0000000 --- a/base/third_party/symbolize/symbolize.h +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: Satoru Takabayashi -// -// This library provides Symbolize() function that symbolizes program -// counters to their corresponding symbol names on linux platforms. -// This library has a minimal implementation of an ELF symbol table -// reader (i.e. it doesn't depend on libelf, etc.). -// -// The algorithm used in Symbolize() is as follows. -// -// 1. Go through a list of maps in /proc/self/maps and find the map -// containing the program counter. -// -// 2. Open the mapped file and find a regular symbol table inside. -// Iterate over symbols in the symbol table and look for the symbol -// containing the program counter. If such a symbol is found, -// obtain the symbol name, and demangle the symbol if possible. -// If the symbol isn't found in the regular symbol table (binary is -// stripped), try the same thing with a dynamic symbol table. -// -// Note that Symbolize() is originally implemented to be used in -// FailureSignalHandler() in base/google.cc. Hence it doesn't use -// malloc() and other unsafe operations. It should be both -// thread-safe and async-signal-safe. - -#ifndef BASE_SYMBOLIZE_H_ -#define BASE_SYMBOLIZE_H_ - -#include "utilities.h" -#include "config.h" -#include "glog/logging.h" - -#ifdef HAVE_SYMBOLIZE - -#if defined(__ELF__) // defined by gcc -#if defined(__OpenBSD__) -#include <sys/exec_elf.h> -#else -#include <elf.h> -#endif - -#if !defined(ANDROID) -#include <link.h> // For ElfW() macro. -#endif - -// For systems where SIZEOF_VOID_P is not defined, determine it -// based on __LP64__ (defined by gcc on 64-bit systems) -#if !defined(SIZEOF_VOID_P) -# if defined(__LP64__) -# define SIZEOF_VOID_P 8 -# else -# define SIZEOF_VOID_P 4 -# endif -#endif - -// If there is no ElfW macro, let's define it by ourself. -#ifndef ElfW -# if SIZEOF_VOID_P == 4 -# define ElfW(type) Elf32_##type -# elif SIZEOF_VOID_P == 8 -# define ElfW(type) Elf64_##type -# else -# error "Unknown sizeof(void *)" -# endif -#endif - -_START_GOOGLE_NAMESPACE_ - -// Gets the section header for the given name, if it exists. Returns true on -// success. Otherwise, returns false. -bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, - ElfW(Shdr) *out); - -_END_GOOGLE_NAMESPACE_ - -#endif /* __ELF__ */ - -_START_GOOGLE_NAMESPACE_ - -// Restrictions on the callbacks that follow: -// - The callbacks must not use heaps but only use stacks. -// - The callbacks must be async-signal-safe. - -// Installs a callback function, which will be called right before a symbol name -// is printed. The callback is intended to be used for showing a file name and a -// line number preceding a symbol name. -// "fd" is a file descriptor of the object file containing the program -// counter "pc". The callback function should write output to "out" -// and return the size of the output written. On error, the callback -// function should return -1. -typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size, - uint64_t relocation); -void InstallSymbolizeCallback(SymbolizeCallback callback); - -// Installs a callback function, which will be called instead of -// OpenObjectFileContainingPcAndGetStartAddress. The callback is expected -// to 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). -typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc, - uint64_t &start_address, - uint64_t &base_address, - char *out_file_name, - int out_file_name_size); -void InstallSymbolizeOpenObjectFileCallback( - SymbolizeOpenObjectFileCallback callback); - -_END_GOOGLE_NAMESPACE_ - -#endif - -_START_GOOGLE_NAMESPACE_ - -// Symbolizes a program counter. On success, returns true and write the -// symbol name to "out". The symbol name is demangled if possible -// (supports symbols generated by GCC 3.x or newer). Otherwise, -// returns false. -bool Symbolize(void *pc, char *out, int out_size); - -_END_GOOGLE_NAMESPACE_ - -#endif // BASE_SYMBOLIZE_H_
diff --git a/base/third_party/symbolize/utilities.h b/base/third_party/symbolize/utilities.h deleted file mode 100644 index 65c5ba0..0000000 --- a/base/third_party/symbolize/utilities.h +++ /dev/null
@@ -1,11 +0,0 @@ -// 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 <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define HAVE_SYMBOLIZE 1 -#define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc index daccc0e..d45211f 100644 --- a/base/threading/platform_thread_win.cc +++ b/base/threading/platform_thread_win.cc
@@ -6,9 +6,6 @@ #include <stddef.h> -#include "base/debug/activity_tracker.h" -#include "base/debug/alias.h" -#include "base/debug/profiler.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/strings/utf_string_conversions.h" @@ -237,10 +234,6 @@ if (!thread_id) last_error = ::GetLastError(); - // Record information about the exiting thread in case joining hangs. - base::debug::Alias(&thread_id); - base::debug::Alias(&last_error); - // Record the event that this thread is blocking upon (for hang diagnosis). base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc index 5aacdad..83c9dcb 100644 --- a/base/threading/post_task_and_reply_impl.cc +++ b/base/threading/post_task_and_reply_impl.cc
@@ -7,7 +7,6 @@ #include <utility> #include "base/bind.h" -#include "base/debug/leak_annotations.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" @@ -54,7 +53,6 @@ // https://crbug.com/829122). auto relay_to_delete = std::make_unique<PostTaskAndReplyRelay>(std::move(*this)); - ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get()); reply_task_runner_->DeleteSoon(from_here_, std::move(relay_to_delete)); }
diff --git a/base/win/scoped_handle_verifier.cc b/base/win/scoped_handle_verifier.cc index 930d147..080bdbc 100644 --- a/base/win/scoped_handle_verifier.cc +++ b/base/win/scoped_handle_verifier.cc
@@ -9,8 +9,6 @@ #include <unordered_map> -#include "base/debug/alias.h" -#include "base/debug/stack_trace.h" #include "base/synchronization/lock_impl.h" #include "base/win/current_module.h" @@ -156,9 +154,7 @@ std::pair<HandleMap::iterator, bool> result = map_.insert(item); if (!result.second) { ScopedHandleVerifierInfo other = result.first->second; - base::debug::Alias(&other); auto creation_stack = creation_stack_; - base::debug::Alias(&creation_stack); CHECK(false); // Attempt to start tracking already tracked handle. } } @@ -174,15 +170,12 @@ HandleMap::iterator i = map_.find(handle); if (i == map_.end()) { auto creation_stack = creation_stack_; - base::debug::Alias(&creation_stack); CHECK(false); // Attempting to close an untracked handle. } ScopedHandleVerifierInfo other = i->second; if (other.owner != owner) { - base::debug::Alias(&other); auto creation_stack = creation_stack_; - base::debug::Alias(&creation_stack); CHECK(false); // Attempting to close a handle not owned by opener. } @@ -206,9 +199,7 @@ return; ScopedHandleVerifierInfo other = i->second; - base::debug::Alias(&other); auto creation_stack = creation_stack_; - base::debug::Alias(&creation_stack); CHECK(false); // CloseHandle called on tracked handle. }
diff --git a/base/win/scoped_handle_verifier.h b/base/win/scoped_handle_verifier.h index 008e790..a7c131e 100644 --- a/base/win/scoped_handle_verifier.h +++ b/base/win/scoped_handle_verifier.h
@@ -10,7 +10,6 @@ #include <unordered_map> #include "base/base_export.h" -#include "base/debug/stack_trace.h" #include "base/hash.h" #include "base/synchronization/lock_impl.h" #include "base/threading/thread_local.h"
diff --git a/base/win/scoped_hdc.h b/base/win/scoped_hdc.h index 890e34a..ef50e05 100644 --- a/base/win/scoped_hdc.h +++ b/base/win/scoped_hdc.h
@@ -7,7 +7,6 @@ #include <windows.h> -#include "base/debug/gdi_debug_util_win.h" #include "base/logging.h" #include "base/macros.h" #include "base/win/scoped_handle.h"
diff --git a/build/gen.py b/build/gen.py index 39257c9..2755480 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -231,11 +231,6 @@ 'base/callback_helpers.cc', 'base/callback_internal.cc', 'base/command_line.cc', - 'base/debug/alias.cc', - 'base/debug/crash_logging.cc', - 'base/debug/dump_without_crashing.cc', - 'base/debug/stack_trace.cc', - 'base/debug/task_annotator.cc', 'base/environment.cc', 'base/files/file.cc', 'base/files/file_enumerator.cc', @@ -543,8 +538,6 @@ if is_posix: static_libraries['base']['sources'].extend([ 'base/base_paths_posix.cc', - 'base/debug/debugger_posix.cc', - 'base/debug/stack_trace_posix.cc', 'base/files/file_enumerator_posix.cc', 'base/files/file_descriptor_watcher_posix.cc', 'base/files/file_posix.cc', @@ -664,11 +657,6 @@ static_libraries['base']['sources'].extend([ 'base/base_paths_win.cc', 'base/cpu.cc', - 'base/debug/close_handle_hook_win.cc', - 'base/debug/debugger.cc', - 'base/debug/debugger_win.cc', - 'base/debug/profiler.cc', - 'base/debug/stack_trace_win.cc', 'base/files/file_enumerator_win.cc', 'base/files/file_path_watcher_win.cc', 'base/files/file_util_win.cc', @@ -686,8 +674,6 @@ 'base/process/process_iterator_win.cc', 'base/process/process_metrics_win.cc', 'base/process/process_win.cc', - 'base/profiler/native_stack_sampler_win.cc', - 'base/profiler/win32_stack_frame_unwinder.cc', 'base/rand_util_win.cc', 'base/strings/sys_string_conversions_win.cc', 'base/synchronization/condition_variable_win.cc',