Merge pull request #8232 from liamwhite/backtrace
core/arm: separate backtrace collection
This commit is contained in:
		| @@ -14,96 +14,28 @@ | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| #include "core/arm/dynarmic/arm_dynarmic_32.h" | ||||
| #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||||
|  | ||||
| namespace Core { | ||||
|  | ||||
| constexpr u64 SEGMENT_BASE = 0x7100000000ull; | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( | ||||
|     System& system, const ThreadContext64& ctx) { | ||||
|     std::vector<BacktraceEntry> out; | ||||
|     auto& memory = system.Memory(); | ||||
|  | ||||
|     auto fp = ctx.cpu_registers[29]; | ||||
|     auto lr = ctx.cpu_registers[30]; | ||||
|     while (true) { | ||||
|         out.push_back({ | ||||
|             .module = "", | ||||
|             .address = 0, | ||||
|             .original_address = lr, | ||||
|             .offset = 0, | ||||
|             .name = {}, | ||||
|         }); | ||||
|  | ||||
|         if (fp == 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         lr = memory.Read64(fp + 8) - 4; | ||||
|         fp = memory.Read64(fp); | ||||
|     } | ||||
|  | ||||
|     std::map<VAddr, std::string> modules; | ||||
|     auto& loader{system.GetAppLoader()}; | ||||
|     if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     std::map<std::string, Symbols::Symbols> symbols; | ||||
|     for (const auto& module : modules) { | ||||
|         symbols.insert_or_assign(module.second, | ||||
|                                  Symbols::GetSymbols(module.first, system.Memory(), | ||||
|                                                      system.CurrentProcess()->Is64BitProcess())); | ||||
|     } | ||||
|  | ||||
|     for (auto& entry : out) { | ||||
|         VAddr base = 0; | ||||
|         for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { | ||||
|             const auto& module{*iter}; | ||||
|             if (entry.original_address >= module.first) { | ||||
|                 entry.module = module.second; | ||||
|                 base = module.first; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         entry.offset = entry.original_address - base; | ||||
|         entry.address = SEGMENT_BASE + entry.offset; | ||||
|  | ||||
|         if (entry.module.empty()) | ||||
|             entry.module = "unknown"; | ||||
|  | ||||
|         const auto symbol_set = symbols.find(entry.module); | ||||
|         if (symbol_set != symbols.end()) { | ||||
|             const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); | ||||
|             if (symbol.has_value()) { | ||||
|                 // TODO(DarkLordZach): Add demangling of symbol names. | ||||
|                 entry.name = *symbol; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return out; | ||||
|     Core::System& system, const ARM_Interface::ThreadContext32& ctx) { | ||||
|     return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx); | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | ||||
|     std::vector<BacktraceEntry> out; | ||||
|     auto& memory = system.Memory(); | ||||
|  | ||||
|     auto fp = GetReg(29); | ||||
|     auto lr = GetReg(30); | ||||
|     while (true) { | ||||
|         out.push_back({"", 0, lr, 0, ""}); | ||||
|         if (!fp) { | ||||
|             break; | ||||
|         } | ||||
|         lr = memory.Read64(fp + 8) - 4; | ||||
|         fp = memory.Read64(fp); | ||||
|     } | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( | ||||
|     Core::System& system, const ARM_Interface::ThreadContext64& ctx) { | ||||
|     return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx); | ||||
| } | ||||
|  | ||||
| void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) { | ||||
|     std::map<VAddr, std::string> modules; | ||||
|     auto& loader{system.GetAppLoader()}; | ||||
|     if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { | ||||
|         return {}; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     std::map<std::string, Symbols::Symbols> symbols; | ||||
| @@ -127,8 +59,9 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | ||||
|         entry.offset = entry.original_address - base; | ||||
|         entry.address = SEGMENT_BASE + entry.offset; | ||||
|  | ||||
|         if (entry.module.empty()) | ||||
|         if (entry.module.empty()) { | ||||
|             entry.module = "unknown"; | ||||
|         } | ||||
|  | ||||
|         const auto symbol_set = symbols.find(entry.module); | ||||
|         if (symbol_set != symbols.end()) { | ||||
| @@ -139,12 +72,10 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return out; | ||||
| } | ||||
|  | ||||
| void ARM_Interface::LogBacktrace() const { | ||||
|     const VAddr sp = GetReg(13); | ||||
|     const VAddr sp = GetSP(); | ||||
|     const VAddr pc = GetPC(); | ||||
|     LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); | ||||
|     LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", | ||||
|   | ||||
| @@ -100,6 +100,12 @@ public: | ||||
|      */ | ||||
|     virtual u64 GetPC() const = 0; | ||||
|  | ||||
|     /** | ||||
|      * Get the current Stack Pointer | ||||
|      * @return Returns current SP | ||||
|      */ | ||||
|     virtual u64 GetSP() const = 0; | ||||
|  | ||||
|     /** | ||||
|      * Get an ARM register | ||||
|      * @param index Register index | ||||
| @@ -182,17 +188,13 @@ public: | ||||
|         std::string name; | ||||
|     }; | ||||
|  | ||||
|     static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, | ||||
|                                                                const ThreadContext32& ctx); | ||||
|     static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, | ||||
|                                                                const ThreadContext64& ctx); | ||||
|  | ||||
|     std::vector<BacktraceEntry> GetBacktrace() const; | ||||
|     virtual std::vector<BacktraceEntry> GetBacktrace() const = 0; | ||||
|  | ||||
|     /// fp (= r29) points to the last frame record. | ||||
|     /// Note that this is the frame record for the *previous* frame, not the current one. | ||||
|     /// Note we need to subtract 4 from our last read to get the proper address | ||||
|     /// Frame records are two words long: | ||||
|     /// fp+0 : pointer to previous frame record | ||||
|     /// fp+8 : value of lr for frame | ||||
|     void LogBacktrace() const; | ||||
|  | ||||
| protected: | ||||
| @@ -200,6 +202,8 @@ protected: | ||||
|     System& system; | ||||
|     CPUInterrupts& interrupt_handlers; | ||||
|     bool uses_wall_clock; | ||||
|  | ||||
|     static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); | ||||
| }; | ||||
|  | ||||
| } // namespace Core | ||||
|   | ||||
| @@ -268,6 +268,10 @@ u64 ARM_Dynarmic_32::GetPC() const { | ||||
|     return jit.load()->Regs()[15]; | ||||
| } | ||||
|  | ||||
| u64 ARM_Dynarmic_32::GetSP() const { | ||||
|     return jit.load()->Regs()[13]; | ||||
| } | ||||
|  | ||||
| u64 ARM_Dynarmic_32::GetReg(int index) const { | ||||
|     return jit.load()->Regs()[index]; | ||||
| } | ||||
| @@ -362,4 +366,19 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, | ||||
|     jit_cache.emplace(key, std::move(new_jit)); | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, | ||||
|                                                                          u64 sp, u64 lr) { | ||||
|     // No way to get accurate stack traces in A32 yet | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( | ||||
|     System& system, const ThreadContext32& ctx) { | ||||
|     return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]); | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { | ||||
|     return GetBacktrace(system, GetReg(13), GetReg(14)); | ||||
| } | ||||
|  | ||||
| } // namespace Core | ||||
|   | ||||
| @@ -35,6 +35,7 @@ public: | ||||
|  | ||||
|     void SetPC(u64 pc) override; | ||||
|     u64 GetPC() const override; | ||||
|     u64 GetSP() const override; | ||||
|     u64 GetReg(int index) const override; | ||||
|     void SetReg(int index, u64 value) override; | ||||
|     u128 GetVectorReg(int index) const override; | ||||
| @@ -66,9 +67,16 @@ public: | ||||
|     void PageTableChanged(Common::PageTable& new_page_table, | ||||
|                           std::size_t new_address_space_size_in_bits) override; | ||||
|  | ||||
|     static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, | ||||
|                                                                const ThreadContext32& ctx); | ||||
|  | ||||
|     std::vector<BacktraceEntry> GetBacktrace() const override; | ||||
|  | ||||
| private: | ||||
|     std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; | ||||
|  | ||||
|     static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr); | ||||
|  | ||||
|     using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; | ||||
|     using JitCacheType = | ||||
|         std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; | ||||
|   | ||||
| @@ -328,6 +328,10 @@ u64 ARM_Dynarmic_64::GetPC() const { | ||||
|     return jit.load()->GetPC(); | ||||
| } | ||||
|  | ||||
| u64 ARM_Dynarmic_64::GetSP() const { | ||||
|     return jit.load()->GetSP(); | ||||
| } | ||||
|  | ||||
| u64 ARM_Dynarmic_64::GetReg(int index) const { | ||||
|     return jit.load()->GetRegister(index); | ||||
| } | ||||
| @@ -430,4 +434,38 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, | ||||
|     jit_cache.emplace(key, std::move(new_jit)); | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, | ||||
|                                                                          u64 fp, u64 lr) { | ||||
|     std::vector<BacktraceEntry> out; | ||||
|     auto& memory = system.Memory(); | ||||
|  | ||||
|     // fp (= r29) points to the last frame record. | ||||
|     // Note that this is the frame record for the *previous* frame, not the current one. | ||||
|     // Note we need to subtract 4 from our last read to get the proper address | ||||
|     // Frame records are two words long: | ||||
|     // fp+0 : pointer to previous frame record | ||||
|     // fp+8 : value of lr for frame | ||||
|     while (true) { | ||||
|         out.push_back({"", 0, lr, 0, ""}); | ||||
|         if (!fp) { | ||||
|             break; | ||||
|         } | ||||
|         lr = memory.Read64(fp + 8) - 4; | ||||
|         fp = memory.Read64(fp); | ||||
|     } | ||||
|  | ||||
|     SymbolicateBacktrace(system, out); | ||||
|  | ||||
|     return out; | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( | ||||
|     System& system, const ThreadContext64& ctx) { | ||||
|     return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]); | ||||
| } | ||||
|  | ||||
| std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { | ||||
|     return GetBacktrace(system, GetReg(29), GetReg(30)); | ||||
| } | ||||
|  | ||||
| } // namespace Core | ||||
|   | ||||
| @@ -33,6 +33,7 @@ public: | ||||
|  | ||||
|     void SetPC(u64 pc) override; | ||||
|     u64 GetPC() const override; | ||||
|     u64 GetSP() const override; | ||||
|     u64 GetReg(int index) const override; | ||||
|     void SetReg(int index, u64 value) override; | ||||
|     u128 GetVectorReg(int index) const override; | ||||
| @@ -60,10 +61,17 @@ public: | ||||
|     void PageTableChanged(Common::PageTable& new_page_table, | ||||
|                           std::size_t new_address_space_size_in_bits) override; | ||||
|  | ||||
|     static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, | ||||
|                                                                const ThreadContext64& ctx); | ||||
|  | ||||
|     std::vector<BacktraceEntry> GetBacktrace() const override; | ||||
|  | ||||
| private: | ||||
|     std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, | ||||
|                                                 std::size_t address_space_bits) const; | ||||
|  | ||||
|     static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr); | ||||
|  | ||||
|     using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; | ||||
|     using JitCacheType = | ||||
|         std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user