mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 12:12:02 +00:00
Implemented exception handler.
The exception handler is capable of logging CPU registers and a stack trace using the current log implementation. Furthermore, if borealis has been initialized, it'll also display the PC register value using a CrashFrame. Otherwise, console output is used to display the same message. Other changes include: * utils: made utilsPrintConsoleError non-static. * utils: implemented a workaround to restore console output after initializing nxlink.
This commit is contained in:
parent
4bea23b758
commit
3f9cde8185
7 changed files with 332 additions and 44 deletions
|
@ -151,6 +151,9 @@ void utilsCreateDirectoryTree(const char *path, bool create_last_element);
|
||||||
/// Furthermore, if the full length for the generated path is >= FS_MAX_PATH, NULL will be returned.
|
/// Furthermore, if the full length for the generated path is >= FS_MAX_PATH, NULL will be returned.
|
||||||
char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension);
|
char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension);
|
||||||
|
|
||||||
|
/// Prints an error message using the standard console output and waits for the user to press a button.
|
||||||
|
void utilsPrintConsoleError(const char *msg);
|
||||||
|
|
||||||
/// Returns the current application updated state.
|
/// Returns the current application updated state.
|
||||||
bool utilsGetApplicationUpdatedState(void);
|
bool utilsGetApplicationUpdatedState(void);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
"line_07": "\uE016 The folks from NSWDB.COM and No-Intro.org, for being kind enough to put up public APIs to perform online checksum lookups.",
|
"line_07": "\uE016 The folks from NSWDB.COM and No-Intro.org, for being kind enough to put up public APIs to perform online checksum lookups.",
|
||||||
"line_08": "\uE016 The folks at the nxdumptool Discord server.",
|
"line_08": "\uE016 The folks at the nxdumptool Discord server.",
|
||||||
"line_09": "\uE016 The Comfy Boyes, for always being awesome and supportive. You know who you are.",
|
"line_09": "\uE016 The Comfy Boyes, for always being awesome and supportive. You know who you are.",
|
||||||
"line_10": "\uE016 My girlfriend, for always being by my side and motivating me to keep working on all my projects. I love you. \uE87D",
|
"line_10": "\uE016 Andreína, for always being by my side and motivating me to keep working on all my projects.",
|
||||||
"line_11": "\uE016 And, at last but not least, you! Thank you for using my work!"
|
"line_11": "\uE016 And, at last but not least, you! Thank you for using my work!"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,8 @@
|
||||||
"__time_format_comment__": "Use 12 for a 12-hour clock, or 24 for a 24-hour clock",
|
"__time_format_comment__": "Use 12 for a 12-hour clock, or 24 for a 24-hour clock",
|
||||||
|
|
||||||
"date": "{1:02d}/{2:02d}/{0} {3:02d}:{4:02d}:{5:02d} {6}",
|
"date": "{1:02d}/{2:02d}/{0} {3:02d}:{4:02d}:{5:02d} {6}",
|
||||||
"__date_comment__": "{0} = Year, {1} = Month, {2} = Day, {3} = Hour, {4} = Minute, {5} = Second, {6} = AM/PM (if time_format is set to 12)"
|
"__date_comment__": "{0} = Year, {1} = Month, {2} = Day, {3} = Hour, {4} = Minute, {5} = Second, {6} = AM/PM (if time_format is set to 12)",
|
||||||
|
|
||||||
|
"libnx_abort": "Fatal error triggered in libnx!\nError code: 0x{:08X}.",
|
||||||
|
"exception_triggered": "Fatal exception triggered!\nReason: {} (0x{:X})."
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ static AppletHookCookie g_systemOverclockCookie = {0};
|
||||||
|
|
||||||
static bool g_longRunningProcess = false;
|
static bool g_longRunningProcess = false;
|
||||||
|
|
||||||
static int g_nxLinkSocketFd = -1;
|
static int g_stdoutFd = -1, g_stderrFd = -1, g_nxLinkSocketFd = -1;
|
||||||
|
|
||||||
static const char *g_sizeSuffixes[] = { "B", "KiB", "MiB", "GiB" };
|
static const char *g_sizeSuffixes[] = { "B", "KiB", "MiB", "GiB" };
|
||||||
static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes);
|
static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes);
|
||||||
|
@ -106,7 +106,8 @@ static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param);
|
||||||
|
|
||||||
static void utilsChangeHomeButtonBlockStatus(bool block);
|
static void utilsChangeHomeButtonBlockStatus(bool block);
|
||||||
|
|
||||||
static void utilsPrintConsoleError(void);
|
NX_INLINE void utilsCloseFileDescriptor(int *fd);
|
||||||
|
static void utilsRestoreConsoleOutput(void);
|
||||||
|
|
||||||
static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos);
|
static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos);
|
||||||
|
|
||||||
|
@ -120,6 +121,9 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
||||||
ret = g_resourcesInit;
|
ret = g_resourcesInit;
|
||||||
if (ret) break;
|
if (ret) break;
|
||||||
|
|
||||||
|
/* Lock applet exit. */
|
||||||
|
appletLockExit();
|
||||||
|
|
||||||
/* Retrieve pointer to the application launch path. */
|
/* Retrieve pointer to the application launch path. */
|
||||||
_utilsGetLaunchPath(program_argc, program_argv);
|
_utilsGetLaunchPath(program_argc, program_argv);
|
||||||
|
|
||||||
|
@ -132,7 +136,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
||||||
|
|
||||||
/* Create logfile. */
|
/* Create logfile. */
|
||||||
logWriteStringToLogFile("________________________________________________________________\r\n");
|
logWriteStringToLogFile("________________________________________________________________\r\n");
|
||||||
LOG_MSG(APP_TITLE " v%u.%u.%u starting (" GIT_REV "). Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
|
LOG_MSG(APP_TITLE " v" APP_VERSION " starting (" GIT_REV "). Built on " __DATE__ " - " __TIME__ ".");
|
||||||
|
|
||||||
/* Log Horizon OS version. */
|
/* Log Horizon OS version. */
|
||||||
u32 hos_version = hosversionGet();
|
u32 hos_version = hosversionGet();
|
||||||
|
@ -237,14 +241,33 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
||||||
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
|
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redirect stdout and stderr over network to nxlink. */
|
/* Duplicate the stdout and stderr file handles, then redirect stdout and stderr over network to nxlink. */
|
||||||
|
g_stdoutFd = dup(STDOUT_FILENO);
|
||||||
|
g_stderrFd = dup(STDERR_FILENO);
|
||||||
g_nxLinkSocketFd = nxlinkConnectToHost(true, true);
|
g_nxLinkSocketFd = nxlinkConnectToHost(true, true);
|
||||||
|
|
||||||
/* Update flags. */
|
/* Update flags. */
|
||||||
ret = g_resourcesInit = true;
|
ret = g_resourcesInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret) utilsPrintConsoleError();
|
if (!ret)
|
||||||
|
{
|
||||||
|
size_t msg_size = 0;
|
||||||
|
char log_msg[0x100] = {0}, *msg = NULL;
|
||||||
|
|
||||||
|
/* Get last log message. */
|
||||||
|
logGetLastMessage(log_msg, sizeof(log_msg));
|
||||||
|
|
||||||
|
/* Generate error message. */
|
||||||
|
utilsAppendFormattedStringToBuffer(&msg, &msg_size, "An error occurred while initializing resources.");
|
||||||
|
if (*log_msg) utilsAppendFormattedStringToBuffer(&msg, &msg_size, "\n\n%s", log_msg);
|
||||||
|
|
||||||
|
/* Print error message. */
|
||||||
|
utilsPrintConsoleError(msg);
|
||||||
|
|
||||||
|
/* Free error message. */
|
||||||
|
if (msg) free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -253,12 +276,8 @@ void utilsCloseResources(void)
|
||||||
{
|
{
|
||||||
SCOPED_LOCK(&g_resourcesMutex)
|
SCOPED_LOCK(&g_resourcesMutex)
|
||||||
{
|
{
|
||||||
/* Close nxlink socket. */
|
/* Restore console output. */
|
||||||
if (g_nxLinkSocketFd >= 0)
|
utilsRestoreConsoleOutput();
|
||||||
{
|
|
||||||
close(g_nxLinkSocketFd);
|
|
||||||
g_nxLinkSocketFd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unset long running process state. */
|
/* Unset long running process state. */
|
||||||
utilsSetLongRunningProcessState(false);
|
utilsSetLongRunningProcessState(false);
|
||||||
|
@ -316,6 +335,9 @@ void utilsCloseResources(void)
|
||||||
/* Close logfile. */
|
/* Close logfile. */
|
||||||
logCloseLogFile();
|
logCloseLogFile();
|
||||||
|
|
||||||
|
/* Unlock applet exit. */
|
||||||
|
appletUnlockExit();
|
||||||
|
|
||||||
g_resourcesInit = false;
|
g_resourcesInit = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -836,6 +858,48 @@ end:
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void utilsPrintConsoleError(const char *msg)
|
||||||
|
{
|
||||||
|
PadState pad = {0};
|
||||||
|
|
||||||
|
/* Don't consider stick movement as button inputs. */
|
||||||
|
u64 flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \
|
||||||
|
HidNpadButton_StickRUp | HidNpadButton_StickRDown);
|
||||||
|
|
||||||
|
/* Configure input. */
|
||||||
|
/* Up to 8 different, full controller inputs. */
|
||||||
|
/* Individual Joy-Cons not supported. */
|
||||||
|
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
|
||||||
|
padInitializeWithMask(&pad, 0x1000000FFUL);
|
||||||
|
|
||||||
|
/* Restore console output. */
|
||||||
|
utilsRestoreConsoleOutput();
|
||||||
|
|
||||||
|
/* Initialize console output. */
|
||||||
|
consoleInit(NULL);
|
||||||
|
|
||||||
|
/* Print message. */
|
||||||
|
if (msg && *msg)
|
||||||
|
{
|
||||||
|
printf("%s", msg);
|
||||||
|
} else {
|
||||||
|
printf("An error occurred.");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n\nFor more information, please check the logfile. Press any button to exit.");
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
/* Wait until the user presses a button. */
|
||||||
|
while(appletMainLoop())
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
if (padGetButtonsDown(&pad) & flag) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deinitialize console output. */
|
||||||
|
consoleExit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
bool utilsGetApplicationUpdatedState(void)
|
bool utilsGetApplicationUpdatedState(void)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
@ -1078,39 +1142,31 @@ static void utilsChangeHomeButtonBlockStatus(bool block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void utilsPrintConsoleError(void)
|
NX_INLINE void utilsCloseFileDescriptor(int *fd)
|
||||||
{
|
{
|
||||||
PadState pad = {0};
|
if (!fd || *fd < 0) return;
|
||||||
char msg[0x100] = {0};
|
close(*fd);
|
||||||
|
*fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Don't consider stick movement as button inputs. */
|
static void utilsRestoreConsoleOutput(void)
|
||||||
u64 flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \
|
{
|
||||||
HidNpadButton_StickRUp | HidNpadButton_StickRDown);
|
SCOPED_LOCK(&g_resourcesMutex)
|
||||||
|
|
||||||
/* Configure input. */
|
|
||||||
/* Up to 8 different, full controller inputs. */
|
|
||||||
/* Individual Joy-Cons not supported. */
|
|
||||||
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
|
|
||||||
padInitializeWithMask(&pad, 0x1000000FFUL);
|
|
||||||
|
|
||||||
/* Get last log message. */
|
|
||||||
logGetLastMessage(msg, sizeof(msg));
|
|
||||||
|
|
||||||
consoleInit(NULL);
|
|
||||||
|
|
||||||
printf("An error occurred while initializing resources.\n\n");
|
|
||||||
if (*msg) printf("%s\n\n", msg);
|
|
||||||
printf("For more information, please check the logfile. Press any button to exit.");
|
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
|
||||||
|
|
||||||
while(appletMainLoop())
|
|
||||||
{
|
{
|
||||||
padUpdate(&pad);
|
if (g_nxLinkSocketFd >= 0) utilsCloseFileDescriptor(&g_nxLinkSocketFd);
|
||||||
if (padGetButtonsDown(&pad) & flag) break;
|
|
||||||
|
if (g_stdoutFd >= 0)
|
||||||
|
{
|
||||||
|
dup2(g_stdoutFd, STDOUT_FILENO);
|
||||||
|
utilsCloseFileDescriptor(&g_stdoutFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
consoleExit(NULL);
|
if (g_stderrFd >= 0)
|
||||||
|
{
|
||||||
|
dup2(g_stderrFd, STDERR_FILENO);
|
||||||
|
utilsCloseFileDescriptor(&g_stderrFd);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos)
|
static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos)
|
||||||
|
|
|
@ -846,7 +846,7 @@ void titleFreeUserApplicationData(TitleUserApplicationData *user_app_data)
|
||||||
bool titleAreOrphanTitlesAvailable(void)
|
bool titleAreOrphanTitlesAvailable(void)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
SCOPED_LOCK(&g_titleMutex) ret = (g_titleInterfaceInit && g_orphanTitleInfo && *g_orphanTitleInfo && g_orphanTitleInfoCount > 0);
|
SCOPED_TRY_LOCK(&g_titleMutex) ret = (g_titleInterfaceInit && g_orphanTitleInfo && *g_orphanTitleInfo && g_orphanTitleInfoCount > 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
223
source/exception_handler.cpp
Normal file
223
source/exception_handler.cpp
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* exception_handler.cpp
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||||
|
* Copyright (c) 2019-2020, WerWolv.
|
||||||
|
*
|
||||||
|
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||||
|
* Loosely based on debug_helpers.cpp from EdiZon-Rewrite.
|
||||||
|
*
|
||||||
|
* nxdumptool is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* nxdumptool is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nxdt_utils.h>
|
||||||
|
#include <borealis.hpp>
|
||||||
|
|
||||||
|
#define FP_MASK 0xFFFFFFFFFF000000UL
|
||||||
|
#define STACK_TRACE_SIZE 0x20
|
||||||
|
#define IS_HB_ADDR(x) (info.addr && info.size && (x) >= info.addr && (x) < (info.addr + info.size))
|
||||||
|
|
||||||
|
namespace i18n = brls::i18n; /* For getStr(). */
|
||||||
|
using namespace i18n::literals; /* For _i18n. */
|
||||||
|
|
||||||
|
extern bool g_borealisInitialized;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
/* Overrides for libnx weak symbols. */
|
||||||
|
u32 __nx_applet_exit_mode = 0;
|
||||||
|
alignas(16) u8 __nx_exception_stack[0x8000];
|
||||||
|
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nxdt::utils {
|
||||||
|
static void GetHomebrewMemoryInfo(MemoryInfo *out)
|
||||||
|
{
|
||||||
|
if (!out)
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid parameters!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 p = 0;
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
/* Find the memory region in which this function is stored. */
|
||||||
|
/* The start of it will be the base address the homebrew was mapped to. */
|
||||||
|
rc = svcQueryMemory(out, &p, static_cast<u64>(reinterpret_cast<uintptr_t>(&GetHomebrewMemoryInfo)));
|
||||||
|
if (R_FAILED(rc)) LOG_MSG("svcQueryMemory failed! (0x%08X).", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool UnwindStack(u64 *out_stack_trace, u32 *out_stack_trace_size, size_t max_stack_trace_size, u64 cur_fp)
|
||||||
|
{
|
||||||
|
if (!out_stack_trace || !out_stack_trace_size || !max_stack_trace_size || !cur_fp)
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid parameters!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StackFrame {
|
||||||
|
u64 fp; ///< Frame Pointer (pointer to previous stack frame).
|
||||||
|
u64 lr; ///< Link Register (return address).
|
||||||
|
};
|
||||||
|
|
||||||
|
*out_stack_trace_size = 0;
|
||||||
|
u64 fp_base = (cur_fp & FP_MASK);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < max_stack_trace_size; i++)
|
||||||
|
{
|
||||||
|
/* Last check fixes a crash while dealing with certain stack frame addresses. */
|
||||||
|
if (!cur_fp || (cur_fp % sizeof(u64)) || (cur_fp & FP_MASK) != fp_base) break;
|
||||||
|
|
||||||
|
auto cur_trace = reinterpret_cast<StackFrame*>(cur_fp);
|
||||||
|
out_stack_trace[(*out_stack_trace_size)++] = cur_trace->lr;
|
||||||
|
cur_fp = cur_trace->fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*out_stack_trace_size > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NORETURN AbortProgramExecution(std::string str)
|
||||||
|
{
|
||||||
|
if (g_borealisInitialized)
|
||||||
|
{
|
||||||
|
/* Display crash frame and exit Borealis. */
|
||||||
|
brls::Application::crash(str);
|
||||||
|
while(brls::Application::mainLoop());
|
||||||
|
} else {
|
||||||
|
/* Print error message using console output. */
|
||||||
|
utilsPrintConsoleError(str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up resources. */
|
||||||
|
utilsCloseResources();
|
||||||
|
|
||||||
|
/* Exit application. */
|
||||||
|
__nx_applet_exit_mode = 1;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
/* libnx abort function override. */
|
||||||
|
void diagAbortWithResult(Result res)
|
||||||
|
{
|
||||||
|
/* Log error. */
|
||||||
|
LOG_MSG("*** libnx aborted with error code: 0x%08X ***", res);
|
||||||
|
|
||||||
|
/* Abort program execution. */
|
||||||
|
std::string crash_str = (g_borealisInitialized ? i18n::getStr("generic/libnx_abort"_i18n, res) : fmt::format("Fatal error triggered in libnx!\nError code: 0x{:08X}.", res));
|
||||||
|
nxdt::utils::AbortProgramExecution(crash_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* libnx exception handler override. */
|
||||||
|
void __libnx_exception_handler(ThreadExceptionDump *ctx)
|
||||||
|
{
|
||||||
|
MemoryInfo info = {0};
|
||||||
|
u32 stack_trace_size = 0;
|
||||||
|
u64 stack_trace[STACK_TRACE_SIZE] = {0};
|
||||||
|
|
||||||
|
char *exception_str = NULL;
|
||||||
|
size_t exception_str_size = 0;
|
||||||
|
|
||||||
|
std::string error_desc_str, crash_str;
|
||||||
|
|
||||||
|
/* Get homebrew memory info. */
|
||||||
|
nxdt::utils::GetHomebrewMemoryInfo(&info);
|
||||||
|
|
||||||
|
/* Log exception type. */
|
||||||
|
LOG_MSG("*** Exception Triggered ***");
|
||||||
|
|
||||||
|
switch(ctx->error_desc)
|
||||||
|
{
|
||||||
|
case ThreadExceptionDesc_InstructionAbort:
|
||||||
|
error_desc_str = "Instruction Abort";
|
||||||
|
break;
|
||||||
|
case ThreadExceptionDesc_MisalignedPC:
|
||||||
|
error_desc_str = "Misaligned Program Counter";
|
||||||
|
break;
|
||||||
|
case ThreadExceptionDesc_MisalignedSP:
|
||||||
|
error_desc_str = "Misaligned Stack Pointer";
|
||||||
|
break;
|
||||||
|
case ThreadExceptionDesc_SError:
|
||||||
|
error_desc_str = "SError";
|
||||||
|
break;
|
||||||
|
case ThreadExceptionDesc_BadSVC:
|
||||||
|
error_desc_str = "Bad SVC";
|
||||||
|
break;
|
||||||
|
case ThreadExceptionDesc_Trap:
|
||||||
|
error_desc_str = "SIGTRAP";
|
||||||
|
break;
|
||||||
|
case ThreadExceptionDesc_Other:
|
||||||
|
error_desc_str = "Segmentation Fault";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_desc_str = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "Type: %s (0x%X)\r\n", error_desc_str.c_str(), ctx->error_desc);
|
||||||
|
|
||||||
|
/* Log CPU registers. */
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "Registers:");
|
||||||
|
|
||||||
|
for(size_t i = 0; i < MAX_ELEMENTS(ctx->cpu_gprs); i++)
|
||||||
|
{
|
||||||
|
u64 reg = ctx->cpu_gprs[i].x;
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n X%02lu: 0x%lX", i, reg);
|
||||||
|
if (IS_HB_ADDR(reg)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", reg - info.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n FP: 0x%lX", ctx->fp.x);
|
||||||
|
if (IS_HB_ADDR(ctx->fp.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->fp.x - info.addr);
|
||||||
|
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n LR: 0x%lX", ctx->lr.x);
|
||||||
|
if (IS_HB_ADDR(ctx->lr.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->lr.x - info.addr);
|
||||||
|
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n SP: 0x%lX", ctx->sp.x);
|
||||||
|
if (IS_HB_ADDR(ctx->sp.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->sp.x - info.addr);
|
||||||
|
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n PC: 0x%lX", ctx->pc.x);
|
||||||
|
if (IS_HB_ADDR(ctx->pc.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->pc.x - info.addr);
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n");
|
||||||
|
|
||||||
|
/* Unwind stack. */
|
||||||
|
if (nxdt::utils::UnwindStack(stack_trace, &stack_trace_size, STACK_TRACE_SIZE, ctx->fp.x))
|
||||||
|
{
|
||||||
|
/* Log stack trace. */
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "Stack Trace:");
|
||||||
|
|
||||||
|
for(u32 i = 0; i < stack_trace_size; i++)
|
||||||
|
{
|
||||||
|
u64 addr = stack_trace[i];
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n [%02u]: 0x%lX", stack_trace_size - i - 1, addr);
|
||||||
|
if (IS_HB_ADDR(addr)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", addr - info.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write log string. */
|
||||||
|
logWriteStringToLogFile(exception_str);
|
||||||
|
|
||||||
|
/* Free exception info string. */
|
||||||
|
if (exception_str) free(exception_str);
|
||||||
|
|
||||||
|
/* Abort program execution. */
|
||||||
|
crash_str = (g_borealisInitialized ? i18n::getStr("generic/exception_triggered"_i18n, error_desc_str, ctx->error_desc) : fmt::format("Fatal exception triggered!\nReason: {} (0x{:X}).", error_desc_str, ctx->error_desc));
|
||||||
|
crash_str += (fmt::format("\nPC: 0x{:X}", ctx->pc.x) + (IS_HB_ADDR(ctx->pc.x) ? fmt::format(" (BASE + 0x{:X}).", ctx->pc.x - info.addr) : "."));
|
||||||
|
nxdt::utils::AbortProgramExecution(crash_str);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
using namespace brls::i18n::literals; /* For _i18n. */
|
using namespace brls::i18n::literals; /* For _i18n. */
|
||||||
|
|
||||||
|
bool g_borealisInitialized = false;
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
/* Set scope guard to clean up resources at exit. */
|
/* Set scope guard to clean up resources at exit. */
|
||||||
|
@ -45,6 +47,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
/* Initialize Borealis. */
|
/* Initialize Borealis. */
|
||||||
if (!brls::Application::init(APP_TITLE)) return EXIT_FAILURE;
|
if (!brls::Application::init(APP_TITLE)) return EXIT_FAILURE;
|
||||||
|
g_borealisInitialized = true;
|
||||||
|
|
||||||
/* Check if we're running under applet mode. */
|
/* Check if we're running under applet mode. */
|
||||||
if (utilsAppletModeCheck())
|
if (utilsAppletModeCheck())
|
||||||
|
|
Loading…
Reference in a new issue