diff options
Diffstat (limited to 'base/debug')
-rw-r--r-- | base/debug/activity_analyzer.cc | 412 | ||||
-rw-r--r-- | base/debug/activity_analyzer.h | 262 | ||||
-rw-r--r-- | base/debug/activity_analyzer_unittest.cc | 546 | ||||
-rw-r--r-- | base/debug/asan_invalid_access.cc | 101 | ||||
-rw-r--r-- | base/debug/asan_invalid_access.h | 46 | ||||
-rw-r--r-- | base/debug/crash_logging_unittest.cc | 17 | ||||
-rw-r--r-- | base/debug/proc_maps_linux_unittest.cc | 328 | ||||
-rw-r--r-- | base/debug/stack_trace_unittest.cc | 320 | ||||
-rw-r--r-- | base/debug/thread_heap_usage_tracker.cc | 340 | ||||
-rw-r--r-- | base/debug/thread_heap_usage_tracker_unittest.cc | 607 |
10 files changed, 0 insertions, 2979 deletions
diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc deleted file mode 100644 index d787829579..0000000000 --- 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 9add85a9e0..0000000000 --- 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/activity_analyzer_unittest.cc b/base/debug/activity_analyzer_unittest.cc deleted file mode 100644 index e08b43aff3..0000000000 --- a/base/debug/activity_analyzer_unittest.cc +++ /dev/null @@ -1,546 +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 <atomic> -#include <memory> - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/debug/activity_tracker.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/ptr_util.h" -#include "base/pending_task.h" -#include "base/process/process.h" -#include "base/stl_util.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/spin_wait.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { - -namespace { - -class TestActivityTracker : public ThreadActivityTracker { - public: - TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size) - : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size), - mem_segment_(std::move(memory)) {} - - ~TestActivityTracker() override = default; - - private: - std::unique_ptr<char[]> mem_segment_; -}; - -} // namespace - - -class ActivityAnalyzerTest : public testing::Test { - public: - const int kMemorySize = 1 << 20; // 1MiB - const int kStackSize = 1 << 10; // 1KiB - - ActivityAnalyzerTest() = default; - - ~ActivityAnalyzerTest() override { - GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); - if (global_tracker) { - global_tracker->ReleaseTrackerForCurrentThreadForTesting(); - delete global_tracker; - } - } - - std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() { - std::unique_ptr<char[]> memory(new char[kStackSize]); - return std::make_unique<TestActivityTracker>(std::move(memory), kStackSize); - } - - template <typename Function> - void AsOtherProcess(int64_t pid, Function function) { - std::unique_ptr<GlobalActivityTracker> old_global = - GlobalActivityTracker::ReleaseForTesting(); - ASSERT_TRUE(old_global); - - PersistentMemoryAllocator* old_allocator = old_global->allocator(); - std::unique_ptr<PersistentMemoryAllocator> new_allocator( - std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(old_allocator->data()), old_allocator->size(), 0, - 0, "", false)); - GlobalActivityTracker::CreateWithAllocator(std::move(new_allocator), 3, - pid); - - function(); - - GlobalActivityTracker::ReleaseForTesting(); - GlobalActivityTracker::SetForTesting(std::move(old_global)); - } - - static void DoNothing() {} -}; - -TEST_F(ActivityAnalyzerTest, ThreadAnalyzerConstruction) { - std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker(); - { - ThreadActivityAnalyzer analyzer(*tracker); - EXPECT_TRUE(analyzer.IsValid()); - EXPECT_EQ(PlatformThread::GetName(), analyzer.GetThreadName()); - } - - // TODO(bcwhite): More tests once Analyzer does more. -} - - -// GlobalActivityAnalyzer tests below. - -namespace { - -class SimpleActivityThread : public SimpleThread { - public: - SimpleActivityThread(const std::string& name, - const void* source, - Activity::Type activity, - const ActivityData& data) - : SimpleThread(name, Options()), - source_(source), - activity_(activity), - data_(data), - ready_(false), - exit_(false), - exit_condition_(&lock_) {} - - ~SimpleActivityThread() override = default; - - void Run() override { - ThreadActivityTracker::ActivityId id = - GlobalActivityTracker::Get() - ->GetOrCreateTrackerForCurrentThread() - ->PushActivity(source_, activity_, data_); - - { - AutoLock auto_lock(lock_); - ready_.store(true, std::memory_order_release); - while (!exit_.load(std::memory_order_relaxed)) - exit_condition_.Wait(); - } - - GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id); - } - - void Exit() { - AutoLock auto_lock(lock_); - exit_.store(true, std::memory_order_relaxed); - exit_condition_.Signal(); - } - - void WaitReady() { - SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire)); - } - - private: - const void* source_; - Activity::Type activity_; - ActivityData data_; - - std::atomic<bool> ready_; - std::atomic<bool> exit_; - Lock lock_; - ConditionVariable exit_condition_; - - DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); -}; - -} // namespace - -TEST_F(ActivityAnalyzerTest, GlobalAnalyzerConstruction) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker::Get()->process_data().SetString("foo", "bar"); - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true)); - - // The only thread at this point is the test thread of this process. - const int64_t pid = analyzer.GetFirstProcess(); - ASSERT_NE(0, pid); - ThreadActivityAnalyzer* ta1 = analyzer.GetFirstAnalyzer(pid); - ASSERT_TRUE(ta1); - EXPECT_FALSE(analyzer.GetNextAnalyzer()); - ThreadActivityAnalyzer::ThreadKey tk1 = ta1->GetThreadKey(); - EXPECT_EQ(ta1, analyzer.GetAnalyzerForThread(tk1)); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - // Create a second thread that will do something. - SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK, - ActivityData::ForTask(11)); - t2.Start(); - t2.WaitReady(); - - // Now there should be two. Calling GetFirstProcess invalidates any - // previously returned analyzer pointers. - ASSERT_EQ(pid, analyzer.GetFirstProcess()); - EXPECT_TRUE(analyzer.GetFirstAnalyzer(pid)); - EXPECT_TRUE(analyzer.GetNextAnalyzer()); - EXPECT_FALSE(analyzer.GetNextAnalyzer()); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - // Let thread exit. - t2.Exit(); - t2.Join(); - - // Now there should be only one again. - ASSERT_EQ(pid, analyzer.GetFirstProcess()); - ThreadActivityAnalyzer* ta2 = analyzer.GetFirstAnalyzer(pid); - ASSERT_TRUE(ta2); - EXPECT_FALSE(analyzer.GetNextAnalyzer()); - ThreadActivityAnalyzer::ThreadKey tk2 = ta2->GetThreadKey(); - EXPECT_EQ(ta2, analyzer.GetAnalyzerForThread(tk2)); - EXPECT_EQ(tk1, tk2); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - // Verify that there is process data. - const ActivityUserData::Snapshot& data_snapshot = - analyzer.GetProcessDataSnapshot(pid); - ASSERT_LE(1U, data_snapshot.size()); - EXPECT_EQ("bar", data_snapshot.at("foo").GetString()); -} - -TEST_F(ActivityAnalyzerTest, GlobalAnalyzerFromSharedMemory) { - SharedMemoryHandle handle1; - SharedMemoryHandle handle2; - - { - std::unique_ptr<SharedMemory> shmem(new SharedMemory()); - ASSERT_TRUE(shmem->CreateAndMapAnonymous(kMemorySize)); - handle1 = shmem->handle().Duplicate(); - ASSERT_TRUE(handle1.IsValid()); - handle2 = shmem->handle().Duplicate(); - ASSERT_TRUE(handle2.IsValid()); - } - - GlobalActivityTracker::CreateWithSharedMemoryHandle(handle1, kMemorySize, 0, - "", 3); - GlobalActivityTracker::Get()->process_data().SetString("foo", "bar"); - - std::unique_ptr<GlobalActivityAnalyzer> analyzer = - GlobalActivityAnalyzer::CreateWithSharedMemoryHandle(handle2, - kMemorySize); - - const int64_t pid = analyzer->GetFirstProcess(); - ASSERT_NE(0, pid); - const ActivityUserData::Snapshot& data_snapshot = - analyzer->GetProcessDataSnapshot(pid); - ASSERT_LE(1U, data_snapshot.size()); - EXPECT_EQ("bar", data_snapshot.at("foo").GetString()); -} - -TEST_F(ActivityAnalyzerTest, UserDataSnapshotTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - ThreadActivityAnalyzer::Snapshot tracker_snapshot; - - const char string1a[] = "string1a"; - const char string1b[] = "string1b"; - const char string2a[] = "string2a"; - const char string2b[] = "string2b"; - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer global_analyzer( - std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", - true)); - - ThreadActivityTracker* tracker = - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - - { - ScopedActivity activity1(1, 11, 111); - ActivityUserData& user_data1 = activity1.user_data(); - user_data1.Set("raw1", "foo1", 4); - user_data1.SetString("string1", "bar1"); - user_data1.SetChar("char1", '1'); - user_data1.SetInt("int1", -1111); - user_data1.SetUint("uint1", 1111); - user_data1.SetBool("bool1", true); - user_data1.SetReference("ref1", string1a, sizeof(string1a)); - user_data1.SetStringReference("sref1", string1b); - - { - ScopedActivity activity2(2, 22, 222); - ActivityUserData& user_data2 = activity2.user_data(); - user_data2.Set("raw2", "foo2", 4); - user_data2.SetString("string2", "bar2"); - user_data2.SetChar("char2", '2'); - user_data2.SetInt("int2", -2222); - user_data2.SetUint("uint2", 2222); - user_data2.SetBool("bool2", false); - user_data2.SetReference("ref2", string2a, sizeof(string2a)); - user_data2.SetStringReference("sref2", string2b); - - ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot)); - ASSERT_EQ(2U, tracker_snapshot.activity_stack.size()); - - ThreadActivityAnalyzer analyzer(*tracker); - analyzer.AddGlobalInformation(&global_analyzer); - const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot = - analyzer.activity_snapshot(); - ASSERT_EQ(2U, analyzer_snapshot.user_data_stack.size()); - const ActivityUserData::Snapshot& user_data = - analyzer_snapshot.user_data_stack.at(1); - EXPECT_EQ(8U, user_data.size()); - ASSERT_TRUE(ContainsKey(user_data, "raw2")); - EXPECT_EQ("foo2", user_data.at("raw2").Get().as_string()); - ASSERT_TRUE(ContainsKey(user_data, "string2")); - EXPECT_EQ("bar2", user_data.at("string2").GetString().as_string()); - ASSERT_TRUE(ContainsKey(user_data, "char2")); - EXPECT_EQ('2', user_data.at("char2").GetChar()); - ASSERT_TRUE(ContainsKey(user_data, "int2")); - EXPECT_EQ(-2222, user_data.at("int2").GetInt()); - ASSERT_TRUE(ContainsKey(user_data, "uint2")); - EXPECT_EQ(2222U, user_data.at("uint2").GetUint()); - ASSERT_TRUE(ContainsKey(user_data, "bool2")); - EXPECT_FALSE(user_data.at("bool2").GetBool()); - ASSERT_TRUE(ContainsKey(user_data, "ref2")); - EXPECT_EQ(string2a, user_data.at("ref2").GetReference().data()); - EXPECT_EQ(sizeof(string2a), user_data.at("ref2").GetReference().size()); - ASSERT_TRUE(ContainsKey(user_data, "sref2")); - EXPECT_EQ(string2b, user_data.at("sref2").GetStringReference().data()); - EXPECT_EQ(strlen(string2b), - user_data.at("sref2").GetStringReference().size()); - } - - ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot)); - ASSERT_EQ(1U, tracker_snapshot.activity_stack.size()); - - ThreadActivityAnalyzer analyzer(*tracker); - analyzer.AddGlobalInformation(&global_analyzer); - const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot = - analyzer.activity_snapshot(); - ASSERT_EQ(1U, analyzer_snapshot.user_data_stack.size()); - const ActivityUserData::Snapshot& user_data = - analyzer_snapshot.user_data_stack.at(0); - EXPECT_EQ(8U, user_data.size()); - EXPECT_EQ("foo1", user_data.at("raw1").Get().as_string()); - EXPECT_EQ("bar1", user_data.at("string1").GetString().as_string()); - EXPECT_EQ('1', user_data.at("char1").GetChar()); - EXPECT_EQ(-1111, user_data.at("int1").GetInt()); - EXPECT_EQ(1111U, user_data.at("uint1").GetUint()); - EXPECT_TRUE(user_data.at("bool1").GetBool()); - EXPECT_EQ(string1a, user_data.at("ref1").GetReference().data()); - EXPECT_EQ(sizeof(string1a), user_data.at("ref1").GetReference().size()); - EXPECT_EQ(string1b, user_data.at("sref1").GetStringReference().data()); - EXPECT_EQ(strlen(string1b), - user_data.at("sref1").GetStringReference().size()); - } - - ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot)); - ASSERT_EQ(0U, tracker_snapshot.activity_stack.size()); -} - -TEST_F(ActivityAnalyzerTest, GlobalUserDataTest) { - const int64_t pid = GetCurrentProcId(); - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - - const char string1[] = "foo"; - const char string2[] = "bar"; - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer global_analyzer( - std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", - true)); - - ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data(); - ASSERT_NE(0U, process_data.id()); - process_data.Set("raw", "foo", 3); - process_data.SetString("string", "bar"); - process_data.SetChar("char", '9'); - process_data.SetInt("int", -9999); - process_data.SetUint("uint", 9999); - process_data.SetBool("bool", true); - process_data.SetReference("ref", string1, sizeof(string1)); - process_data.SetStringReference("sref", string2); - - int64_t first_pid = global_analyzer.GetFirstProcess(); - DCHECK_EQ(pid, first_pid); - const ActivityUserData::Snapshot& snapshot = - global_analyzer.GetProcessDataSnapshot(pid); - ASSERT_TRUE(ContainsKey(snapshot, "raw")); - EXPECT_EQ("foo", snapshot.at("raw").Get().as_string()); - ASSERT_TRUE(ContainsKey(snapshot, "string")); - EXPECT_EQ("bar", snapshot.at("string").GetString().as_string()); - ASSERT_TRUE(ContainsKey(snapshot, "char")); - EXPECT_EQ('9', snapshot.at("char").GetChar()); - ASSERT_TRUE(ContainsKey(snapshot, "int")); - EXPECT_EQ(-9999, snapshot.at("int").GetInt()); - ASSERT_TRUE(ContainsKey(snapshot, "uint")); - EXPECT_EQ(9999U, snapshot.at("uint").GetUint()); - ASSERT_TRUE(ContainsKey(snapshot, "bool")); - EXPECT_TRUE(snapshot.at("bool").GetBool()); - ASSERT_TRUE(ContainsKey(snapshot, "ref")); - EXPECT_EQ(string1, snapshot.at("ref").GetReference().data()); - EXPECT_EQ(sizeof(string1), snapshot.at("ref").GetReference().size()); - ASSERT_TRUE(ContainsKey(snapshot, "sref")); - EXPECT_EQ(string2, snapshot.at("sref").GetStringReference().data()); - EXPECT_EQ(strlen(string2), snapshot.at("sref").GetStringReference().size()); -} - -TEST_F(ActivityAnalyzerTest, GlobalModulesTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - - PersistentMemoryAllocator* allocator = global->allocator(); - GlobalActivityAnalyzer global_analyzer( - std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", - true)); - - GlobalActivityTracker::ModuleInfo info1; - info1.is_loaded = true; - info1.address = 0x12345678; - info1.load_time = 1111; - info1.size = 0xABCDEF; - info1.timestamp = 111; - info1.age = 11; - info1.identifier[0] = 1; - info1.file = "anything"; - info1.debug_file = "elsewhere"; - - global->RecordModuleInfo(info1); - std::vector<GlobalActivityTracker::ModuleInfo> modules1; - modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess()); - ASSERT_EQ(1U, modules1.size()); - GlobalActivityTracker::ModuleInfo& stored1a = modules1[0]; - EXPECT_EQ(info1.is_loaded, stored1a.is_loaded); - EXPECT_EQ(info1.address, stored1a.address); - EXPECT_NE(info1.load_time, stored1a.load_time); - EXPECT_EQ(info1.size, stored1a.size); - EXPECT_EQ(info1.timestamp, stored1a.timestamp); - EXPECT_EQ(info1.age, stored1a.age); - EXPECT_EQ(info1.identifier[0], stored1a.identifier[0]); - EXPECT_EQ(info1.file, stored1a.file); - EXPECT_EQ(info1.debug_file, stored1a.debug_file); - - info1.is_loaded = false; - global->RecordModuleInfo(info1); - modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess()); - ASSERT_EQ(1U, modules1.size()); - GlobalActivityTracker::ModuleInfo& stored1b = modules1[0]; - EXPECT_EQ(info1.is_loaded, stored1b.is_loaded); - EXPECT_EQ(info1.address, stored1b.address); - EXPECT_NE(info1.load_time, stored1b.load_time); - EXPECT_EQ(info1.size, stored1b.size); - EXPECT_EQ(info1.timestamp, stored1b.timestamp); - EXPECT_EQ(info1.age, stored1b.age); - EXPECT_EQ(info1.identifier[0], stored1b.identifier[0]); - EXPECT_EQ(info1.file, stored1b.file); - EXPECT_EQ(info1.debug_file, stored1b.debug_file); - - GlobalActivityTracker::ModuleInfo info2; - info2.is_loaded = true; - info2.address = 0x87654321; - info2.load_time = 2222; - info2.size = 0xFEDCBA; - info2.timestamp = 222; - info2.age = 22; - info2.identifier[0] = 2; - info2.file = "nothing"; - info2.debug_file = "farewell"; - - global->RecordModuleInfo(info2); - std::vector<GlobalActivityTracker::ModuleInfo> modules2; - modules2 = global_analyzer.GetModules(global_analyzer.GetFirstProcess()); - ASSERT_EQ(2U, modules2.size()); - GlobalActivityTracker::ModuleInfo& stored2 = modules2[1]; - EXPECT_EQ(info2.is_loaded, stored2.is_loaded); - EXPECT_EQ(info2.address, stored2.address); - EXPECT_NE(info2.load_time, stored2.load_time); - EXPECT_EQ(info2.size, stored2.size); - EXPECT_EQ(info2.timestamp, stored2.timestamp); - EXPECT_EQ(info2.age, stored2.age); - EXPECT_EQ(info2.identifier[0], stored2.identifier[0]); - EXPECT_EQ(info2.file, stored2.file); - EXPECT_EQ(info2.debug_file, stored2.debug_file); -} - -TEST_F(ActivityAnalyzerTest, GlobalLogMessages) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true)); - - GlobalActivityTracker::Get()->RecordLogMessage("hello world"); - GlobalActivityTracker::Get()->RecordLogMessage("foo bar"); - - std::vector<std::string> messages = analyzer.GetLogMessages(); - ASSERT_EQ(2U, messages.size()); - EXPECT_EQ("hello world", messages[0]); - EXPECT_EQ("foo bar", messages[1]); -} - -TEST_F(ActivityAnalyzerTest, GlobalMultiProcess) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 1001); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - PersistentMemoryAllocator* allocator = global->allocator(); - EXPECT_EQ(1001, global->process_id()); - - int64_t process_id; - int64_t create_stamp; - ActivityUserData::GetOwningProcessId( - GlobalActivityTracker::Get()->process_data().GetBaseAddress(), - &process_id, &create_stamp); - ASSERT_EQ(1001, process_id); - - GlobalActivityTracker::Get()->process_data().SetInt("pid", - global->process_id()); - - GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>( - const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true)); - - AsOtherProcess(2002, [&global]() { - ASSERT_NE(global, GlobalActivityTracker::Get()); - EXPECT_EQ(2002, GlobalActivityTracker::Get()->process_id()); - - int64_t process_id; - int64_t create_stamp; - ActivityUserData::GetOwningProcessId( - GlobalActivityTracker::Get()->process_data().GetBaseAddress(), - &process_id, &create_stamp); - ASSERT_EQ(2002, process_id); - - GlobalActivityTracker::Get()->process_data().SetInt( - "pid", GlobalActivityTracker::Get()->process_id()); - }); - ASSERT_EQ(global, GlobalActivityTracker::Get()); - EXPECT_EQ(1001, GlobalActivityTracker::Get()->process_id()); - - const int64_t pid1 = analyzer.GetFirstProcess(); - ASSERT_EQ(1001, pid1); - const int64_t pid2 = analyzer.GetNextProcess(); - ASSERT_EQ(2002, pid2); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - const ActivityUserData::Snapshot& pdata1 = - analyzer.GetProcessDataSnapshot(pid1); - const ActivityUserData::Snapshot& pdata2 = - analyzer.GetProcessDataSnapshot(pid2); - EXPECT_EQ(1001, pdata1.at("pid").GetInt()); - EXPECT_EQ(2002, pdata2.at("pid").GetInt()); -} - -} // namespace debug -} // namespace base diff --git a/base/debug/asan_invalid_access.cc b/base/debug/asan_invalid_access.cc deleted file mode 100644 index 07c19db9c5..0000000000 --- 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/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 dc9a7ee647..0000000000 --- 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/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/crash_logging_unittest.cc b/base/debug/crash_logging_unittest.cc deleted file mode 100644 index c10d36e368..0000000000 --- a/base/debug/crash_logging_unittest.cc +++ /dev/null @@ -1,17 +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/crash_logging.h" - -#include "testing/gtest/include/gtest/gtest.h" - -TEST(CrashLoggingTest, UninitializedCrashKeyStringSupport) { - auto* crash_key = base::debug::AllocateCrashKeyString( - "test", base::debug::CrashKeySize::Size32); - EXPECT_FALSE(crash_key); - - base::debug::SetCrashKeyString(crash_key, "value"); - - base::debug::ClearCrashKeyString(crash_key); -} diff --git a/base/debug/proc_maps_linux_unittest.cc b/base/debug/proc_maps_linux_unittest.cc deleted file mode 100644 index 7abf152b0e..0000000000 --- a/base/debug/proc_maps_linux_unittest.cc +++ /dev/null @@ -1,328 +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 <stddef.h> -#include <stdint.h> - -#include "base/debug/proc_maps_linux.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/strings/stringprintf.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { - -TEST(ProcMapsTest, Empty) { - std::vector<MappedMemoryRegion> regions; - EXPECT_TRUE(ParseProcMaps("", ®ions)); - EXPECT_EQ(0u, regions.size()); -} - -TEST(ProcMapsTest, NoSpaces) { - static const char kNoSpaces[] = - "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kNoSpaces, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00002200u, regions[0].offset); - EXPECT_EQ("/bin/cat", regions[0].path); -} - -TEST(ProcMapsTest, Spaces) { - static const char kSpaces[] = - "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kSpaces, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00002200u, regions[0].offset); - EXPECT_EQ("/bin/space cat", regions[0].path); -} - -TEST(ProcMapsTest, NoNewline) { - static const char kNoSpaces[] = - "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_FALSE(ParseProcMaps(kNoSpaces, ®ions)); -} - -TEST(ProcMapsTest, NoPath) { - static const char kNoPath[] = - "00400000-0040b000 rw-p 00000000 00:00 0 \n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kNoPath, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("", regions[0].path); -} - -TEST(ProcMapsTest, Heap) { - static const char kHeap[] = - "022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kHeap, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x022ac000u, regions[0].start); - EXPECT_EQ(0x022cd000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("[heap]", regions[0].path); -} - -#if defined(ARCH_CPU_32_BITS) -TEST(ProcMapsTest, Stack32) { - static const char kStack[] = - "beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0xbeb04000u, regions[0].start); - EXPECT_EQ(0xbeb25000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("[stack]", regions[0].path); -} -#elif defined(ARCH_CPU_64_BITS) -TEST(ProcMapsTest, Stack64) { - static const char kStack[] = - "7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x7fff69c5b000u, regions[0].start); - EXPECT_EQ(0x7fff69c7d000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("[stack]", regions[0].path); -} -#endif - -TEST(ProcMapsTest, Multiple) { - static const char kMultiple[] = - "00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n" - "0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n" - "0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n"; - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(kMultiple, ®ions)); - ASSERT_EQ(3u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("/bin/cat", regions[0].path); - - EXPECT_EQ(0x0060a000u, regions[1].start); - EXPECT_EQ(0x0060b000u, regions[1].end); - EXPECT_EQ(0x0000a000u, regions[1].offset); - EXPECT_EQ("/bin/cat", regions[1].path); - - EXPECT_EQ(0x0060b000u, regions[2].start); - EXPECT_EQ(0x0060c000u, regions[2].end); - EXPECT_EQ(0x0000b000u, regions[2].offset); - EXPECT_EQ("/bin/cat", regions[2].path); -} - -TEST(ProcMapsTest, Permissions) { - static struct { - const char* input; - uint8_t permissions; - } kTestCases[] = { - {"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0}, - {"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0}, - {"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ}, - {"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::WRITE}, - {"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::EXECUTE}, - {"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | - MappedMemoryRegion::EXECUTE}, - {"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ | MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::WRITE | MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | - MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, - }; - - for (size_t i = 0; i < arraysize(kTestCases); ++i) { - SCOPED_TRACE( - base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i].input)); - - std::vector<MappedMemoryRegion> regions; - EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, ®ions)); - EXPECT_EQ(1u, regions.size()); - if (regions.empty()) - continue; - EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions); - } -} - -#if defined(ADDRESS_SANITIZER) -// AddressSanitizer may move local variables to a dedicated "fake stack" which -// is outside the stack region listed in /proc/self/maps. We disable ASan -// instrumentation for this function to force the variable to be local. -__attribute__((no_sanitize_address)) -#endif -void CheckProcMapsRegions(const std::vector<MappedMemoryRegion> ®ions) { - // We should be able to find both the current executable as well as the stack - // mapped into memory. Use the address of |exe_path| as a way of finding the - // stack. - FilePath exe_path; - EXPECT_TRUE(PathService::Get(FILE_EXE, &exe_path)); - uintptr_t address = reinterpret_cast<uintptr_t>(&exe_path); - bool found_exe = false; - bool found_stack = false; - bool found_address = false; - - for (size_t i = 0; i < regions.size(); ++i) { - if (regions[i].path == exe_path.value()) { - // It's OK to find the executable mapped multiple times as there'll be - // multiple sections (e.g., text, data). - found_exe = true; - } - - if (regions[i].path == "[stack]") { -// On Android the test is run on a background thread, since [stack] is for -// the main thread, we cannot test this. -#if !defined(OS_ANDROID) - EXPECT_GE(address, regions[i].start); - EXPECT_LT(address, regions[i].end); -#endif - EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::READ); - EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::WRITE); - EXPECT_FALSE(regions[i].permissions & MappedMemoryRegion::EXECUTE); - EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::PRIVATE); - EXPECT_FALSE(found_stack) << "Found duplicate stacks"; - found_stack = true; - } - - if (address >= regions[i].start && address < regions[i].end) { - EXPECT_FALSE(found_address) << "Found same address in multiple regions"; - found_address = true; - } - } - - EXPECT_TRUE(found_exe); - EXPECT_TRUE(found_stack); - EXPECT_TRUE(found_address); -} - -TEST(ProcMapsTest, ReadProcMaps) { - std::string proc_maps; - ASSERT_TRUE(ReadProcMaps(&proc_maps)); - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(proc_maps, ®ions)); - ASSERT_FALSE(regions.empty()); - - CheckProcMapsRegions(regions); -} - -TEST(ProcMapsTest, ReadProcMapsNonEmptyString) { - std::string old_string("I forgot to clear the string"); - std::string proc_maps(old_string); - ASSERT_TRUE(ReadProcMaps(&proc_maps)); - EXPECT_EQ(std::string::npos, proc_maps.find(old_string)); -} - -TEST(ProcMapsTest, MissingFields) { - static const char* const kTestCases[] = { - "00400000\n", // Missing end + beyond. - "00400000-0040b000\n", // Missing perms + beyond. - "00400000-0040b000 r-xp\n", // Missing offset + beyond. - "00400000-0040b000 r-xp 00000000\n", // Missing device + beyond. - "00400000-0040b000 r-xp 00000000 fc:00\n", // Missing inode + beyond. - "00400000-0040b000 00000000 fc:00 794418 /bin/cat\n", // Missing perms. - "00400000-0040b000 r-xp fc:00 794418 /bin/cat\n", // Missing offset. - "00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n", // Missing inode. - "00400000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing end. - "-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing start. - "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n", // Missing device. - }; - - for (size_t i = 0; i < arraysize(kTestCases); ++i) { - SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); - std::vector<MappedMemoryRegion> regions; - EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); - } -} - -TEST(ProcMapsTest, InvalidInput) { - static const char* const kTestCases[] = { - "thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", - "0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n", - "00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n", - "00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n", - "00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n", - "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n", - }; - - for (size_t i = 0; i < arraysize(kTestCases); ++i) { - SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); - std::vector<MappedMemoryRegion> regions; - EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); - } -} - -TEST(ProcMapsTest, ParseProcMapsEmptyString) { - std::vector<MappedMemoryRegion> regions; - EXPECT_TRUE(ParseProcMaps("", ®ions)); - EXPECT_EQ(0ULL, regions.size()); -} - -// Testing a couple of remotely possible weird things in the input: -// - Line ending with \r\n or \n\r. -// - File name contains quotes. -// - File name has whitespaces. -TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) { - std::vector<MappedMemoryRegion> regions; - const std::string kContents = - "00400000-0040b000 r-xp 00000000 fc:00 2106562 " - " /bin/cat\r\n" - "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 " - " /lib/x86_64-linux-gnu/libc-2.15.so\n\r" - "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 " - " /lib/x86_64-linux-gnu/ld-2.15.so\n" - "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 " - " \"vd so\"\n" - "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 " - " [vsys call]\n"; - EXPECT_TRUE(ParseProcMaps(kContents, ®ions)); - EXPECT_EQ(5ULL, regions.size()); - EXPECT_EQ("/bin/cat", regions[0].path); - EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path); - EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path); - EXPECT_EQ("\"vd so\"", regions[3].path); - EXPECT_EQ("[vsys call]", regions[4].path); -} - -} // namespace debug -} // namespace base diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc deleted file mode 100644 index 02f076a2ae..0000000000 --- a/base/debug/stack_trace_unittest.cc +++ /dev/null @@ -1,320 +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 <stddef.h> - -#include <limits> -#include <sstream> -#include <string> - -#include "base/debug/debugging_buildflags.h" -#include "base/debug/stack_trace.h" -#include "base/logging.h" -#include "base/process/kill.h" -#include "base/process/process_handle.h" -#include "base/test/test_timeouts.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) -#include "base/test/multiprocess_test.h" -#endif - -namespace base { -namespace debug { - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) -typedef MultiProcessTest StackTraceTest; -#else -typedef testing::Test StackTraceTest; -#endif - -// Note: On Linux, this test currently only fully works on Debug builds. -// See comments in the #ifdef soup if you intend to change this. -#if defined(OS_WIN) -// Always fails on Windows: crbug.com/32070 -#define MAYBE_OutputToStream DISABLED_OutputToStream -#else -#define MAYBE_OutputToStream OutputToStream -#endif -#if !defined(__UCLIBC__) && !defined(_AIX) -TEST_F(StackTraceTest, MAYBE_OutputToStream) { - StackTrace trace; - - // Dump the trace into a string. - std::ostringstream os; - trace.OutputToStream(&os); - std::string backtrace_message = os.str(); - - // ToString() should produce the same output. - EXPECT_EQ(backtrace_message, trace.ToString()); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG - // Stack traces require an extra data table that bloats our binaries, - // so they're turned off for release builds. We stop the test here, - // at least letting us verify that the calls don't crash. - return; -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG - - size_t frames_found = 0; - trace.Addresses(&frames_found); - ASSERT_GE(frames_found, 5u) << - "No stack frames found. Skipping rest of test."; - - // Check if the output has symbol initialization warning. If it does, fail. - ASSERT_EQ(backtrace_message.find("Dumping unresolved backtrace"), - std::string::npos) << - "Unable to resolve symbols. Skipping rest of test."; - -#if defined(OS_MACOSX) -#if 0 - // Disabled due to -fvisibility=hidden in build config. - - // Symbol resolution via the backtrace_symbol function does not work well - // in OS X. - // See this thread: - // - // http://lists.apple.com/archives/darwin-dev/2009/Mar/msg00111.html - // - // Just check instead that we find our way back to the "start" symbol - // which should be the first symbol in the trace. - // - // TODO(port): Find a more reliable way to resolve symbols. - - // Expect to at least find main. - EXPECT_TRUE(backtrace_message.find("start") != std::string::npos) - << "Expected to find start in backtrace:\n" - << backtrace_message; - -#endif -#elif defined(USE_SYMBOLIZE) - // This branch is for gcc-compiled code, but not Mac due to the - // above #if. - // Expect a demangled symbol. - EXPECT_TRUE(backtrace_message.find("testing::Test::Run()") != - std::string::npos) - << "Expected a demangled symbol in backtrace:\n" - << backtrace_message; - -#elif 0 - // This is the fall-through case; it used to cover Windows. - // But it's disabled because of varying buildbot configs; - // some lack symbols. - - // Expect to at least find main. - EXPECT_TRUE(backtrace_message.find("main") != std::string::npos) - << "Expected to find main in backtrace:\n" - << backtrace_message; - -#if defined(OS_WIN) -// MSVC doesn't allow the use of C99's __func__ within C++, so we fake it with -// MSVC's __FUNCTION__ macro. -#define __func__ __FUNCTION__ -#endif - - // Expect to find this function as well. - // Note: This will fail if not linked with -rdynamic (aka -export_dynamic) - EXPECT_TRUE(backtrace_message.find(__func__) != std::string::npos) - << "Expected to find " << __func__ << " in backtrace:\n" - << backtrace_message; - -#endif // define(OS_MACOSX) -} - -#if !defined(OFFICIAL_BUILD) && !defined(NO_UNWIND_TABLES) -// Disabled in Official builds, where Link-Time Optimization can result in two -// or fewer stack frames being available, causing the test to fail. -TEST_F(StackTraceTest, TruncatedTrace) { - StackTrace trace; - - size_t count = 0; - trace.Addresses(&count); - ASSERT_LT(2u, count); - - StackTrace truncated(2); - truncated.Addresses(&count); - EXPECT_EQ(2u, count); -} -#endif // !defined(OFFICIAL_BUILD) - -// The test is used for manual testing, e.g., to see the raw output. -TEST_F(StackTraceTest, DebugOutputToStream) { - StackTrace trace; - std::ostringstream os; - trace.OutputToStream(&os); - VLOG(1) << os.str(); -} - -// The test is used for manual testing, e.g., to see the raw output. -TEST_F(StackTraceTest, DebugPrintBacktrace) { - StackTrace().Print(); -} -#endif // !defined(__UCLIBC__) - -#if defined(OS_POSIX) && !defined(OS_ANDROID) -#if !defined(OS_IOS) -static char* newArray() { - // Clang warns about the mismatched new[]/delete if they occur in the same - // function. - return new char[10]; -} - -MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) { - char* pointer = newArray(); - delete pointer; - return 2; -} - -// Regression test for StackDumpingSignalHandler async-signal unsafety. -// Combined with tcmalloc's debugallocation, that signal handler -// and e.g. mismatched new[]/delete would cause a hang because -// of re-entering malloc. -TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) { - Process child = SpawnChild("MismatchedMallocChildProcess"); - ASSERT_TRUE(child.IsValid()); - int exit_code; - ASSERT_TRUE( - child.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &exit_code)); -} -#endif // !defined(OS_IOS) - -namespace { - -std::string itoa_r_wrapper(intptr_t i, size_t sz, int base, size_t padding) { - char buffer[1024]; - CHECK_LE(sz, sizeof(buffer)); - - char* result = internal::itoa_r(i, buffer, sz, base, padding); - EXPECT_TRUE(result); - return std::string(buffer); -} - -} // namespace - -TEST_F(StackTraceTest, itoa_r) { - EXPECT_EQ("0", itoa_r_wrapper(0, 128, 10, 0)); - EXPECT_EQ("-1", itoa_r_wrapper(-1, 128, 10, 0)); - - // Test edge cases. - if (sizeof(intptr_t) == 4) { - EXPECT_EQ("ffffffff", itoa_r_wrapper(-1, 128, 16, 0)); - EXPECT_EQ("-2147483648", - itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 10, 0)); - EXPECT_EQ("2147483647", - itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 10, 0)); - - EXPECT_EQ("80000000", - itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 16, 0)); - EXPECT_EQ("7fffffff", - itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 16, 0)); - } else if (sizeof(intptr_t) == 8) { - EXPECT_EQ("ffffffffffffffff", itoa_r_wrapper(-1, 128, 16, 0)); - EXPECT_EQ("-9223372036854775808", - itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 10, 0)); - EXPECT_EQ("9223372036854775807", - itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 10, 0)); - - EXPECT_EQ("8000000000000000", - itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 16, 0)); - EXPECT_EQ("7fffffffffffffff", - itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 16, 0)); - } else { - ADD_FAILURE() << "Missing test case for your size of intptr_t (" - << sizeof(intptr_t) << ")"; - } - - // Test hex output. - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 0)); - EXPECT_EQ("deadbeef", itoa_r_wrapper(0xdeadbeef, 128, 16, 0)); - - // Check that itoa_r respects passed buffer size limit. - char buffer[1024]; - EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 10, 16, 0)); - EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 9, 16, 0)); - EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 8, 16, 0)); - EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 7, 16, 0)); - EXPECT_TRUE(internal::itoa_r(0xbeef, buffer, 5, 16, 4)); - EXPECT_FALSE(internal::itoa_r(0xbeef, buffer, 5, 16, 5)); - EXPECT_FALSE(internal::itoa_r(0xbeef, buffer, 5, 16, 6)); - - // Test padding. - EXPECT_EQ("1", itoa_r_wrapper(1, 128, 10, 0)); - EXPECT_EQ("1", itoa_r_wrapper(1, 128, 10, 1)); - EXPECT_EQ("01", itoa_r_wrapper(1, 128, 10, 2)); - EXPECT_EQ("001", itoa_r_wrapper(1, 128, 10, 3)); - EXPECT_EQ("0001", itoa_r_wrapper(1, 128, 10, 4)); - EXPECT_EQ("00001", itoa_r_wrapper(1, 128, 10, 5)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 0)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 1)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 2)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 3)); - EXPECT_EQ("0688", itoa_r_wrapper(0x688, 128, 16, 4)); - EXPECT_EQ("00688", itoa_r_wrapper(0x688, 128, 16, 5)); -} -#endif // defined(OS_POSIX) && !defined(OS_ANDROID) - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -template <size_t Depth> -void NOINLINE ExpectStackFramePointers(const void** frames, - size_t max_depth) { - code_start: - // Calling __builtin_frame_address() forces compiler to emit - // frame pointers, even if they are not enabled. - EXPECT_NE(nullptr, __builtin_frame_address(0)); - ExpectStackFramePointers<Depth - 1>(frames, max_depth); - - constexpr size_t frame_index = Depth - 1; - const void* frame = frames[frame_index]; - EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index; - EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index; - code_end: return; -} - -template <> -void NOINLINE ExpectStackFramePointers<1>(const void** frames, - size_t max_depth) { - code_start: - // Calling __builtin_frame_address() forces compiler to emit - // frame pointers, even if they are not enabled. - EXPECT_NE(nullptr, __builtin_frame_address(0)); - size_t count = TraceStackFramePointers(frames, max_depth, 0); - ASSERT_EQ(max_depth, count); - - const void* frame = frames[0]; - EXPECT_GE(frame, &&code_start) << "For the top frame"; - EXPECT_LE(frame, &&code_end) << "For the top frame"; - code_end: return; -} - -#if defined(MEMORY_SANITIZER) -// The test triggers use-of-uninitialized-value errors on MSan bots. -// This is expected because we're walking and reading the stack, and -// sometimes we read fp / pc from the place that previously held -// uninitialized value. -#define MAYBE_TraceStackFramePointers DISABLED_TraceStackFramePointers -#else -#define MAYBE_TraceStackFramePointers TraceStackFramePointers -#endif -TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) { - constexpr size_t kDepth = 5; - const void* frames[kDepth]; - ExpectStackFramePointers<kDepth>(frames, kDepth); -} - -#if defined(OS_ANDROID) || defined(OS_MACOSX) -#define MAYBE_StackEnd StackEnd -#else -#define MAYBE_StackEnd DISABLED_StackEnd -#endif - -TEST_F(StackTraceTest, MAYBE_StackEnd) { - EXPECT_NE(0u, GetStackEnd()); -} - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -} // namespace debug -} // namespace base diff --git a/base/debug/thread_heap_usage_tracker.cc b/base/debug/thread_heap_usage_tracker.cc deleted file mode 100644 index 6d00b1ccbb..0000000000 --- a/base/debug/thread_heap_usage_tracker.cc +++ /dev/null @@ -1,340 +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/thread_heap_usage_tracker.h" - -#include <stdint.h> -#include <algorithm> -#include <limits> -#include <new> -#include <type_traits> - -#include "base/allocator/allocator_shim.h" -#include "base/allocator/buildflags.h" -#include "base/logging.h" -#include "base/no_destructor.h" -#include "base/threading/thread_local_storage.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) || defined(OS_IOS) -#include <malloc/malloc.h> -#else -#include <malloc.h> -#endif - -namespace base { -namespace debug { - -namespace { - -using base::allocator::AllocatorDispatch; - -const uintptr_t kSentinelMask = std::numeric_limits<uintptr_t>::max() - 1; -ThreadHeapUsage* const kInitializationSentinel = - reinterpret_cast<ThreadHeapUsage*>(kSentinelMask); -ThreadHeapUsage* const kTeardownSentinel = - reinterpret_cast<ThreadHeapUsage*>(kSentinelMask | 1); - -ThreadLocalStorage::Slot& ThreadAllocationUsage() { - static NoDestructor<ThreadLocalStorage::Slot> thread_allocator_usage( - [](void* thread_heap_usage) { - // This destructor will be called twice. Once to destroy the actual - // ThreadHeapUsage instance and a second time, immediately after, for - // the sentinel. Re-setting the TLS slow (below) does re-initialize the - // TLS slot. The ThreadLocalStorage code is designed to deal with this - // use case and will re-call the destructor with the kTeardownSentinel - // as arg. - if (thread_heap_usage == kTeardownSentinel) - return; - DCHECK_NE(thread_heap_usage, kInitializationSentinel); - - // Deleting the ThreadHeapUsage TLS object will re-enter the shim and - // hit RecordFree() (see below). The sentinel prevents RecordFree() from - // re-creating another ThreadHeapUsage object. - ThreadAllocationUsage().Set(kTeardownSentinel); - delete static_cast<ThreadHeapUsage*>(thread_heap_usage); - }); - return *thread_allocator_usage; -} - -bool g_heap_tracking_enabled = false; - -// Forward declared as it needs to delegate memory allocation to the next -// lower shim. -ThreadHeapUsage* GetOrCreateThreadUsage(); - -size_t GetAllocSizeEstimate(const AllocatorDispatch* next, - void* ptr, - void* context) { - if (ptr == nullptr) - return 0U; - - return next->get_size_estimate_function(next, ptr, context); -} - -void RecordAlloc(const AllocatorDispatch* next, - void* ptr, - size_t size, - void* context) { - ThreadHeapUsage* usage = GetOrCreateThreadUsage(); - if (usage == nullptr) - return; - - usage->alloc_ops++; - size_t estimate = GetAllocSizeEstimate(next, ptr, context); - if (size && estimate) { - // Only keep track of the net number of bytes allocated in the scope if the - // size estimate function returns sane values, e.g. non-zero. - usage->alloc_bytes += estimate; - usage->alloc_overhead_bytes += estimate - size; - - // Record the max outstanding number of bytes, but only if the difference - // is net positive (e.g. more bytes allocated than freed in the scope). - if (usage->alloc_bytes > usage->free_bytes) { - uint64_t allocated_bytes = usage->alloc_bytes - usage->free_bytes; - if (allocated_bytes > usage->max_allocated_bytes) - usage->max_allocated_bytes = allocated_bytes; - } - } else { - usage->alloc_bytes += size; - } -} - -void RecordFree(const AllocatorDispatch* next, void* ptr, void* context) { - ThreadHeapUsage* usage = GetOrCreateThreadUsage(); - if (usage == nullptr) - return; - - size_t estimate = GetAllocSizeEstimate(next, ptr, context); - usage->free_ops++; - usage->free_bytes += estimate; -} - -void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) { - void* ret = self->next->alloc_function(self->next, size, context); - if (ret != nullptr) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void* AllocZeroInitializedFn(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - void* ret = - self->next->alloc_zero_initialized_function(self->next, n, size, context); - if (ret != nullptr) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void* AllocAlignedFn(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - void* ret = - self->next->alloc_aligned_function(self->next, alignment, size, context); - if (ret != nullptr) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void* ReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - if (address != nullptr) - RecordFree(self->next, address, context); - - void* ret = self->next->realloc_function(self->next, address, size, context); - if (ret != nullptr && size != 0) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void FreeFn(const AllocatorDispatch* self, void* address, void* context) { - if (address != nullptr) - RecordFree(self->next, address, context); - self->next->free_function(self->next, address, context); -} - -size_t GetSizeEstimateFn(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->get_size_estimate_function(self->next, address, context); -} - -unsigned BatchMallocFn(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - unsigned count = self->next->batch_malloc_function(self->next, size, results, - num_requested, context); - for (unsigned i = 0; i < count; ++i) { - RecordAlloc(self->next, results[i], size, context); - } - return count; -} - -void BatchFreeFn(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - for (unsigned i = 0; i < num_to_be_freed; ++i) { - if (to_be_freed[i] != nullptr) { - RecordFree(self->next, to_be_freed[i], context); - } - } - self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, - context); -} - -void FreeDefiniteSizeFn(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - if (ptr != nullptr) - RecordFree(self->next, ptr, context); - self->next->free_definite_size_function(self->next, ptr, size, context); -} - -// The allocator dispatch used to intercept heap operations. -AllocatorDispatch allocator_dispatch = {&AllocFn, - &AllocZeroInitializedFn, - &AllocAlignedFn, - &ReallocFn, - &FreeFn, - &GetSizeEstimateFn, - &BatchMallocFn, - &BatchFreeFn, - &FreeDefiniteSizeFn, - nullptr}; - -ThreadHeapUsage* GetOrCreateThreadUsage() { - auto tls_ptr = reinterpret_cast<uintptr_t>(ThreadAllocationUsage().Get()); - if ((tls_ptr & kSentinelMask) == kSentinelMask) - return nullptr; // Re-entrancy case. - - auto* allocator_usage = reinterpret_cast<ThreadHeapUsage*>(tls_ptr); - if (allocator_usage == nullptr) { - // Prevent reentrancy due to the allocation below. - ThreadAllocationUsage().Set(kInitializationSentinel); - - allocator_usage = new ThreadHeapUsage(); - static_assert(std::is_pod<ThreadHeapUsage>::value, - "AllocatorDispatch must be POD"); - memset(allocator_usage, 0, sizeof(*allocator_usage)); - ThreadAllocationUsage().Set(allocator_usage); - } - - return allocator_usage; -} - -} // namespace - -ThreadHeapUsageTracker::ThreadHeapUsageTracker() : thread_usage_(nullptr) { - static_assert(std::is_pod<ThreadHeapUsage>::value, "Must be POD."); -} - -ThreadHeapUsageTracker::~ThreadHeapUsageTracker() { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (thread_usage_ != nullptr) { - // If this tracker wasn't stopped, make it inclusive so that the - // usage isn't lost. - Stop(false); - } -} - -void ThreadHeapUsageTracker::Start() { - DCHECK(thread_checker_.CalledOnValidThread()); - - thread_usage_ = GetOrCreateThreadUsage(); - usage_ = *thread_usage_; - - // Reset the stats for our current scope. - // The per-thread usage instance now tracks this scope's usage, while this - // instance persists the outer scope's usage stats. On destruction, this - // instance will restore the outer scope's usage stats with this scope's - // usage added. - memset(thread_usage_, 0, sizeof(*thread_usage_)); -} - -void ThreadHeapUsageTracker::Stop(bool usage_is_exclusive) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_NE(nullptr, thread_usage_); - - ThreadHeapUsage current = *thread_usage_; - if (usage_is_exclusive) { - // Restore the outer scope. - *thread_usage_ = usage_; - } else { - // Update the outer scope with the accrued inner usage. - if (thread_usage_->max_allocated_bytes) { - uint64_t outer_net_alloc_bytes = usage_.alloc_bytes - usage_.free_bytes; - - thread_usage_->max_allocated_bytes = - std::max(usage_.max_allocated_bytes, - outer_net_alloc_bytes + thread_usage_->max_allocated_bytes); - } - - thread_usage_->alloc_ops += usage_.alloc_ops; - thread_usage_->alloc_bytes += usage_.alloc_bytes; - thread_usage_->alloc_overhead_bytes += usage_.alloc_overhead_bytes; - thread_usage_->free_ops += usage_.free_ops; - thread_usage_->free_bytes += usage_.free_bytes; - } - - thread_usage_ = nullptr; - usage_ = current; -} - -ThreadHeapUsage ThreadHeapUsageTracker::GetUsageSnapshot() { - ThreadHeapUsage* usage = GetOrCreateThreadUsage(); - DCHECK_NE(nullptr, usage); - return *usage; -} - -void ThreadHeapUsageTracker::EnableHeapTracking() { - EnsureTLSInitialized(); - - CHECK_EQ(false, g_heap_tracking_enabled) << "No double-enabling."; - g_heap_tracking_enabled = true; -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - base::allocator::InsertAllocatorDispatch(&allocator_dispatch); -#else - CHECK(false) << "Can't enable heap tracking without the shim."; -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) -} - -bool ThreadHeapUsageTracker::IsHeapTrackingEnabled() { - return g_heap_tracking_enabled; -} - -void ThreadHeapUsageTracker::DisableHeapTrackingForTesting() { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - base::allocator::RemoveAllocatorDispatchForTesting(&allocator_dispatch); -#else - CHECK(false) << "Can't disable heap tracking without the shim."; -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - DCHECK_EQ(true, g_heap_tracking_enabled) << "Heap tracking not enabled."; - g_heap_tracking_enabled = false; -} - -base::allocator::AllocatorDispatch* -ThreadHeapUsageTracker::GetDispatchForTesting() { - return &allocator_dispatch; -} - -void ThreadHeapUsageTracker::EnsureTLSInitialized() { - ignore_result(ThreadAllocationUsage()); -} - -} // namespace debug -} // namespace base diff --git a/base/debug/thread_heap_usage_tracker_unittest.cc b/base/debug/thread_heap_usage_tracker_unittest.cc deleted file mode 100644 index b99576cbad..0000000000 --- a/base/debug/thread_heap_usage_tracker_unittest.cc +++ /dev/null @@ -1,607 +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/thread_heap_usage_tracker.h" - -#include <map> - -#include "base/allocator/allocator_shim.h" -#include "base/allocator/buildflags.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_MACOSX) -#include "base/allocator/allocator_interception_mac.h" -#endif - -namespace base { -namespace debug { - -namespace { - -class TestingThreadHeapUsageTracker : public ThreadHeapUsageTracker { - public: - using ThreadHeapUsageTracker::DisableHeapTrackingForTesting; - using ThreadHeapUsageTracker::EnsureTLSInitialized; - using ThreadHeapUsageTracker::GetDispatchForTesting; -}; - -// A fixture class that allows testing the AllocatorDispatch associated with -// the ThreadHeapUsageTracker class in isolation against a mocked -// underlying -// heap implementation. -class ThreadHeapUsageTrackerTest : public testing::Test { - public: - using AllocatorDispatch = base::allocator::AllocatorDispatch; - - static const size_t kAllocationPadding; - enum SizeFunctionKind { - EXACT_SIZE_FUNCTION, - PADDING_SIZE_FUNCTION, - ZERO_SIZE_FUNCTION, - }; - - ThreadHeapUsageTrackerTest() : size_function_kind_(EXACT_SIZE_FUNCTION) { - EXPECT_EQ(nullptr, g_self); - g_self = this; - } - - ~ThreadHeapUsageTrackerTest() override { - EXPECT_EQ(this, g_self); - g_self = nullptr; - } - - void set_size_function_kind(SizeFunctionKind kind) { - size_function_kind_ = kind; - } - - void SetUp() override { - TestingThreadHeapUsageTracker::EnsureTLSInitialized(); - - dispatch_under_test_ = - TestingThreadHeapUsageTracker::GetDispatchForTesting(); - ASSERT_EQ(nullptr, dispatch_under_test_->next); - - dispatch_under_test_->next = &g_mock_dispatch; - } - - void TearDown() override { - ASSERT_EQ(&g_mock_dispatch, dispatch_under_test_->next); - - dispatch_under_test_->next = nullptr; - } - - void* MockMalloc(size_t size) { - return dispatch_under_test_->alloc_function(dispatch_under_test_, size, - nullptr); - } - - void* MockCalloc(size_t n, size_t size) { - return dispatch_under_test_->alloc_zero_initialized_function( - dispatch_under_test_, n, size, nullptr); - } - - void* MockAllocAligned(size_t alignment, size_t size) { - return dispatch_under_test_->alloc_aligned_function( - dispatch_under_test_, alignment, size, nullptr); - } - - void* MockRealloc(void* address, size_t size) { - return dispatch_under_test_->realloc_function(dispatch_under_test_, address, - size, nullptr); - } - - void MockFree(void* address) { - dispatch_under_test_->free_function(dispatch_under_test_, address, nullptr); - } - - size_t MockGetSizeEstimate(void* address) { - return dispatch_under_test_->get_size_estimate_function( - dispatch_under_test_, address, nullptr); - } - - private: - void RecordAlloc(void* address, size_t size) { - if (address != nullptr) - allocation_size_map_[address] = size; - } - - void DeleteAlloc(void* address) { - if (address != nullptr) - EXPECT_EQ(1U, allocation_size_map_.erase(address)); - } - - size_t GetSizeEstimate(void* address) { - auto it = allocation_size_map_.find(address); - if (it == allocation_size_map_.end()) - return 0; - - size_t ret = it->second; - switch (size_function_kind_) { - case EXACT_SIZE_FUNCTION: - break; - case PADDING_SIZE_FUNCTION: - ret += kAllocationPadding; - break; - case ZERO_SIZE_FUNCTION: - ret = 0; - break; - } - - return ret; - } - - static void* OnAllocFn(const AllocatorDispatch* self, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - void* ret = malloc(size); - g_self->RecordAlloc(ret, size); - return ret; - } - - static void* OnAllocZeroInitializedFn(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - void* ret = calloc(n, size); - g_self->RecordAlloc(ret, n * size); - return ret; - } - - static void* OnAllocAlignedFn(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - // This is a cheat as it doesn't return aligned allocations. This has the - // advantage of working for all platforms for this test. - void* ret = malloc(size); - g_self->RecordAlloc(ret, size); - return ret; - } - - static void* OnReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - g_self->DeleteAlloc(address); - void* ret = realloc(address, size); - g_self->RecordAlloc(ret, size); - return ret; - } - - static void OnFreeFn(const AllocatorDispatch* self, - void* address, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - g_self->DeleteAlloc(address); - free(address); - } - - static size_t OnGetSizeEstimateFn(const AllocatorDispatch* self, - void* address, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - return g_self->GetSizeEstimate(address); - } - - using AllocationSizeMap = std::map<void*, size_t>; - - SizeFunctionKind size_function_kind_; - AllocationSizeMap allocation_size_map_; - AllocatorDispatch* dispatch_under_test_; - - static base::allocator::AllocatorDispatch g_mock_dispatch; - static ThreadHeapUsageTrackerTest* g_self; -}; - -const size_t ThreadHeapUsageTrackerTest::kAllocationPadding = 23; - -ThreadHeapUsageTrackerTest* ThreadHeapUsageTrackerTest::g_self = nullptr; - -base::allocator::AllocatorDispatch ThreadHeapUsageTrackerTest::g_mock_dispatch = - { - &ThreadHeapUsageTrackerTest::OnAllocFn, // alloc_function - &ThreadHeapUsageTrackerTest:: - OnAllocZeroInitializedFn, // alloc_zero_initialized_function - &ThreadHeapUsageTrackerTest:: - OnAllocAlignedFn, // alloc_aligned_function - &ThreadHeapUsageTrackerTest::OnReallocFn, // realloc_function - &ThreadHeapUsageTrackerTest::OnFreeFn, // free_function - &ThreadHeapUsageTrackerTest:: - OnGetSizeEstimateFn, // get_size_estimate_function - nullptr, // batch_malloc - nullptr, // batch_free - nullptr, // free_definite_size_function - nullptr, // next -}; - -} // namespace - -TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithExactSizeFunction) { - set_size_function_kind(EXACT_SIZE_FUNCTION); - - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - - EXPECT_EQ(0U, u1.alloc_ops); - EXPECT_EQ(0U, u1.alloc_bytes); - EXPECT_EQ(0U, u1.alloc_overhead_bytes); - EXPECT_EQ(0U, u1.free_ops); - EXPECT_EQ(0U, u1.free_bytes); - EXPECT_EQ(0U, u1.max_allocated_bytes); - - const size_t kAllocSize = 1029U; - void* ptr = MockMalloc(kAllocSize); - MockFree(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u2 = usage_tracker.usage(); - - EXPECT_EQ(1U, u2.alloc_ops); - EXPECT_EQ(kAllocSize, u2.alloc_bytes); - EXPECT_EQ(0U, u2.alloc_overhead_bytes); - EXPECT_EQ(1U, u2.free_ops); - EXPECT_EQ(kAllocSize, u2.free_bytes); - EXPECT_EQ(kAllocSize, u2.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithPaddingSizeFunction) { - set_size_function_kind(PADDING_SIZE_FUNCTION); - - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - - EXPECT_EQ(0U, u1.alloc_ops); - EXPECT_EQ(0U, u1.alloc_bytes); - EXPECT_EQ(0U, u1.alloc_overhead_bytes); - EXPECT_EQ(0U, u1.free_ops); - EXPECT_EQ(0U, u1.free_bytes); - EXPECT_EQ(0U, u1.max_allocated_bytes); - - const size_t kAllocSize = 1029U; - void* ptr = MockMalloc(kAllocSize); - MockFree(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u2 = usage_tracker.usage(); - - EXPECT_EQ(1U, u2.alloc_ops); - EXPECT_EQ(kAllocSize + kAllocationPadding, u2.alloc_bytes); - EXPECT_EQ(kAllocationPadding, u2.alloc_overhead_bytes); - EXPECT_EQ(1U, u2.free_ops); - EXPECT_EQ(kAllocSize + kAllocationPadding, u2.free_bytes); - EXPECT_EQ(kAllocSize + kAllocationPadding, u2.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithZeroSizeFunction) { - set_size_function_kind(ZERO_SIZE_FUNCTION); - - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(0U, u1.alloc_ops); - EXPECT_EQ(0U, u1.alloc_bytes); - EXPECT_EQ(0U, u1.alloc_overhead_bytes); - EXPECT_EQ(0U, u1.free_ops); - EXPECT_EQ(0U, u1.free_bytes); - EXPECT_EQ(0U, u1.max_allocated_bytes); - - const size_t kAllocSize = 1029U; - void* ptr = MockMalloc(kAllocSize); - MockFree(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u2 = usage_tracker.usage(); - - // With a get-size function that returns zero, there's no way to get the size - // of an allocation that's being freed, hence the shim can't tally freed bytes - // nor the high-watermark allocated bytes. - EXPECT_EQ(1U, u2.alloc_ops); - EXPECT_EQ(kAllocSize, u2.alloc_bytes); - EXPECT_EQ(0U, u2.alloc_overhead_bytes); - EXPECT_EQ(1U, u2.free_ops); - EXPECT_EQ(0U, u2.free_bytes); - EXPECT_EQ(0U, u2.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, ReallocCorrectlyTallied) { - const size_t kAllocSize = 237U; - - { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - // Reallocating nullptr should count as a single alloc. - void* ptr = MockRealloc(nullptr, kAllocSize); - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(1U, usage.alloc_ops); - EXPECT_EQ(kAllocSize, usage.alloc_bytes); - EXPECT_EQ(0U, usage.alloc_overhead_bytes); - EXPECT_EQ(0U, usage.free_ops); - EXPECT_EQ(0U, usage.free_bytes); - EXPECT_EQ(kAllocSize, usage.max_allocated_bytes); - - // Reallocating a valid pointer to a zero size should count as a single - // free. - ptr = MockRealloc(ptr, 0U); - - usage_tracker.Stop(false); - EXPECT_EQ(1U, usage_tracker.usage().alloc_ops); - EXPECT_EQ(kAllocSize, usage_tracker.usage().alloc_bytes); - EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes); - EXPECT_EQ(1U, usage_tracker.usage().free_ops); - EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes); - EXPECT_EQ(kAllocSize, usage_tracker.usage().max_allocated_bytes); - - // Realloc to zero size may or may not return a nullptr - make sure to - // free the zero-size alloc in the latter case. - if (ptr != nullptr) - MockFree(ptr); - } - - { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - void* ptr = MockMalloc(kAllocSize); - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(1U, usage.alloc_ops); - - // Now try reallocating a valid pointer to a larger size, this should count - // as one free and one alloc. - const size_t kLargerAllocSize = kAllocSize + 928U; - ptr = MockRealloc(ptr, kLargerAllocSize); - - usage_tracker.Stop(false); - EXPECT_EQ(2U, usage_tracker.usage().alloc_ops); - EXPECT_EQ(kAllocSize + kLargerAllocSize, usage_tracker.usage().alloc_bytes); - EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes); - EXPECT_EQ(1U, usage_tracker.usage().free_ops); - EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes); - EXPECT_EQ(kLargerAllocSize, usage_tracker.usage().max_allocated_bytes); - - MockFree(ptr); - } -} - -TEST_F(ThreadHeapUsageTrackerTest, NestedMaxWorks) { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - const size_t kOuterAllocSize = 1029U; - void* ptr = MockMalloc(kOuterAllocSize); - MockFree(ptr); - - EXPECT_EQ(kOuterAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - const size_t kInnerAllocSize = 673U; - ptr = MockMalloc(kInnerAllocSize); - MockFree(ptr); - - inner_usage_tracker.Stop(false); - - EXPECT_EQ(kInnerAllocSize, inner_usage_tracker.usage().max_allocated_bytes); - } - - // The greater, outer allocation size should have been restored. - EXPECT_EQ(kOuterAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - const size_t kLargerInnerAllocSize = kOuterAllocSize + 673U; - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - ptr = MockMalloc(kLargerInnerAllocSize); - MockFree(ptr); - - inner_usage_tracker.Stop(false); - EXPECT_EQ(kLargerInnerAllocSize, - inner_usage_tracker.usage().max_allocated_bytes); - } - - // The greater, inner allocation size should have been preserved. - EXPECT_EQ(kLargerInnerAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - // Now try the case with an outstanding net alloc size when entering the - // inner scope. - void* outer_ptr = MockMalloc(kOuterAllocSize); - EXPECT_EQ(kLargerInnerAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - ptr = MockMalloc(kLargerInnerAllocSize); - MockFree(ptr); - - inner_usage_tracker.Stop(false); - EXPECT_EQ(kLargerInnerAllocSize, - inner_usage_tracker.usage().max_allocated_bytes); - } - - // While the inner scope saw only the inner net outstanding allocation size, - // the outer scope saw both outstanding at the same time. - EXPECT_EQ(kOuterAllocSize + kLargerInnerAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - MockFree(outer_ptr); - - // Test a net-negative scope. - ptr = MockMalloc(kLargerInnerAllocSize); - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - MockFree(ptr); - - const size_t kInnerAllocSize = 1; - ptr = MockMalloc(kInnerAllocSize); - - inner_usage_tracker.Stop(false); - // Since the scope is still net-negative, the max is clamped at zero. - EXPECT_EQ(0U, inner_usage_tracker.usage().max_allocated_bytes); - } - - MockFree(ptr); -} - -TEST_F(ThreadHeapUsageTrackerTest, NoStopImpliesInclusive) { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - const size_t kOuterAllocSize = 1029U; - void* ptr = MockMalloc(kOuterAllocSize); - MockFree(ptr); - - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes); - - const size_t kInnerLargerAllocSize = kOuterAllocSize + 673U; - - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - // Make a larger allocation than the outer scope. - ptr = MockMalloc(kInnerLargerAllocSize); - MockFree(ptr); - - // inner_usage_tracker goes out of scope without a Stop(). - } - - ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(usage.alloc_ops + 1, current.alloc_ops); - EXPECT_EQ(usage.alloc_bytes + kInnerLargerAllocSize, current.alloc_bytes); - EXPECT_EQ(usage.free_ops + 1, current.free_ops); - EXPECT_EQ(usage.free_bytes + kInnerLargerAllocSize, current.free_bytes); - EXPECT_EQ(kInnerLargerAllocSize, current.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, ExclusiveScopesWork) { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - const size_t kOuterAllocSize = 1029U; - void* ptr = MockMalloc(kOuterAllocSize); - MockFree(ptr); - - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes); - - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - // Make a larger allocation than the outer scope. - ptr = MockMalloc(kOuterAllocSize + 673U); - MockFree(ptr); - - // This tracker is exlusive, all activity should be private to this scope. - inner_usage_tracker.Stop(true); - } - - ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(usage.alloc_ops, current.alloc_ops); - EXPECT_EQ(usage.alloc_bytes, current.alloc_bytes); - EXPECT_EQ(usage.alloc_overhead_bytes, current.alloc_overhead_bytes); - EXPECT_EQ(usage.free_ops, current.free_ops); - EXPECT_EQ(usage.free_bytes, current.free_bytes); - EXPECT_EQ(usage.max_allocated_bytes, current.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, AllShimFunctionsAreProvided) { - const size_t kAllocSize = 100; - void* alloc = MockMalloc(kAllocSize); - size_t estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - MockFree(alloc); - - alloc = MockCalloc(kAllocSize, 1); - estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - MockFree(alloc); - - alloc = MockAllocAligned(1, kAllocSize); - estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - - alloc = MockRealloc(alloc, kAllocSize); - estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - MockFree(alloc); -} - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -class ThreadHeapUsageShimTest : public testing::Test { -#if defined(OS_MACOSX) - void SetUp() override { allocator::InitializeAllocatorShim(); } - void TearDown() override { allocator::UninterceptMallocZonesForTesting(); } -#endif -}; - -TEST_F(ThreadHeapUsageShimTest, HooksIntoMallocWhenShimAvailable) { - ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled()); - - ThreadHeapUsageTracker::EnableHeapTracking(); - - ASSERT_TRUE(ThreadHeapUsageTracker::IsHeapTrackingEnabled()); - - const size_t kAllocSize = 9993; - // This test verifies that the scoped heap data is affected by malloc & - // free only when the shim is available. - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - void* ptr = malloc(kAllocSize); - // Prevent the compiler from optimizing out the malloc/free pair. - ASSERT_NE(nullptr, ptr); - - ThreadHeapUsage u2 = ThreadHeapUsageTracker::GetUsageSnapshot(); - free(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u3 = usage_tracker.usage(); - - // Verify that at least one allocation operation was recorded, and that free - // operations are at least monotonically growing. - EXPECT_LE(0U, u1.alloc_ops); - EXPECT_LE(u1.alloc_ops + 1, u2.alloc_ops); - EXPECT_LE(u1.alloc_ops + 1, u3.alloc_ops); - - // Verify that at least the bytes above were recorded. - EXPECT_LE(u1.alloc_bytes + kAllocSize, u2.alloc_bytes); - - // Verify that at least the one free operation above was recorded. - EXPECT_LE(u2.free_ops + 1, u3.free_ops); - - TestingThreadHeapUsageTracker::DisableHeapTrackingForTesting(); - - ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled()); -} -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - -} // namespace debug -} // namespace base |