From 8f8c0b69dc4d56a0bc29bf8a0e59921334a808a8 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Sat, 25 Jun 2022 12:54:24 -0400
Subject: [PATCH] core/arm: better support for backtrace generation

---
 src/core/arm/arm_interface.cpp            | 15 ++++++++++++
 src/core/arm/dynarmic/arm_dynarmic_32.cpp | 30 +++++++++++++++++++----
 src/core/arm/dynarmic/arm_dynarmic_32.h   |  2 +-
 src/core/arm/dynarmic/arm_dynarmic_64.cpp | 17 +++++++------
 src/core/arm/dynarmic/arm_dynarmic_64.h   |  2 +-
 5 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 6425e131f..6c9c9d96d 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,6 +1,10 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#ifndef _MSC_VER
+#include <cxxabi.h>
+#endif
+
 #include <map>
 #include <optional>
 #include "common/bit_field.h"
@@ -68,8 +72,19 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
         if (symbol_set != symbols.end()) {
             const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
             if (symbol.has_value()) {
+#ifdef _MSC_VER
                 // TODO(DarkLordZach): Add demangling of symbol names.
                 entry.name = *symbol;
+#else
+                int status{-1};
+                char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
+                if (status == 0 && demangled != nullptr) {
+                    entry.name = demangled;
+                    std::free(demangled);
+                } else {
+                    entry.name = *symbol;
+                }
+#endif
             }
         }
     }
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 8c90c8be0..dc01d9d1c 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -409,18 +409,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
 }
 
 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 {};
+                                                                         u64 fp, u64 lr, u64 pc) {
+    std::vector<BacktraceEntry> out;
+    auto& memory = system.Memory();
+
+    out.push_back({"", 0, pc, 0, ""});
+
+    // fp (= r11) points to the last frame record.
+    // Frame records are two words long:
+    // fp+0 : pointer to previous frame record
+    // fp+4 : value of lr for frame
+    while (true) {
+        out.push_back({"", 0, lr, 0, ""});
+        if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
+            break;
+        }
+        lr = memory.Read32(fp + 4);
+        fp = memory.Read32(fp);
+    }
+
+    SymbolicateBacktrace(system, out);
+
+    return out;
 }
 
 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]);
+    const auto& reg = ctx.cpu_registers;
+    return GetBacktrace(system, reg[11], reg[14], reg[15]);
 }
 
 std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
-    return GetBacktrace(system, GetReg(13), GetReg(14));
+    return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
 }
 
 } // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index fcbe24f0c..346e9abf8 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -78,7 +78,7 @@ protected:
 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);
+    static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
 
     using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
     using JitCacheType =
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 4370ca294..8a20774e4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -480,22 +480,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
 }
 
 std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
-                                                                         u64 fp, u64 lr) {
+                                                                         u64 fp, u64 lr, u64 pc) {
     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
+    out.push_back({"", 0, pc, 0, ""});
+
+    // fp (= x29) points to the previous frame record.
     // 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) {
+        if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
             break;
         }
-        lr = memory.Read64(fp + 8) - 4;
+        lr = memory.Read64(fp + 8);
         fp = memory.Read64(fp);
     }
 
@@ -506,11 +506,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
 
 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]);
+    const auto& reg = ctx.cpu_registers;
+    return GetBacktrace(system, reg[29], reg[30], ctx.pc);
 }
 
 std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
-    return GetBacktrace(system, GetReg(29), GetReg(30));
+    return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
 }
 
 } // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 71dbaac5e..c77a83ad7 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -73,7 +73,7 @@ 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);
+    static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
 
     using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
     using JitCacheType =