187 lines
7.1 KiB
C++
187 lines
7.1 KiB
C++
// Copyright 2022 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/libunwindstack_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/Error.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 "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Unwinder.h"
|
|
|
|
#include "base/logging.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/notreached.h"
|
|
#include "base/profiler/module_cache.h"
|
|
#include "base/profiler/native_unwinder_android.h"
|
|
#include "base/profiler/profile_builder.h"
|
|
#include "base/trace_event/base_tracing.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_);
|
|
}
|
|
|
|
size_t GetSize() const override { return size_; }
|
|
|
|
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 base::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 base::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)
|
|
}
|
|
|
|
} // namespace
|
|
|
|
LibunwindstackUnwinderAndroid::LibunwindstackUnwinderAndroid()
|
|
: memory_regions_map_(NativeUnwinderAndroid::CreateMemoryRegionsMap(
|
|
/*use_updatable_maps=*/true)),
|
|
process_memory_(std::shared_ptr<unwindstack::Memory>(
|
|
memory_regions_map_->TakeMemory().release())) {
|
|
TRACE_EVENT_INSTANT(
|
|
TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
|
|
"LibunwindstackUnwinderAndroid::LibunwindstackUnwinderAndroid");
|
|
}
|
|
|
|
LibunwindstackUnwinderAndroid::~LibunwindstackUnwinderAndroid() = default;
|
|
|
|
void LibunwindstackUnwinderAndroid::InitializeModules() {}
|
|
|
|
bool LibunwindstackUnwinderAndroid::CanUnwindFrom(
|
|
const Frame& current_frame) const {
|
|
return true;
|
|
}
|
|
|
|
unwindstack::JitDebug* LibunwindstackUnwinderAndroid::GetOrCreateJitDebug(
|
|
unwindstack::ArchEnum arch) {
|
|
if (!jit_debug_) {
|
|
jit_debug_ =
|
|
unwindstack::CreateJitDebug(arch, process_memory_, search_libs_);
|
|
}
|
|
return jit_debug_.get();
|
|
}
|
|
|
|
unwindstack::DexFiles* LibunwindstackUnwinderAndroid::GetOrCreateDexFiles(
|
|
unwindstack::ArchEnum arch) {
|
|
if (!dex_files_) {
|
|
dex_files_ =
|
|
unwindstack::CreateDexFiles(arch, process_memory_, search_libs_);
|
|
}
|
|
return dex_files_.get();
|
|
}
|
|
|
|
UnwindResult LibunwindstackUnwinderAndroid::TryUnwind(
|
|
RegisterContext* thread_context,
|
|
uintptr_t stack_top,
|
|
std::vector<Frame>* stack) {
|
|
TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
|
|
"LibunwindstackUnwinderAndroid::TryUnwind");
|
|
// 500 is taken from traced_perf's own limit:
|
|
// https://cs.android.com/android/platform/superproject/+/master:external/perfetto/src/profiling/memory/unwinding.cc;l=64;drc=5860970a8606bb48059aa31ee506328286b9bf92
|
|
const int kMaxFrames = 500;
|
|
|
|
// We use a struct and lambda here to cleanly express the result of an attempt
|
|
// to unwind. Sometimes when we fail we can succeed if we reparse maps and so
|
|
// we will call |attempt_unwind| twice.
|
|
struct UnwindValues {
|
|
unwindstack::ErrorCode error_code;
|
|
uint64_t warnings;
|
|
std::vector<unwindstack::FrameData> frames;
|
|
};
|
|
|
|
std::unique_ptr<unwindstack::Regs> regs =
|
|
CreateFromRegisterContext(thread_context);
|
|
DCHECK(regs);
|
|
unwindstack::Unwinder unwinder(kMaxFrames, memory_regions_map_->GetMaps(),
|
|
regs.get(), process_memory_);
|
|
|
|
unwinder.SetJitDebug(GetOrCreateJitDebug(regs->Arch()));
|
|
unwinder.SetDexFiles(GetOrCreateDexFiles(regs->Arch()));
|
|
|
|
unwinder.Unwind(/*initial_map_names_to_skip=*/nullptr,
|
|
/*map_suffixes_to_ignore=*/nullptr);
|
|
// Currently libunwindstack doesn't support warnings.
|
|
UnwindValues values =
|
|
UnwindValues{unwinder.LastErrorCode(), /*unwinder.warnings()*/ 0,
|
|
unwinder.ConsumeFrames()};
|
|
|
|
if (values.error_code != unwindstack::ERROR_NONE) {
|
|
TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
|
|
"TryUnwind Failure", "error", values.error_code,
|
|
"warning", values.warnings, "num_frames",
|
|
values.frames.size());
|
|
}
|
|
if (values.frames.empty()) {
|
|
return UnwindResult::kCompleted;
|
|
}
|
|
|
|
// The list of frames provided by Libunwindstack's Unwind() contains the
|
|
// executing frame. The executing frame is also added by
|
|
// StackSamplerImpl::WalkStack(). Ignore the frame from the latter to avoid
|
|
// duplication. In case a java method was being interpreted libunwindstack
|
|
// adds a dummy frame for it and then writes the corresponding native frame.
|
|
// In such a scenario we want to prefer the frames produced by
|
|
// libunwindstack.
|
|
DCHECK_EQ(stack->size(), 1u);
|
|
stack->clear();
|
|
|
|
for (const unwindstack::FrameData& frame : values.frames) {
|
|
const ModuleCache::Module* module =
|
|
module_cache()->GetModuleForAddress(frame.pc);
|
|
if (module == nullptr && frame.map_info != nullptr) {
|
|
auto module_for_caching =
|
|
std::make_unique<NonElfModule>(frame.map_info.get());
|
|
module = module_for_caching.get();
|
|
module_cache()->AddCustomNativeModule(std::move(module_for_caching));
|
|
}
|
|
stack->emplace_back(frame.pc, module, frame.function_name);
|
|
}
|
|
return UnwindResult::kCompleted;
|
|
}
|
|
} // namespace base
|