Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
2
TMessagesProj/jni/voip/webrtc/base/debug/OWNERS
Normal file
2
TMessagesProj/jni/voip/webrtc/base/debug/OWNERS
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# For activity tracking:
|
||||
per-file activity_*=bcwhite@chromium.org
|
||||
405
TMessagesProj/jni/voip/webrtc/base/debug/activity_analyzer.cc
Normal file
405
TMessagesProj/jni/voip/webrtc/base/debug/activity_analyzer.cc
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
// 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/logging.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
namespace {
|
||||
|
||||
const ActivityUserData::Snapshot& GetEmptyUserDataSnapshot() {
|
||||
// An empty snapshot that can be returned when there otherwise is none.
|
||||
static const NoDestructor<ActivityUserData::Snapshot> empty_snapshot;
|
||||
return *empty_snapshot;
|
||||
}
|
||||
|
||||
// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
|
||||
enum AnalyzerCreationError {
|
||||
kInvalidMemoryMappedFile,
|
||||
kPmaBadFile,
|
||||
kPmaUninitialized,
|
||||
kPmaDeleted,
|
||||
kPmaCorrupt,
|
||||
kAnalyzerCreationErrorMax // Keep this last.
|
||||
};
|
||||
|
||||
void LogAnalyzerCreationError(AnalyzerCreationError error) {
|
||||
UmaHistogramEnumeration("ActivityTracker.Collect.AnalyzerCreationError",
|
||||
error, kAnalyzerCreationErrorMax);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ThreadActivityAnalyzer::Snapshot::Snapshot() = default;
|
||||
ThreadActivityAnalyzer::Snapshot::~Snapshot() = default;
|
||||
|
||||
ThreadActivityAnalyzer::ThreadActivityAnalyzer(
|
||||
const ThreadActivityTracker& tracker)
|
||||
: activity_snapshot_valid_(tracker.CreateSnapshot(&activity_snapshot_)) {}
|
||||
|
||||
ThreadActivityAnalyzer::ThreadActivityAnalyzer(void* base, size_t size)
|
||||
: ThreadActivityAnalyzer(ThreadActivityTracker(base, size)) {}
|
||||
|
||||
ThreadActivityAnalyzer::ThreadActivityAnalyzer(
|
||||
PersistentMemoryAllocator* allocator,
|
||||
PersistentMemoryAllocator::Reference reference)
|
||||
: ThreadActivityAnalyzer(allocator->GetAsArray<char>(
|
||||
reference,
|
||||
GlobalActivityTracker::kTypeIdActivityTracker,
|
||||
PersistentMemoryAllocator::kSizeAny),
|
||||
allocator->GetAllocSize(reference)) {}
|
||||
|
||||
ThreadActivityAnalyzer::~ThreadActivityAnalyzer() = default;
|
||||
|
||||
void ThreadActivityAnalyzer::AddGlobalInformation(
|
||||
GlobalActivityAnalyzer* global) {
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
// User-data is held at the global scope even though it's referenced at the
|
||||
// thread scope.
|
||||
activity_snapshot_.user_data_stack.clear();
|
||||
for (auto& activity : activity_snapshot_.activity_stack) {
|
||||
// The global GetUserDataSnapshot will return an empty snapshot if the ref
|
||||
// or id is not valid.
|
||||
activity_snapshot_.user_data_stack.push_back(global->GetUserDataSnapshot(
|
||||
activity_snapshot_.process_id, activity.user_data_ref,
|
||||
activity.user_data_id));
|
||||
}
|
||||
}
|
||||
|
||||
GlobalActivityAnalyzer::GlobalActivityAnalyzer(
|
||||
std::unique_ptr<PersistentMemoryAllocator> allocator)
|
||||
: allocator_(std::move(allocator)),
|
||||
analysis_stamp_(0LL),
|
||||
allocator_iterator_(allocator_.get()) {
|
||||
DCHECK(allocator_);
|
||||
}
|
||||
|
||||
GlobalActivityAnalyzer::~GlobalActivityAnalyzer() = default;
|
||||
|
||||
// static
|
||||
std::unique_ptr<GlobalActivityAnalyzer>
|
||||
GlobalActivityAnalyzer::CreateWithAllocator(
|
||||
std::unique_ptr<PersistentMemoryAllocator> allocator) {
|
||||
if (allocator->GetMemoryState() ==
|
||||
PersistentMemoryAllocator::MEMORY_UNINITIALIZED) {
|
||||
LogAnalyzerCreationError(kPmaUninitialized);
|
||||
return nullptr;
|
||||
}
|
||||
if (allocator->GetMemoryState() ==
|
||||
PersistentMemoryAllocator::MEMORY_DELETED) {
|
||||
LogAnalyzerCreationError(kPmaDeleted);
|
||||
return nullptr;
|
||||
}
|
||||
if (allocator->IsCorrupt()) {
|
||||
LogAnalyzerCreationError(kPmaCorrupt);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<GlobalActivityAnalyzer>(std::move(allocator));
|
||||
}
|
||||
|
||||
#if !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());
|
||||
if (!mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE)) {
|
||||
LogAnalyzerCreationError(kInvalidMemoryMappedFile);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
|
||||
LogAnalyzerCreationError(kPmaBadFile);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateWithAllocator(std::make_unique<FilePersistentMemoryAllocator>(
|
||||
std::move(mmfile), 0, 0, StringPiece(), /*readonly=*/true));
|
||||
}
|
||||
#endif // !defined(OS_NACL)
|
||||
|
||||
// static
|
||||
std::unique_ptr<GlobalActivityAnalyzer>
|
||||
GlobalActivityAnalyzer::CreateWithSharedMemory(
|
||||
base::ReadOnlySharedMemoryMapping mapping) {
|
||||
if (!mapping.IsValid() ||
|
||||
!ReadOnlySharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(
|
||||
mapping)) {
|
||||
return nullptr;
|
||||
}
|
||||
return CreateWithAllocator(
|
||||
std::make_unique<ReadOnlySharedPersistentMemoryAllocator>(
|
||||
std::move(mapping), 0, StringPiece()));
|
||||
}
|
||||
|
||||
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 GetEmptyUserDataSnapshot();
|
||||
if (iter->second.create_stamp > analysis_stamp_)
|
||||
return GetEmptyUserDataSnapshot();
|
||||
DCHECK_EQ(pid, iter->second.process_id);
|
||||
return iter->second.data;
|
||||
}
|
||||
|
||||
std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
|
||||
std::vector<std::string> messages;
|
||||
PersistentMemoryAllocator::Reference ref;
|
||||
|
||||
PersistentMemoryAllocator::Iterator iter(allocator_.get());
|
||||
while ((ref = iter.GetNextOfType(
|
||||
GlobalActivityTracker::kTypeIdGlobalLogMessage)) != 0) {
|
||||
const char* message = allocator_->GetAsArray<char>(
|
||||
ref, GlobalActivityTracker::kTypeIdGlobalLogMessage,
|
||||
PersistentMemoryAllocator::kSizeAny);
|
||||
if (message)
|
||||
messages.push_back(message);
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
std::vector<GlobalActivityTracker::ModuleInfo>
|
||||
GlobalActivityAnalyzer::GetModules(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::Contains(analyzers_, analyzer->GetThreadKey()));
|
||||
analyzer->allocator_reference_ = ref;
|
||||
analyzers_[analyzer->GetThreadKey()] = std::move(analyzer);
|
||||
} break;
|
||||
|
||||
case GlobalActivityTracker::kTypeIdProcessDataRecord: {
|
||||
// Get the PID associated with this data record.
|
||||
int64_t process_id;
|
||||
int64_t create_stamp;
|
||||
ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
|
||||
DCHECK(!base::Contains(process_data_, process_id));
|
||||
|
||||
// Create a snapshot of the data. This can fail if the data is somehow
|
||||
// corrupted or the process shutdown and the memory being released.
|
||||
UserDataSnapshot& snapshot = process_data_[process_id];
|
||||
snapshot.process_id = process_id;
|
||||
snapshot.create_stamp = create_stamp;
|
||||
const ActivityUserData process_data(base, size);
|
||||
if (!process_data.CreateSnapshot(&snapshot.data))
|
||||
break;
|
||||
|
||||
// Check that nothing changed. If it did, forget what was recorded.
|
||||
ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
|
||||
if (process_id != snapshot.process_id ||
|
||||
create_stamp != snapshot.create_stamp) {
|
||||
process_data_.erase(process_id);
|
||||
break;
|
||||
}
|
||||
|
||||
// Track PIDs.
|
||||
if (seen_pids.find(process_id) == seen_pids.end()) {
|
||||
process_ids_.push_back(process_id);
|
||||
seen_pids.insert(process_id);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the list of PIDs so that they get popped in the order found.
|
||||
std::reverse(process_ids_.begin(), process_ids_.end());
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
257
TMessagesProj/jni/voip/webrtc/base/debug/activity_analyzer.h
Normal file
257
TMessagesProj/jni/voip/webrtc/base/debug/activity_analyzer.h
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
// 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"
|
||||
#include "base/memory/shared_memory_mapping.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(
|
||||
base::ReadOnlySharedMemoryMapping mapping);
|
||||
|
||||
// Iterates over all known valid processes and returns their PIDs or zero
|
||||
// if there are no more. Calls to GetFirstProcess() will perform a global
|
||||
// snapshot in order to provide a relatively consistent state across the
|
||||
// future calls to GetNextProcess() and GetFirst/NextAnalyzer(). PIDs are
|
||||
// returned in the order they're found meaning that a first-launched
|
||||
// controlling process will be found first. Note, however, that space
|
||||
// freed by an exiting process may be re-used by a later process.
|
||||
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_
|
||||
1802
TMessagesProj/jni/voip/webrtc/base/debug/activity_tracker.cc
Normal file
1802
TMessagesProj/jni/voip/webrtc/base/debug/activity_tracker.cc
Normal file
File diff suppressed because it is too large
Load diff
1368
TMessagesProj/jni/voip/webrtc/base/debug/activity_tracker.h
Normal file
1368
TMessagesProj/jni/voip/webrtc/base/debug/activity_tracker.h
Normal file
File diff suppressed because it is too large
Load diff
16
TMessagesProj/jni/voip/webrtc/base/debug/alias.cc
Normal file
16
TMessagesProj/jni/voip/webrtc/base/debug/alias.cc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/alias.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// This file/function should be excluded from LTO/LTCG to ensure that the
|
||||
// compiler can't see this function's implementation when compiling calls to it.
|
||||
NOINLINE void Alias(const void* var) {}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
44
TMessagesProj/jni/voip/webrtc/base/debug/alias.h
Normal file
44
TMessagesProj/jni/voip/webrtc/base/debug/alias.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_ALIAS_H_
|
||||
#define BASE_DEBUG_ALIAS_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Make the optimizer think that var is aliased. This is to prevent it from
|
||||
// optimizing out local variables that would not otherwise be live at the point
|
||||
// of a potential crash.
|
||||
// base::debug::Alias should only be used for local variables, not globals,
|
||||
// object members, or function return values - these must be copied to locals if
|
||||
// you want to ensure they are recorded in crash dumps.
|
||||
// Note that if the local variable is a pointer then its value will be retained
|
||||
// but the memory that it points to will probably not be saved in the crash
|
||||
// dump - by default only stack memory is saved. Therefore the aliasing
|
||||
// technique is usually only worthwhile with non-pointer variables. If you have
|
||||
// a pointer to an object and you want to retain the object's state you need to
|
||||
// copy the object or its fields to local variables. Example usage:
|
||||
// int last_error = err_;
|
||||
// base::debug::Alias(&last_error);
|
||||
// DEBUG_ALIAS_FOR_CSTR(name_copy, p->name, 16);
|
||||
// CHECK(false);
|
||||
void BASE_EXPORT Alias(const void* var);
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
// Convenience macro that copies the null-terminated string from |c_str| into a
|
||||
// stack-allocated char array named |var_name| that holds up to |char_count|
|
||||
// characters and should be preserved in memory dumps.
|
||||
#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count) \
|
||||
char var_name[char_count]; \
|
||||
::base::strlcpy(var_name, (c_str), base::size(var_name)); \
|
||||
::base::debug::Alias(var_name);
|
||||
|
||||
#endif // BASE_DEBUG_ALIAS_H_
|
||||
104
TMessagesProj/jni/voip/webrtc/base/debug/asan_invalid_access.cc
Normal file
104
TMessagesProj/jni/voip/webrtc/base/debug/asan_invalid_access.cc
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// 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) || BUILDFLAG(IS_HWASAN)
|
||||
// NOTE(sebmarchand): We intentionally perform some invalid heap access here in
|
||||
// order to trigger an AddressSanitizer (ASan) error report.
|
||||
|
||||
// This variable is used to size an array of ints. It needs to be a multiple of
|
||||
// 4 so that off-by-one overflows are detected by HWASan, which has a shadow
|
||||
// granularity of 16 bytes.
|
||||
static const size_t kArraySize = 4;
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Defines some functions that intentionally do an invalid memory access in
|
||||
// order to trigger an AddressSanitizer (ASan) error report.
|
||||
|
||||
#ifndef BASE_DEBUG_ASAN_INVALID_ACCESS_H_
|
||||
#define BASE_DEBUG_ASAN_INVALID_ACCESS_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/sanitizer_buildflags.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
#if defined(ADDRESS_SANITIZER) || BUILDFLAG(IS_HWASAN)
|
||||
|
||||
// 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_
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/close_handle_hook_win.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <psapi.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/win/iat_patch_function.h"
|
||||
#include "base/win/pe_image.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace {
|
||||
|
||||
typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
|
||||
|
||||
typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
|
||||
HANDLE source_handle,
|
||||
HANDLE target_process,
|
||||
HANDLE* target_handle,
|
||||
DWORD desired_access,
|
||||
BOOL inherit_handle,
|
||||
DWORD options);
|
||||
|
||||
CloseHandleType g_close_function = NULL;
|
||||
DuplicateHandleType g_duplicate_function = NULL;
|
||||
|
||||
// The entry point for CloseHandle interception. This function notifies the
|
||||
// verifier about the handle that is being closed, and calls the original
|
||||
// function.
|
||||
BOOL WINAPI CloseHandleHook(HANDLE handle) {
|
||||
base::win::OnHandleBeingClosed(handle);
|
||||
return g_close_function(handle);
|
||||
}
|
||||
|
||||
BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
|
||||
HANDLE source_handle,
|
||||
HANDLE target_process,
|
||||
HANDLE* target_handle,
|
||||
DWORD desired_access,
|
||||
BOOL inherit_handle,
|
||||
DWORD options) {
|
||||
if ((options & DUPLICATE_CLOSE_SOURCE) &&
|
||||
(GetProcessId(source_process) == ::GetCurrentProcessId())) {
|
||||
base::win::OnHandleBeingClosed(source_handle);
|
||||
}
|
||||
|
||||
return g_duplicate_function(source_process, source_handle, target_process,
|
||||
target_handle, desired_access, inherit_handle,
|
||||
options);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
namespace {
|
||||
|
||||
// Provides a simple way to temporarily change the protection of a memory page.
|
||||
class AutoProtectMemory {
|
||||
public:
|
||||
AutoProtectMemory()
|
||||
: changed_(false), address_(NULL), bytes_(0), old_protect_(0) {}
|
||||
|
||||
~AutoProtectMemory() {
|
||||
RevertProtection();
|
||||
}
|
||||
|
||||
// Grants write access to a given memory range.
|
||||
bool ChangeProtection(void* address, size_t bytes);
|
||||
|
||||
// Restores the original page protection.
|
||||
void RevertProtection();
|
||||
|
||||
private:
|
||||
bool changed_;
|
||||
void* address_;
|
||||
size_t bytes_;
|
||||
DWORD old_protect_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
|
||||
};
|
||||
|
||||
bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
|
||||
DCHECK(!changed_);
|
||||
DCHECK(address);
|
||||
|
||||
// Change the page protection so that we can write.
|
||||
MEMORY_BASIC_INFORMATION memory_info;
|
||||
if (!VirtualQuery(address, &memory_info, sizeof(memory_info)))
|
||||
return false;
|
||||
|
||||
DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
|
||||
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
|
||||
memory_info.Protect;
|
||||
|
||||
DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||
if (!VirtualProtect(address, bytes, protect, &old_protect_))
|
||||
return false;
|
||||
|
||||
changed_ = true;
|
||||
address_ = address;
|
||||
bytes_ = bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoProtectMemory::RevertProtection() {
|
||||
if (!changed_)
|
||||
return;
|
||||
|
||||
DCHECK(address_);
|
||||
DCHECK(bytes_);
|
||||
|
||||
VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
|
||||
changed_ = false;
|
||||
address_ = NULL;
|
||||
bytes_ = 0;
|
||||
old_protect_ = 0;
|
||||
}
|
||||
|
||||
// Performs an EAT interception.
|
||||
void EATPatch(HMODULE module, const char* function_name,
|
||||
void* new_function, void** old_function) {
|
||||
if (!module)
|
||||
return;
|
||||
|
||||
base::win::PEImage pe(module);
|
||||
if (!pe.VerifyMagic())
|
||||
return;
|
||||
|
||||
DWORD* eat_entry = pe.GetExportEntry(function_name);
|
||||
if (!eat_entry)
|
||||
return;
|
||||
|
||||
if (!(*old_function))
|
||||
*old_function = pe.RVAToAddr(*eat_entry);
|
||||
|
||||
AutoProtectMemory memory;
|
||||
if (!memory.ChangeProtection(eat_entry, sizeof(DWORD)))
|
||||
return;
|
||||
|
||||
// Perform the patch.
|
||||
*eat_entry = static_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
|
||||
reinterpret_cast<uintptr_t>(module));
|
||||
}
|
||||
|
||||
// Performs an IAT interception.
|
||||
base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name,
|
||||
void* new_function, void** old_function) {
|
||||
if (!module)
|
||||
return NULL;
|
||||
|
||||
base::win::IATPatchFunction* patch = new base::win::IATPatchFunction;
|
||||
__try {
|
||||
// There is no guarantee that |module| is still loaded at this point.
|
||||
if (patch->PatchFromModule(module, "kernel32.dll", function_name,
|
||||
new_function)) {
|
||||
delete patch;
|
||||
return NULL;
|
||||
}
|
||||
} __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
|
||||
GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
|
||||
GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ?
|
||||
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
|
||||
// Leak the patch.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(*old_function)) {
|
||||
// Things are probably messed up if each intercepted function points to
|
||||
// a different place, but we need only one function to call.
|
||||
*old_function = patch->original_function();
|
||||
}
|
||||
return patch;
|
||||
}
|
||||
|
||||
// Keeps track of all the hooks needed to intercept functions which could
|
||||
// possibly close handles.
|
||||
class HandleHooks {
|
||||
public:
|
||||
HandleHooks() {}
|
||||
~HandleHooks() {}
|
||||
|
||||
void AddIATPatch(HMODULE module);
|
||||
void AddEATPatch();
|
||||
|
||||
private:
|
||||
std::vector<base::win::IATPatchFunction*> hooks_;
|
||||
DISALLOW_COPY_AND_ASSIGN(HandleHooks);
|
||||
};
|
||||
|
||||
void HandleHooks::AddIATPatch(HMODULE module) {
|
||||
if (!module)
|
||||
return;
|
||||
|
||||
base::win::IATPatchFunction* patch = NULL;
|
||||
patch =
|
||||
IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook),
|
||||
reinterpret_cast<void**>(&g_close_function));
|
||||
if (!patch)
|
||||
return;
|
||||
hooks_.push_back(patch);
|
||||
|
||||
patch = IATPatch(module, "DuplicateHandle",
|
||||
reinterpret_cast<void*>(&DuplicateHandleHook),
|
||||
reinterpret_cast<void**>(&g_duplicate_function));
|
||||
if (!patch)
|
||||
return;
|
||||
hooks_.push_back(patch);
|
||||
}
|
||||
|
||||
void HandleHooks::AddEATPatch() {
|
||||
// An attempt to restore the entry on the table at destruction is not safe.
|
||||
EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
|
||||
reinterpret_cast<void*>(&CloseHandleHook),
|
||||
reinterpret_cast<void**>(&g_close_function));
|
||||
EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
|
||||
reinterpret_cast<void*>(&DuplicateHandleHook),
|
||||
reinterpret_cast<void**>(&g_duplicate_function));
|
||||
}
|
||||
|
||||
void PatchLoadedModules(HandleHooks* hooks) {
|
||||
const DWORD kSize = 256;
|
||||
DWORD returned;
|
||||
std::unique_ptr<HMODULE[]> modules(new HMODULE[kSize]);
|
||||
if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
|
||||
kSize * sizeof(HMODULE), &returned)) {
|
||||
return;
|
||||
}
|
||||
returned /= sizeof(HMODULE);
|
||||
returned = std::min(kSize, returned);
|
||||
|
||||
for (DWORD current = 0; current < returned; current++) {
|
||||
hooks->AddIATPatch(modules[current]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InstallHandleHooks() {
|
||||
static HandleHooks* hooks = new HandleHooks();
|
||||
|
||||
// Performing EAT interception first is safer in the presence of other
|
||||
// threads attempting to call CloseHandle.
|
||||
hooks->AddEATPatch();
|
||||
PatchLoadedModules(hooks);
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
|
||||
#define BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Installs the hooks required to debug use of improper handles.
|
||||
BASE_EXPORT void InstallHandleHooks();
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
|
||||
54
TMessagesProj/jni/voip/webrtc/base/debug/crash_logging.cc
Normal file
54
TMessagesProj/jni/voip/webrtc/base/debug/crash_logging.cc
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/crash_logging.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
namespace {
|
||||
|
||||
CrashKeyImplementation* g_crash_key_impl = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashKeyString* AllocateCrashKeyString(const char name[],
|
||||
CrashKeySize value_length) {
|
||||
if (!g_crash_key_impl)
|
||||
return nullptr;
|
||||
|
||||
return g_crash_key_impl->Allocate(name, value_length);
|
||||
}
|
||||
|
||||
void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) {
|
||||
if (!g_crash_key_impl || !crash_key)
|
||||
return;
|
||||
|
||||
g_crash_key_impl->Set(crash_key, value);
|
||||
}
|
||||
|
||||
void ClearCrashKeyString(CrashKeyString* crash_key) {
|
||||
if (!g_crash_key_impl || !crash_key)
|
||||
return;
|
||||
|
||||
g_crash_key_impl->Clear(crash_key);
|
||||
}
|
||||
|
||||
ScopedCrashKeyString::ScopedCrashKeyString(CrashKeyString* crash_key,
|
||||
base::StringPiece value)
|
||||
: crash_key_(crash_key) {
|
||||
SetCrashKeyString(crash_key_, value);
|
||||
}
|
||||
|
||||
ScopedCrashKeyString::~ScopedCrashKeyString() {
|
||||
ClearCrashKeyString(crash_key_);
|
||||
}
|
||||
|
||||
void SetCrashKeyImplementation(std::unique_ptr<CrashKeyImplementation> impl) {
|
||||
delete g_crash_key_impl;
|
||||
g_crash_key_impl = impl.release();
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
104
TMessagesProj/jni/voip/webrtc/base/debug/crash_logging.h
Normal file
104
TMessagesProj/jni/voip/webrtc/base/debug/crash_logging.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_CRASH_LOGGING_H_
|
||||
#define BASE_DEBUG_CRASH_LOGGING_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// A crash key is an annotation that is carried along with a crash report, to
|
||||
// provide additional debugging information beyond a stack trace. Crash keys
|
||||
// have a name and a string value.
|
||||
//
|
||||
// The preferred API is //components/crash/core/common:crash_key, however not
|
||||
// all clients can hold a direct dependency on that target. The API provided
|
||||
// in this file indirects the dependency.
|
||||
//
|
||||
// Example usage:
|
||||
// static CrashKeyString* crash_key =
|
||||
// AllocateCrashKeyString("name", CrashKeySize::Size32);
|
||||
// SetCrashKeyString(crash_key, "value");
|
||||
// ClearCrashKeyString(crash_key);
|
||||
|
||||
// The maximum length for a crash key's value must be one of the following
|
||||
// pre-determined values.
|
||||
enum class CrashKeySize {
|
||||
Size32 = 32,
|
||||
Size64 = 64,
|
||||
Size256 = 256,
|
||||
};
|
||||
|
||||
struct CrashKeyString;
|
||||
|
||||
// Allocates a new crash key with the specified |name| with storage for a
|
||||
// value up to length |size|. This will return null if the crash key system is
|
||||
// not initialized.
|
||||
BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[],
|
||||
CrashKeySize size);
|
||||
|
||||
// Stores |value| into the specified |crash_key|. The |crash_key| may be null
|
||||
// if AllocateCrashKeyString() returned null. If |value| is longer than the
|
||||
// size with which the key was allocated, it will be truncated.
|
||||
BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key,
|
||||
base::StringPiece value);
|
||||
|
||||
// Clears any value that was stored in |crash_key|. The |crash_key| may be
|
||||
// null.
|
||||
BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key);
|
||||
|
||||
// A scoper that sets the specified key to value for the lifetime of the
|
||||
// object, and clears it on destruction.
|
||||
class BASE_EXPORT ScopedCrashKeyString {
|
||||
public:
|
||||
ScopedCrashKeyString(CrashKeyString* crash_key, base::StringPiece value);
|
||||
~ScopedCrashKeyString();
|
||||
|
||||
private:
|
||||
CrashKeyString* const crash_key_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// The following declarations are used to initialize the crash key system
|
||||
// in //base by providing implementations for the above functions.
|
||||
|
||||
// The virtual interface that provides the implementation for the crash key
|
||||
// API. This is implemented by a higher-layer component, and the instance is
|
||||
// set using the function below.
|
||||
class CrashKeyImplementation {
|
||||
public:
|
||||
virtual ~CrashKeyImplementation() = default;
|
||||
|
||||
virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0;
|
||||
virtual void Set(CrashKeyString* crash_key, base::StringPiece value) = 0;
|
||||
virtual void Clear(CrashKeyString* crash_key) = 0;
|
||||
};
|
||||
|
||||
// Initializes the crash key system in base by replacing the existing
|
||||
// implementation, if it exists, with |impl|. The |impl| is copied into base.
|
||||
BASE_EXPORT void SetCrashKeyImplementation(
|
||||
std::unique_ptr<CrashKeyImplementation> impl);
|
||||
|
||||
// The base structure for a crash key, storing the allocation metadata.
|
||||
struct CrashKeyString {
|
||||
constexpr CrashKeyString(const char name[], CrashKeySize size)
|
||||
: name(name), size(size) {}
|
||||
const char* const name;
|
||||
const CrashKeySize size;
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_CRASH_LOGGING_H_
|
||||
42
TMessagesProj/jni/voip/webrtc/base/debug/debugger.cc
Normal file
42
TMessagesProj/jni/voip/webrtc/base/debug/debugger.cc
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/debugger.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
static bool is_debug_ui_suppressed = false;
|
||||
|
||||
bool WaitForDebugger(int wait_seconds, bool silent) {
|
||||
#if defined(OS_ANDROID)
|
||||
// The pid from which we know which process to attach to are not output by
|
||||
// android ddms, so we have to print it out explicitly.
|
||||
DLOG(INFO) << "DebugUtil::WaitForDebugger(pid=" << static_cast<int>(getpid())
|
||||
<< ")";
|
||||
#endif
|
||||
for (int i = 0; i < wait_seconds * 10; ++i) {
|
||||
if (BeingDebugged()) {
|
||||
if (!silent)
|
||||
BreakDebugger();
|
||||
return true;
|
||||
}
|
||||
PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetSuppressDebugUI(bool suppress) {
|
||||
is_debug_ui_suppressed = suppress;
|
||||
}
|
||||
|
||||
bool IsDebugUISuppressed() {
|
||||
return is_debug_ui_suppressed;
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
50
TMessagesProj/jni/voip/webrtc/base/debug/debugger.h
Normal file
50
TMessagesProj/jni/voip/webrtc/base/debug/debugger.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This is a cross platform interface for helper functions related to
|
||||
// debuggers. You should use this to test if you're running under a debugger,
|
||||
// and if you would like to yield (breakpoint) into the debugger.
|
||||
|
||||
#ifndef BASE_DEBUG_DEBUGGER_H_
|
||||
#define BASE_DEBUG_DEBUGGER_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Waits wait_seconds seconds for a debugger to attach to the current process.
|
||||
// When silent is false, an exception is thrown when a debugger is detected.
|
||||
BASE_EXPORT bool WaitForDebugger(int wait_seconds, bool silent);
|
||||
|
||||
// Returns true if the given process is being run under a debugger.
|
||||
//
|
||||
// On OS X, the underlying mechanism doesn't work when the sandbox is enabled.
|
||||
// To get around this, this function caches its value.
|
||||
//
|
||||
// WARNING: Because of this, on OS X, a call MUST be made to this function
|
||||
// BEFORE the sandbox is enabled.
|
||||
BASE_EXPORT bool BeingDebugged();
|
||||
|
||||
// Break into the debugger, assumes a debugger is present.
|
||||
BASE_EXPORT void BreakDebugger();
|
||||
|
||||
// Used in test code, this controls whether showing dialogs and breaking into
|
||||
// the debugger is suppressed for debug errors, even in debug mode (normally
|
||||
// release mode doesn't do this stuff -- this is controlled separately).
|
||||
// Normally UI is not suppressed. This is normally used when running automated
|
||||
// tests where we want a crash rather than a dialog or a debugger.
|
||||
BASE_EXPORT void SetSuppressDebugUI(bool suppress);
|
||||
BASE_EXPORT bool IsDebugUISuppressed();
|
||||
|
||||
// If a debugger is present, verifies that it is properly set up, and DCHECK()s
|
||||
// if misconfigured. Currently only verifies that //tools/gdb/gdbinit has been
|
||||
// sourced when using gdb on Linux and //tools/lldb/lldbinit.py has been sourced
|
||||
// when using lldb on macOS.
|
||||
BASE_EXPORT void VerifyDebugger();
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_DEBUGGER_H_
|
||||
360
TMessagesProj/jni/voip/webrtc/base/debug/debugger_posix.cc
Normal file
360
TMessagesProj/jni/voip/webrtc/base/debug/debugger_posix.cc
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/debugger.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/clang_profiling_buildflags.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(__GLIBCXX__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX) || defined(OS_BSD)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_FREEBSD)
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
#include <zircon/process.h>
|
||||
#include <zircon/syscalls.h>
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "base/debug/alias.h"
|
||||
#include "base/debug/debugging_buildflags.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
|
||||
#if BUILDFLAG(CLANG_PROFILING)
|
||||
#include "base/test/clang_profiling.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_SYMBOLIZE)
|
||||
#include "base/third_party/symbolize/symbolize.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
#if defined(OS_MACOSX) || defined(OS_BSD)
|
||||
|
||||
// Based on Apple's recommended method as described in
|
||||
// http://developer.apple.com/qa/qa2004/qa1361.html
|
||||
bool BeingDebugged() {
|
||||
// NOTE: This code MUST be async-signal safe (it's used by in-process
|
||||
// stack dumping signal handler). NO malloc or stdio is allowed here.
|
||||
//
|
||||
// While some code used below may be async-signal unsafe, note how
|
||||
// the result is cached (see |is_set| and |being_debugged| static variables
|
||||
// right below). If this code is properly warmed-up early
|
||||
// in the start-up process, it should be safe to use later.
|
||||
|
||||
// If the process is sandboxed then we can't use the sysctl, so cache the
|
||||
// value.
|
||||
static bool is_set = false;
|
||||
static bool being_debugged = false;
|
||||
|
||||
if (is_set)
|
||||
return being_debugged;
|
||||
|
||||
// Initialize mib, which tells sysctl what info we want. In this case,
|
||||
// we're looking for information about a specific process ID.
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
getpid()
|
||||
#if defined(OS_OPENBSD)
|
||||
, sizeof(struct kinfo_proc),
|
||||
0
|
||||
#endif
|
||||
};
|
||||
|
||||
// Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and
|
||||
// binary interfaces may change.
|
||||
struct kinfo_proc info;
|
||||
size_t info_size = sizeof(info);
|
||||
|
||||
#if defined(OS_OPENBSD)
|
||||
if (sysctl(mib, base::size(mib), NULL, &info_size, NULL, 0) < 0)
|
||||
return -1;
|
||||
|
||||
mib[5] = (info_size / sizeof(struct kinfo_proc));
|
||||
#endif
|
||||
|
||||
int sysctl_result = sysctl(mib, base::size(mib), &info, &info_size, NULL, 0);
|
||||
DCHECK_EQ(sysctl_result, 0);
|
||||
if (sysctl_result != 0) {
|
||||
is_set = true;
|
||||
being_debugged = false;
|
||||
return being_debugged;
|
||||
}
|
||||
|
||||
// This process is being debugged if the P_TRACED flag is set.
|
||||
is_set = true;
|
||||
#if defined(OS_FREEBSD)
|
||||
being_debugged = (info.ki_flag & P_TRACED) != 0;
|
||||
#elif defined(OS_BSD)
|
||||
being_debugged = (info.p_flag & P_TRACED) != 0;
|
||||
#else
|
||||
being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
#endif
|
||||
return being_debugged;
|
||||
}
|
||||
|
||||
void VerifyDebugger() {
|
||||
#if BUILDFLAG(ENABLE_LLDBINIT_WARNING)
|
||||
if (Environment::Create()->HasVar("CHROMIUM_LLDBINIT_SOURCED"))
|
||||
return;
|
||||
if (!BeingDebugged())
|
||||
return;
|
||||
DCHECK(false)
|
||||
<< "Detected lldb without sourcing //tools/lldb/lldbinit.py. lldb may "
|
||||
"not be able to find debug symbols. Please see debug instructions for "
|
||||
"using //tools/lldb/lldbinit.py:\n"
|
||||
"https://chromium.googlesource.com/chromium/src/+/master/docs/"
|
||||
"lldbinit.md\n"
|
||||
"To continue anyway, type 'continue' in lldb. To always skip this "
|
||||
"check, define an environment variable CHROMIUM_LLDBINIT_SOURCED=1";
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
|
||||
|
||||
// We can look in /proc/self/status for TracerPid. We are likely used in crash
|
||||
// handling, so we are careful not to use the heap or have side effects.
|
||||
// Another option that is common is to try to ptrace yourself, but then we
|
||||
// can't detach without forking(), and that's not so great.
|
||||
// static
|
||||
Process GetDebuggerProcess() {
|
||||
// NOTE: This code MUST be async-signal safe (it's used by in-process
|
||||
// stack dumping signal handler). NO malloc or stdio is allowed here.
|
||||
|
||||
int status_fd = open("/proc/self/status", O_RDONLY);
|
||||
if (status_fd == -1)
|
||||
return Process();
|
||||
|
||||
// We assume our line will be in the first 1024 characters and that we can
|
||||
// read this much all at once. In practice this will generally be true.
|
||||
// This simplifies and speeds up things considerably.
|
||||
char buf[1024];
|
||||
|
||||
ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
|
||||
if (IGNORE_EINTR(close(status_fd)) < 0)
|
||||
return Process();
|
||||
|
||||
if (num_read <= 0)
|
||||
return Process();
|
||||
|
||||
StringPiece status(buf, num_read);
|
||||
StringPiece tracer("TracerPid:\t");
|
||||
|
||||
StringPiece::size_type pid_index = status.find(tracer);
|
||||
if (pid_index == StringPiece::npos)
|
||||
return Process();
|
||||
pid_index += tracer.size();
|
||||
StringPiece::size_type pid_end_index = status.find('\n', pid_index);
|
||||
if (pid_end_index == StringPiece::npos)
|
||||
return Process();
|
||||
|
||||
StringPiece pid_str(buf + pid_index, pid_end_index - pid_index);
|
||||
int pid = 0;
|
||||
if (!StringToInt(pid_str, &pid))
|
||||
return Process();
|
||||
|
||||
return Process(pid);
|
||||
}
|
||||
|
||||
bool BeingDebugged() {
|
||||
return GetDebuggerProcess().IsValid();
|
||||
}
|
||||
|
||||
void VerifyDebugger() {
|
||||
#if BUILDFLAG(ENABLE_GDBINIT_WARNING)
|
||||
// Quick check before potentially slower GetDebuggerProcess().
|
||||
if (Environment::Create()->HasVar("CHROMIUM_GDBINIT_SOURCED"))
|
||||
return;
|
||||
|
||||
Process proc = GetDebuggerProcess();
|
||||
if (!proc.IsValid())
|
||||
return;
|
||||
|
||||
FilePath cmdline_file =
|
||||
FilePath("/proc").Append(NumberToString(proc.Handle())).Append("cmdline");
|
||||
std::string cmdline;
|
||||
if (!ReadFileToString(cmdline_file, &cmdline))
|
||||
return;
|
||||
|
||||
// /proc/*/cmdline separates arguments with null bytes, but we only care about
|
||||
// the executable name, so interpret |cmdline| as a null-terminated C string
|
||||
// to extract the exe portion.
|
||||
StringPiece exe(cmdline.c_str());
|
||||
|
||||
DCHECK(ToLowerASCII(exe).find("gdb") == std::string::npos)
|
||||
<< "Detected gdb without sourcing //tools/gdb/gdbinit. gdb may not be "
|
||||
"able to find debug symbols, and pretty-printing of STL types may not "
|
||||
"work. Please see debug instructions for using //tools/gdb/gdbinit:\n"
|
||||
"https://chromium.googlesource.com/chromium/src/+/master/docs/"
|
||||
"gdbinit.md\n"
|
||||
"To continue anyway, type 'continue' in gdb. To always skip this "
|
||||
"check, define an environment variable CHROMIUM_GDBINIT_SOURCED=1";
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(OS_FUCHSIA)
|
||||
|
||||
bool BeingDebugged() {
|
||||
zx_info_process_t info = {};
|
||||
// Ignore failures. The 0-initialization above will result in "false" for
|
||||
// error cases.
|
||||
zx_object_get_info(zx_process_self(), ZX_INFO_PROCESS, &info, sizeof(info),
|
||||
nullptr, nullptr);
|
||||
return info.debugger_attached;
|
||||
}
|
||||
|
||||
void VerifyDebugger() {}
|
||||
|
||||
#else
|
||||
|
||||
bool BeingDebugged() {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
void VerifyDebugger() {}
|
||||
|
||||
#endif
|
||||
|
||||
// We want to break into the debugger in Debug mode, and cause a crash dump in
|
||||
// Release mode. Breakpad behaves as follows:
|
||||
//
|
||||
// +-------+-----------------+-----------------+
|
||||
// | OS | Dump on SIGTRAP | Dump on SIGABRT |
|
||||
// +-------+-----------------+-----------------+
|
||||
// | Linux | N | Y |
|
||||
// | Mac | Y | N |
|
||||
// +-------+-----------------+-----------------+
|
||||
//
|
||||
// Thus we do the following:
|
||||
// Linux: Debug mode if a debugger is attached, send SIGTRAP; otherwise send
|
||||
// SIGABRT
|
||||
// Mac: Always send SIGTRAP.
|
||||
|
||||
#if defined(ARCH_CPU_ARMEL)
|
||||
#define DEBUG_BREAK_ASM() asm("bkpt 0")
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
#define DEBUG_BREAK_ASM() asm("brk 0")
|
||||
#elif defined(ARCH_CPU_MIPS_FAMILY)
|
||||
#define DEBUG_BREAK_ASM() asm("break 2")
|
||||
#elif defined(ARCH_CPU_X86_FAMILY)
|
||||
#define DEBUG_BREAK_ASM() asm("int3")
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
||||
#define DEBUG_BREAK() abort()
|
||||
#elif defined(OS_NACL)
|
||||
// The NaCl verifier doesn't let use use int3. For now, we call abort(). We
|
||||
// should ask for advice from some NaCl experts about the optimum thing here.
|
||||
// http://code.google.com/p/nativeclient/issues/detail?id=645
|
||||
#define DEBUG_BREAK() abort()
|
||||
#elif !defined(OS_MACOSX)
|
||||
// Though Android has a "helpful" process called debuggerd to catch native
|
||||
// signals on the general assumption that they are fatal errors. If no debugger
|
||||
// is attached, we call abort since Breakpad needs SIGABRT to create a dump.
|
||||
// When debugger is attached, for ARM platform the bkpt instruction appears
|
||||
// to cause SIGBUS which is trapped by debuggerd, and we've had great
|
||||
// difficulty continuing in a debugger once we stop from SIG triggered by native
|
||||
// code, use GDB to set |go| to 1 to resume execution; for X86 platform, use
|
||||
// "int3" to setup breakpiont and raise SIGTRAP.
|
||||
//
|
||||
// On other POSIX architectures, except Mac OS X, we use the same logic to
|
||||
// ensure that breakpad creates a dump on crashes while it is still possible to
|
||||
// use a debugger.
|
||||
namespace {
|
||||
void DebugBreak() {
|
||||
if (!BeingDebugged()) {
|
||||
abort();
|
||||
} else {
|
||||
#if defined(DEBUG_BREAK_ASM)
|
||||
DEBUG_BREAK_ASM();
|
||||
#else
|
||||
volatile int go = 0;
|
||||
while (!go)
|
||||
PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
#define DEBUG_BREAK() DebugBreak()
|
||||
#elif defined(DEBUG_BREAK_ASM)
|
||||
#define DEBUG_BREAK() DEBUG_BREAK_ASM()
|
||||
#else
|
||||
#error "Don't know how to debug break on this architecture/OS"
|
||||
#endif
|
||||
|
||||
void BreakDebugger() {
|
||||
#if BUILDFLAG(CLANG_PROFILING)
|
||||
WriteClangProfilingProfile();
|
||||
#endif
|
||||
|
||||
// NOTE: This code MUST be async-signal safe (it's used by in-process
|
||||
// stack dumping signal handler). NO malloc or stdio is allowed here.
|
||||
|
||||
// Linker's ICF feature may merge this function with other functions with the
|
||||
// same definition (e.g. any function whose sole job is to call abort()) and
|
||||
// it may confuse the crash report processing system. http://crbug.com/508489
|
||||
static int static_variable_to_make_this_function_unique = 0;
|
||||
Alias(&static_variable_to_make_this_function_unique);
|
||||
|
||||
DEBUG_BREAK();
|
||||
#if defined(OS_ANDROID) && !defined(OFFICIAL_BUILD)
|
||||
// For Android development we always build release (debug builds are
|
||||
// unmanageably large), so the unofficial build is used for debugging. It is
|
||||
// helpful to be able to insert BreakDebugger() statements in the source,
|
||||
// attach the debugger, inspect the state of the program and then resume it by
|
||||
// setting the 'go' variable above.
|
||||
#elif defined(NDEBUG)
|
||||
// Terminate the program after signaling the debug break.
|
||||
// When DEBUG_BREAK() expands to abort(), this is unreachable code. Rather
|
||||
// than carefully tracking in which cases DEBUG_BREAK()s is noreturn, just
|
||||
// disable the unreachable code warning here.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunreachable-code"
|
||||
_exit(1);
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
37
TMessagesProj/jni/voip/webrtc/base/debug/debugger_win.cc
Normal file
37
TMessagesProj/jni/voip/webrtc/base/debug/debugger_win.cc
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/debugger.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/clang_profiling_buildflags.h"
|
||||
|
||||
#if BUILDFLAG(CLANG_PROFILING)
|
||||
#include "base/test/clang_profiling.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
bool BeingDebugged() {
|
||||
return ::IsDebuggerPresent() != 0;
|
||||
}
|
||||
|
||||
void BreakDebugger() {
|
||||
#if BUILDFLAG(CLANG_PROFILING)
|
||||
WriteClangProfilingProfile();
|
||||
#endif
|
||||
|
||||
if (IsDebugUISuppressed())
|
||||
_exit(1);
|
||||
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
void VerifyDebugger() {}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/dump_without_crashing.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Pointer to the function that's called by DumpWithoutCrashing() to dump the
|
||||
// process's memory.
|
||||
void(CDECL* dump_without_crashing_function_)() = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace debug {
|
||||
|
||||
bool DumpWithoutCrashing() {
|
||||
if (dump_without_crashing_function_) {
|
||||
(*dump_without_crashing_function_)();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetDumpWithoutCrashingFunction(void (CDECL *function)()) {
|
||||
#if !defined(COMPONENT_BUILD)
|
||||
// In component builds, the same base is shared between modules
|
||||
// so might be initialized several times. However in non-
|
||||
// component builds this should never happen.
|
||||
DCHECK(!dump_without_crashing_function_);
|
||||
#endif
|
||||
dump_without_crashing_function_ = function;
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
|
||||
#define BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace debug {
|
||||
|
||||
// Handler to silently dump the current process without crashing.
|
||||
// Before calling this function, call SetDumpWithoutCrashingFunction to pass a
|
||||
// function pointer.
|
||||
// Windows:
|
||||
// This must be done for each instance of base (i.e. module) and is normally
|
||||
// chrome_elf!DumpProcessWithoutCrash. See example code in chrome_main.cc that
|
||||
// does this for chrome.dll and chrome_child.dll. Note: Crashpad sets this up
|
||||
// for main chrome.exe as part of calling crash_reporter::InitializeCrashpad.
|
||||
// Mac/Linux:
|
||||
// Crashpad does this as part of crash_reporter::InitializeCrashpad.
|
||||
// Returns false if called before SetDumpWithoutCrashingFunction.
|
||||
BASE_EXPORT bool DumpWithoutCrashing();
|
||||
|
||||
// Sets a function that'll be invoked to dump the current process when
|
||||
// DumpWithoutCrashing() is called.
|
||||
BASE_EXPORT void SetDumpWithoutCrashingFunction(void (CDECL *function)());
|
||||
|
||||
} // namespace debug
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
|
||||
189
TMessagesProj/jni/voip/webrtc/base/debug/elf_reader.cc
Normal file
189
TMessagesProj/jni/voip/webrtc/base/debug/elf_reader.cc
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/elf_reader.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <elf.h>
|
||||
|
||||
#include "base/bits.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/hash/sha1.h"
|
||||
#include "base/strings/safe_sprintf.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
// NOTE: This code may be used in crash handling code, so the implementation
|
||||
// must avoid dynamic memory allocation or using data structures which rely on
|
||||
// dynamic allocation.
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
namespace {
|
||||
|
||||
#if __SIZEOF_POINTER__ == 4
|
||||
using Ehdr = Elf32_Ehdr;
|
||||
using Dyn = Elf32_Dyn;
|
||||
using Half = Elf32_Half;
|
||||
using Nhdr = Elf32_Nhdr;
|
||||
using Word = Elf32_Word;
|
||||
#else
|
||||
using Ehdr = Elf64_Ehdr;
|
||||
using Dyn = Elf64_Dyn;
|
||||
using Half = Elf64_Half;
|
||||
using Nhdr = Elf64_Nhdr;
|
||||
using Word = Elf64_Word;
|
||||
#endif
|
||||
|
||||
constexpr char kGnuNoteName[] = "GNU";
|
||||
|
||||
// Returns a pointer to the header of the ELF binary mapped into memory,
|
||||
// or a null pointer if the header is invalid.
|
||||
const Ehdr* GetElfHeader(const void* elf_mapped_base) {
|
||||
const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
|
||||
if (strncmp(elf_base, ELFMAG, SELFMAG) != 0)
|
||||
return nullptr;
|
||||
|
||||
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
|
||||
return elf_header;
|
||||
}
|
||||
|
||||
// Returns the ELF base address that should be used as a starting point to
|
||||
// access other segments.
|
||||
const char* GetElfBaseVirtualAddress(const void* elf_mapped_base) {
|
||||
const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
|
||||
for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
|
||||
if (header.p_type == PT_LOAD) {
|
||||
size_t load_bias = static_cast<size_t>(header.p_vaddr);
|
||||
CHECK_GE(reinterpret_cast<uintptr_t>(elf_base), load_bias);
|
||||
return elf_base - load_bias;
|
||||
}
|
||||
}
|
||||
return elf_base;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
span<const Phdr> GetElfProgramHeaders(const void* elf_mapped_base) {
|
||||
// NOTE: Function should use async signal safe calls only.
|
||||
|
||||
const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
|
||||
const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
|
||||
if (!elf_header)
|
||||
return {};
|
||||
|
||||
return span<const Phdr>(
|
||||
reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff),
|
||||
elf_header->e_phnum);
|
||||
}
|
||||
|
||||
size_t ReadElfBuildId(const void* elf_mapped_base,
|
||||
bool uppercase,
|
||||
ElfBuildIdBuffer build_id) {
|
||||
// NOTE: Function should use async signal safe calls only.
|
||||
|
||||
const char* elf_virtual_base = GetElfBaseVirtualAddress(elf_mapped_base);
|
||||
const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
|
||||
if (!elf_header)
|
||||
return 0;
|
||||
|
||||
for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
|
||||
if (header.p_type != PT_NOTE)
|
||||
continue;
|
||||
|
||||
// Look for a NT_GNU_BUILD_ID note with name == "GNU".
|
||||
const char* current_section = elf_virtual_base + header.p_vaddr;
|
||||
const char* section_end = current_section + header.p_memsz;
|
||||
const Nhdr* current_note = nullptr;
|
||||
bool found = false;
|
||||
while (current_section < section_end) {
|
||||
current_note = reinterpret_cast<const Nhdr*>(current_section);
|
||||
if (current_note->n_type == NT_GNU_BUILD_ID) {
|
||||
StringPiece note_name(current_section + sizeof(Nhdr),
|
||||
current_note->n_namesz);
|
||||
// Explicit constructor is used to include the '\0' character.
|
||||
if (note_name == StringPiece(kGnuNoteName, sizeof(kGnuNoteName))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t section_size = bits::Align(current_note->n_namesz, 4) +
|
||||
bits::Align(current_note->n_descsz, 4) +
|
||||
sizeof(Nhdr);
|
||||
if (section_size > static_cast<size_t>(section_end - current_section))
|
||||
return 0;
|
||||
current_section += section_size;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
// Validate that the serialized build ID will fit inside |build_id|.
|
||||
size_t note_size = current_note->n_descsz;
|
||||
if ((note_size * 2) > kMaxBuildIdStringLength)
|
||||
continue;
|
||||
|
||||
// Write out the build ID as a null-terminated hex string.
|
||||
const uint8_t* build_id_raw =
|
||||
reinterpret_cast<const uint8_t*>(current_note) + sizeof(Nhdr) +
|
||||
bits::Align(current_note->n_namesz, 4);
|
||||
size_t i = 0;
|
||||
for (i = 0; i < current_note->n_descsz; ++i) {
|
||||
strings::SafeSNPrintf(&build_id[i * 2], 3, (uppercase ? "%02X" : "%02x"),
|
||||
build_id_raw[i]);
|
||||
}
|
||||
build_id[i * 2] = '\0';
|
||||
|
||||
// Return the length of the string.
|
||||
return i * 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Optional<StringPiece> ReadElfLibraryName(const void* elf_mapped_base) {
|
||||
// NOTE: Function should use async signal safe calls only.
|
||||
|
||||
const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
|
||||
const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
|
||||
if (!elf_header)
|
||||
return {};
|
||||
|
||||
for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
|
||||
if (header.p_type != PT_DYNAMIC)
|
||||
continue;
|
||||
|
||||
// Read through the ELF dynamic sections to find the string table and
|
||||
// SONAME offsets, which are used to compute the offset of the library
|
||||
// name string.
|
||||
const Dyn* dynamic_start =
|
||||
reinterpret_cast<const Dyn*>(elf_base + header.p_vaddr);
|
||||
const Dyn* dynamic_end = reinterpret_cast<const Dyn*>(
|
||||
elf_base + header.p_vaddr + header.p_memsz);
|
||||
Word soname_strtab_offset = 0;
|
||||
const char* strtab_addr = 0;
|
||||
for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end;
|
||||
++dynamic_iter) {
|
||||
if (dynamic_iter->d_tag == DT_STRTAB) {
|
||||
#if defined(OS_FUCHSIA) || defined(OS_ANDROID)
|
||||
// Fuchsia and Android executables are position-independent, so treat
|
||||
// pointers in the ELF header as offsets into the address space instead
|
||||
// of absolute addresses.
|
||||
strtab_addr = (size_t)dynamic_iter->d_un.d_ptr + (const char*)elf_base;
|
||||
#else
|
||||
strtab_addr = (const char*)dynamic_iter->d_un.d_ptr;
|
||||
#endif
|
||||
} else if (dynamic_iter->d_tag == DT_SONAME) {
|
||||
soname_strtab_offset = dynamic_iter->d_un.d_val;
|
||||
}
|
||||
}
|
||||
if (soname_strtab_offset && strtab_addr)
|
||||
return StringPiece(strtab_addr + soname_strtab_offset);
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
50
TMessagesProj/jni/voip/webrtc/base/debug/elf_reader.h
Normal file
50
TMessagesProj/jni/voip/webrtc/base/debug/elf_reader.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_ELF_READER_H_
|
||||
#define BASE_DEBUG_ELF_READER_H_
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/hash/sha1.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
|
||||
// Functions for querying metadata from ELF binaries.
|
||||
// All functions require that the file be fully memory mapped.
|
||||
|
||||
#if __SIZEOF_POINTER__ == 4
|
||||
using Phdr = Elf32_Phdr;
|
||||
#else
|
||||
using Phdr = Elf64_Phdr;
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Hex-encodes the build ID from the ELF binary located at |elf_base|.
|
||||
// Returns the length of the build ID in bytes, or zero if the build ID couldn't
|
||||
// be read.
|
||||
// When |uppercase| is |true|, the output string is written using uppercase hex
|
||||
// characters. Otherwise, the output is lowercased.
|
||||
constexpr size_t kMaxBuildIdStringLength = kSHA1Length * 2;
|
||||
using ElfBuildIdBuffer = char[kMaxBuildIdStringLength + 1];
|
||||
size_t BASE_EXPORT ReadElfBuildId(const void* elf_base,
|
||||
bool uppercase,
|
||||
ElfBuildIdBuffer build_id);
|
||||
|
||||
// Returns the library name from the ELF file mapped at |elf_base|.
|
||||
// Returns an empty result if the name could not be read.
|
||||
Optional<StringPiece> BASE_EXPORT ReadElfLibraryName(const void* elf_base);
|
||||
|
||||
// Returns a span of ELF program headers for the ELF file mapped at
|
||||
// |elf_base|, or an empty span if the header couldn't be read.
|
||||
span<const Phdr> BASE_EXPORT GetElfProgramHeaders(const void* elf_base);
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_ELF_READER_H_
|
||||
509
TMessagesProj/jni/voip/webrtc/base/debug/gdi_debug_util_win.cc
Normal file
509
TMessagesProj/jni/voip/webrtc/base/debug/gdi_debug_util_win.cc
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
#include "base/debug/gdi_debug_util_win.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <TlHelp32.h>
|
||||
#include <psapi.h>
|
||||
#include <stddef.h>
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#include "base/debug/alias.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/windows_version.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// A partial PEB up until GdiSharedHandleTable.
|
||||
// Derived from the ntdll symbols (ntdll!_PEB).
|
||||
template <typename PointerType>
|
||||
struct PartialWinPeb {
|
||||
unsigned char InheritedAddressSpace;
|
||||
unsigned char ReadImageFileExecOptions;
|
||||
unsigned char BeingDebugged;
|
||||
unsigned char ImageUsesLargePages : 1;
|
||||
unsigned char IsProtectedProcess : 1;
|
||||
unsigned char IsLegacyProcess : 1;
|
||||
unsigned char IsImageDynamicallyRelocated : 1;
|
||||
unsigned char SkipPatchingUser32Forwarders : 1;
|
||||
unsigned char IsAppContainer : 1;
|
||||
unsigned char IsProtectedProcessLight : 1;
|
||||
unsigned char IsLongPathAwareProcess : 1;
|
||||
PointerType Mutant;
|
||||
PointerType ImageBaseAddress;
|
||||
PointerType Ldr;
|
||||
PointerType ProcessParamters;
|
||||
PointerType SubSystemData;
|
||||
PointerType ProcessHeap;
|
||||
PointerType FastPebLock;
|
||||
PointerType AtlThunkSListPtr;
|
||||
PointerType IFEOKey;
|
||||
uint32_t ProcessInJob : 1;
|
||||
uint32_t ProcessInitializing : 1;
|
||||
uint32_t ProcessUsingVEH : 1;
|
||||
uint32_t ProcessUsingVCH : 1;
|
||||
uint32_t ProcessUsingFTH : 1;
|
||||
uint32_t ProcessPreviouslyThrottled : 1;
|
||||
uint32_t ProcessCurrentlyThrottled : 1;
|
||||
uint32_t ProcessImagesHotPatched : 1;
|
||||
PointerType KernelCallbackTable;
|
||||
uint32_t SystemReserved;
|
||||
uint32_t AtlThunkSListPtr32;
|
||||
PointerType ApiSetMap;
|
||||
uint32_t TlsExpansionCounter;
|
||||
PointerType TlsBitmap;
|
||||
uint32_t TlsBitmapBits[2];
|
||||
PointerType ReadOnlySharedMemoryBase;
|
||||
PointerType HotpatchInformation;
|
||||
PointerType ReadOnlyStaticServerData;
|
||||
PointerType AnsiCodePageData;
|
||||
PointerType OemCodePageData;
|
||||
PointerType UnicodeCaseTableData;
|
||||
uint32_t NumberOfProcessors;
|
||||
uint32_t NtGlobalFlag;
|
||||
uint64_t CriticalSectionTimeout;
|
||||
PointerType HeapSegmentReserve;
|
||||
PointerType HeapSegmentCommit;
|
||||
PointerType HeapDeCommitTotalFreeThreshold;
|
||||
PointerType HeapDeCommitFreeBlockThreshold;
|
||||
uint32_t NumberOfHeaps;
|
||||
uint32_t MaximumNumberOfHeaps;
|
||||
PointerType ProcessHeaps;
|
||||
PointerType GdiSharedHandleTable;
|
||||
};
|
||||
|
||||
// Found from
|
||||
// https://stackoverflow.com/questions/13905661/how-to-get-list-of-gdi-handles.
|
||||
enum GdiHandleType : USHORT {
|
||||
kDC = 1,
|
||||
kRegion = 4,
|
||||
kBitmap = 5,
|
||||
kPalette = 8,
|
||||
kFont = 10,
|
||||
kBrush = 16,
|
||||
kPen = 48,
|
||||
};
|
||||
|
||||
// Adapted from GDICELL.
|
||||
template <typename PointerType>
|
||||
struct GdiTableEntry {
|
||||
PointerType pKernelAddress;
|
||||
USHORT wProcessId;
|
||||
USHORT wCount;
|
||||
USHORT wUpper;
|
||||
GdiHandleType wType;
|
||||
PointerType pUserAddress;
|
||||
};
|
||||
|
||||
// Types and names used for regular processes.
|
||||
struct RegularProcessTypes {
|
||||
using QueryInformationProcessFunc = decltype(NtQueryInformationProcess);
|
||||
static const char* query_information_process_name;
|
||||
// PROCESS_BASIC_INFORMATION
|
||||
struct ProcessBasicInformation {
|
||||
PVOID Reserved1;
|
||||
PVOID PebBaseAddress;
|
||||
PVOID Reserved2[2];
|
||||
ULONG_PTR UniqueProcessId;
|
||||
PVOID Reserved3;
|
||||
};
|
||||
|
||||
using ReadVirtualMemoryFunc = NTSTATUS NTAPI(IN HANDLE ProcessHandle,
|
||||
IN PVOID BaseAddress,
|
||||
OUT PVOID Buffer,
|
||||
IN SIZE_T Size,
|
||||
OUT PSIZE_T NumberOfBytesRead);
|
||||
static const char* read_virtual_memory_func_name;
|
||||
using NativePointerType = PVOID;
|
||||
};
|
||||
|
||||
// static
|
||||
const char* RegularProcessTypes::query_information_process_name =
|
||||
"NtQueryInformationProcess";
|
||||
|
||||
// static
|
||||
const char* RegularProcessTypes::read_virtual_memory_func_name =
|
||||
"NtReadVirtualMemory";
|
||||
|
||||
// Types and names used for WOW based processes.
|
||||
struct WowProcessTypes {
|
||||
// http://crbug.com/972185: Clang doesn't handle PVOID64 correctly, so we use
|
||||
// uint64_t as a substitute.
|
||||
|
||||
// NtWow64QueryInformationProcess64 and NtQueryInformationProcess share the
|
||||
// same signature.
|
||||
using QueryInformationProcessFunc = decltype(NtQueryInformationProcess);
|
||||
static const char* query_information_process_name;
|
||||
// PROCESS_BASIC_INFORMATION_WOW64
|
||||
struct ProcessBasicInformation {
|
||||
PVOID Reserved1[2];
|
||||
uint64_t PebBaseAddress;
|
||||
PVOID Reserved2[4];
|
||||
ULONG_PTR UniqueProcessId[2];
|
||||
PVOID Reserved3[2];
|
||||
};
|
||||
|
||||
using ReadVirtualMemoryFunc = NTSTATUS NTAPI(IN HANDLE ProcessHandle,
|
||||
IN uint64_t BaseAddress,
|
||||
OUT PVOID Buffer,
|
||||
IN ULONG64 Size,
|
||||
OUT PULONG64 NumberOfBytesRead);
|
||||
static const char* read_virtual_memory_func_name;
|
||||
using NativePointerType = uint64_t;
|
||||
};
|
||||
|
||||
// static
|
||||
const char* WowProcessTypes::query_information_process_name =
|
||||
"NtWow64QueryInformationProcess64";
|
||||
|
||||
// static
|
||||
const char* WowProcessTypes::read_virtual_memory_func_name =
|
||||
"NtWow64ReadVirtualMemory64";
|
||||
|
||||
// To prevent from having to write a regular and WOW codepaths that do the same
|
||||
// thing with different structures and functions, GetGdiTableEntries is
|
||||
// templated to expect either RegularProcessTypes or WowProcessTypes.
|
||||
template <typename ProcessType>
|
||||
std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>
|
||||
GetGdiTableEntries(const base::Process& process) {
|
||||
using GdiTableEntryVector =
|
||||
std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>;
|
||||
HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
|
||||
if (!ntdll)
|
||||
return GdiTableEntryVector();
|
||||
|
||||
static auto query_information_process_func =
|
||||
reinterpret_cast<typename ProcessType::QueryInformationProcessFunc*>(
|
||||
GetProcAddress(ntdll, ProcessType::query_information_process_name));
|
||||
if (!query_information_process_func) {
|
||||
LOG(ERROR) << ProcessType::query_information_process_name << " Missing";
|
||||
return GdiTableEntryVector();
|
||||
}
|
||||
|
||||
typename ProcessType::ProcessBasicInformation basic_info;
|
||||
NTSTATUS result =
|
||||
query_information_process_func(process.Handle(), ProcessBasicInformation,
|
||||
&basic_info, sizeof(basic_info), nullptr);
|
||||
if (result != 0) {
|
||||
LOG(ERROR) << ProcessType::query_information_process_name << " Failed "
|
||||
<< std::hex << result;
|
||||
return GdiTableEntryVector();
|
||||
}
|
||||
|
||||
static auto read_virtual_mem_func =
|
||||
reinterpret_cast<typename ProcessType::ReadVirtualMemoryFunc*>(
|
||||
GetProcAddress(ntdll, ProcessType::read_virtual_memory_func_name));
|
||||
if (!read_virtual_mem_func) {
|
||||
LOG(ERROR) << ProcessType::read_virtual_memory_func_name << " Missing";
|
||||
return GdiTableEntryVector();
|
||||
}
|
||||
|
||||
PartialWinPeb<typename ProcessType::NativePointerType> peb;
|
||||
result = read_virtual_mem_func(process.Handle(), basic_info.PebBaseAddress,
|
||||
&peb, sizeof(peb), nullptr);
|
||||
if (result != 0) {
|
||||
LOG(ERROR) << ProcessType::read_virtual_memory_func_name << " PEB Failed "
|
||||
<< std::hex << result;
|
||||
return GdiTableEntryVector();
|
||||
}
|
||||
|
||||
// Estimated size derived from address space allocation of the table:
|
||||
// Windows 10
|
||||
// 32-bit Size: 1052672 bytes
|
||||
// 64-bit Size: 1576960 bytes
|
||||
// sizeof(GdiTableEntry)
|
||||
// 32-bit: 16 bytes
|
||||
// 64-bit: 24 bytes
|
||||
// Entry Count
|
||||
// 32-bit: 65792
|
||||
// 64-bit: 65706ish
|
||||
// So we'll take a look at 65536 entries since that's the maximum handle count.
|
||||
constexpr int kGdiTableEntryCount = 65536;
|
||||
GdiTableEntryVector entries;
|
||||
entries.resize(kGdiTableEntryCount);
|
||||
result = read_virtual_mem_func(
|
||||
process.Handle(), peb.GdiSharedHandleTable, &entries[0],
|
||||
sizeof(typename GdiTableEntryVector::value_type) * entries.size(),
|
||||
nullptr);
|
||||
if (result != 0) {
|
||||
LOG(ERROR) << ProcessType::read_virtual_memory_func_name
|
||||
<< " GDI Handle Table Failed " << std::hex << result;
|
||||
return GdiTableEntryVector();
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
// Iterates through |gdi_table| and finds handles that belong to |pid|,
|
||||
// incrementing the appropriate fields in |base::debug::GdiHandleCounts|.
|
||||
template <typename PointerType>
|
||||
base::debug::GdiHandleCounts CountHandleTypesFromTable(
|
||||
DWORD pid,
|
||||
const std::vector<GdiTableEntry<PointerType>>& gdi_table) {
|
||||
base::debug::GdiHandleCounts counts{};
|
||||
for (const auto& entry : gdi_table) {
|
||||
if (entry.wProcessId != pid)
|
||||
continue;
|
||||
|
||||
switch (entry.wType & 0x7F) {
|
||||
case GdiHandleType::kDC:
|
||||
++counts.dcs;
|
||||
break;
|
||||
case GdiHandleType::kRegion:
|
||||
++counts.regions;
|
||||
break;
|
||||
case GdiHandleType::kBitmap:
|
||||
++counts.bitmaps;
|
||||
break;
|
||||
case GdiHandleType::kPalette:
|
||||
++counts.palettes;
|
||||
break;
|
||||
case GdiHandleType::kFont:
|
||||
++counts.fonts;
|
||||
break;
|
||||
case GdiHandleType::kBrush:
|
||||
++counts.brushes;
|
||||
break;
|
||||
case GdiHandleType::kPen:
|
||||
++counts.pens;
|
||||
break;
|
||||
default:
|
||||
++counts.unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
counts.total_tracked = counts.dcs + counts.regions + counts.bitmaps +
|
||||
counts.palettes + counts.fonts + counts.brushes +
|
||||
counts.pens + counts.unknown;
|
||||
return counts;
|
||||
}
|
||||
|
||||
template <typename ProcessType>
|
||||
base::Optional<base::debug::GdiHandleCounts> CollectGdiHandleCountsImpl(
|
||||
DWORD pid) {
|
||||
base::Process process = base::Process::OpenWithExtraPrivileges(pid);
|
||||
if (!process.IsValid())
|
||||
return base::nullopt;
|
||||
|
||||
std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>
|
||||
gdi_entries = GetGdiTableEntries<ProcessType>(process);
|
||||
return CountHandleTypesFromTable(pid, gdi_entries);
|
||||
}
|
||||
|
||||
// Returns the GDI Handle counts from the GDI Shared handle table. Empty on
|
||||
// failure.
|
||||
base::Optional<base::debug::GdiHandleCounts> CollectGdiHandleCounts(DWORD pid) {
|
||||
if (base::win::OSInfo::GetInstance()->wow64_status() ==
|
||||
base::win::OSInfo::WOW64_ENABLED) {
|
||||
return CollectGdiHandleCountsImpl<WowProcessTypes>(pid);
|
||||
}
|
||||
|
||||
return CollectGdiHandleCountsImpl<RegularProcessTypes>(pid);
|
||||
}
|
||||
|
||||
constexpr size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
|
||||
|
||||
HANDLE NOINLINE GetToolhelpSnapshot() {
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
CHECK_NE(INVALID_HANDLE_VALUE, snapshot);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void NOINLINE GetFirstProcess(HANDLE snapshot, PROCESSENTRY32* proc_entry) {
|
||||
proc_entry->dwSize = sizeof(PROCESSENTRY32);
|
||||
CHECK(Process32First(snapshot, proc_entry));
|
||||
}
|
||||
|
||||
void NOINLINE CrashIfExcessiveHandles(DWORD num_gdi_handles) {
|
||||
// By default, Windows 10 allows a max of 10,000 GDI handles per process.
|
||||
// Number found by inspecting
|
||||
//
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\
|
||||
// CurrentVersion\Windows\GDIProcessHandleQuota
|
||||
//
|
||||
// on a Windows 10 laptop.
|
||||
static constexpr DWORD kLotsOfHandles = 9990;
|
||||
CHECK_LE(num_gdi_handles, kLotsOfHandles);
|
||||
}
|
||||
|
||||
void NOINLINE
|
||||
CrashIfPagefileUsageTooLarge(const PROCESS_MEMORY_COUNTERS_EX& pmc) {
|
||||
CHECK_LE(pmc.PagefileUsage, kLotsOfMemory);
|
||||
}
|
||||
|
||||
void NOINLINE
|
||||
CrashIfPrivateUsageTooLarge(const PROCESS_MEMORY_COUNTERS_EX& pmc) {
|
||||
CHECK_LE(pmc.PrivateUsage, kLotsOfMemory);
|
||||
}
|
||||
|
||||
void NOINLINE CrashIfCannotAllocateSmallBitmap(BITMAPINFOHEADER* header,
|
||||
HANDLE shared_section) {
|
||||
void* small_data = nullptr;
|
||||
base::debug::Alias(&small_data);
|
||||
header->biWidth = 5;
|
||||
header->biHeight = -5;
|
||||
HBITMAP small_bitmap =
|
||||
CreateDIBSection(nullptr, reinterpret_cast<BITMAPINFO*>(&header), 0,
|
||||
&small_data, shared_section, 0);
|
||||
CHECK(small_bitmap != nullptr);
|
||||
DeleteObject(small_bitmap);
|
||||
}
|
||||
|
||||
void NOINLINE GetProcessMemoryInfo(PROCESS_MEMORY_COUNTERS_EX* pmc) {
|
||||
pmc->cb = sizeof(*pmc);
|
||||
CHECK(GetProcessMemoryInfo(GetCurrentProcess(),
|
||||
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(pmc),
|
||||
sizeof(*pmc)));
|
||||
}
|
||||
|
||||
DWORD NOINLINE GetNumGdiHandles() {
|
||||
DWORD num_gdi_handles = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
|
||||
if (num_gdi_handles == 0) {
|
||||
DWORD get_gui_resources_error = GetLastError();
|
||||
base::debug::Alias(&get_gui_resources_error);
|
||||
CHECK(false);
|
||||
}
|
||||
return num_gdi_handles;
|
||||
}
|
||||
|
||||
void CollectChildGDIUsageAndDie(DWORD parent_pid) {
|
||||
HANDLE snapshot = GetToolhelpSnapshot();
|
||||
|
||||
int total_process_count = 0;
|
||||
base::debug::Alias(&total_process_count);
|
||||
int total_peak_gdi_count = 0;
|
||||
base::debug::Alias(&total_peak_gdi_count);
|
||||
int total_gdi_count = 0;
|
||||
base::debug::Alias(&total_gdi_count);
|
||||
int total_user_count = 0;
|
||||
base::debug::Alias(&total_user_count);
|
||||
|
||||
int child_count = 0;
|
||||
base::debug::Alias(&child_count);
|
||||
int peak_gdi_count = 0;
|
||||
base::debug::Alias(&peak_gdi_count);
|
||||
int sum_gdi_count = 0;
|
||||
base::debug::Alias(&sum_gdi_count);
|
||||
int sum_user_count = 0;
|
||||
base::debug::Alias(&sum_user_count);
|
||||
|
||||
PROCESSENTRY32 proc_entry = {};
|
||||
GetFirstProcess(snapshot, &proc_entry);
|
||||
|
||||
do {
|
||||
base::win::ScopedHandle process(
|
||||
OpenProcess(PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
proc_entry.th32ProcessID));
|
||||
if (!process.IsValid())
|
||||
continue;
|
||||
|
||||
int num_gdi_handles = GetGuiResources(process.Get(), GR_GDIOBJECTS);
|
||||
int num_user_handles = GetGuiResources(process.Get(), GR_USEROBJECTS);
|
||||
|
||||
// Compute sum and peak counts for all processes.
|
||||
++total_process_count;
|
||||
total_user_count += num_user_handles;
|
||||
total_gdi_count += num_gdi_handles;
|
||||
total_peak_gdi_count = std::max(total_peak_gdi_count, num_gdi_handles);
|
||||
|
||||
if (parent_pid != proc_entry.th32ParentProcessID)
|
||||
continue;
|
||||
|
||||
// Compute sum and peak counts for child processes.
|
||||
++child_count;
|
||||
sum_user_count += num_user_handles;
|
||||
sum_gdi_count += num_gdi_handles;
|
||||
peak_gdi_count = std::max(peak_gdi_count, num_gdi_handles);
|
||||
|
||||
} while (Process32Next(snapshot, &proc_entry));
|
||||
|
||||
CloseHandle(snapshot);
|
||||
CHECK(false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
void CollectGDIUsageAndDie(BITMAPINFOHEADER* header, HANDLE shared_section) {
|
||||
// Make sure parameters are saved in the minidump.
|
||||
DWORD last_error = GetLastError();
|
||||
bool is_gdi_available = base::win::IsUser32AndGdi32Available();
|
||||
|
||||
LONG width = header ? header->biWidth : 0;
|
||||
LONG height = header ? header->biHeight : 0;
|
||||
|
||||
base::debug::Alias(&last_error);
|
||||
base::debug::Alias(&is_gdi_available);
|
||||
base::debug::Alias(&width);
|
||||
base::debug::Alias(&height);
|
||||
base::debug::Alias(&shared_section);
|
||||
|
||||
DWORD num_user_handles = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
|
||||
DWORD num_gdi_handles = GetNumGdiHandles();
|
||||
|
||||
base::debug::Alias(&num_gdi_handles);
|
||||
base::debug::Alias(&num_user_handles);
|
||||
|
||||
base::Optional<GdiHandleCounts> optional_handle_counts =
|
||||
CollectGdiHandleCounts(GetCurrentProcessId());
|
||||
bool handle_counts_set = optional_handle_counts.has_value();
|
||||
GdiHandleCounts handle_counts =
|
||||
optional_handle_counts.value_or(GdiHandleCounts());
|
||||
int tracked_dcs = handle_counts.dcs;
|
||||
int tracked_regions = handle_counts.regions;
|
||||
int tracked_bitmaps = handle_counts.bitmaps;
|
||||
int tracked_palettes = handle_counts.palettes;
|
||||
int tracked_fonts = handle_counts.fonts;
|
||||
int tracked_brushes = handle_counts.brushes;
|
||||
int tracked_pens = handle_counts.pens;
|
||||
int tracked_unknown_handles = handle_counts.unknown;
|
||||
int tracked_total = handle_counts.total_tracked;
|
||||
|
||||
base::debug::Alias(&handle_counts_set);
|
||||
base::debug::Alias(&tracked_dcs);
|
||||
base::debug::Alias(&tracked_regions);
|
||||
base::debug::Alias(&tracked_bitmaps);
|
||||
base::debug::Alias(&tracked_palettes);
|
||||
base::debug::Alias(&tracked_fonts);
|
||||
base::debug::Alias(&tracked_brushes);
|
||||
base::debug::Alias(&tracked_pens);
|
||||
base::debug::Alias(&tracked_unknown_handles);
|
||||
base::debug::Alias(&tracked_total);
|
||||
|
||||
CrashIfExcessiveHandles(num_gdi_handles);
|
||||
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
GetProcessMemoryInfo(&pmc);
|
||||
CrashIfPagefileUsageTooLarge(pmc);
|
||||
CrashIfPrivateUsageTooLarge(pmc);
|
||||
|
||||
if (std::abs(height) * width > 100) {
|
||||
// Huh, that's weird. We don't have crazy handle count, we don't have
|
||||
// ridiculous memory usage. Try to allocate a small bitmap and see if that
|
||||
// fails too.
|
||||
CrashIfCannotAllocateSmallBitmap(header, shared_section);
|
||||
}
|
||||
// Maybe the child processes are the ones leaking GDI or USER resouces.
|
||||
CollectChildGDIUsageAndDie(GetCurrentProcessId());
|
||||
}
|
||||
|
||||
GdiHandleCounts GetGDIHandleCountsInCurrentProcessForTesting() {
|
||||
base::Optional<GdiHandleCounts> handle_counts =
|
||||
CollectGdiHandleCounts(GetCurrentProcessId());
|
||||
DCHECK(handle_counts.has_value());
|
||||
return handle_counts.value_or(GdiHandleCounts());
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
|
||||
#define BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
struct BASE_EXPORT GdiHandleCounts {
|
||||
int dcs = 0;
|
||||
int regions = 0;
|
||||
int bitmaps = 0;
|
||||
int palettes = 0;
|
||||
int fonts = 0;
|
||||
int brushes = 0;
|
||||
int pens = 0;
|
||||
int unknown = 0;
|
||||
int total_tracked = 0;
|
||||
};
|
||||
|
||||
// Crashes the process, using base::debug::Alias to leave valuable debugging
|
||||
// information in the crash dump. Pass values for |header| and |shared_section|
|
||||
// in the event of a bitmap allocation failure, to gather information about
|
||||
// those as well.
|
||||
BASE_EXPORT void CollectGDIUsageAndDie(BITMAPINFOHEADER* header = nullptr,
|
||||
HANDLE shared_section = nullptr);
|
||||
|
||||
BASE_EXPORT GdiHandleCounts GetGDIHandleCountsInCurrentProcessForTesting();
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/invalid_access_win.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/win/windows_version.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
namespace win {
|
||||
|
||||
namespace {
|
||||
|
||||
void CreateSyntheticHeapCorruption() {
|
||||
EXCEPTION_RECORD record = {};
|
||||
record.ExceptionCode = STATUS_HEAP_CORRUPTION;
|
||||
RaiseFailFastException(&record, nullptr,
|
||||
FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void TerminateWithHeapCorruption() {
|
||||
__try {
|
||||
// Pre-Windows 10, it's hard to trigger a heap corruption fast fail, so
|
||||
// artificially create one instead.
|
||||
if (base::win::GetVersion() < base::win::Version::WIN10)
|
||||
CreateSyntheticHeapCorruption();
|
||||
HANDLE heap = ::HeapCreate(0, 0, 0);
|
||||
CHECK(heap);
|
||||
CHECK(HeapSetInformation(heap, HeapEnableTerminationOnCorruption, nullptr,
|
||||
0));
|
||||
void* addr = ::HeapAlloc(heap, 0, 0x1000);
|
||||
CHECK(addr);
|
||||
// Corrupt heap header.
|
||||
char* addr_mutable = reinterpret_cast<char*>(addr);
|
||||
memset(addr_mutable - sizeof(addr), 0xCC, sizeof(addr));
|
||||
|
||||
HeapFree(heap, 0, addr);
|
||||
HeapDestroy(heap);
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
// Heap corruption exception should never be caught.
|
||||
CHECK(false);
|
||||
}
|
||||
// Should never reach here.
|
||||
abort();
|
||||
}
|
||||
|
||||
} // namespace win
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_INVALID_ACCESS_WIN_H_
|
||||
#define BASE_DEBUG_INVALID_ACCESS_WIN_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
namespace win {
|
||||
|
||||
// Creates a synthetic heap corruption that causes the current process to
|
||||
// terminate immediately with a fast fail exception.
|
||||
[[noreturn]] BASE_EXPORT void TerminateWithHeapCorruption();
|
||||
|
||||
} // namespace win
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_INVALID_ACCESS_WIN_H_
|
||||
46
TMessagesProj/jni/voip/webrtc/base/debug/leak_annotations.h
Normal file
46
TMessagesProj/jni/voip/webrtc/base/debug/leak_annotations.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_
|
||||
#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
// This file defines macros which can be used to annotate intentional memory
|
||||
// leaks. Support for annotations is implemented in LeakSanitizer. Annotated
|
||||
// objects will be treated as a source of live pointers, i.e. any heap objects
|
||||
// reachable by following pointers from an annotated object will not be
|
||||
// reported as leaks.
|
||||
//
|
||||
// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope
|
||||
// will be annotated as leaks.
|
||||
// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will
|
||||
// be annotated as a leak.
|
||||
|
||||
#if defined(LEAK_SANITIZER) && !defined(OS_NACL)
|
||||
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
|
||||
class ScopedLeakSanitizerDisabler {
|
||||
public:
|
||||
ScopedLeakSanitizerDisabler() { __lsan_disable(); }
|
||||
~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler);
|
||||
};
|
||||
|
||||
#define ANNOTATE_SCOPED_MEMORY_LEAK \
|
||||
ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
|
||||
|
||||
#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X);
|
||||
|
||||
#else
|
||||
|
||||
#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
|
||||
#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_
|
||||
142
TMessagesProj/jni/voip/webrtc/base/debug/leak_tracker.h
Normal file
142
TMessagesProj/jni/voip/webrtc/base/debug/leak_tracker.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_LEAK_TRACKER_H_
|
||||
#define BASE_DEBUG_LEAK_TRACKER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
// Only enable leak tracking in non-uClibc debug builds.
|
||||
#if !defined(NDEBUG) && !defined(__UCLIBC__)
|
||||
#define ENABLE_LEAK_TRACKER
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LEAK_TRACKER
|
||||
#include "base/containers/linked_list.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/logging.h"
|
||||
#endif // ENABLE_LEAK_TRACKER
|
||||
|
||||
// LeakTracker is a helper to verify that all instances of a class
|
||||
// have been destroyed.
|
||||
//
|
||||
// It is particularly useful for classes that are bound to a single thread --
|
||||
// before destroying that thread, one can check that there are no remaining
|
||||
// instances of that class.
|
||||
//
|
||||
// For example, to enable leak tracking for class net::URLRequest, start by
|
||||
// adding a member variable of type LeakTracker<net::URLRequest>.
|
||||
//
|
||||
// class URLRequest {
|
||||
// ...
|
||||
// private:
|
||||
// base::LeakTracker<URLRequest> leak_tracker_;
|
||||
// };
|
||||
//
|
||||
//
|
||||
// Next, when we believe all instances of net::URLRequest have been deleted:
|
||||
//
|
||||
// LeakTracker<net::URLRequest>::CheckForLeaks();
|
||||
//
|
||||
// Should the check fail (because there are live instances of net::URLRequest),
|
||||
// then the allocation callstack for each leaked instances is dumped to
|
||||
// the error log.
|
||||
//
|
||||
// If ENABLE_LEAK_TRACKER is not defined, then the check has no effect.
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
#ifndef ENABLE_LEAK_TRACKER
|
||||
|
||||
// If leak tracking is disabled, do nothing.
|
||||
template<typename T>
|
||||
class LeakTracker {
|
||||
public:
|
||||
// This destructor suppresses warnings about instances of this class not being
|
||||
// used.
|
||||
~LeakTracker() {}
|
||||
static void CheckForLeaks() {}
|
||||
static int NumLiveInstances() { return -1; }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// If leak tracking is enabled we track where the object was allocated from.
|
||||
|
||||
template<typename T>
|
||||
class LeakTracker : public LinkNode<LeakTracker<T> > {
|
||||
public:
|
||||
LeakTracker() {
|
||||
instances()->Append(this);
|
||||
}
|
||||
|
||||
~LeakTracker() {
|
||||
this->RemoveFromList();
|
||||
}
|
||||
|
||||
static void CheckForLeaks() {
|
||||
// Walk the allocation list and print each entry it contains.
|
||||
size_t count = 0;
|
||||
|
||||
// Copy the first 3 leak allocation callstacks onto the stack.
|
||||
// This way if we hit the CHECK() in a release build, the leak
|
||||
// information will be available in mini-dump.
|
||||
const size_t kMaxStackTracesToCopyOntoStack = 3;
|
||||
StackTrace stacktraces[kMaxStackTracesToCopyOntoStack];
|
||||
|
||||
for (LinkNode<LeakTracker<T> >* node = instances()->head();
|
||||
node != instances()->end();
|
||||
node = node->next()) {
|
||||
StackTrace& allocation_stack = node->value()->allocation_stack_;
|
||||
|
||||
if (count < kMaxStackTracesToCopyOntoStack)
|
||||
stacktraces[count] = allocation_stack;
|
||||
|
||||
++count;
|
||||
if (LOG_IS_ON(ERROR)) {
|
||||
LOG_STREAM(ERROR) << "Leaked " << node << " which was allocated by:";
|
||||
allocation_stack.OutputToStream(&LOG_STREAM(ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_EQ(0u, count);
|
||||
|
||||
// Hack to keep |stacktraces| and |count| alive (so compiler
|
||||
// doesn't optimize it out, and it will appear in mini-dumps).
|
||||
if (count == 0x1234) {
|
||||
for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i)
|
||||
stacktraces[i].Print();
|
||||
}
|
||||
}
|
||||
|
||||
static int NumLiveInstances() {
|
||||
// Walk the allocation list and count how many entries it has.
|
||||
int count = 0;
|
||||
for (LinkNode<LeakTracker<T> >* node = instances()->head();
|
||||
node != instances()->end();
|
||||
node = node->next()) {
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
// Each specialization of LeakTracker gets its own static storage.
|
||||
static LinkedList<LeakTracker<T> >* instances() {
|
||||
static LinkedList<LeakTracker<T> > list;
|
||||
return &list;
|
||||
}
|
||||
|
||||
StackTrace allocation_stack_;
|
||||
};
|
||||
|
||||
#endif // ENABLE_LEAK_TRACKER
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_LEAK_TRACKER_H_
|
||||
169
TMessagesProj/jni/voip/webrtc/base/debug/proc_maps_linux.cc
Normal file
169
TMessagesProj/jni/voip/webrtc/base/debug/proc_maps_linux.cc
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/proc_maps_linux.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID) && !defined(__LP64__)
|
||||
// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an
|
||||
// unsigned long int, which is incompatible with Bionic's stdint.h
|
||||
// defining uintptr_t as an unsigned int:
|
||||
// https://code.google.com/p/android/issues/detail?id=57218
|
||||
#undef SCNxPTR
|
||||
#define SCNxPTR "x"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Scans |proc_maps| starting from |pos| returning true if the gate VMA was
|
||||
// found, otherwise returns false.
|
||||
static bool ContainsGateVMA(std::string* proc_maps, size_t pos) {
|
||||
#if defined(ARCH_CPU_ARM_FAMILY)
|
||||
// The gate VMA on ARM kernels is the interrupt vectors page.
|
||||
return proc_maps->find(" [vectors]\n", pos) != std::string::npos;
|
||||
#elif defined(ARCH_CPU_X86_64)
|
||||
// The gate VMA on x86 64-bit kernels is the virtual system call page.
|
||||
return proc_maps->find(" [vsyscall]\n", pos) != std::string::npos;
|
||||
#else
|
||||
// Otherwise assume there is no gate VMA in which case we shouldn't
|
||||
// get duplicate entires.
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ReadProcMaps(std::string* proc_maps) {
|
||||
// seq_file only writes out a page-sized amount on each call. Refer to header
|
||||
// file for details.
|
||||
const long kReadSize = sysconf(_SC_PAGESIZE);
|
||||
|
||||
base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY)));
|
||||
if (!fd.is_valid()) {
|
||||
DPLOG(ERROR) << "Couldn't open /proc/self/maps";
|
||||
return false;
|
||||
}
|
||||
proc_maps->clear();
|
||||
|
||||
while (true) {
|
||||
// To avoid a copy, resize |proc_maps| so read() can write directly into it.
|
||||
// Compute |buffer| afterwards since resize() may reallocate.
|
||||
size_t pos = proc_maps->size();
|
||||
proc_maps->resize(pos + kReadSize);
|
||||
void* buffer = &(*proc_maps)[pos];
|
||||
|
||||
ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, kReadSize));
|
||||
if (bytes_read < 0) {
|
||||
DPLOG(ERROR) << "Couldn't read /proc/self/maps";
|
||||
proc_maps->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... and don't forget to trim off excess bytes.
|
||||
proc_maps->resize(pos + bytes_read);
|
||||
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
|
||||
// The gate VMA is handled as a special case after seq_file has finished
|
||||
// iterating through all entries in the virtual memory table.
|
||||
//
|
||||
// Unfortunately, if additional entries are added at this point in time
|
||||
// seq_file gets confused and the next call to read() will return duplicate
|
||||
// entries including the gate VMA again.
|
||||
//
|
||||
// Avoid this by searching for the gate VMA and breaking early.
|
||||
if (ContainsGateVMA(proc_maps, pos))
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseProcMaps(const std::string& input,
|
||||
std::vector<MappedMemoryRegion>* regions_out) {
|
||||
CHECK(regions_out);
|
||||
std::vector<MappedMemoryRegion> regions;
|
||||
|
||||
// This isn't async safe nor terribly efficient, but it doesn't need to be at
|
||||
// this point in time.
|
||||
std::vector<std::string> lines = SplitString(
|
||||
input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
// Due to splitting on '\n' the last line should be empty.
|
||||
if (i == lines.size() - 1) {
|
||||
if (!lines[i].empty()) {
|
||||
DLOG(WARNING) << "Last line not empty";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
MappedMemoryRegion region;
|
||||
const char* line = lines[i].c_str();
|
||||
char permissions[5] = {'\0'}; // Ensure NUL-terminated string.
|
||||
uint8_t dev_major = 0;
|
||||
uint8_t dev_minor = 0;
|
||||
long inode = 0;
|
||||
int path_index = 0;
|
||||
|
||||
// Sample format from man 5 proc:
|
||||
//
|
||||
// address perms offset dev inode pathname
|
||||
// 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
|
||||
//
|
||||
// The final %n term captures the offset in the input string, which is used
|
||||
// to determine the path name. It *does not* increment the return value.
|
||||
// Refer to man 3 sscanf for details.
|
||||
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n",
|
||||
®ion.start, ®ion.end, permissions, ®ion.offset,
|
||||
&dev_major, &dev_minor, &inode, &path_index) < 7) {
|
||||
DPLOG(WARNING) << "sscanf failed for line: " << line;
|
||||
return false;
|
||||
}
|
||||
|
||||
region.permissions = 0;
|
||||
|
||||
if (permissions[0] == 'r')
|
||||
region.permissions |= MappedMemoryRegion::READ;
|
||||
else if (permissions[0] != '-')
|
||||
return false;
|
||||
|
||||
if (permissions[1] == 'w')
|
||||
region.permissions |= MappedMemoryRegion::WRITE;
|
||||
else if (permissions[1] != '-')
|
||||
return false;
|
||||
|
||||
if (permissions[2] == 'x')
|
||||
region.permissions |= MappedMemoryRegion::EXECUTE;
|
||||
else if (permissions[2] != '-')
|
||||
return false;
|
||||
|
||||
if (permissions[3] == 'p')
|
||||
region.permissions |= MappedMemoryRegion::PRIVATE;
|
||||
else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory.
|
||||
return false;
|
||||
|
||||
// Pushing then assigning saves us a string copy.
|
||||
regions.push_back(region);
|
||||
regions.back().path.assign(line + path_index);
|
||||
}
|
||||
|
||||
regions_out->swap(regions);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
94
TMessagesProj/jni/voip/webrtc/base/debug/proc_maps_linux.h
Normal file
94
TMessagesProj/jni/voip/webrtc/base/debug/proc_maps_linux.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_PROC_MAPS_LINUX_H_
|
||||
#define BASE_DEBUG_PROC_MAPS_LINUX_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Describes a region of mapped memory and the path of the file mapped.
|
||||
struct MappedMemoryRegion {
|
||||
enum Permission {
|
||||
READ = 1 << 0,
|
||||
WRITE = 1 << 1,
|
||||
EXECUTE = 1 << 2,
|
||||
PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared.
|
||||
};
|
||||
|
||||
// The address range [start,end) of mapped memory.
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
|
||||
// Byte offset into |path| of the range mapped into memory.
|
||||
unsigned long long offset;
|
||||
|
||||
// Image base, if this mapping corresponds to an ELF image.
|
||||
uintptr_t base;
|
||||
|
||||
// Bitmask of read/write/execute/private/shared permissions.
|
||||
uint8_t permissions;
|
||||
|
||||
// Name of the file mapped into memory.
|
||||
//
|
||||
// NOTE: path names aren't guaranteed to point at valid files. For example,
|
||||
// "[heap]" and "[stack]" are used to represent the location of the process'
|
||||
// heap and stack, respectively.
|
||||
std::string path;
|
||||
};
|
||||
|
||||
// Reads the data from /proc/self/maps and stores the result in |proc_maps|.
|
||||
// Returns true if successful, false otherwise.
|
||||
//
|
||||
// There is *NO* guarantee that the resulting contents will be free of
|
||||
// duplicates or even contain valid entries by time the method returns.
|
||||
//
|
||||
//
|
||||
// THE GORY DETAILS
|
||||
//
|
||||
// Did you know it's next-to-impossible to atomically read the whole contents
|
||||
// of /proc/<pid>/maps? You would think that if we passed in a large-enough
|
||||
// buffer to read() that It Should Just Work(tm), but sadly that's not the case.
|
||||
//
|
||||
// Linux's procfs uses seq_file [1] for handling iteration, text formatting,
|
||||
// and dealing with resulting data that is larger than the size of a page. That
|
||||
// last bit is especially important because it means that seq_file will never
|
||||
// return more than the size of a page in a single call to read().
|
||||
//
|
||||
// Unfortunately for a program like Chrome the size of /proc/self/maps is
|
||||
// larger than the size of page so we're forced to call read() multiple times.
|
||||
// If the virtual memory table changed in any way between calls to read() (e.g.,
|
||||
// a different thread calling mprotect()), it can make seq_file generate
|
||||
// duplicate entries or skip entries.
|
||||
//
|
||||
// Even if seq_file was changed to keep flushing the contents of its page-sized
|
||||
// buffer to the usermode buffer inside a single call to read(), it has to
|
||||
// release its lock on the virtual memory table to handle page faults while
|
||||
// copying data to usermode. This puts us in the same situation where the table
|
||||
// can change while we're copying data.
|
||||
//
|
||||
// Alternatives such as fork()-and-suspend-the-parent-while-child-reads were
|
||||
// attempted, but they present more subtle problems than it's worth. Depending
|
||||
// on your use case your best bet may be to read /proc/<pid>/maps prior to
|
||||
// starting other threads.
|
||||
//
|
||||
// [1] http://kernelnewbies.org/Documents/SeqFileHowTo
|
||||
BASE_EXPORT bool ReadProcMaps(std::string* proc_maps);
|
||||
|
||||
// Parses /proc/<pid>/maps input data and stores in |regions|. Returns true
|
||||
// and updates |regions| if and only if all of |input| was successfully parsed.
|
||||
BASE_EXPORT bool ParseProcMaps(const std::string& input,
|
||||
std::vector<MappedMemoryRegion>* regions);
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_
|
||||
180
TMessagesProj/jni/voip/webrtc/base/debug/profiler.cc
Normal file
180
TMessagesProj/jni/voip/webrtc/base/debug/profiler.cc
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/profiler.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/allocator/buildflags.h"
|
||||
#include "base/debug/debugging_buildflags.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/current_module.h"
|
||||
#include "base/win/pe_image.h"
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
// TODO(peria): Enable profiling on Windows.
|
||||
#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
|
||||
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// TODO(peria): Enable profiling on Windows.
|
||||
#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
|
||||
|
||||
static int profile_count = 0;
|
||||
|
||||
void StartProfiling(const std::string& name) {
|
||||
++profile_count;
|
||||
std::string full_name(name);
|
||||
std::string pid = NumberToString(GetCurrentProcId());
|
||||
std::string count = NumberToString(profile_count);
|
||||
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
|
||||
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
|
||||
ProfilerStart(full_name.c_str());
|
||||
}
|
||||
|
||||
void StopProfiling() {
|
||||
ProfilerFlush();
|
||||
ProfilerStop();
|
||||
}
|
||||
|
||||
void FlushProfiling() {
|
||||
ProfilerFlush();
|
||||
}
|
||||
|
||||
bool BeingProfiled() {
|
||||
return ProfilingIsEnabledForAllThreads();
|
||||
}
|
||||
|
||||
void RestartProfilingAfterFork() {
|
||||
ProfilerRegisterThread();
|
||||
}
|
||||
|
||||
bool IsProfilingSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void StartProfiling(const std::string& name) {
|
||||
}
|
||||
|
||||
void StopProfiling() {
|
||||
}
|
||||
|
||||
void FlushProfiling() {
|
||||
}
|
||||
|
||||
bool BeingProfiled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RestartProfilingAfterFork() {
|
||||
}
|
||||
|
||||
bool IsProfilingSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(OS_WIN)
|
||||
|
||||
ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else // defined(OS_WIN)
|
||||
|
||||
namespace {
|
||||
|
||||
struct FunctionSearchContext {
|
||||
const char* name;
|
||||
FARPROC function;
|
||||
};
|
||||
|
||||
// Callback function to PEImage::EnumImportChunks.
|
||||
bool FindResolutionFunctionInImports(
|
||||
const base::win::PEImage &image, const char* module_name,
|
||||
PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
|
||||
PVOID cookie) {
|
||||
FunctionSearchContext* context =
|
||||
reinterpret_cast<FunctionSearchContext*>(cookie);
|
||||
|
||||
DCHECK(context);
|
||||
DCHECK(!context->function);
|
||||
|
||||
// Our import address table contains pointers to the functions we import
|
||||
// at this point. Let's retrieve the first such function and use it to
|
||||
// find the module this import was resolved to by the loader.
|
||||
const wchar_t* function_in_module =
|
||||
reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
|
||||
|
||||
// Retrieve the module by a function in the module.
|
||||
const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
|
||||
HMODULE module = NULL;
|
||||
if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
|
||||
// This can happen if someone IAT patches us to a thunk.
|
||||
return true;
|
||||
}
|
||||
|
||||
// See whether this module exports the function we're looking for.
|
||||
FARPROC exported_func = ::GetProcAddress(module, context->name);
|
||||
if (exported_func != NULL) {
|
||||
// We found it, return the function and terminate the enumeration.
|
||||
context->function = exported_func;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep going.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
FunctionType FindFunctionInImports(const char* function_name) {
|
||||
base::win::PEImage image(CURRENT_MODULE());
|
||||
|
||||
FunctionSearchContext ctx = { function_name, NULL };
|
||||
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx, nullptr);
|
||||
|
||||
return reinterpret_cast<FunctionType>(ctx.function);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
|
||||
return FindFunctionInImports<ReturnAddressLocationResolver>(
|
||||
"ResolveReturnAddressLocation");
|
||||
}
|
||||
|
||||
AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
|
||||
return FindFunctionInImports<AddDynamicSymbol>(
|
||||
"AddDynamicSymbol");
|
||||
}
|
||||
|
||||
MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
|
||||
return FindFunctionInImports<MoveDynamicSymbol>(
|
||||
"MoveDynamicSymbol");
|
||||
}
|
||||
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
76
TMessagesProj/jni/voip/webrtc/base/debug/profiler.h
Normal file
76
TMessagesProj/jni/voip/webrtc/base/debug/profiler.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_PROFILER_H_
|
||||
#define BASE_DEBUG_PROFILER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
// The Profiler functions allow usage of the underlying sampling based
|
||||
// profiler. If the application has not been built with the necessary
|
||||
// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions
|
||||
// are noops.
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Start profiling with the supplied name.
|
||||
// {pid} will be replaced by the process' pid and {count} will be replaced
|
||||
// by the count of the profile run (starts at 1 with each process).
|
||||
BASE_EXPORT void StartProfiling(const std::string& name);
|
||||
|
||||
// Stop profiling and write out data.
|
||||
BASE_EXPORT void StopProfiling();
|
||||
|
||||
// Force data to be written to file.
|
||||
BASE_EXPORT void FlushProfiling();
|
||||
|
||||
// Returns true if process is being profiled.
|
||||
BASE_EXPORT bool BeingProfiled();
|
||||
|
||||
// Reset profiling after a fork, which disables timers.
|
||||
BASE_EXPORT void RestartProfilingAfterFork();
|
||||
|
||||
// Returns true iff this executable supports profiling.
|
||||
BASE_EXPORT bool IsProfilingSupported();
|
||||
|
||||
// There's a class of profilers that use "return address swizzling" to get a
|
||||
// hook on function exits. This class of profilers uses some form of entry hook,
|
||||
// like e.g. binary instrumentation, or a compiler flag, that calls a hook each
|
||||
// time a function is invoked. The hook then switches the return address on the
|
||||
// stack for the address of an exit hook function, and pushes the original
|
||||
// return address to a shadow stack of some type. When in due course the CPU
|
||||
// executes a return to the exit hook, the exit hook will do whatever work it
|
||||
// does on function exit, then arrange to return to the original return address.
|
||||
// This class of profiler does not play well with programs that look at the
|
||||
// return address, as does e.g. V8. V8 uses the return address to certain
|
||||
// runtime functions to find the JIT code that called it, and from there finds
|
||||
// the V8 data structures associated to the JS function involved.
|
||||
// A return address resolution function is used to fix this. It allows such
|
||||
// programs to resolve a location on stack where a return address originally
|
||||
// resided, to the shadow stack location where the profiler stashed it.
|
||||
typedef uintptr_t (*ReturnAddressLocationResolver)(
|
||||
uintptr_t return_addr_location);
|
||||
|
||||
typedef void (*AddDynamicSymbol)(const void* address,
|
||||
size_t length,
|
||||
const char* name,
|
||||
size_t name_len);
|
||||
typedef void (*MoveDynamicSymbol)(const void* address, const void* new_address);
|
||||
|
||||
|
||||
// If this binary is instrumented and the instrumentation supplies a function
|
||||
// for each of those purposes, find and return the function in question.
|
||||
// Otherwise returns NULL.
|
||||
BASE_EXPORT ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc();
|
||||
BASE_EXPORT AddDynamicSymbol GetProfilerAddDynamicSymbolFunc();
|
||||
BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc();
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_PROFILER_H_
|
||||
305
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace.cc
Normal file
305
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace.cc
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/stl_util.h"
|
||||
|
||||
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
#include <pthread.h>
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX) && defined(__GLIBC__)
|
||||
extern "C" void* __libc_stack_end;
|
||||
#endif
|
||||
|
||||
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
namespace {
|
||||
|
||||
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
|
||||
// GCC and LLVM generate slightly different frames on ARM, see
|
||||
// https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates
|
||||
// x86-compatible frame, while GCC needs adjustment.
|
||||
constexpr size_t kStackFrameAdjustment = sizeof(uintptr_t);
|
||||
#else
|
||||
constexpr size_t kStackFrameAdjustment = 0;
|
||||
#endif
|
||||
|
||||
uintptr_t GetNextStackFrame(uintptr_t fp) {
|
||||
const uintptr_t* fp_addr = reinterpret_cast<const uintptr_t*>(fp);
|
||||
MSAN_UNPOISON(fp_addr, sizeof(uintptr_t));
|
||||
return fp_addr[0] - kStackFrameAdjustment;
|
||||
}
|
||||
|
||||
uintptr_t GetStackFramePC(uintptr_t fp) {
|
||||
const uintptr_t* fp_addr = reinterpret_cast<const uintptr_t*>(fp);
|
||||
MSAN_UNPOISON(&fp_addr[1], sizeof(uintptr_t));
|
||||
return fp_addr[1];
|
||||
}
|
||||
|
||||
bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) {
|
||||
// With the stack growing downwards, older stack frame must be
|
||||
// at a greater address that the current one.
|
||||
if (fp <= prev_fp) return false;
|
||||
|
||||
// Assume huge stack frames are bogus.
|
||||
if (fp - prev_fp > 100000) return false;
|
||||
|
||||
// Check alignment.
|
||||
if (fp & (sizeof(uintptr_t) - 1)) return false;
|
||||
|
||||
if (stack_end) {
|
||||
// Both fp[0] and fp[1] must be within the stack.
|
||||
if (fp > stack_end - 2 * sizeof(uintptr_t)) return false;
|
||||
|
||||
// Additional check to filter out false positives.
|
||||
if (GetStackFramePC(fp) < 32768) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ScanStackForNextFrame() scans the stack for a valid frame to allow unwinding
|
||||
// past system libraries. Only supported on Linux where system libraries are
|
||||
// usually in the middle of the trace:
|
||||
//
|
||||
// TraceStackFramePointers
|
||||
// <more frames from Chrome>
|
||||
// base::WorkSourceDispatch <-- unwinding stops (next frame is invalid),
|
||||
// g_main_context_dispatch ScanStackForNextFrame() is called
|
||||
// <more frames from glib>
|
||||
// g_main_context_iteration
|
||||
// base::MessagePumpGlib::Run <-- ScanStackForNextFrame() finds valid frame,
|
||||
// base::RunLoop::Run unwinding resumes
|
||||
// <more frames from Chrome>
|
||||
// __libc_start_main
|
||||
//
|
||||
// For stack scanning to be efficient it's very important for the thread to
|
||||
// be started by Chrome. In that case we naturally terminate unwinding once
|
||||
// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
|
||||
// not started by Chrome (e.g. Android's main thread), then we end up always
|
||||
// scanning area at the origin of the stack, wasting time and not finding any
|
||||
// frames (since Android libraries don't have frame pointers).
|
||||
//
|
||||
// ScanStackForNextFrame() returns 0 if it couldn't find a valid frame
|
||||
// (or if stack scanning is not supported on the current platform).
|
||||
uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
|
||||
#if defined(OS_LINUX)
|
||||
// Enough to resume almost all prematurely terminated traces.
|
||||
constexpr size_t kMaxStackScanArea = 8192;
|
||||
|
||||
if (!stack_end) {
|
||||
// Too dangerous to scan without knowing where the stack ends.
|
||||
return 0;
|
||||
}
|
||||
|
||||
fp += sizeof(uintptr_t); // current frame is known to be invalid
|
||||
uintptr_t last_fp_to_scan = std::min(fp + kMaxStackScanArea, stack_end) -
|
||||
sizeof(uintptr_t);
|
||||
for (;fp <= last_fp_to_scan; fp += sizeof(uintptr_t)) {
|
||||
uintptr_t next_fp = GetNextStackFrame(fp);
|
||||
if (IsStackFrameValid(next_fp, fp, stack_end)) {
|
||||
// Check two frames deep. Since stack frame is just a pointer to
|
||||
// a higher address on the stack, it's relatively easy to find
|
||||
// something that looks like one. However two linked frames are
|
||||
// far less likely to be bogus.
|
||||
uintptr_t next2_fp = GetNextStackFrame(next_fp);
|
||||
if (IsStackFrameValid(next2_fp, next_fp, stack_end)) {
|
||||
return fp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // defined(OS_LINUX)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
|
||||
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
|
||||
// Both frame pointers must come from __builtin_frame_address().
|
||||
// Returns previous stack frame |fp| was linked to.
|
||||
void* LinkStackFrames(void* fpp, void* parent_fp) {
|
||||
uintptr_t fp = reinterpret_cast<uintptr_t>(fpp) - kStackFrameAdjustment;
|
||||
void* prev_parent_fp = reinterpret_cast<void**>(fp)[0];
|
||||
reinterpret_cast<void**>(fp)[0] = parent_fp;
|
||||
return prev_parent_fp;
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
} // namespace
|
||||
|
||||
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
uintptr_t GetStackEnd() {
|
||||
#if defined(OS_ANDROID)
|
||||
// Bionic reads proc/maps on every call to pthread_getattr_np() when called
|
||||
// from the main thread. So we need to cache end of stack in that case to get
|
||||
// acceptable performance.
|
||||
// For all other threads pthread_getattr_np() is fast enough as it just reads
|
||||
// values from its pthread_t argument.
|
||||
static uintptr_t main_stack_end = 0;
|
||||
|
||||
bool is_main_thread = GetCurrentProcId() == PlatformThread::CurrentId();
|
||||
if (is_main_thread && main_stack_end) {
|
||||
return main_stack_end;
|
||||
}
|
||||
|
||||
uintptr_t stack_begin = 0;
|
||||
size_t stack_size = 0;
|
||||
pthread_attr_t attributes;
|
||||
int error = pthread_getattr_np(pthread_self(), &attributes);
|
||||
if (!error) {
|
||||
error = pthread_attr_getstack(
|
||||
&attributes, reinterpret_cast<void**>(&stack_begin), &stack_size);
|
||||
pthread_attr_destroy(&attributes);
|
||||
}
|
||||
DCHECK(!error);
|
||||
|
||||
uintptr_t stack_end = stack_begin + stack_size;
|
||||
if (is_main_thread) {
|
||||
main_stack_end = stack_end;
|
||||
}
|
||||
return stack_end; // 0 in case of error
|
||||
|
||||
#elif defined(OS_LINUX) && defined(__GLIBC__)
|
||||
|
||||
if (GetCurrentProcId() == PlatformThread::CurrentId()) {
|
||||
// For the main thread we have a shortcut.
|
||||
return reinterpret_cast<uintptr_t>(__libc_stack_end);
|
||||
}
|
||||
|
||||
// No easy way to get end of the stack for non-main threads,
|
||||
// see crbug.com/617730.
|
||||
#elif defined(OS_MACOSX)
|
||||
return reinterpret_cast<uintptr_t>(pthread_get_stackaddr_np(pthread_self()));
|
||||
#endif
|
||||
|
||||
// Don't know how to get end of the stack.
|
||||
return 0;
|
||||
}
|
||||
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
StackTrace::StackTrace() : StackTrace(base::size(trace_)) {}
|
||||
|
||||
StackTrace::StackTrace(size_t count) {
|
||||
count_ = CollectStackTrace(trace_, std::min(count, base::size(trace_)));
|
||||
}
|
||||
|
||||
StackTrace::StackTrace(const void* const* trace, size_t count) {
|
||||
count = std::min(count, base::size(trace_));
|
||||
if (count)
|
||||
memcpy(trace_, trace, count * sizeof(trace_[0]));
|
||||
count_ = count;
|
||||
}
|
||||
|
||||
const void *const *StackTrace::Addresses(size_t* count) const {
|
||||
*count = count_;
|
||||
if (count_)
|
||||
return trace_;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void StackTrace::Print() const {
|
||||
PrintWithPrefix(nullptr);
|
||||
}
|
||||
|
||||
void StackTrace::OutputToStream(std::ostream* os) const {
|
||||
OutputToStreamWithPrefix(os, nullptr);
|
||||
}
|
||||
|
||||
std::string StackTrace::ToString() const {
|
||||
return ToStringWithPrefix(nullptr);
|
||||
}
|
||||
std::string StackTrace::ToStringWithPrefix(const char* prefix_string) const {
|
||||
std::stringstream stream;
|
||||
#if !defined(__UCLIBC__) && !defined(_AIX)
|
||||
OutputToStreamWithPrefix(&stream, prefix_string);
|
||||
#endif
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StackTrace& s) {
|
||||
#if !defined(__UCLIBC__) & !defined(_AIX)
|
||||
s.OutputToStream(&os);
|
||||
#else
|
||||
os << "StackTrace::OutputToStream not implemented.";
|
||||
#endif
|
||||
return os;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
size_t TraceStackFramePointers(const void** out_trace,
|
||||
size_t max_depth,
|
||||
size_t skip_initial) {
|
||||
// Usage of __builtin_frame_address() enables frame pointers in this
|
||||
// function even if they are not enabled globally. So 'fp' will always
|
||||
// be valid.
|
||||
uintptr_t fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
|
||||
kStackFrameAdjustment;
|
||||
|
||||
uintptr_t stack_end = GetStackEnd();
|
||||
|
||||
size_t depth = 0;
|
||||
while (depth < max_depth) {
|
||||
if (skip_initial != 0) {
|
||||
skip_initial--;
|
||||
} else {
|
||||
out_trace[depth++] = reinterpret_cast<const void*>(GetStackFramePC(fp));
|
||||
}
|
||||
|
||||
uintptr_t next_fp = GetNextStackFrame(fp);
|
||||
if (IsStackFrameValid(next_fp, fp, stack_end)) {
|
||||
fp = next_fp;
|
||||
continue;
|
||||
}
|
||||
|
||||
next_fp = ScanStackForNextFrame(fp, stack_end);
|
||||
if (next_fp) {
|
||||
fp = next_fp;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Failed to find next frame.
|
||||
break;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp)
|
||||
: fp_(fp),
|
||||
parent_fp_(parent_fp),
|
||||
original_parent_fp_(LinkStackFrames(fp, parent_fp)) {}
|
||||
|
||||
ScopedStackFrameLinker::~ScopedStackFrameLinker() {
|
||||
void* previous_parent_fp = LinkStackFrames(fp_, original_parent_fp_);
|
||||
CHECK_EQ(parent_fp_, previous_parent_fp)
|
||||
<< "Stack frame's parent pointer has changed!";
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
232
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace.h
Normal file
232
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace.h
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_STACK_TRACE_H_
|
||||
#define BASE_DEBUG_STACK_TRACE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/debug/debugging_buildflags.h"
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
#if !defined(OS_NACL)
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
struct _EXCEPTION_POINTERS;
|
||||
struct _CONTEXT;
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Enables stack dump to console output on exception and signals.
|
||||
// When enabled, the process will quit immediately. This is meant to be used in
|
||||
// unit_tests only! This is not thread-safe: only call from main thread.
|
||||
// In sandboxed processes, this has to be called before the sandbox is turned
|
||||
// on.
|
||||
// Calling this function on Linux opens /proc/self/maps and caches its
|
||||
// contents. In non-official builds, this function also opens the object files
|
||||
// that are loaded in memory and caches their file descriptors (this cannot be
|
||||
// done in official builds because it has security implications).
|
||||
BASE_EXPORT bool EnableInProcessStackDumping();
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_NACL)
|
||||
// Sets a first-chance callback for the stack dump signal handler. This callback
|
||||
// is called at the beginning of the signal handler to handle special kinds of
|
||||
// signals, like out-of-bounds memory accesses in WebAssembly (WebAssembly Trap
|
||||
// Handler).
|
||||
// {SetStackDumpFirstChanceCallback} returns {true} if the callback
|
||||
// has been set correctly. It returns {false} if the stack dump signal handler
|
||||
// has not been registered with the OS, e.g. because of ASAN.
|
||||
BASE_EXPORT bool SetStackDumpFirstChanceCallback(bool (*handler)(int,
|
||||
siginfo_t*,
|
||||
void*));
|
||||
#endif
|
||||
|
||||
// Returns end of the stack, or 0 if we couldn't get it.
|
||||
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
BASE_EXPORT uintptr_t GetStackEnd();
|
||||
#endif
|
||||
|
||||
// A stacktrace can be helpful in debugging. For example, you can include a
|
||||
// stacktrace member in a object (probably around #ifndef NDEBUG) so that you
|
||||
// can later see where the given object was created from.
|
||||
class BASE_EXPORT StackTrace {
|
||||
public:
|
||||
// Creates a stacktrace from the current location.
|
||||
StackTrace();
|
||||
|
||||
// Creates a stacktrace from the current location, of up to |count| entries.
|
||||
// |count| will be limited to at most |kMaxTraces|.
|
||||
explicit StackTrace(size_t count);
|
||||
|
||||
// Creates a stacktrace from an existing array of instruction
|
||||
// pointers (such as returned by Addresses()). |count| will be
|
||||
// limited to at most |kMaxTraces|.
|
||||
StackTrace(const void* const* trace, size_t count);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Creates a stacktrace for an exception.
|
||||
// Note: this function will throw an import not found (StackWalk64) exception
|
||||
// on system without dbghelp 5.1.
|
||||
StackTrace(_EXCEPTION_POINTERS* exception_pointers);
|
||||
StackTrace(const _CONTEXT* context);
|
||||
#endif
|
||||
|
||||
// Copying and assignment are allowed with the default functions.
|
||||
|
||||
// Gets an array of instruction pointer values. |*count| will be set to the
|
||||
// number of elements in the returned array. Addresses()[0] will contain an
|
||||
// address from the leaf function, and Addresses()[count-1] will contain an
|
||||
// address from the root function (i.e.; the thread's entry point).
|
||||
const void* const* Addresses(size_t* count) const;
|
||||
|
||||
// Prints the stack trace to stderr.
|
||||
void Print() const;
|
||||
|
||||
// Prints the stack trace to stderr, prepending the given string before
|
||||
// each output line.
|
||||
void PrintWithPrefix(const char* prefix_string) const;
|
||||
|
||||
#if !defined(__UCLIBC__) & !defined(_AIX)
|
||||
// Resolves backtrace to symbols and write to stream.
|
||||
void OutputToStream(std::ostream* os) const;
|
||||
// Resolves backtrace to symbols and write to stream, with the provided
|
||||
// prefix string prepended to each line.
|
||||
void OutputToStreamWithPrefix(std::ostream* os,
|
||||
const char* prefix_string) const;
|
||||
#endif
|
||||
|
||||
// Resolves backtrace to symbols and returns as string.
|
||||
std::string ToString() const;
|
||||
|
||||
// Resolves backtrace to symbols and returns as string, prepending the
|
||||
// provided prefix string to each line.
|
||||
std::string ToStringWithPrefix(const char* prefix_string) const;
|
||||
|
||||
private:
|
||||
#if defined(OS_WIN)
|
||||
void InitTrace(const _CONTEXT* context_record);
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// TODO(https://crbug.com/925525): Testing indicates that Android has issues
|
||||
// with a larger value here, so leave Android at 62.
|
||||
static constexpr int kMaxTraces = 62;
|
||||
#else
|
||||
// For other platforms, use 250. This seems reasonable without
|
||||
// being huge.
|
||||
static constexpr int kMaxTraces = 250;
|
||||
#endif
|
||||
|
||||
void* trace_[kMaxTraces];
|
||||
|
||||
// The number of valid frames in |trace_|.
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
// Forwards to StackTrace::OutputToStream().
|
||||
BASE_EXPORT std::ostream& operator<<(std::ostream& os, const StackTrace& s);
|
||||
|
||||
// Record a stack trace with up to |count| frames into |trace|. Returns the
|
||||
// number of frames read.
|
||||
BASE_EXPORT size_t CollectStackTrace(void** trace, size_t count);
|
||||
|
||||
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
// Traces the stack by using frame pointers. This function is faster but less
|
||||
// reliable than StackTrace. It should work for debug and profiling builds,
|
||||
// but not for release builds (although there are some exceptions).
|
||||
//
|
||||
// Writes at most |max_depth| frames (instruction pointers) into |out_trace|
|
||||
// after skipping |skip_initial| frames. Note that the function itself is not
|
||||
// added to the trace so |skip_initial| should be 0 in most cases.
|
||||
// Returns number of frames written.
|
||||
BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
|
||||
size_t max_depth,
|
||||
size_t skip_initial);
|
||||
|
||||
// Links stack frame |fp| to |parent_fp|, so that during stack unwinding
|
||||
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
|
||||
// Both frame pointers must come from __builtin_frame_address().
|
||||
// Destructor restores original linkage of |fp| to avoid corrupting caller's
|
||||
// frame register on return.
|
||||
//
|
||||
// This class can be used to repair broken stack frame chain in cases
|
||||
// when execution flow goes into code built without frame pointers:
|
||||
//
|
||||
// void DoWork() {
|
||||
// Call_SomeLibrary();
|
||||
// }
|
||||
// static __thread void* g_saved_fp;
|
||||
// void Call_SomeLibrary() {
|
||||
// g_saved_fp = __builtin_frame_address(0);
|
||||
// some_library_call(...); // indirectly calls SomeLibrary_Callback()
|
||||
// }
|
||||
// void SomeLibrary_Callback() {
|
||||
// ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp);
|
||||
// ...
|
||||
// TraceStackFramePointers(...);
|
||||
// }
|
||||
//
|
||||
// This produces the following trace:
|
||||
//
|
||||
// #0 SomeLibrary_Callback()
|
||||
// #1 <address of the code inside SomeLibrary that called #0>
|
||||
// #2 DoWork()
|
||||
// ...rest of the trace...
|
||||
//
|
||||
// SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback()
|
||||
// is called, stack frame register contains bogus value that becomes callback'
|
||||
// parent frame address. Without ScopedStackFrameLinker unwinding would've
|
||||
// stopped at that bogus frame address yielding just two first frames (#0, #1).
|
||||
// ScopedStackFrameLinker overwrites callback's parent frame address with
|
||||
// Call_SomeLibrary's frame, so unwinder produces full trace without even
|
||||
// noticing that stack frame chain was broken.
|
||||
class BASE_EXPORT ScopedStackFrameLinker {
|
||||
public:
|
||||
ScopedStackFrameLinker(void* fp, void* parent_fp);
|
||||
~ScopedStackFrameLinker();
|
||||
|
||||
private:
|
||||
void* fp_;
|
||||
void* parent_fp_;
|
||||
void* original_parent_fp_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker);
|
||||
};
|
||||
|
||||
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
|
||||
|
||||
namespace internal {
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
// POSIX doesn't define any async-signal safe function for converting
|
||||
// an integer to ASCII. We'll have to define our own version.
|
||||
// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
|
||||
// conversion was successful or NULL otherwise. It never writes more than "sz"
|
||||
// bytes. Output will be truncated as needed, and a NUL character is always
|
||||
// appended.
|
||||
BASE_EXPORT char *itoa_r(intptr_t i,
|
||||
char *buf,
|
||||
size_t sz,
|
||||
int base,
|
||||
size_t padding);
|
||||
#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_STACK_TRACE_H_
|
||||
151
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace_android.cc
Normal file
151
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace_android.cc
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <stddef.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
|
||||
#include "base/debug/proc_maps_linux.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
|
||||
#ifdef __LP64__
|
||||
#define FMT_ADDR "0x%016lx"
|
||||
#else
|
||||
#define FMT_ADDR "0x%08x"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct StackCrawlState {
|
||||
StackCrawlState(uintptr_t* frames, size_t max_depth)
|
||||
: frames(frames),
|
||||
frame_count(0),
|
||||
max_depth(max_depth),
|
||||
have_skipped_self(false) {}
|
||||
|
||||
uintptr_t* frames;
|
||||
size_t frame_count;
|
||||
size_t max_depth;
|
||||
bool have_skipped_self;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code TraceStackFrame(_Unwind_Context* context, void* arg) {
|
||||
StackCrawlState* state = static_cast<StackCrawlState*>(arg);
|
||||
uintptr_t ip = _Unwind_GetIP(context);
|
||||
|
||||
// The first stack frame is this function itself. Skip it.
|
||||
if (ip != 0 && !state->have_skipped_self) {
|
||||
state->have_skipped_self = true;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
state->frames[state->frame_count++] = ip;
|
||||
if (state->frame_count >= state->max_depth)
|
||||
return _URC_END_OF_STACK;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
bool EndsWith(const std::string& s, const std::string& suffix) {
|
||||
return s.size() >= suffix.size() &&
|
||||
s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
bool EnableInProcessStackDumping() {
|
||||
// When running in an application, our code typically expects SIGPIPE
|
||||
// to be ignored. Therefore, when testing that same code, it should run
|
||||
// with SIGPIPE ignored as well.
|
||||
// TODO(phajdan.jr): De-duplicate this SIGPIPE code.
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = SIG_IGN;
|
||||
sigemptyset(&action.sa_mask);
|
||||
return (sigaction(SIGPIPE, &action, NULL) == 0);
|
||||
}
|
||||
|
||||
size_t CollectStackTrace(void** trace, size_t count) {
|
||||
StackCrawlState state(reinterpret_cast<uintptr_t*>(trace), count);
|
||||
_Unwind_Backtrace(&TraceStackFrame, &state);
|
||||
return state.frame_count;
|
||||
}
|
||||
|
||||
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
|
||||
std::string backtrace = ToStringWithPrefix(prefix_string);
|
||||
__android_log_write(ANDROID_LOG_ERROR, "chromium", backtrace.c_str());
|
||||
}
|
||||
|
||||
// NOTE: Native libraries in APKs are stripped before installing. Print out the
|
||||
// relocatable address and library names so host computers can use tools to
|
||||
// symbolize and demangle (e.g., addr2line, c++filt).
|
||||
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
|
||||
const char* prefix_string) const {
|
||||
std::string proc_maps;
|
||||
std::vector<MappedMemoryRegion> regions;
|
||||
// Allow IO to read /proc/self/maps. Reading this file doesn't hit the disk
|
||||
// since it lives in procfs, and this is currently used to print a stack trace
|
||||
// on fatal log messages in debug builds only. If the restriction is enabled
|
||||
// then it will recursively trigger fatal failures when this enters on the
|
||||
// UI thread.
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
if (!ReadProcMaps(&proc_maps)) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "chromium", "Failed to read /proc/self/maps");
|
||||
} else if (!ParseProcMaps(proc_maps, ®ions)) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "chromium", "Failed to parse /proc/self/maps");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count_; ++i) {
|
||||
// Subtract one as return address of function may be in the next
|
||||
// function when a function is annotated as noreturn.
|
||||
uintptr_t address = reinterpret_cast<uintptr_t>(trace_[i]) - 1;
|
||||
|
||||
std::vector<MappedMemoryRegion>::iterator iter = regions.begin();
|
||||
while (iter != regions.end()) {
|
||||
if (address >= iter->start && address < iter->end &&
|
||||
!iter->path.empty()) {
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (prefix_string)
|
||||
*os << prefix_string;
|
||||
|
||||
// Adjust absolute address to be an offset within the mapped region, to
|
||||
// match the format dumped by Android's crash output.
|
||||
if (iter != regions.end()) {
|
||||
address -= iter->start;
|
||||
}
|
||||
|
||||
// The format below intentionally matches that of Android's debuggerd
|
||||
// output. This simplifies decoding by scripts such as stack.py.
|
||||
*os << base::StringPrintf("#%02zd pc " FMT_ADDR " ", i, address);
|
||||
|
||||
if (iter != regions.end()) {
|
||||
*os << base::StringPrintf("%s", iter->path.c_str());
|
||||
if (EndsWith(iter->path, ".apk")) {
|
||||
*os << base::StringPrintf(" (offset 0x%llx)", iter->offset);
|
||||
}
|
||||
} else {
|
||||
*os << "<unknown>";
|
||||
}
|
||||
|
||||
*os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
283
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace_fuchsia.cc
Normal file
283
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace_fuchsia.cc
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <stddef.h>
|
||||
#include <threads.h>
|
||||
#include <unwind.h>
|
||||
#include <zircon/process.h>
|
||||
#include <zircon/syscalls.h>
|
||||
#include <zircon/syscalls/port.h>
|
||||
#include <zircon/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/atomic_sequence_num.h"
|
||||
#include "base/debug/elf_reader.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/stl_util.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
namespace {
|
||||
|
||||
struct BacktraceData {
|
||||
void** trace_array;
|
||||
size_t* count;
|
||||
size_t max;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context,
|
||||
void* user_data) {
|
||||
BacktraceData* data = reinterpret_cast<BacktraceData*>(user_data);
|
||||
uintptr_t pc = _Unwind_GetIP(context);
|
||||
data->trace_array[*data->count] = reinterpret_cast<void*>(pc);
|
||||
*data->count += 1;
|
||||
if (*data->count == data->max)
|
||||
return _URC_END_OF_STACK;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
// Build a "rwx" C string-based representation of the permission bits.
|
||||
// The output buffer is reused across calls, and should not be retained across
|
||||
// consecutive invocations of this function.
|
||||
const char* PermissionFlagsToString(int flags, char permission_buf[4]) {
|
||||
char* permission = permission_buf;
|
||||
|
||||
if (flags & PF_R)
|
||||
(*permission++) = 'r';
|
||||
|
||||
if (flags & PF_W)
|
||||
(*permission++) = 'w';
|
||||
|
||||
if (flags & PF_X)
|
||||
(*permission++) = 'x';
|
||||
|
||||
*permission = '\0';
|
||||
|
||||
return permission_buf;
|
||||
}
|
||||
|
||||
// Stores and queries debugging symbol map info for the current process.
|
||||
class SymbolMap {
|
||||
public:
|
||||
struct Segment {
|
||||
const void* addr = nullptr;
|
||||
size_t relative_addr = 0;
|
||||
int permission_flags = 0;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
struct Module {
|
||||
// Maximum number of PT_LOAD segments to process per ELF binary. Most
|
||||
// binaries have only 2-3 such segments.
|
||||
static constexpr size_t kMaxSegmentCount = 8;
|
||||
|
||||
const void* addr = nullptr;
|
||||
std::array<Segment, kMaxSegmentCount> segments;
|
||||
size_t segment_count = 0;
|
||||
char name[ZX_MAX_NAME_LEN + 1] = {0};
|
||||
char build_id[kMaxBuildIdStringLength + 1] = {0};
|
||||
};
|
||||
|
||||
SymbolMap();
|
||||
~SymbolMap() = default;
|
||||
|
||||
// Gets all entries for the symbol map.
|
||||
span<Module> GetModules() { return {modules_.data(), count_}; }
|
||||
|
||||
private:
|
||||
// Component builds of Chrome pull about 250 shared libraries (on Linux), so
|
||||
// 512 entries should be enough in most cases.
|
||||
static const size_t kMaxMapEntries = 512;
|
||||
|
||||
void Populate();
|
||||
|
||||
// Sorted in descending order by address, for lookup purposes.
|
||||
std::array<Module, kMaxMapEntries> modules_;
|
||||
|
||||
size_t count_ = 0;
|
||||
bool valid_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SymbolMap);
|
||||
};
|
||||
|
||||
SymbolMap::SymbolMap() {
|
||||
Populate();
|
||||
}
|
||||
|
||||
void SymbolMap::Populate() {
|
||||
zx_handle_t process = zx_process_self();
|
||||
|
||||
// Try to fetch the name of the process' main executable, which was set as the
|
||||
// name of the |process| kernel object.
|
||||
// TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so
|
||||
// if we keep hitting problems with truncation, find a way to plumb argv[0]
|
||||
// through to here instead, e.g. using CommandLine::GetProgramName().
|
||||
char app_name[std::extent<decltype(SymbolMap::Module::name)>()];
|
||||
zx_status_t status =
|
||||
zx_object_get_property(process, ZX_PROP_NAME, app_name, sizeof(app_name));
|
||||
if (status == ZX_OK) {
|
||||
// The process name may have a process type suffix at the end (e.g.
|
||||
// "context", "renderer", gpu"), which doesn't belong in the module list.
|
||||
// Trim the suffix from the name.
|
||||
for (size_t i = 0; i < base::size(app_name) && app_name[i] != '\0'; ++i) {
|
||||
if (app_name[i] == ':') {
|
||||
app_name[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DPLOG(WARNING)
|
||||
<< "Couldn't get name, falling back to 'app' for program name: "
|
||||
<< status;
|
||||
strlcat(app_name, "app", sizeof(app_name));
|
||||
}
|
||||
|
||||
// Retrieve the debug info struct.
|
||||
uintptr_t debug_addr;
|
||||
status = zx_object_get_property(process, ZX_PROP_PROCESS_DEBUG_ADDR,
|
||||
&debug_addr, sizeof(debug_addr));
|
||||
if (status != ZX_OK) {
|
||||
DPLOG(ERROR) << "Couldn't get symbol map for process: " << status;
|
||||
return;
|
||||
}
|
||||
r_debug* debug_info = reinterpret_cast<r_debug*>(debug_addr);
|
||||
|
||||
// Get the link map from the debug info struct.
|
||||
link_map* lmap = reinterpret_cast<link_map*>(debug_info->r_map);
|
||||
if (!lmap) {
|
||||
DPLOG(ERROR) << "Null link_map for process.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate ELF binary metadata into |modules_|.
|
||||
while (lmap != nullptr) {
|
||||
if (count_ >= kMaxMapEntries)
|
||||
break;
|
||||
|
||||
SymbolMap::Module& next_entry = modules_[count_];
|
||||
++count_;
|
||||
|
||||
next_entry.addr = reinterpret_cast<void*>(lmap->l_addr);
|
||||
|
||||
// Create Segment sub-entries for all PT_LOAD headers.
|
||||
// Each Segment corresponds to a "mmap" line in the output.
|
||||
next_entry.segment_count = 0;
|
||||
for (const Elf64_Phdr& phdr : GetElfProgramHeaders(next_entry.addr)) {
|
||||
if (phdr.p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
if (next_entry.segment_count > Module::kMaxSegmentCount) {
|
||||
LOG(WARNING) << "Exceeded the maximum number of segments.";
|
||||
break;
|
||||
}
|
||||
|
||||
Segment segment;
|
||||
segment.addr =
|
||||
reinterpret_cast<const char*>(next_entry.addr) + phdr.p_vaddr;
|
||||
segment.relative_addr = phdr.p_vaddr;
|
||||
segment.size = phdr.p_memsz;
|
||||
segment.permission_flags = phdr.p_flags;
|
||||
|
||||
next_entry.segments[next_entry.segment_count] = std::move(segment);
|
||||
++next_entry.segment_count;
|
||||
}
|
||||
|
||||
// Get the human-readable library name from the ELF header, falling back on
|
||||
// using names from the link map for binaries that aren't shared libraries.
|
||||
Optional<StringPiece> elf_library_name =
|
||||
ReadElfLibraryName(next_entry.addr);
|
||||
if (elf_library_name) {
|
||||
strlcpy(next_entry.name, elf_library_name->data(),
|
||||
elf_library_name->size() + 1);
|
||||
} else {
|
||||
StringPiece link_map_name(lmap->l_name[0] ? lmap->l_name : app_name);
|
||||
|
||||
// The "module" stack trace annotation doesn't allow for strings which
|
||||
// resemble paths, so extract the filename portion from |link_map_name|.
|
||||
size_t directory_prefix_idx = link_map_name.find_last_of("/");
|
||||
if (directory_prefix_idx != StringPiece::npos) {
|
||||
link_map_name = link_map_name.substr(
|
||||
directory_prefix_idx + 1,
|
||||
link_map_name.size() - directory_prefix_idx - 1);
|
||||
}
|
||||
strlcpy(next_entry.name, link_map_name.data(), link_map_name.size() + 1);
|
||||
}
|
||||
|
||||
if (!ReadElfBuildId(next_entry.addr, false, next_entry.build_id)) {
|
||||
LOG(WARNING) << "Couldn't read build ID.";
|
||||
continue;
|
||||
}
|
||||
|
||||
lmap = lmap->l_next;
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool EnableInProcessStackDumping() {
|
||||
// StackTrace works to capture the current stack (e.g. for diagnostics added
|
||||
// to code), but for local capture and print of backtraces, we just let the
|
||||
// system crashlogger take over. It handles printing out a nicely formatted
|
||||
// backtrace with dso information, relative offsets, etc. that we can then
|
||||
// filter with addr2line in the run script to get file/line info.
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CollectStackTrace(void** trace, size_t count) {
|
||||
size_t frame_count = 0;
|
||||
BacktraceData data = {trace, &frame_count, count};
|
||||
_Unwind_Backtrace(&UnwindStore, &data);
|
||||
return frame_count;
|
||||
}
|
||||
|
||||
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
|
||||
OutputToStreamWithPrefix(&std::cerr, prefix_string);
|
||||
}
|
||||
|
||||
// Emits stack trace data using the symbolizer markup format specified at:
|
||||
// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
|
||||
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
|
||||
const char* prefix_string) const {
|
||||
SymbolMap map;
|
||||
|
||||
int module_id = 0;
|
||||
*os << "{{{reset}}}\n";
|
||||
for (const SymbolMap::Module& entry : map.GetModules()) {
|
||||
*os << "{{{module:" << module_id << ":" << entry.name
|
||||
<< ":elf:" << entry.build_id << "}}}\n";
|
||||
|
||||
for (size_t i = 0; i < entry.segment_count; ++i) {
|
||||
const SymbolMap::Segment& segment = entry.segments[i];
|
||||
|
||||
char permission_string[4] = {};
|
||||
*os << "{{{mmap:" << segment.addr << ":0x" << std::hex << segment.size
|
||||
<< std::dec << ":load:" << module_id << ":"
|
||||
<< PermissionFlagsToString(segment.permission_flags,
|
||||
permission_string)
|
||||
<< ":"
|
||||
<< "0x" << std::hex << segment.relative_addr << std::dec << "}}}\n";
|
||||
}
|
||||
|
||||
++module_id;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count_; ++i)
|
||||
*os << "{{{bt:" << i << ":" << trace_[i] << "}}}\n";
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
363
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace_win.cc
Normal file
363
TMessagesProj/jni/voip/webrtc/base/debug/stack_trace_win.cc
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
namespace {
|
||||
|
||||
// Previous unhandled filter. Will be called if not NULL when we intercept an
|
||||
// exception. Only used in unit tests.
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
|
||||
|
||||
bool g_initialized_symbols = false;
|
||||
DWORD g_init_error = ERROR_SUCCESS;
|
||||
|
||||
// Prints the exception call stack.
|
||||
// This is the unit tests exception filter.
|
||||
long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
|
||||
DWORD exc_code = info->ExceptionRecord->ExceptionCode;
|
||||
std::cerr << "Received fatal exception ";
|
||||
switch (exc_code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
std::cerr << "EXCEPTION_ACCESS_VIOLATION";
|
||||
break;
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
std::cerr << "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
||||
break;
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
std::cerr << "EXCEPTION_BREAKPOINT";
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
std::cerr << "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
std::cerr << "EXCEPTION_FLT_DENORMAL_OPERAND";
|
||||
break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
std::cerr << "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||
break;
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
std::cerr << "EXCEPTION_FLT_INEXACT_RESULT";
|
||||
break;
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
std::cerr << "EXCEPTION_FLT_INVALID_OPERATION";
|
||||
break;
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
std::cerr << "EXCEPTION_FLT_OVERFLOW";
|
||||
break;
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
std::cerr << "EXCEPTION_FLT_STACK_CHECK";
|
||||
break;
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
std::cerr << "EXCEPTION_FLT_UNDERFLOW";
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
std::cerr << "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||
break;
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
std::cerr << "EXCEPTION_IN_PAGE_ERROR";
|
||||
break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
std::cerr << "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||
break;
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
std::cerr << "EXCEPTION_INT_OVERFLOW";
|
||||
break;
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
std::cerr << "EXCEPTION_INVALID_DISPOSITION";
|
||||
break;
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
std::cerr << "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
||||
break;
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
std::cerr << "EXCEPTION_PRIV_INSTRUCTION";
|
||||
break;
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
std::cerr << "EXCEPTION_SINGLE_STEP";
|
||||
break;
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
std::cerr << "EXCEPTION_STACK_OVERFLOW";
|
||||
break;
|
||||
default:
|
||||
std::cerr << "0x" << std::hex << exc_code;
|
||||
break;
|
||||
}
|
||||
std::cerr << "\n";
|
||||
|
||||
debug::StackTrace(info).Print();
|
||||
if (g_previous_filter)
|
||||
return g_previous_filter(info);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
FilePath GetExePath() {
|
||||
wchar_t system_buffer[MAX_PATH];
|
||||
GetModuleFileName(NULL, system_buffer, MAX_PATH);
|
||||
system_buffer[MAX_PATH - 1] = L'\0';
|
||||
return FilePath(system_buffer);
|
||||
}
|
||||
|
||||
bool InitializeSymbols() {
|
||||
if (g_initialized_symbols) {
|
||||
// Force a reinitialization. Will ensure any modules loaded after process
|
||||
// startup also get symbolized.
|
||||
SymCleanup(GetCurrentProcess());
|
||||
g_initialized_symbols = false;
|
||||
}
|
||||
g_initialized_symbols = true;
|
||||
// Defer symbol load until they're needed, use undecorated names, and get line
|
||||
// numbers.
|
||||
SymSetOptions(SYMOPT_DEFERRED_LOADS |
|
||||
SYMOPT_UNDNAME |
|
||||
SYMOPT_LOAD_LINES);
|
||||
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) {
|
||||
g_init_error = GetLastError();
|
||||
// TODO(awong): Handle error: SymInitialize can fail with
|
||||
// ERROR_INVALID_PARAMETER.
|
||||
// When it fails, we should not call debugbreak since it kills the current
|
||||
// process (prevents future tests from running or kills the browser
|
||||
// process).
|
||||
DLOG(ERROR) << "SymInitialize failed: " << g_init_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
// When transferring the binaries e.g. between bots, path put
|
||||
// into the executable will get off. To still retrieve symbols correctly,
|
||||
// add the directory of the executable to symbol search path.
|
||||
// All following errors are non-fatal.
|
||||
static constexpr size_t kSymbolsArraySize = 1024;
|
||||
wchar_t symbols_path[kSymbolsArraySize];
|
||||
|
||||
// Note: The below function takes buffer size as number of characters,
|
||||
// not number of bytes!
|
||||
if (!SymGetSearchPathW(GetCurrentProcess(), symbols_path,
|
||||
kSymbolsArraySize)) {
|
||||
g_init_error = GetLastError();
|
||||
DLOG(WARNING) << "SymGetSearchPath failed: " << g_init_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring new_path = StringPrintf(L"%ls;%ls", symbols_path,
|
||||
GetExePath().DirName().value().c_str());
|
||||
if (!SymSetSearchPathW(GetCurrentProcess(), new_path.c_str())) {
|
||||
g_init_error = GetLastError();
|
||||
DLOG(WARNING) << "SymSetSearchPath failed." << g_init_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
g_init_error = ERROR_SUCCESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
|
||||
// of functions. The Sym* family of functions may only be invoked by one
|
||||
// thread at a time. SymbolContext code may access a symbol server over the
|
||||
// network while holding the lock for this singleton. In the case of high
|
||||
// latency, this code will adversely affect performance.
|
||||
//
|
||||
// There is also a known issue where this backtrace code can interact
|
||||
// badly with breakpad if breakpad is invoked in a separate thread while
|
||||
// we are using the Sym* functions. This is because breakpad does now
|
||||
// share a lock with this function. See this related bug:
|
||||
//
|
||||
// https://crbug.com/google-breakpad/311
|
||||
//
|
||||
// This is a very unlikely edge case, and the current solution is to
|
||||
// just ignore it.
|
||||
class SymbolContext {
|
||||
public:
|
||||
static SymbolContext* GetInstance() {
|
||||
// We use a leaky singleton because code may call this during process
|
||||
// termination.
|
||||
return
|
||||
Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
|
||||
}
|
||||
|
||||
// For the given trace, attempts to resolve the symbols, and output a trace
|
||||
// to the ostream os. The format for each line of the backtrace is:
|
||||
//
|
||||
// <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
|
||||
//
|
||||
// This function should only be called if Init() has been called. We do not
|
||||
// LOG(FATAL) here because this code is called might be triggered by a
|
||||
// LOG(FATAL) itself. Also, it should not be calling complex code that is
|
||||
// extensible like PathService since that can in turn fire CHECKs.
|
||||
void OutputTraceToStream(const void* const* trace,
|
||||
size_t count,
|
||||
std::ostream* os,
|
||||
const char* prefix_string) {
|
||||
AutoLock lock(lock_);
|
||||
|
||||
for (size_t i = 0; (i < count) && os->good(); ++i) {
|
||||
const int kMaxNameLength = 256;
|
||||
DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
|
||||
|
||||
// Code adapted from MSDN example:
|
||||
// http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
|
||||
ULONG64 buffer[
|
||||
(sizeof(SYMBOL_INFO) +
|
||||
kMaxNameLength * sizeof(wchar_t) +
|
||||
sizeof(ULONG64) - 1) /
|
||||
sizeof(ULONG64)];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
// Initialize symbol information retrieval structures.
|
||||
DWORD64 sym_displacement = 0;
|
||||
PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = kMaxNameLength - 1;
|
||||
BOOL has_symbol = SymFromAddr(GetCurrentProcess(), frame,
|
||||
&sym_displacement, symbol);
|
||||
|
||||
// Attempt to retrieve line number information.
|
||||
DWORD line_displacement = 0;
|
||||
IMAGEHLP_LINE64 line = {};
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
|
||||
&line_displacement, &line);
|
||||
|
||||
// Output the backtrace line.
|
||||
if (prefix_string)
|
||||
(*os) << prefix_string;
|
||||
(*os) << "\t";
|
||||
if (has_symbol) {
|
||||
(*os) << symbol->Name << " [0x" << trace[i] << "+"
|
||||
<< sym_displacement << "]";
|
||||
} else {
|
||||
// If there is no symbol information, add a spacer.
|
||||
(*os) << "(No symbol) [0x" << trace[i] << "]";
|
||||
}
|
||||
if (has_line) {
|
||||
(*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
|
||||
}
|
||||
(*os) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct DefaultSingletonTraits<SymbolContext>;
|
||||
|
||||
SymbolContext() {
|
||||
InitializeSymbols();
|
||||
}
|
||||
|
||||
Lock lock_;
|
||||
DISALLOW_COPY_AND_ASSIGN(SymbolContext);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EnableInProcessStackDumping() {
|
||||
// Add stack dumping support on exception on windows. Similar to OS_POSIX
|
||||
// signal() handling in process_util_posix.cc.
|
||||
g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
|
||||
|
||||
// Need to initialize symbols early in the process or else this fails on
|
||||
// swarming (since symbols are in different directory than in the exes) and
|
||||
// also release x64.
|
||||
return InitializeSymbols();
|
||||
}
|
||||
|
||||
NOINLINE size_t CollectStackTrace(void** trace, size_t count) {
|
||||
// When walking our own stack, use CaptureStackBackTrace().
|
||||
return CaptureStackBackTrace(0, count, trace, NULL);
|
||||
}
|
||||
|
||||
StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
|
||||
InitTrace(exception_pointers->ContextRecord);
|
||||
}
|
||||
|
||||
StackTrace::StackTrace(const CONTEXT* context) {
|
||||
InitTrace(context);
|
||||
}
|
||||
|
||||
void StackTrace::InitTrace(const CONTEXT* context_record) {
|
||||
// StackWalk64 modifies the register context in place, so we have to copy it
|
||||
// so that downstream exception handlers get the right context. The incoming
|
||||
// context may have had more register state (YMM, etc) than we need to unwind
|
||||
// the stack. Typically StackWalk64 only needs integer and control registers.
|
||||
CONTEXT context_copy;
|
||||
memcpy(&context_copy, context_record, sizeof(context_copy));
|
||||
context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
|
||||
|
||||
// When walking an exception stack, we need to use StackWalk64().
|
||||
count_ = 0;
|
||||
// Initialize stack walking.
|
||||
STACKFRAME64 stack_frame;
|
||||
memset(&stack_frame, 0, sizeof(stack_frame));
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
int machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||
stack_frame.AddrPC.Offset = context_record->Rip;
|
||||
stack_frame.AddrFrame.Offset = context_record->Rbp;
|
||||
stack_frame.AddrStack.Offset = context_record->Rsp;
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
int machine_type = IMAGE_FILE_MACHINE_ARM64;
|
||||
stack_frame.AddrPC.Offset = context_record->Pc;
|
||||
stack_frame.AddrFrame.Offset = context_record->Fp;
|
||||
stack_frame.AddrStack.Offset = context_record->Sp;
|
||||
#elif defined(ARCH_CPU_X86)
|
||||
int machine_type = IMAGE_FILE_MACHINE_I386;
|
||||
stack_frame.AddrPC.Offset = context_record->Eip;
|
||||
stack_frame.AddrFrame.Offset = context_record->Ebp;
|
||||
stack_frame.AddrStack.Offset = context_record->Esp;
|
||||
#else
|
||||
#error Unsupported Windows Arch
|
||||
#endif
|
||||
stack_frame.AddrPC.Mode = AddrModeFlat;
|
||||
stack_frame.AddrFrame.Mode = AddrModeFlat;
|
||||
stack_frame.AddrStack.Mode = AddrModeFlat;
|
||||
while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
|
||||
&stack_frame, &context_copy, NULL,
|
||||
&SymFunctionTableAccess64, &SymGetModuleBase64, NULL) &&
|
||||
count_ < size(trace_)) {
|
||||
trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
|
||||
}
|
||||
|
||||
for (size_t i = count_; i < size(trace_); ++i)
|
||||
trace_[i] = NULL;
|
||||
}
|
||||
|
||||
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
|
||||
OutputToStreamWithPrefix(&std::cerr, prefix_string);
|
||||
}
|
||||
|
||||
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
|
||||
const char* prefix_string) const {
|
||||
SymbolContext* context = SymbolContext::GetInstance();
|
||||
if (g_init_error != ERROR_SUCCESS) {
|
||||
(*os) << "Error initializing symbols (" << g_init_error
|
||||
<< "). Dumping unresolved backtrace:\n";
|
||||
for (size_t i = 0; (i < count_) && os->good(); ++i) {
|
||||
if (prefix_string)
|
||||
(*os) << prefix_string;
|
||||
(*os) << "\t" << trace_[i] << "\n";
|
||||
}
|
||||
} else {
|
||||
(*os) << "Backtrace:\n";
|
||||
context->OutputTraceToStream(trace_, count_, os, prefix_string);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
111
TMessagesProj/jni/voip/webrtc/base/debug/task_trace.cc
Normal file
111
TMessagesProj/jni/voip/webrtc/base/debug/task_trace.cc
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/debug/task_trace.h"
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include <android/log.h>
|
||||
#endif // OS_ANDROID
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include "base/no_destructor.h"
|
||||
#endif
|
||||
|
||||
#include "base/pending_task.h"
|
||||
#include "base/task/common/task_annotator.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
namespace {
|
||||
#if defined(OS_ANDROID)
|
||||
// Android sends stdout and stderr to /dev/null; logging should be done through
|
||||
// the __android_log_write() function. Here we create an override of
|
||||
// std::stringbuf that writes to the Android log.
|
||||
class AndroidErrBuffer : public std::stringbuf {
|
||||
protected:
|
||||
int sync() override {
|
||||
__android_log_write(ANDROID_LOG_ERROR, "chromium", str().c_str());
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& DefaultOutputStream() {
|
||||
static NoDestructor<AndroidErrBuffer> buf;
|
||||
static NoDestructor<std::ostream> out(buf.get());
|
||||
return *out;
|
||||
}
|
||||
#else
|
||||
// Use stderr by default.
|
||||
std::ostream& DefaultOutputStream() {
|
||||
return std::cerr;
|
||||
}
|
||||
#endif // OS_ANDROID
|
||||
} // namespace
|
||||
|
||||
TaskTrace::TaskTrace() {
|
||||
const PendingTask* current_task = TaskAnnotator::CurrentTaskForThread();
|
||||
if (!current_task)
|
||||
return;
|
||||
std::array<const void*, PendingTask::kTaskBacktraceLength + 1> task_trace;
|
||||
task_trace[0] = current_task->posted_from.program_counter();
|
||||
std::copy(current_task->task_backtrace.begin(),
|
||||
current_task->task_backtrace.end(), task_trace.begin() + 1);
|
||||
size_t length = 0;
|
||||
while (length < task_trace.size() && task_trace[length])
|
||||
++length;
|
||||
if (length == 0)
|
||||
return;
|
||||
stack_trace_.emplace(task_trace.data(), length);
|
||||
trace_overflow_ = current_task->task_backtrace_overflow;
|
||||
}
|
||||
|
||||
bool TaskTrace::empty() const {
|
||||
return !stack_trace_.has_value();
|
||||
}
|
||||
|
||||
void TaskTrace::Print() const {
|
||||
OutputToStream(&DefaultOutputStream());
|
||||
}
|
||||
|
||||
void TaskTrace::OutputToStream(std::ostream* os) const {
|
||||
*os << "Task trace:" << std::endl;
|
||||
if (!stack_trace_) {
|
||||
*os << "No active task.";
|
||||
return;
|
||||
}
|
||||
*os << *stack_trace_;
|
||||
if (trace_overflow_) {
|
||||
*os << "Task trace buffer limit hit, update "
|
||||
"PendingTask::kTaskBacktraceLength to increase."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string TaskTrace::ToString() const {
|
||||
std::stringstream stream;
|
||||
OutputToStream(&stream);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
base::span<const void* const> TaskTrace::AddressesForTesting() const {
|
||||
if (empty())
|
||||
return {};
|
||||
size_t count = 0;
|
||||
const void* const* addresses = stack_trace_->Addresses(&count);
|
||||
return {addresses, count};
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TaskTrace& task_trace) {
|
||||
task_trace.OutputToStream(&os);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
67
TMessagesProj/jni/voip/webrtc/base/debug/task_trace.h
Normal file
67
TMessagesProj/jni/voip/webrtc/base/debug/task_trace.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_DEBUG_TASK_TRACE_H_
|
||||
#define BASE_DEBUG_TASK_TRACE_H_
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/optional.h"
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// Provides a snapshot of which places in the code called
|
||||
// base::TaskRunner::PostTask() that led to the TaskTrace() constructor call.
|
||||
// Analogous to base::StackTrace, but for posted tasks rather than function
|
||||
// calls.
|
||||
//
|
||||
// Example usage:
|
||||
// TaskTrace().Print();
|
||||
//
|
||||
// Example output:
|
||||
// Task trace:
|
||||
// #0 content::ServiceWorkerContextWrapper::DidCheckHasServiceWorker()
|
||||
// #1 content::ServiceWorkerStorage::FindForDocumentInDB()
|
||||
// #2 content::ServiceWorkerStorage::FindRegistrationForDocument()
|
||||
// #3 content::ServiceWorkerContextWrapper::CheckHasServiceWorker()
|
||||
// #4 content::ManifestIconDownloader::ScaleIcon()
|
||||
// Task trace buffer limit hit, update PendingTask::kTaskBacktraceLength to
|
||||
// increase.
|
||||
class BASE_EXPORT TaskTrace {
|
||||
public:
|
||||
TaskTrace();
|
||||
|
||||
// Whether there is any trace data.
|
||||
bool empty() const;
|
||||
|
||||
// Outputs to stderr via OutputToStream.
|
||||
void Print() const;
|
||||
|
||||
// Outputs trace to |os|, may be called when empty() is true.
|
||||
void OutputToStream(std::ostream* os) const;
|
||||
|
||||
// Resolves trace to symbols and returns as string.
|
||||
std::string ToString() const;
|
||||
|
||||
// Returns the list of addresses in the task trace for testing.
|
||||
base::span<const void* const> AddressesForTesting() const;
|
||||
|
||||
private:
|
||||
base::Optional<StackTrace> stack_trace_;
|
||||
bool trace_overflow_ = false;
|
||||
};
|
||||
|
||||
// Forwards to TaskTrace::OutputToStream.
|
||||
BASE_EXPORT std::ostream& operator<<(std::ostream& os,
|
||||
const TaskTrace& task_trace);
|
||||
|
||||
} // namespace debug
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_DEBUG_TASK_TRACE_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue