Support for saving crash dumps

This commit is contained in:
Dillon Beliveau 2023-08-27 20:40:51 -07:00
parent 06dd84da3f
commit ca9585b526
11 changed files with 231 additions and 18 deletions

View file

@ -46,6 +46,15 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules")
include(CTest)
message("CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}")
find_package(Git REQUIRED)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE _git_hash
OUTPUT_STRIP_TRAILING_WHITESPACE
)
check_c_compiler_flag("-mssse3" HAS_SSSE3)
check_c_compiler_flag("-msse4.1" HAS_SSE4_1)
@ -59,5 +68,7 @@ add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:${DEFAULT_C_COMP_OPTIONS}>")
add_link_options("$<$<COMPILE_LANGUAGE:C>:${DEFAULT_C_LINK_OPTIONS}>")
add_link_options("$<$<COMPILE_LANGUAGE:CXX>:${DEFAULT_C_LINK_OPTIONS}>")
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/version.h.in ${CMAKE_CURRENT_LIST_DIR}/src/generated/version.h)
add_subdirectory(src)
add_subdirectory(tests)

5
cmake/version.h.in Normal file
View file

@ -0,0 +1,5 @@
#ifndef N64_VERSION_H
#define N64_VERSION_H
#include <assert.h>
#define N64_GIT_COMMIT_HASH "${_git_hash}"
#endif // N64_VERSION_H

View file

