| // 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/metrics/metrics_hashes.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_impl.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, |
| bool is_java_name_hashing_enabled) |
| : start_(map_info->start()), |
| size_(map_info->end() - start_), |
| map_info_name_(map_info->name()), |
| is_java_name_hashing_enabled_(is_java_name_hashing_enabled) {} |
| ~NonElfModule() override = default; |
| |
| uintptr_t GetBaseAddress() const override { return start_; } |
| |
| std::string GetId() const override { |
| // We provide a non-empty string only if Java name hashing is enabled, to |
| // allow us to easily filter out the results from outside the experiment. |
| if (is_java_name_hashing_enabled_) { |
| // Synthetic build id to use for DEX files that provide hashed function |
| // names rather than instruction pointers. |
| return "44444444BC18564712E780518FB3032B999"; |
| } else { |
| return ""; |
| } |
| } |
| |
| 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_; |
| const bool is_java_name_hashing_enabled_; |
| }; |
| |
| 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) |
| } |
| |
| } // 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<NativeUnwinderAndroidMemoryRegionsMapImpl>( |
| std::move(maps), unwindstack::Memory::CreateLocalProcessMemory()); |
| } |
| |
| NativeUnwinderAndroid::NativeUnwinderAndroid( |
| uintptr_t exclude_module_with_base_address, |
| NativeUnwinderAndroidMapDelegate* map_delegate, |
| bool is_java_name_hashing_enabled) |
| : is_java_name_hashing_enabled_(is_java_name_hashing_enabled), |
| exclude_module_with_base_address_(exclude_module_with_base_address), |
| map_delegate_(map_delegate), |
| memory_regions_map_( |
| static_cast<NativeUnwinderAndroidMemoryRegionsMapImpl*>( |
| 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_->maps()->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_->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; |
| // map_info->GetElf() may return a valid elf whose memory() is nullptr. |
| // In the case, elf->StepIfSignalHandler() and elf->Step() are not |
| // available, because the method depends on elf->memory(). |
| // (Regarding Step(), EvalRegister() needs memory.) |
| bool stepped = |
| elf->memory() && |
| (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(), arch, 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_->maps()->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, |
| is_java_name_hashing_enabled_); |
| } |
| |
| unwindstack::DexFiles* NativeUnwinderAndroid::GetOrCreateDexFiles( |
| unwindstack::ArchEnum arch) { |
| if (!dex_files_) { |
| dex_files_ = unwindstack::CreateDexFiles( |
| arch, memory_regions_map_->memory(), search_libs_); |
| } |
| return dex_files_.get(); |
| } |
| |
| void NativeUnwinderAndroid::EmitDexFrame(uintptr_t dex_pc, |
| unwindstack::ArchEnum arch, |
| std::vector<Frame>* stack) { |
| 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_->maps()->Find(dex_pc).get(); |
| if (map_info) { |
| auto new_module = std::make_unique<NonElfModule>( |
| map_info, is_java_name_hashing_enabled_); |
| module = new_module.get(); |
| module_cache()->AddCustomNativeModule(std::move(new_module)); |
| } |
| } |
| |
| if (is_java_name_hashing_enabled_) { |
| unwindstack::SharedString function_name; |
| uint64_t function_offset = 0; |
| GetOrCreateDexFiles(arch)->GetFunctionName( |
| memory_regions_map_->maps(), dex_pc, &function_name, &function_offset); |
| stack->emplace_back( |
| HashMetricNameAs32Bits(static_cast<const std::string&>(function_name)), |
| module); |
| |
| } else { |
| stack->emplace_back(dex_pc, module); |
| } |
| } |
| |
| } // namespace base |