mirror of
https://github.com/Dillonb/n64.git
synced 2024-05-17 03:20:34 -04:00
Support for saving crash dumps
This commit is contained in:
parent
06dd84da3f
commit
ca9585b526
|
@ -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
5
cmake/version.h.in
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
1
src/generated/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
version.h
|
70
src/system/crashdump.c
Normal file
70
src/system/crashdump.c
Normal 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
53
src/system/crashdump.h
Normal 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
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue