mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 12:21:51 +01: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")
|
add_subdirectory("libraries/adrenotools")
|
||||||
|
|
||||||
# Build Skyline with full debugging data and -Og for debug builds
|
# 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
|
# Include headers from libraries as system headers to silence warnings from them
|
||||||
function(target_link_libraries_system target)
|
function(target_link_libraries_system target)
|
||||||
@ -135,6 +135,7 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/emu_jni.cpp
|
${source_DIR}/emu_jni.cpp
|
||||||
${source_DIR}/loader_jni.cpp
|
${source_DIR}/loader_jni.cpp
|
||||||
${source_DIR}/skyline/common.cpp
|
${source_DIR}/skyline/common.cpp
|
||||||
|
${source_DIR}/skyline/common/exception.cpp
|
||||||
${source_DIR}/skyline/common/logger.cpp
|
${source_DIR}/skyline/common/logger.cpp
|
||||||
${source_DIR}/skyline/common/settings.cpp
|
${source_DIR}/skyline/common/settings.cpp
|
||||||
${source_DIR}/skyline/common/signal.cpp
|
${source_DIR}/skyline/common/signal.cpp
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <compare>
|
#include <compare>
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
#include <common/exception.h>
|
||||||
#include <common/span.h>
|
#include <common/span.h>
|
||||||
#include <common/result.h>
|
#include <common/result.h>
|
||||||
#include <common/logger.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
|
* @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 <frozen/string.h>
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
#include "exception.h"
|
||||||
|
|
||||||
namespace skyline::util {
|
namespace skyline::util {
|
||||||
/**
|
/**
|
||||||
|
@ -92,12 +92,20 @@ namespace skyline::loader {
|
|||||||
size_t length{};
|
size_t length{};
|
||||||
std::unique_ptr<char, decltype(&std::free)> demangled{abi::__cxa_demangle(info.dli_sname, nullptr, &length, &status), std::free};
|
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)
|
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)
|
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);
|
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)
|
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
|
else
|
||||||
return fmt::format("\n* 0x{:X}", reinterpret_cast<uintptr_t>(pointer));
|
return fmt::format("\n* 0x{:X}", reinterpret_cast<uintptr_t>(pointer));
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,6 +50,7 @@ namespace skyline::nce {
|
|||||||
} else {
|
} else {
|
||||||
Logger::EmulationContext.Flush();
|
Logger::EmulationContext.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
abi::__cxa_end_catch(); // We call this prior to the longjmp to cause the exception object to be destroyed
|
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);
|
std::longjmp(state.thread->originalCtx, true);
|
||||||
} catch (const ExitException &e) {
|
} catch (const ExitException &e) {
|
||||||
@ -57,6 +58,19 @@ namespace skyline::nce {
|
|||||||
signal::BlockSignal({SIGINT});
|
signal::BlockSignal({SIGINT});
|
||||||
state.process->Kill(false);
|
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);
|
std::longjmp(state.thread->originalCtx, true);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
if (svc)
|
if (svc)
|
||||||
@ -70,6 +84,7 @@ namespace skyline::nce {
|
|||||||
signal::BlockSignal({SIGINT});
|
signal::BlockSignal({SIGINT});
|
||||||
state.process->Kill(false);
|
state.process->Kill(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
abi::__cxa_end_catch();
|
abi::__cxa_end_catch();
|
||||||
std::longjmp(state.thread->originalCtx, true);
|
std::longjmp(state.thread->originalCtx, true);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@ namespace skyline::service {
|
|||||||
TRACE_EVENT("service", perfetto::StaticString{function.name});
|
TRACE_EVENT("service", perfetto::StaticString{function.name});
|
||||||
try {
|
try {
|
||||||
return function(session, request, response);
|
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) {
|
} catch (const std::exception &e) {
|
||||||
throw exception("{} (Service: {})", e.what(), function.name);
|
throw exception("{} (Service: {})", e.what(), function.name);
|
||||||
}
|
}
|
||||||
|
@ -350,6 +350,11 @@ namespace skyline::soc::gm20b {
|
|||||||
signal::BlockSignal({SIGINT});
|
signal::BlockSignal({SIGINT});
|
||||||
state.process->Kill(false);
|
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) {
|
} catch (const std::exception &e) {
|
||||||
Logger::Error(e.what());
|
Logger::Error(e.what());
|
||||||
Logger::EmulationContext.Flush();
|
Logger::EmulationContext.Flush();
|
||||||
|
@ -130,6 +130,11 @@ namespace skyline::soc::host1x {
|
|||||||
signal::BlockSignal({SIGINT});
|
signal::BlockSignal({SIGINT});
|
||||||
state.process->Kill(false);
|
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) {
|
} catch (const std::exception &e) {
|
||||||
Logger::Error(e.what());
|
Logger::Error(e.what());
|
||||||
Logger::EmulationContext.Flush();
|
Logger::EmulationContext.Flush();
|
||||||
|
Loading…
Reference in New Issue
Block a user