286 lines
10 KiB
C++
286 lines
10 KiB
C++
|
|
// Copyright 2019 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "base/profiler/native_unwinder_android.h"
|
||
|
|
|
||
|
|
#include <sys/mman.h>
|
||
|
|
|
||
|
|
#include <string>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Elf.h"
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Maps.h"
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Memory.h"
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Regs.h"
|
||
|
|
|
||
|
|
#include "base/memory/ptr_util.h"
|
||
|
|
#include "base/notreached.h"
|
||
|
|
#include "base/profiler/module_cache.h"
|
||
|
|
#include "base/profiler/native_unwinder_android_map_delegate.h"
|
||
|
|
#include "base/profiler/native_unwinder_android_memory_regions_map.h"
|
||
|
|
#include "base/profiler/profile_builder.h"
|
||
|
|
#include "build/build_config.h"
|
||
|
|
|
||
|
|
#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/MachineArm.h"
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/RegsArm.h"
|
||
|
|
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/MachineArm64.h"
|
||
|
|
#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/RegsArm64.h"
|
||
|
|
#endif // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
|
||
|
|
namespace base {
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
class NonElfModule : public ModuleCache::Module {
|
||
|
|
public:
|
||
|
|
explicit NonElfModule(unwindstack::MapInfo* map_info)
|
||
|
|
: start_(map_info->start()),
|
||
|
|
size_(map_info->end() - start_),
|
||
|
|
map_info_name_(map_info->name()) {}
|
||
|
|
~NonElfModule() override = default;
|
||
|
|
|
||
|
|
uintptr_t GetBaseAddress() const override { return start_; }
|
||
|
|
|
||
|
|
std::string GetId() const override { return std::string(); }
|
||
|
|
|
||
|
|
FilePath GetDebugBasename() const override {
|
||
|
|
return FilePath(map_info_name_);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Gets the size of the module.
|
||
|
|
size_t GetSize() const override { return size_; }
|
||
|
|
|
||
|
|
// True if this is a native module.
|
||
|
|
bool IsNative() const override { return true; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
const uintptr_t start_;
|
||
|
|
const size_t size_;
|
||
|
|
const std::string map_info_name_;
|
||
|
|
};
|
||
|
|
|
||
|
|
std::unique_ptr<unwindstack::Regs> CreateFromRegisterContext(
|
||
|
|
RegisterContext* thread_context) {
|
||
|
|
#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
return WrapUnique<unwindstack::Regs>(unwindstack::RegsArm::Read(
|
||
|
|
reinterpret_cast<void*>(&thread_context->arm_r0)));
|
||
|
|
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
|
||
|
|
return WrapUnique<unwindstack::Regs>(unwindstack::RegsArm64::Read(
|
||
|
|
reinterpret_cast<void*>(&thread_context->regs[0])));
|
||
|
|
#else // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
NOTREACHED();
|
||
|
|
return nullptr;
|
||
|
|
#endif // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
}
|
||
|
|
|
||
|
|
void CopyToRegisterContext(unwindstack::Regs* regs,
|
||
|
|
RegisterContext* thread_context) {
|
||
|
|
#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
memcpy(reinterpret_cast<void*>(&thread_context->arm_r0), regs->RawData(),
|
||
|
|
unwindstack::ARM_REG_LAST * sizeof(uintptr_t));
|
||
|
|
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
|
||
|
|
memcpy(reinterpret_cast<void*>(&thread_context->regs[0]), regs->RawData(),
|
||
|
|
unwindstack::ARM64_REG_LAST * sizeof(uintptr_t));
|
||
|
|
#else // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
NOTREACHED();
|
||
|
|
#endif // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
|
||
|
|
}
|
||
|
|
|
||
|
|
// The wrapper class exists to avoid the reference of concrete libunwindstack
|
||
|
|
// types in chrome code. Only code in the stack unwinder DFM has the access to
|
||
|
|
// third_party/libunwindstack/src/libunwindstack. Files within the stack
|
||
|
|
// unwinder DFM can be found by searching `native_unwinder_android` source set
|
||
|
|
// in `base/BUILD.gn`.
|
||
|
|
class MemoryRegionsMap : public base::NativeUnwinderAndroidMemoryRegionsMap {
|
||
|
|
public:
|
||
|
|
MemoryRegionsMap(std::unique_ptr<unwindstack::Maps> maps,
|
||
|
|
std::unique_ptr<unwindstack::Memory> memory)
|
||
|
|
: maps_(std::move(maps)), memory_(std::move(memory)) {}
|
||
|
|
|
||
|
|
unwindstack::Maps* GetMaps() override { return maps_.get(); }
|
||
|
|
unwindstack::Memory* GetMemory() override { return memory_.get(); }
|
||
|
|
std::unique_ptr<unwindstack::Memory> TakeMemory() override {
|
||
|
|
return std::move(memory_);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
std::unique_ptr<unwindstack::Maps> maps_;
|
||
|
|
std::unique_ptr<unwindstack::Memory> memory_;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
UnwindStackMemoryAndroid::UnwindStackMemoryAndroid(uintptr_t stack_ptr,
|
||
|
|
uintptr_t stack_top)
|
||
|
|
: stack_ptr_(stack_ptr), stack_top_(stack_top) {
|
||
|
|
DCHECK_LE(stack_ptr_, stack_top_);
|
||
|
|
}
|
||
|
|
|
||
|
|
UnwindStackMemoryAndroid::~UnwindStackMemoryAndroid() = default;
|
||
|
|
|
||
|
|
size_t UnwindStackMemoryAndroid::Read(uint64_t addr, void* dst, size_t size) {
|
||
|
|
if (addr < stack_ptr_)
|
||
|
|
return 0;
|
||
|
|
if (size >= stack_top_ || addr > stack_top_ - size)
|
||
|
|
return 0;
|
||
|
|
memcpy(dst, reinterpret_cast<void*>(addr), size);
|
||
|
|
return size;
|
||
|
|
}
|
||
|
|
|
||
|
|
// static
|
||
|
|
std::unique_ptr<NativeUnwinderAndroidMemoryRegionsMap>
|
||
|
|
NativeUnwinderAndroid::CreateMemoryRegionsMap(bool use_updatable_maps) {
|
||
|
|
std::unique_ptr<unwindstack::Maps> maps;
|
||
|
|
if (use_updatable_maps) {
|
||
|
|
maps = std::make_unique<unwindstack::LocalUpdatableMaps>();
|
||
|
|
} else {
|
||
|
|
maps = std::make_unique<unwindstack::LocalMaps>();
|
||
|
|
}
|
||
|
|
const bool success = maps->Parse();
|
||
|
|
DCHECK(success);
|
||
|
|
|
||
|
|
return std::make_unique<MemoryRegionsMap>(
|
||
|
|
std::move(maps), unwindstack::Memory::CreateLocalProcessMemory());
|
||
|
|
}
|
||
|
|
|
||
|
|
NativeUnwinderAndroid::NativeUnwinderAndroid(
|
||
|
|
uintptr_t exclude_module_with_base_address,
|
||
|
|
NativeUnwinderAndroidMapDelegate* map_delegate)
|
||
|
|
: exclude_module_with_base_address_(exclude_module_with_base_address),
|
||
|
|
map_delegate_(map_delegate),
|
||
|
|
memory_regions_map_(map_delegate->GetMapReference()) {
|
||
|
|
DCHECK(map_delegate_);
|
||
|
|
DCHECK(memory_regions_map_);
|
||
|
|
}
|
||
|
|
|
||
|
|
NativeUnwinderAndroid::~NativeUnwinderAndroid() {
|
||
|
|
if (module_cache())
|
||
|
|
module_cache()->UnregisterAuxiliaryModuleProvider(this);
|
||
|
|
|
||
|
|
map_delegate_->ReleaseMapReference();
|
||
|
|
}
|
||
|
|
|
||
|
|
void NativeUnwinderAndroid::InitializeModules() {
|
||
|
|
module_cache()->RegisterAuxiliaryModuleProvider(this);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool NativeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const {
|
||
|
|
return current_frame.module && current_frame.module->IsNative() &&
|
||
|
|
current_frame.module->GetBaseAddress() !=
|
||
|
|
exclude_module_with_base_address_;
|
||
|
|
}
|
||
|
|
|
||
|
|
UnwindResult NativeUnwinderAndroid::TryUnwind(RegisterContext* thread_context,
|
||
|
|
uintptr_t stack_top,
|
||
|
|
std::vector<Frame>* stack) {
|
||
|
|
auto regs = CreateFromRegisterContext(thread_context);
|
||
|
|
DCHECK(regs);
|
||
|
|
unwindstack::ArchEnum arch = regs->Arch();
|
||
|
|
|
||
|
|
do {
|
||
|
|
uint64_t cur_pc = regs->pc();
|
||
|
|
uint64_t cur_sp = regs->sp();
|
||
|
|
unwindstack::MapInfo* map_info =
|
||
|
|
memory_regions_map_->GetMaps()->Find(cur_pc).get();
|
||
|
|
if (map_info == nullptr ||
|
||
|
|
map_info->flags() & unwindstack::MAPS_FLAGS_DEVICE_MAP) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
unwindstack::Elf* elf = map_info->GetElf(
|
||
|
|
{memory_regions_map_->GetMemory(), [](unwindstack::Memory*) {}}, arch);
|
||
|
|
if (!elf->valid())
|
||
|
|
break;
|
||
|
|
|
||
|
|
UnwindStackMemoryAndroid stack_memory(cur_sp, stack_top);
|
||
|
|
uintptr_t rel_pc = elf->GetRelPc(cur_pc, map_info);
|
||
|
|
bool is_signal_frame = false;
|
||
|
|
bool finished = false;
|
||
|
|
bool stepped =
|
||
|
|
elf->StepIfSignalHandler(rel_pc, regs.get(), &stack_memory) ||
|
||
|
|
elf->Step(rel_pc, regs.get(), &stack_memory, &finished,
|
||
|
|
&is_signal_frame);
|
||
|
|
if (stepped && finished)
|
||
|
|
return UnwindResult::kCompleted;
|
||
|
|
|
||
|
|
if (!stepped) {
|
||
|
|
// Stepping failed. Try unwinding using return address.
|
||
|
|
if (stack->size() == 1) {
|
||
|
|
if (!regs->SetPcFromReturnAddress(&stack_memory))
|
||
|
|
return UnwindResult::kAborted;
|
||
|
|
} else {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// If the pc and sp didn't change, then consider everything stopped.
|
||
|
|
if (cur_pc == regs->pc() && cur_sp == regs->sp())
|
||
|
|
return UnwindResult::kAborted;
|
||
|
|
|
||
|
|
// Exclusive range of expected stack pointer values after the unwind.
|
||
|
|
struct {
|
||
|
|
uintptr_t start;
|
||
|
|
uintptr_t end;
|
||
|
|
} expected_stack_pointer_range = {static_cast<uintptr_t>(cur_sp),
|
||
|
|
stack_top};
|
||
|
|
if (regs->sp() < expected_stack_pointer_range.start ||
|
||
|
|
regs->sp() >= expected_stack_pointer_range.end) {
|
||
|
|
return UnwindResult::kAborted;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (regs->dex_pc() != 0) {
|
||
|
|
// Add a frame to represent the dex file.
|
||
|
|
EmitDexFrame(regs->dex_pc(), stack);
|
||
|
|
|
||
|
|
// Clear the dex pc so that we don't repeat this frame later.
|
||
|
|
regs->set_dex_pc(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add the frame to |stack|. Must use GetModuleForAddress rather than
|
||
|
|
// GetExistingModuleForAddress because the unwound-to address may be in a
|
||
|
|
// module associated with a different unwinder.
|
||
|
|
const ModuleCache::Module* module =
|
||
|
|
module_cache()->GetModuleForAddress(regs->pc());
|
||
|
|
stack->emplace_back(regs->pc(), module);
|
||
|
|
} while (CanUnwindFrom(stack->back()));
|
||
|
|
|
||
|
|
// Restore registers necessary for further unwinding in |thread_context|.
|
||
|
|
CopyToRegisterContext(regs.get(), thread_context);
|
||
|
|
return UnwindResult::kUnrecognizedFrame;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::unique_ptr<const ModuleCache::Module>
|
||
|
|
NativeUnwinderAndroid::TryCreateModuleForAddress(uintptr_t address) {
|
||
|
|
unwindstack::MapInfo* map_info =
|
||
|
|
memory_regions_map_->GetMaps()->Find(address).get();
|
||
|
|
if (map_info == nullptr || !(map_info->flags() & PROT_EXEC) ||
|
||
|
|
map_info->flags() & unwindstack::MAPS_FLAGS_DEVICE_MAP) {
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
return std::make_unique<NonElfModule>(map_info);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NativeUnwinderAndroid::EmitDexFrame(uintptr_t dex_pc,
|
||
|
|
std::vector<Frame>* stack) const {
|
||
|
|
const ModuleCache::Module* module =
|
||
|
|
module_cache()->GetExistingModuleForAddress(dex_pc);
|
||
|
|
if (!module) {
|
||
|
|
// The region containing |dex_pc| may not be in module_cache() since it's
|
||
|
|
// usually not executable (.dex file). Since non-executable regions
|
||
|
|
// are used much less commonly, it's lazily added here instead of from
|
||
|
|
// AddInitialModulesFromMaps().
|
||
|
|
unwindstack::MapInfo* map_info =
|
||
|
|
memory_regions_map_->GetMaps()->Find(dex_pc).get();
|
||
|
|
if (map_info) {
|
||
|
|
auto new_module = std::make_unique<NonElfModule>(map_info);
|
||
|
|
module = new_module.get();
|
||
|
|
module_cache()->AddCustomNativeModule(std::move(new_module));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
stack->emplace_back(dex_pc, module);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace base
|