| // 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 |