// 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 #include #include #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 CreateFromRegisterContext( RegisterContext* thread_context) { #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) return base::WrapUnique(unwindstack::RegsArm::Read( reinterpret_cast(&thread_context->arm_r0))); #elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS) return base::WrapUnique(unwindstack::RegsArm64::Read( reinterpret_cast(&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 WriteLibunwindstackTraceEventArgs(unwindstack::ErrorCode error_code, std::optional num_frames, perfetto::EventContext& ctx) { auto* track_event = ctx.event(); auto* libunwindstack_unwinder = track_event->set_libunwindstack_unwinder(); using ProtoEnum = perfetto::protos::pbzero::LibunwindstackUnwinder::ErrorCode; libunwindstack_unwinder->set_error_code(static_cast(error_code)); if (num_frames.has_value()) { libunwindstack_unwinder->set_num_frames(*num_frames); } } } // namespace LibunwindstackUnwinderAndroid::LibunwindstackUnwinderAndroid() : memory_regions_map_( static_cast( NativeUnwinderAndroid::CreateMemoryRegionsMap( /*use_updatable_maps=*/false) .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, memory_regions_map_->memory(), search_libs_); } return jit_debug_.get(); } unwindstack::DexFiles* LibunwindstackUnwinderAndroid::GetOrCreateDexFiles( unwindstack::ArchEnum arch) { if (!dex_files_) { dex_files_ = unwindstack::CreateDexFiles( arch, memory_regions_map_->memory(), search_libs_); } return dex_files_.get(); } UnwindResult LibunwindstackUnwinderAndroid::TryUnwind( RegisterContext* thread_context, uintptr_t stack_top, std::vector* 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 frames; }; std::unique_ptr regs = CreateFromRegisterContext(thread_context); DCHECK(regs); TRACE_EVENT_BEGIN(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"), "libunwindstack::Unwind"); unwindstack::Unwinder unwinder(kMaxFrames, memory_regions_map_->maps(), regs.get(), memory_regions_map_->memory()); unwinder.SetJitDebug(GetOrCreateJitDebug(regs->Arch())); unwinder.SetDexFiles(GetOrCreateDexFiles(regs->Arch())); unwinder.Unwind(/*initial_map_names_to_skip=*/nullptr, /*map_suffixes_to_ignore=*/nullptr); TRACE_EVENT_END(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug")); // 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"), "Libunwindstack Failure", [&values](perfetto::EventContext& ctx) { WriteLibunwindstackTraceEventArgs( values.error_code, values.frames.size(), ctx); }); } 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) { // Try searching for the module with same module start. module = module_cache()->GetModuleForAddress(frame.map_info->start()); if (module == nullptr) { auto module_for_caching = std::make_unique(frame.map_info.get()); module = module_for_caching.get(); module_cache()->AddCustomNativeModule(std::move(module_for_caching)); } if (frame.pc < frame.map_info->start() || frame.pc >= frame.map_info->end()) { TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"), "PC out of map range", [&values](perfetto::EventContext& ctx) { WriteLibunwindstackTraceEventArgs( values.error_code, std::nullopt, ctx); }); } } stack->emplace_back(frame.pc, module, frame.function_name); } return UnwindResult::kCompleted; } } // namespace base