@ -36,6 +36,7 @@ endif()
add_library(core
system/n64system.c system/n64system.h
system/crashdump.c system/crashdump.h
system/scheduler.c system/scheduler.h
system/scheduler_utils.c system/scheduler_utils.h

View file

@ -1,2 +1,75 @@
#include "log.h"
#include <SDL.h>
#include <SDL_syswm.h>
#include <stdarg.h>
#include <system/crashdump.h>
#include <generated/version.h>
unsigned int n64_log_verbosity = 0;
unsigned int next_n64_log_verbosity = 0;
#ifdef N64_WIN
#include <windows.h>
SDL_Window* get_window_handle();
void n64_error_messagebox(const char* message) {
SDL_Window* sdl_window = get_window_handle();
HWND handle = NULL;
if (sdl_window) {
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(sdl_window, &wmInfo);
handle = wmInfo.info.win.window;
}
size_t needed = snprintf(NULL, 0, "%s\n\nWould you like to save a crash dump?", message);
char* full_message = malloc(needed + 1);
snprintf(full_message, needed + 1, "%s\n\nWould you like to save a crash dump?", message);
int chosen = MessageBox(handle, full_message, "Fatal emulation error", MB_YESNO | MB_ICONERROR);
free(full_message);
full_message = NULL;
if (chosen == IDYES) {
const char* filename = n64_save_system_state();
const char* saved_fmt = "Saved to %s. Please zip this file and send it to me privately (I can be reached through Discord @dgb) or through GitHub Issues.";
needed = snprintf(NULL, 0, saved_fmt, filename);
char* saved_message = malloc(needed + 1);
snprintf(saved_message, needed + 1, saved_fmt, filename);
MessageBox(handle, saved_message, "Crash dump saved", MB_OK | MB_ICONINFORMATION);
free(saved_message);
saved_message = NULL;
}
}
#else
#define n64_error_messagebox(msg)
#endif
void handle_logfatal(const char* message, const char* file, int line, ...) {
size_t needed = snprintf(NULL, 0, "[FATAL] at %s:%d ", file, line);
char* prefix_buf = malloc(needed + 1);
snprintf(prefix_buf, needed + 1, "[FATAL] at %s:%d ", file, line);
va_list vargs;
va_start(vargs, line);
needed = vsnprintf(NULL, 0, message, vargs);
char* message_buf = malloc(needed + 1);
vsnprintf(message_buf, needed + 1, message, vargs);
needed = snprintf(NULL, 0, "%s%s", prefix_buf, message_buf);
char* everything_buf = malloc(needed + 1);
snprintf(everything_buf, needed + 1, "%s%s", prefix_buf, message_buf);
free(prefix_buf);
prefix_buf = NULL;
free(message_buf);
message_buf = NULL;
fprintf(stderr, "%s%s%s\n", COLOR_RED, everything_buf, COLOR_END);
n64_error_messagebox(everything_buf);
free(everything_buf);
everything_buf = NULL;
}

View file

@ -1,6 +1,10 @@
#ifndef __LOG_H__
#define __LOG_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
@ -36,9 +40,9 @@ extern unsigned int next_n64_log_verbosity;
#define update_delayed_log_verbosity() do {n64_log_verbosity = next_n64_log_verbosity;} while(0)
#define log_get_verbosity() n64_log_verbosity
void handle_logfatal(const char* message, const char* file, int line, ...);
#define logfatal(message,...) do { \
fprintf(stderr, COLOR_RED "[FATAL] at %s:%d ", __FILE__, __LINE__);\
fprintf(stderr, message "\n" COLOR_END, ##__VA_ARGS__);\
handle_logfatal(message, __FILE__, __LINE__, ##__VA_ARGS__); \
exit(EXIT_FAILURE);} while(0)
#define logdie(message,...) do { \
@ -65,4 +69,9 @@ extern unsigned int next_n64_log_verbosity;
#define logtrace(message,...) do {} while(0)
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,3 +1,4 @@
#include <generated/version.h>
#include <stdio.h>
#include <cflags.h>
#include <log.h>
@ -29,6 +30,7 @@ void sig_handler(int signum) {
#endif
int main(int argc, char** argv) {
logalways("Welcome to dgb-n64: built from commit %s", N64_GIT_COMMIT_HASH);
n64_settings_init();
#ifndef N64_WIN

1
src/generated/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
version.h

70
src/system/crashdump.c Normal file
View file

@ -0,0 +1,70 @@
#include "crashdump.h"
#include <dynarec/rsp_dynarec.h>
#include <generated/version.h>
#include <rsp.h>
const char* n64_save_system_state() {
n64_crashdump_t* crash_dump = malloc(sizeof(n64_crashdump_t));
memcpy(&crash_dump->git_commit_hash, N64_GIT_COMMIT_HASH, sizeof(N64_GIT_COMMIT_HASH));
memcpy(&crash_dump->system, &n64sys, sizeof(n64_system_t));
crash_dump->system_size = sizeof(n64_system_t);
crash_dump->system_base = (uintptr_t)&n64sys;
memcpy(&crash_dump->system, &n64sys, crash_dump->system_size);
crash_dump->cpu_size = sizeof(r4300i_t);
crash_dump->cpu_base = (uintptr_t)n64cpu_ptr;
memcpy(&crash_dump->cpu, n64cpu_ptr, crash_dump->cpu_size);
crash_dump->rsp_size = sizeof(rsp_t);
crash_dump->rsp_base = (uintptr_t)&n64rsp;
memcpy(&crash_dump->rsp, &n64rsp, crash_dump->rsp_size);
crash_dump->scheduler_size = sizeof(scheduler_t);
crash_dump->scheduler_base = (uintptr_t)&n64scheduler;
memcpy(&crash_dump->scheduler, &n64scheduler, crash_dump->scheduler_size);
crash_dump->dynarec_size = sizeof(n64_dynarec_t);
crash_dump->dynarec_base = (uintptr_t)&n64dynarec;
memcpy(&crash_dump->dynarec, &n64dynarec, crash_dump->dynarec_size);
crash_dump->rsp_dynarec_size = sizeof(rsp_dynarec_t);
crash_dump->rsp_dynarec_base = (uintptr_t)n64rsp.dynarec;
memcpy(&crash_dump->rsp_dynarec, n64rsp.dynarec, crash_dump->rsp_dynarec_size);
crash_dump->codecache_size = CODECACHE_SIZE;
crash_dump->codecache_base = (uintptr_t)n64dynarec.codecache;
memcpy(&crash_dump->cpu_codecache, n64dynarec.codecache, crash_dump->codecache_size);
crash_dump->rsp_codecache_size = RSP_CODECACHE_SIZE;
crash_dump->rsp_codecache_base = (uintptr_t)n64rsp.dynarec->codecache;
memcpy(&crash_dump->rsp_codecache, n64rsp.dynarec->codecache, crash_dump->rsp_codecache_size);
size_t needed = snprintf(NULL, 0, "%s.crashdump", n64sys.rom_path);
char* path_buf = malloc(needed + 1);
snprintf(path_buf, needed + 1, "%s.crashdump", n64sys.rom_path);
logalways("Saving to %s", path_buf);
FILE* f = fopen(path_buf, "wbx");
int suffix = 0;
while (f == NULL) {
logalways("Unable to create %s", path_buf);
free(path_buf);
needed = snprintf(NULL, 0, "%s.crashdump.%d", n64sys.rom_path, suffix);
path_buf = malloc(needed + 1);
snprintf(path_buf, needed + 1, "%s.crashdump.%d", n64sys.rom_path, suffix);
logalways("Trying %s", path_buf);
f = fopen(path_buf, "wbx");
suffix++;
}
fwrite(crash_dump, sizeof(n64_crashdump_t), 1, f);
logalways("Saved to %s", path_buf);
return path_buf;
}

53
src/system/crashdump.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef N64_CRASHDUMP_H
#define N64_CRASHDUMP_H
#include <dynarec/dynarec.h>
#include <system/n64system.h>
#include <system/scheduler.h>
#include <generated/version.h>
static_assert(sizeof(N64_GIT_COMMIT_HASH) == 41, "git commit hash is not the correct size (40 characters plus null terminator)");
static_assert(sizeof(uintptr_t) == sizeof(u64), "uintptr_t should be 64 bit");
typedef struct n64_crashdump {
char git_commit_hash[41];
size_t system_size;
uintptr_t system_base;
n64_system_t system;
size_t cpu_size;
uintptr_t cpu_base;
r4300i_t cpu;
size_t rsp_size;
uintptr_t rsp_base;
rsp_t rsp;
size_t scheduler_size;
uintptr_t scheduler_base;
scheduler_t scheduler;
size_t dynarec_size;
uintptr_t dynarec_base;
n64_dynarec_t dynarec;
size_t rsp_dynarec_size;
uintptr_t rsp_dynarec_base;
rsp_dynarec_t rsp_dynarec;
size_t codecache_size;
uintptr_t codecache_base;
u8 cpu_codecache[CODECACHE_SIZE];
size_t rsp_codecache_size;
uintptr_t rsp_codecache_base;
u8 rsp_codecache[RSP_CODECACHE_SIZE];
} n64_crashdump_t;
// Saves a crash dump to a file using the above structure
// Saves it to <rom path>.crashdump (will append .N if the file exists, where N is a number)
// Returns the filename saved to
const char* n64_save_system_state();
#endif // N64_CRASHDUMP_H

View file

@ -35,12 +35,8 @@ static bool should_quit = false;
n64_system_t n64sys;
// 32MiB codecache
#define CODECACHE_SIZE (1 << 25)
static u8 codecache[CODECACHE_SIZE] __attribute__((aligned(4096)));
// 32MiB RSP codecache
#define RSP_CODECACHE_SIZE (1 << 25)
static u8 rsp_codecache[RSP_CODECACHE_SIZE] __attribute__((aligned(4096)));
bool n64_should_quit() {

View file

@ -16,18 +16,10 @@ extern "C" {
#define CPU_CYCLES_PER_FRAME (CPU_HERTZ / n64sys.target_fps)
#define CYCLES_PER_INSTR 1
// The CPU runs at 93.75mhz. There are 60 frames per second, and 262 lines on the display.
// There are 1562500 cycles per frame.
// Because this doesn't divide nicely by 262, we have to run some lines for 1 more cycle than others.
// We call these the "long" lines, and the others the "short" lines.
// 5963*68+5964*194 == 1562500
#define NUM_SHORTLINES 68
#define NUM_LONGLINES 194
#define SHORTLINE_CYCLES 5963
#define LONGLINE_CYCLES 5964
// 32MiB codecache
#define CODECACHE_SIZE (1 << 25)
// 32MiB RSP codecache
#define RSP_CODECACHE_SIZE (1 << 25)
typedef enum n64_video_type {
UNKNOWN_VIDEO_TYPE,