mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 09:25:29 +03:00
Add stack tracing to skyline::exception
Skyline's `exception` class now stores a list of all stack frames during the invocation of the exception. These can later be parsed by the exception handler to generate a human-readable stack trace. To assist with more complete stack traces, `-fno-omit-frame-pointer` is now passed on debug builds which forces the inclusion of frames on function calls.
This commit is contained in:
parent
cd8fa66326
commit
41b98c7daa
@ -109,7 +109,7 @@ add_subdirectory("libraries/sirit")
|
||||
add_subdirectory("libraries/adrenotools")
|
||||
|
||||
# Build Skyline with full debugging data and -Og for debug builds
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -glldb -gdwarf-5")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -glldb -gdwarf-5 -fno-omit-frame-pointer")
|
||||
|
||||
# Include headers from libraries as system headers to silence warnings from them
|
||||
function(target_link_libraries_system target)
|
||||
@ -135,6 +135,7 @@ add_library(skyline SHARED
|
||||
${source_DIR}/emu_jni.cpp
|
||||
${source_DIR}/loader_jni.cpp
|
||||
${source_DIR}/skyline/common.cpp
|
||||
${source_DIR}/skyline/common/exception.cpp
|
||||
${source_DIR}/skyline/common/logger.cpp
|
||||
${source_DIR}/skyline/common/settings.cpp
|
||||
${source_DIR}/skyline/common/signal.cpp
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <memory>
|
||||
#include <compare>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <common/exception.h>
|
||||
#include <common/span.h>
|
||||
#include <common/result.h>
|
||||
#include <common/logger.h>
|
||||
|
@ -70,15 +70,6 @@ namespace skyline {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A wrapper over std::runtime_error with {fmt} formatting
|
||||
*/
|
||||
class exception : public std::runtime_error {
|
||||
public:
|
||||
template<typename S, typename... Args>
|
||||
exception(const S &formatStr, Args &&... args) : runtime_error(util::Format(formatStr, args...)) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A deduction guide for overloads required for std::visit with std::variant
|
||||
*/
|
||||
|
20
app/src/main/cpp/skyline/common/exception.cpp
Normal file
20
app/src/main/cpp/skyline/common/exception.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "signal.h"
|
||||
#include "exception.h"
|
||||
|
||||
namespace skyline {
|
||||
std::vector<void *> exception::GetStackFrames() {
|
||||
std::vector<void*> frames;
|
||||
signal::StackFrame *frame{};
|
||||
asm("MOV %0, FP" : "=r"(frame));
|
||||
if (frame)
|
||||
frame = frame->next; // We want to skip the first frame as it's going to be the caller of this function
|
||||
while (frame && frame->lr) {
|
||||
frames.push_back(frame->lr);
|
||||
frame = frame->next;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
}
|
26
app/src/main/cpp/skyline/common/exception.h
Normal file
26
app/src/main/cpp/skyline/common/exception.h
Normal file
@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "base.h"
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief A wrapper over std::runtime_error with {fmt} formatting and stack tracing support
|
||||
*/
|
||||
class exception : public std::runtime_error {
|
||||
public:
|
||||
std::vector<void *> frames; //!< All frames from the stack trace during the exception
|
||||
|
||||
/**
|
||||
* @return A vector of all frames on the caller's stack
|
||||
* @note This cannot be inlined because it depends on having one stack frame itself consistently
|
||||
*/
|
||||
static std::vector<void*> GetStackFrames() __attribute__((noinline));
|
||||
|
||||
template<typename S, typename... Args>
|
||||
exception(const S &formatStr, Args &&... args) : runtime_error(util::Format(formatStr, args...)), frames(GetStackFrames()) {}
|
||||
};
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
#include <frozen/string.h>
|
||||
#include <xxhash.h>
|
||||
#include "base.h"
|
||||
#include "exception.h"
|
||||
|
||||
namespace skyline::util {
|
||||
/**
|
||||
|
@ -92,12 +92,20 @@ namespace skyline::loader {
|
||||
size_t length{};
|
||||
std::unique_ptr<char, decltype(&std::free)> demangled{abi::__cxa_demangle(info.dli_sname, nullptr, &length, &status), std::free};
|
||||
|
||||
auto extractFilename{[](const char *path) {
|
||||
const char *filename{};
|
||||
for (const char *p{path}; *p; p++)
|
||||
if (*p == '/')
|
||||
filename = p + 1;
|
||||
return filename;
|
||||
}}; //!< Extracts the filename from a path as we only want the filename of a shared object
|
||||
|
||||
if (info.dli_sname && info.dli_fname)
|
||||
return fmt::format("\n* 0x{:X} ({} from {})", reinterpret_cast<uintptr_t>(pointer), (status == 0) ? std::string_view(demangled.get()) : info.dli_sname, info.dli_fname);
|
||||
return fmt::format("\n* 0x{:X} ({} from {})", reinterpret_cast<uintptr_t>(pointer), (status == 0) ? std::string_view(demangled.get()) : info.dli_sname, extractFilename(info.dli_fname));
|
||||
else if (info.dli_sname)
|
||||
return fmt::format("\n* 0x{:X} ({})", reinterpret_cast<uintptr_t>(pointer), (status == 0) ? std::string_view(demangled.get()) : info.dli_sname);
|
||||
else if (info.dli_fname)
|
||||
return fmt::format("\n* 0x{:X} (from {})", reinterpret_cast<uintptr_t>(pointer), info.dli_fname);
|
||||
return fmt::format("\n* 0x{:X} (from {})", reinterpret_cast<uintptr_t>(pointer), extractFilename(info.dli_fname));
|
||||
else
|
||||
return fmt::format("\n* 0x{:X}", reinterpret_cast<uintptr_t>(pointer));
|
||||
} else {
|
||||
|
@ -50,6 +50,7 @@ namespace skyline::nce {
|
||||
} else {
|
||||
Logger::EmulationContext.Flush();
|
||||
}
|
||||
|
||||
abi::__cxa_end_catch(); // We call this prior to the longjmp to cause the exception object to be destroyed
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
} catch (const ExitException &e) {
|
||||
@ -57,6 +58,19 @@ namespace skyline::nce {
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
|
||||
abi::__cxa_end_catch();
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
} catch (const exception &e) {
|
||||
Logger::ErrorNoPrefix("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames));
|
||||
Logger::EmulationContext.Flush();
|
||||
|
||||
if (state.thread->id) {
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
|
||||
abi::__cxa_end_catch();
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
} catch (const std::exception &e) {
|
||||
if (svc)
|
||||
@ -70,6 +84,7 @@ namespace skyline::nce {
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
|
||||
abi::__cxa_end_catch();
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ namespace skyline::service {
|
||||
TRACE_EVENT("service", perfetto::StaticString{function.name});
|
||||
try {
|
||||
return function(session, request, response);
|
||||
} catch (exception &e) {
|
||||
// We need to forward any skyline::exception objects without modification even though they inherit from std::exception
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} catch (const std::exception &e) {
|
||||
throw exception("{} (Service: {})", e.what(), function.name);
|
||||
}
|
||||
|
@ -350,6 +350,11 @@ namespace skyline::soc::gm20b {
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
} catch (const exception &e) {
|
||||
Logger::ErrorNoPrefix("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames));
|
||||
Logger::EmulationContext.Flush();
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
} catch (const std::exception &e) {
|
||||
Logger::Error(e.what());
|
||||
Logger::EmulationContext.Flush();
|
||||
|
@ -130,6 +130,11 @@ namespace skyline::soc::host1x {
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
} catch (const exception &e) {
|
||||
Logger::ErrorNoPrefix("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames));
|
||||
Logger::EmulationContext.Flush();
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
} catch (const std::exception &e) {
|
||||
Logger::Error(e.what());
|
||||
Logger::EmulationContext.Flush();
|
||||
|
Loading…
Reference in New Issue
Block a user