diff --git a/build.bat b/build.bat index ca9a98ac..6579a826 100644 --- a/build.bat +++ b/build.bat @@ -83,7 +83,7 @@ pushd build popd :: --- Get Current Git Commit Id ---------------------------------------------- -for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DRADDBG_GIT=\"%%i\" +for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\" :: --- Build & Run Metaprogram ------------------------------------------------ if "%no_meta%"=="1" echo [skipping metagen] diff --git a/src/base/base_command_line.c b/src/base/base_command_line.c index 2a40b5c8..1bf3312d 100644 --- a/src/base/base_command_line.c +++ b/src/base/base_command_line.c @@ -82,6 +82,7 @@ internal CmdLine cmd_line_from_string_list(Arena *arena, String8List command_line) { CmdLine parsed = {0}; + parsed.exe_name = command_line.first->string; // NOTE(rjf): Set up config option table. { diff --git a/src/base/base_command_line.h b/src/base/base_command_line.h index bfbc35ef..b18fc874 100644 --- a/src/base/base_command_line.h +++ b/src/base/base_command_line.h @@ -29,6 +29,7 @@ struct CmdLineOptList typedef struct CmdLine CmdLine; struct CmdLine { + String8 exe_name; CmdLineOptList options; String8List inputs; U64 option_table_size; diff --git a/src/base/base_context_cracking.h b/src/base/base_context_cracking.h index e9b2b8cc..f9f1a782 100644 --- a/src/base/base_context_cracking.h +++ b/src/base/base_context_cracking.h @@ -142,10 +142,52 @@ # define BUILD_SUPPLEMENTARY_UNIT 0 #endif +#if !defined(BUILD_ENTRY_DEFINING_UNIT) +# define BUILD_ENTRY_DEFINING_UNIT 1 +#endif + #if !defined(BUILD_CONSOLE_INTERFACE) # define BUILD_CONSOLE_INTERFACE 0 #endif +#if !defined(BUILD_VERSION_MAJOR) +# define BUILD_VERSION_MAJOR 0 +#endif + +#if !defined(BUILD_VERSION_MINOR) +# define BUILD_VERSION_MINOR 0 +#endif + +#if !defined(BUILD_VERSION_PATCH) +# define BUILD_VERSION_PATCH 0 +#endif + +#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH) +#if BUILD_DEBUG +# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]" +#else +# define BUILD_MODE_STRING_LITERAL_APPEND "" +#endif +#if defined(BUILD_GIT_HASH) +# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]" +#else +# define BUILD_GIT_HASH_STRING_LITERAL_APPEND "" +#endif + +#if !defined(BUILD_TITLE) +# define BUILD_TITLE "Untitled" +#endif + +#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL) +# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#endif + +#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL) +# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGames/raddebugger/issues" +#endif + +#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND + //////////////////////////////// //~ rjf: Zero All Undefined Options diff --git a/src/df/core/df_core.c b/src/df/core/df_core.c index fb30d332..f19a43d4 100644 --- a/src/df/core/df_core.c +++ b/src/df/core/df_core.c @@ -8496,7 +8496,7 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) case DF_CoreCmdKind_RegisterAsJITDebugger: { #if OS_WINDOWS - String8 path_to_debugger_binary = os_get_command_line_arguments().first->string; + String8 path_to_debugger_binary = os_string_from_system_path(scratch.arena, OS_SystemPath_Binary); String8 name8 = str8_lit("Debugger"); String8 data8 = push_str8f(scratch.arena, "%S --jit_pid:%%ld --jit_code:%%ld --jit_addr:0x%%p", path_to_debugger_binary); String16 name16 = str16_from_8(scratch.arena, name8); diff --git a/src/df/gfx/df_gfx.c b/src/df/gfx/df_gfx.c index a7a8e9ad..3bd9d716 100644 --- a/src/df/gfx/df_gfx.c +++ b/src/df/gfx/df_gfx.c @@ -945,7 +945,7 @@ df_window_open(Vec2F32 size, OS_Handle preferred_monitor, DF_CfgSrc cfg_src) window->cfg_src = cfg_src; window->arena = arena_alloc(); { - String8 title = str8_lit_comp(RADDBG_TITLE_STRING_LITERAL); + String8 title = str8_lit_comp(BUILD_TITLE_STRING_LITERAL); window->os = os_window_open(size, title); } window->r = r_window_equip(window->os); @@ -4267,7 +4267,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D UI_CtxMenu(help_menu_key) UI_PrefWidth(ui_em(40.f, 1.f)) { UI_Row UI_TextAlignment(UI_TextAlign_Center) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) - ui_label(str8_lit(RADDBG_TITLE_STRING_LITERAL)); + ui_label(str8_lit(BUILD_TITLE_STRING_LITERAL)); ui_spacer(ui_em(0.25f, 1.f)); UI_Row UI_PrefWidth(ui_text_dim(10, 1)) diff --git a/src/os/core/linux/os_core_linux.c b/src/os/core/linux/os_core_linux.c index b6730957..f8b07e81 100644 --- a/src/os/core/linux/os_core_linux.c +++ b/src/os/core/linux/os_core_linux.c @@ -813,7 +813,7 @@ lnx_safe_call_sig_handler(int){ //~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) internal void -os_init(int argc, char **argv) +os_init(void) { // NOTE(allen): Initialize linux layer mutex { diff --git a/src/os/core/os_core.c b/src/os/core/os_core.c index db67d314..b3cad911 100644 --- a/src/os/core/os_core.c +++ b/src/os/core/os_core.c @@ -67,19 +67,6 @@ os_string_list_from_argcv(Arena *arena, int argc, char **argv) return result; } -//////////////////////////////// -//~ rjf: Process Helpers (Helper, Implemented Once) - -internal void -os_relaunch_self(void){ - Temp scratch = scratch_begin(0, 0); - OS_LaunchOptions opts = {0}; - opts.cmd_line = os_get_command_line_arguments(); - opts.path = os_string_from_system_path(scratch.arena, OS_SystemPath_Initial); - os_launch_process(&opts, 0); - scratch_end(scratch); -} - //////////////////////////////// //~ rjf: Filesystem Helpers (Helpers, Implemented Once) diff --git a/src/os/core/os_core.h b/src/os/core/os_core.h index a162576a..ea82c9d2 100644 --- a/src/os/core/os_core.h +++ b/src/os/core/os_core.h @@ -165,11 +165,6 @@ internal String8 os_string_from_system_path(Arena *arena, OS_SystemPath path); internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv); -//////////////////////////////// -//~ rjf: Process Helpers (Helper, Implemented Once) - -internal void os_relaunch_self(void); - //////////////////////////////// //~ rjf: Filesystem Helpers (Helpers, Implemented Once) @@ -204,7 +199,7 @@ internal void os_condition_variable_broadcast(OS_Handle cv); //////////////////////////////// //~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) -internal void os_init(int argc, char **argv); +internal void os_init(void); //////////////////////////////// //~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) @@ -234,7 +229,6 @@ internal U64 os_logical_core_count(void); //////////////////////////////// //~ rjf: @os_hooks Process & Thread Info (Implemented Per-OS) -internal String8List os_get_command_line_arguments(void); internal S32 os_get_pid(void); internal S32 os_get_tid(void); internal String8List os_get_environment(void); @@ -370,4 +364,15 @@ internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *f internal OS_Guid os_make_guid(void); internal String8 os_string_from_guid(Arena *arena, OS_Guid guid); +//////////////////////////////// +//~ rjf: @os_hooks Entry Points (Implemented Per-OS) + +// NOTE(rjf): The implementation of `os_core` will define low-level entry +// points if BUILD_ENTRY_DEFINING_UNIT is defined to 1. These will call +// into the standard codebase program entry points, named "entry_point". + +#if BUILD_ENTRY_DEFINING_UNIT +internal void entry_point(CmdLine *cmdline); +#endif + #endif // OS_CORE_H diff --git a/src/os/core/win32/os_core_win32.c b/src/os/core/win32/os_core_win32.c index 49b5c444..8ad5b58f 100644 --- a/src/os/core/win32/os_core_win32.c +++ b/src/os/core/win32/os_core_win32.c @@ -185,7 +185,8 @@ w32_thread_base(void *ptr){ //~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) internal void -os_init(int argc, char **argv){ +os_init(void) +{ // Load Fancy Memory Functions { HMODULE module = LoadLibraryA("kernel32.dll"); @@ -214,9 +215,6 @@ os_init(int argc, char **argv){ // Setup initial path w32_initial_path = os_string_from_system_path(w32_perm_arena, OS_SystemPath_Current); - // Setup command line arguments - w32_cmd_line_args = os_string_list_from_argcv(w32_perm_arena, argc, argv); - // rjf: setup environment variables { CHAR *this_proc_env = GetEnvironmentStrings(); @@ -470,12 +468,6 @@ os_logical_core_count(void) //////////////////////////////// //~ rjf: @os_hooks Process Info (Implemented Per-OS) -internal String8List -os_get_command_line_arguments(void) -{ - return w32_cmd_line_args; -} - internal S32 os_get_pid(void){ DWORD id = GetCurrentProcessId(); @@ -1555,7 +1547,8 @@ os_make_guid(void) OS_Guid result; MemoryZeroStruct(&result); UUID uuid; RPC_STATUS rpc_status = UuidCreate(&uuid); - if (rpc_status == RPC_S_OK) { + if(rpc_status == RPC_S_OK) + { result.data1 = uuid.Data1; result.data2 = uuid.Data2; result.data3 = uuid.Data3; @@ -1564,3 +1557,244 @@ os_make_guid(void) return result; } +//////////////////////////////// +//~ rjf: @os_hooks Entry Points (Implemented Per-OS) + +#include +#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside +#include + +internal B32 win32_g_is_quiet = 0; + +internal HRESULT WINAPI +win32_dialog_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LONG_PTR data) +{ + if(msg == TDN_HYPERLINK_CLICKED) + { + ShellExecuteW(NULL, L"open", (LPWSTR)lparam, NULL, NULL, SW_SHOWNORMAL); + } + return S_OK; +} + +internal LONG WINAPI +win32_exception_filter(EXCEPTION_POINTERS* exception_ptrs) +{ + if(win32_g_is_quiet) + { + ExitProcess(1); + } + + static volatile LONG first = 0; + if(InterlockedCompareExchange(&first, 1, 0) != 0) + { + // prevent failures in other threads to popup same message box + // this handler just shows first thread that crashes + // we are terminating afterwards anyway + for (;;) Sleep(1000); + } + + WCHAR buffer[4096] = {0}; + int buflen = 0; + + DWORD exception_code = exception_ptrs->ExceptionRecord->ExceptionCode; + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"A fatal exception (code 0x%x) occurred. The process is terminating.\n", exception_code); + + // load dbghelp dynamically just in case if it is missing + HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); + if(dbghelp) + { + DWORD (WINAPI *dbg_SymSetOptions)(DWORD SymOptions); + BOOL (WINAPI *dbg_SymInitializeW)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess); + BOOL (WINAPI *dbg_StackWalk64)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, + LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); + PVOID (WINAPI *dbg_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); + DWORD64 (WINAPI *dbg_SymGetModuleBase64)(HANDLE hProcess, DWORD64 qwAddr); + BOOL (WINAPI *dbg_SymFromAddrW)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFOW Symbol); + BOOL (WINAPI *dbg_SymGetLineFromAddrW64)(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINEW64 Line); + BOOL (WINAPI *dbg_SymGetModuleInfoW64)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULEW64 ModuleInfo); + + *(FARPROC*)&dbg_SymSetOptions = GetProcAddress(dbghelp, "SymSetOptions"); + *(FARPROC*)&dbg_SymInitializeW = GetProcAddress(dbghelp, "SymInitializeW"); + *(FARPROC*)&dbg_StackWalk64 = GetProcAddress(dbghelp, "StackWalk64"); + *(FARPROC*)&dbg_SymFunctionTableAccess64 = GetProcAddress(dbghelp, "SymFunctionTableAccess64"); + *(FARPROC*)&dbg_SymGetModuleBase64 = GetProcAddress(dbghelp, "SymGetModuleBase64"); + *(FARPROC*)&dbg_SymFromAddrW = GetProcAddress(dbghelp, "SymFromAddrW"); + *(FARPROC*)&dbg_SymGetLineFromAddrW64 = GetProcAddress(dbghelp, "SymGetLineFromAddrW64"); + *(FARPROC*)&dbg_SymGetModuleInfoW64 = GetProcAddress(dbghelp, "SymGetModuleInfoW64"); + + if(dbg_SymSetOptions && dbg_SymInitializeW && dbg_StackWalk64 && dbg_SymFunctionTableAccess64 && dbg_SymGetModuleBase64 && dbg_SymFromAddrW && dbg_SymGetLineFromAddrW64 && dbg_SymGetModuleInfoW64) + { + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + CONTEXT* context = exception_ptrs->ContextRecord; + + dbg_SymSetOptions(SYMOPT_EXACT_SYMBOLS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); + if(dbg_SymInitializeW(process, L"", TRUE)) + { + // check that raddbg.pdb file is good + B32 raddbg_pdb_valid = 0; + { + IMAGEHLP_MODULEW64 module = {0}; + module.SizeOfStruct = sizeof(module); + if(dbg_SymGetModuleInfoW64(process, (DWORD64)&win32_exception_filter, &module)) + { + raddbg_pdb_valid = (module.SymType == SymPdb); + } + } + + if(!raddbg_pdb_valid) + { + buflen += wnsprintfW(buffer + buflen, sizeof(buffer) - buflen, + L"\nThe PDB debug information file for this executable is not valid or was not found. Please rebuild binary to get call stack.\n"); + } + else + { + STACKFRAME64 frame = {0}; + DWORD image_type; +#if defined(_M_AMD64) + image_type = IMAGE_FILE_MACHINE_AMD64; + frame.AddrPC.Offset = context->Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Rbp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Rsp; + frame.AddrStack.Mode = AddrModeFlat; +#elif defined(_M_ARM64) + image_type = IMAGE_FILE_MACHINE_ARM64; + frame.AddrPC.Offset = context->Pc; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Fp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Sp; + frame.AddrStack.Mode = AddrModeFlat; +#else +# error Architecture not supported! +#endif + + for(U32 idx=0; ;idx++) + { + const U32 max_frames = 32; + if(idx == max_frames) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"..."); + break; + } + + if(!dbg_StackWalk64(image_type, process, thread, &frame, context, 0, dbg_SymFunctionTableAccess64, dbg_SymGetModuleBase64, 0)) + { + break; + } + + U64 address = frame.AddrPC.Offset; + if(address == 0) + { + break; + } + + if(idx==0) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, + L"\nPress Ctrl+C to copy this text to clipboard, then create a new issue in\n" + L"%S\n\n", BUILD_ISSUES_LINK_STRING_LITERAL, BUILD_ISSUES_LINK_STRING_LITERAL); + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"Call stack:\n"); + } + + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"%u. [0x%I64x]", idx + 1, address); + + struct { + SYMBOL_INFOW info; + WCHAR name[MAX_SYM_NAME]; + } symbol = {0}; + + symbol.info.SizeOfStruct = sizeof(symbol.info); + symbol.info.MaxNameLen = MAX_SYM_NAME; + + DWORD64 displacement = 0; + if(dbg_SymFromAddrW(process, address, &displacement, &symbol.info)) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L" %s +%u", symbol.info.Name, (DWORD)displacement); + + IMAGEHLP_LINEW64 line = {0}; + line.SizeOfStruct = sizeof(line); + + DWORD line_displacement = 0; + if(dbg_SymGetLineFromAddrW64(process, address, &line_displacement, &line)) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L", %s line %u", PathFindFileNameW(line.FileName), line.LineNumber); + } + } + else + { + IMAGEHLP_MODULEW64 module = {0}; + module.SizeOfStruct = sizeof(module); + if(dbg_SymGetModuleInfoW64(process, address, &module)) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L" %s", module.ModuleName); + } + } + + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\n"); + } + } + } + } + } + + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\nVersion: %S%S", BUILD_VERSION_STRING_LITERAL, BUILD_GIT_HASH_STRING_LITERAL_APPEND); + + TASKDIALOGCONFIG dialog = {0}; + dialog.cbSize = sizeof(dialog); + dialog.dwFlags = TDF_SIZE_TO_CONTENT | TDF_ENABLE_HYPERLINKS | TDF_ALLOW_DIALOG_CANCELLATION; + dialog.pszMainIcon = TD_ERROR_ICON; + dialog.dwCommonButtons = TDCBF_CLOSE_BUTTON; + dialog.pszWindowTitle = L"Fatal Exception"; + dialog.pszContent = buffer; + dialog.pfCallback = &win32_dialog_callback; + TaskDialogIndirect(&dialog, 0, 0, 0); + + ExitProcess(1); +} + +#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside +#define OS_WINDOWS 1 + +#if BUILD_CONSOLE_INTERFACE +int main(int argc, char **argv) +{ + SetUnhandledExceptionFilter(&win32_exception_filter); + for(int i = 0; i < argc; i += 1) + { + String8 arg8 = str8_cstring(argv[i]); + if(str8_match(arg8, str8_lit("--quiet"), StringMatchFlag_CaseInsensitive)) + { + win32_g_is_quiet = 1; + } + } + main_thread_base_entry_point(entry_point, argv, (U64)argc); + return 0; +} +#else +int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + SetUnhandledExceptionFilter(&win32_exception_filter); + Arena *args_arena = arena_alloc__sized(KB(64), KB(64)); + WCHAR *command_line = GetCommandLineW(); + int argc = 0; + WCHAR **argv_16 = CommandLineToArgvW(command_line, &argc); + char **argv = push_array(args_arena, char *, argc); + for(int i = 0; i < argc; i += 1) + { + String16 arg16 = str16_cstring((U16 *)argv_16[i]); + String8 arg8 = str8_from_16(args_arena, arg16); + if(str8_match(arg8, str8_lit("--quiet"), StringMatchFlag_CaseInsensitive)) + { + win32_g_is_quiet = 1; + } + argv[i] = (char *)arg8.str; + } + main_thread_base_entry_point(entry_point, argv, (U64)argc); + return 0; +} +#endif diff --git a/src/raddbg/raddbg.c b/src/raddbg/raddbg.c index 067e5dd7..87425b6b 100644 --- a/src/raddbg/raddbg.c +++ b/src/raddbg/raddbg.c @@ -332,350 +332,3 @@ internal CTRL_WAKEUP_FUNCTION_DEF(wakeup_hook) { os_send_wakeup_event(); } - -internal void -entry_point(int argc, char **argv) -{ - Temp scratch = scratch_begin(0, 0); -#if PROFILE_TELEMETRY - local_persist U8 tm_data[MB(64)]; - tmLoadLibrary(TM_RELEASE); - tmSetMaxThreadCount(1024); - tmInitialize(sizeof(tm_data), (char *)tm_data); -#endif - ThreadNameF("[main]"); - - //- rjf: initialize basic dependencies - os_init(argc, argv); - ts_init(); - - //- rjf: parse command line arguments - CmdLine cmdln = cmd_line_from_string_list(scratch.arena, os_get_command_line_arguments()); - ExecMode exec_mode = ExecMode_Normal; - String8 user_cfg_path = str8_lit(""); - String8 profile_cfg_path = str8_lit(""); - B32 capture = 0; - B32 auto_run = 0; - B32 auto_step = 0; - B32 jit_attach = 0; - U64 jit_pid = 0; - U64 jit_code = 0; - U64 jit_addr = 0; - { - if(cmd_line_has_flag(&cmdln, str8_lit("ipc"))) - { - exec_mode = ExecMode_IPCSender; - } - else if(cmd_line_has_flag(&cmdln, str8_lit("convert"))) - { - exec_mode = ExecMode_Converter; - } - else if(cmd_line_has_flag(&cmdln, str8_lit("?")) || - cmd_line_has_flag(&cmdln, str8_lit("help"))) - { - exec_mode = ExecMode_Help; - } - user_cfg_path = cmd_line_string(&cmdln, str8_lit("user")); - profile_cfg_path = cmd_line_string(&cmdln, str8_lit("profile")); - capture = cmd_line_has_flag(&cmdln, str8_lit("capture")); - auto_run = cmd_line_has_flag(&cmdln, str8_lit("auto_run")); - auto_step = cmd_line_has_flag(&cmdln, str8_lit("auto_step")); - String8 jit_pid_string = {0}; - String8 jit_code_string = {0}; - String8 jit_addr_string = {0}; - jit_pid_string = cmd_line_string(&cmdln, str8_lit("jit_pid")); - jit_code_string = cmd_line_string(&cmdln, str8_lit("jit_code")); - jit_addr_string = cmd_line_string(&cmdln, str8_lit("jit_addr")); - try_u64_from_str8_c_rules(jit_pid_string, &jit_pid); - try_u64_from_str8_c_rules(jit_code_string, &jit_code); - try_u64_from_str8_c_rules(jit_addr_string, &jit_addr); - jit_attach = (jit_addr != 0); - } - - //- rjf: auto-start capture - if(capture) - { - ProfBeginCapture("raddbg"); - } - - //- rjf: set default user/profile paths - { - String8 user_program_data_path = os_string_from_system_path(scratch.arena, OS_SystemPath_UserProgramData); - String8 user_data_folder = push_str8f(scratch.arena, "%S/%S", user_program_data_path, str8_lit("raddbg")); - os_make_directory(user_data_folder); - if(user_cfg_path.size == 0) - { - user_cfg_path = push_str8f(scratch.arena, "%S/default.raddbg_user", user_data_folder); - } - if(profile_cfg_path.size == 0) - { - profile_cfg_path = push_str8f(scratch.arena, "%S/default.raddbg_profile", user_data_folder); - } - } - - //- rjf: dispatch to top-level codepath based on execution mode - switch(exec_mode) - { - //- rjf: normal execution - default: - case ExecMode_Normal: - { - //- rjf: set up shared memory for ipc - OS_Handle ipc_shared_memory = os_shared_memory_alloc(IPC_SHARED_MEMORY_BUFFER_SIZE, ipc_shared_memory_name); - void *ipc_shared_memory_base = os_shared_memory_view_open(ipc_shared_memory, r1u64(0, IPC_SHARED_MEMORY_BUFFER_SIZE)); - OS_Handle ipc_semaphore = os_semaphore_alloc(1, 1, ipc_semaphore_name); - IPCInfo *ipc_info = (IPCInfo *)ipc_shared_memory_base; - ipc_info->msg_size = 0; - - //- rjf: initialize stuff we depend on - { - hs_init(); - fs_init(); - txt_init(); - dbgi_init(); - txti_init(); - demon_init(); - ctrl_init(wakeup_hook); - dasm_init(); - os_graphical_init(); - fp_init(); - r_init(&cmdln); - tex_init(); - geo_init(); - f_init(); - DF_StateDeltaHistory *hist = df_state_delta_history_alloc(); - df_core_init(user_cfg_path, profile_cfg_path, hist); - df_gfx_init(update_and_render, hist); - os_set_cursor(OS_Cursor_Pointer); - } - - //- rjf: setup initial target from command line args - { - String8List args = cmdln.inputs; - if(args.node_count > 0 && args.first->string.size != 0) - { - Temp scratch = scratch_begin(0, 0); - DF_Entity *target = df_entity_alloc(0, df_entity_root(), DF_EntityKind_Target); - df_entity_equip_b32(target, 1); - df_entity_equip_cfg_src(target, DF_CfgSrc_CommandLine); - String8List passthrough_args_list = {0}; - for(String8Node *n = args.first->next; n != 0; n = n->next) - { - str8_list_push(scratch.arena, &passthrough_args_list, n->string); - } - - // rjf: equip exe - if(args.first->string.size != 0) - { - DF_Entity *exe = df_entity_alloc(0, target, DF_EntityKind_Executable); - df_entity_equip_name(0, exe, args.first->string); - } - - // rjf: equip path - String8 path_part_of_arg = str8_chop_last_slash(args.first->string); - if(path_part_of_arg.size != 0) - { - String8 path = push_str8f(scratch.arena, "%S/", path_part_of_arg); - DF_Entity *execution_path = df_entity_alloc(0, target, DF_EntityKind_ExecutionPath); - df_entity_equip_name(0, execution_path, path); - } - - // rjf: equip args - StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")}; - String8 args_str = str8_list_join(scratch.arena, &passthrough_args_list, &join); - if(args_str.size != 0) - { - DF_Entity *args_entity = df_entity_alloc(0, target, DF_EntityKind_Arguments); - df_entity_equip_name(0, args_entity, args_str); - } - scratch_end(scratch); - } - } - - //- rjf: main application loop - { - for(;;) - { - //- rjf: get IPC messages & dispatch ui commands from them - { - if(os_semaphore_take(ipc_semaphore, max_U64)) - { - if(ipc_info->msg_size != 0) - { - U8 *buffer = (U8 *)(ipc_info+1); - U64 msg_size = ipc_info->msg_size; - String8 cmd_string = str8(buffer, msg_size); - ipc_info->msg_size = 0; - DF_Window *dst_window = df_gfx_state->first_window; - for(DF_Window *window = dst_window; window != 0; window = window->next) - { - if(os_window_is_focused(window->os)) - { - dst_window = window; - break; - } - } - if(dst_window != 0) - { - Temp scratch = scratch_begin(0, 0); - String8 cmd_spec_string = df_cmd_name_part_from_string(cmd_string); - DF_CmdSpec *cmd_spec = df_cmd_spec_from_string(cmd_spec_string); - if(!df_cmd_spec_is_nil(cmd_spec)) - { - DF_CmdParams params = df_cmd_params_from_gfx(); - DF_CtrlCtx ctrl_ctx = df_ctrl_ctx_from_window(dst_window); - String8 error = df_cmd_params_apply_spec_query(scratch.arena, &ctrl_ctx, ¶ms, cmd_spec, df_cmd_arg_part_from_string(cmd_string)); - if(error.size == 0) - { - df_push_cmd__root(¶ms, cmd_spec); - } - else - { - DF_CmdParams params = df_cmd_params_from_window(dst_window); - params.string = error; - df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); - df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error)); - } - } - scratch_end(scratch); - } - } - os_semaphore_drop(ipc_semaphore); - } - } - - //- rjf: update & render frame - OS_Handle repaint_window = {0}; - update_and_render(repaint_window, 0); - - //- rjf: auto run - if(auto_run) - { - auto_run = 0; - DF_CmdParams params = df_cmd_params_from_gfx(); - df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_LaunchAndRun)); - } - - //- rjf: auto step - if(auto_step) - { - auto_step = 0; - DF_CmdParams params = df_cmd_params_from_gfx(); - df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_StepInto)); - } - - //- rjf: jit attach - if(jit_attach) - { - jit_attach = 0; - DF_CmdParams params = df_cmd_params_from_gfx(); - df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_ID); - params.id = jit_pid; - df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Attach)); - } - - //- rjf: quit if no windows are left - if(df_gfx_state->first_window == 0) - { - break; - } - } - } - - }break; - - //- rjf: inter-process communication message sender - case ExecMode_IPCSender: - { - Temp scratch = scratch_begin(0, 0); - - //- rjf: grab ipc shared memory - OS_Handle ipc_shared_memory = os_shared_memory_open(ipc_shared_memory_name); - void *ipc_shared_memory_base = os_shared_memory_view_open(ipc_shared_memory, r1u64(0, MB(16))); - if(ipc_shared_memory_base != 0) - { - OS_Handle ipc_semaphore = os_semaphore_open(ipc_semaphore_name); - IPCInfo *ipc_info = (IPCInfo *)ipc_shared_memory_base; - if(os_semaphore_take(ipc_semaphore, os_now_microseconds() + Million(6))) - { - U8 *buffer = (U8 *)(ipc_info+1); - U64 buffer_max = IPC_SHARED_MEMORY_BUFFER_SIZE - sizeof(IPCInfo); - StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")}; - String8 msg = str8_list_join(scratch.arena, &cmdln.inputs, &join); - ipc_info->msg_size = Min(buffer_max, msg.size); - MemoryCopy(buffer, msg.str, ipc_info->msg_size); - os_semaphore_drop(ipc_semaphore); - } - } - - scratch_end(scratch); - }break; - - //- rjf: built-in pdb/dwarf -> raddbg converter mode - case ExecMode_Converter: - { - Temp scratch = scratch_begin(0, 0); - - //- rjf: parse arguments - P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(scratch.arena, &cmdln); - - //- rjf: open output file - String8 output_name = push_str8_copy(scratch.arena, user2convert->output_name); - OS_Handle out_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, output_name); - B32 out_file_is_good = !os_handle_match(out_file, os_handle_zero()); - - //- rjf: convert - P2R_Convert2Bake *convert2bake = 0; - if(out_file_is_good) - { - convert2bake = p2r_convert(scratch.arena, user2convert); - } - - //- rjf: bake - P2R_Bake2Serialize *bake2srlz = 0; - ProfScope("bake") - { - bake2srlz = p2r_bake(scratch.arena, convert2bake); - } - - //- rjf: serialize - String8List serialize_out = rdim_serialized_strings_from_params_bake_section_list(scratch.arena, &convert2bake->bake_params, &bake2srlz->sections); - - //- rjf: write - if(out_file_is_good) - { - U64 off = 0; - for(String8Node *n = serialize_out.first; n != 0; n = n->next) - { - os_file_write(out_file, r1u64(off, off+n->string.size), n->string.str); - off += n->string.size; - } - } - - //- rjf: close output file - os_file_close(out_file); - - scratch_end(scratch); - }break; - - //- rjf: help message box - case ExecMode_Help: - { - os_graphical_message(0, - str8_lit("The RAD Debugger - Help"), - str8_lit("The following options may be used when starting the RAD Debugger from the command line:\n\n" - "--user:\n" - "Use to specify the location of a user file which should be used. User files are used to store settings for users, including window and panel setups, path mapping, and visual settings. If this file does not exist, it will be created as necessary. This file will be autosaved as user-related changes are made.\n\n" - "--profile:\n" - "Use to specify the location of a profile file which should be used. Profile files are used to store settings for users and projects. If this file does not exist, it will be created as necessary. This file will be autosaved as profile-related changes are made.\n\n" - "--auto_step\n" - "This will step into all targets after the debugger initially starts.\n\n" - "--auto_run\n" - "This will run all targets after the debugger initially starts.\n\n" - "--ipc \n" - "This will launch the debugger in the non-graphical IPC mode, which is used to communicate with another running instance of the debugger. The debugger instance will launch, send the specified command, then immediately terminate. This may be used by editors or other programs to control the debugger.\n\n")); - }break; - } - - scratch_end(scratch); -} diff --git a/src/raddbg/raddbg.h b/src/raddbg/raddbg.h index 0f72cb57..19377d0b 100644 --- a/src/raddbg/raddbg.h +++ b/src/raddbg/raddbg.h @@ -400,26 +400,6 @@ #ifndef RADDBG_H #define RADDBG_H -//////////////////////////////// -//~ rjf: Build Settings - -#define RADDBG_VERSION_MAJOR 0 -#define RADDBG_VERSION_MINOR 9 -#define RADDBG_VERSION_PATCH 8 -#define RADDBG_VERSION_STRING_LITERAL Stringify(RADDBG_VERSION_MAJOR) "." Stringify(RADDBG_VERSION_MINOR) "." Stringify(RADDBG_VERSION_PATCH) -#if BUILD_DEBUG -# define RADDBG_BUILD_STR " [Debug]" -#else -# define RADDBG_BUILD_STR "" -#endif -#if defined(RADDBG_GIT) -# define RADDBG_GIT_STR " [" RADDBG_GIT "]" -#else -# define RADDBG_GIT_STR "" -#endif -#define RADDBG_TITLE_STRING_LITERAL "The RAD Debugger (" RADDBG_VERSION_STRING_LITERAL " ALPHA) - " __DATE__ "" RADDBG_GIT_STR RADDBG_BUILD_STR -#define RADDBG_GITHUB_ISSUES "https://github.com/EpicGames/raddebugger/issues" - //////////////////////////////// //~ rjf: Top-Level Execution Types diff --git a/src/raddbg/raddbg_main.cpp b/src/raddbg/raddbg_main.cpp index fef04980..07f049da 100644 --- a/src/raddbg/raddbg_main.cpp +++ b/src/raddbg/raddbg_main.cpp @@ -2,8 +2,13 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// -//~ rjf: Build Settings +//~ rjf: Build Options +#define BUILD_VERSION_MAJOR 0 +#define BUILD_VERSION_MINOR 9 +#define BUILD_VERSION_PATCH 8 +#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#define BUILD_TITLE "The RAD Debugger" #define OS_FEATURE_GRAPHICAL 1 //////////////////////////////// @@ -92,213 +97,15 @@ #include "raddbg.c" //////////////////////////////// -//~ rjf: Low-Level Entry Points +//~ rjf: Entry Point -//- rjf: windows +internal void +entry_point(CmdLine *cmd_line) +{ + Temp scratch = scratch_begin(0, 0); + + //- rjf: windows -> turn off output handles, as we need to control those for target processes #if OS_WINDOWS - -#include - -#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside -#include - -internal B32 g_is_quiet = 0; - -internal HRESULT WINAPI -win32_dialog_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LONG_PTR data) -{ - if(msg == TDN_HYPERLINK_CLICKED) - { - ShellExecuteW(NULL, L"open", (LPWSTR)lparam, NULL, NULL, SW_SHOWNORMAL); - } - return S_OK; -} - -internal LONG WINAPI -win32_exception_filter(EXCEPTION_POINTERS* exception_ptrs) -{ - if(g_is_quiet) - { - ExitProcess(1); - } - - static volatile LONG first = 0; - if(InterlockedCompareExchange(&first, 1, 0) != 0) - { - // prevent failures in other threads to popup same message box - // this handler just shows first thread that crashes - // we are terminating afterwards anyway - for (;;) Sleep(1000); - } - - WCHAR buffer[4096] = {0}; - int buflen = 0; - - DWORD exception_code = exception_ptrs->ExceptionRecord->ExceptionCode; - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"A fatal exception (code 0x%x) occurred. The process is terminating.\n", exception_code); - - // load dbghelp dynamically just in case if it is missing - HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); - if(dbghelp) - { - DWORD (WINAPI *dbg_SymSetOptions)(DWORD SymOptions); - BOOL (WINAPI *dbg_SymInitializeW)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess); - BOOL (WINAPI *dbg_StackWalk64)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, - LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, - PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, - PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); - PVOID (WINAPI *dbg_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); - DWORD64 (WINAPI *dbg_SymGetModuleBase64)(HANDLE hProcess, DWORD64 qwAddr); - BOOL (WINAPI *dbg_SymFromAddrW)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFOW Symbol); - BOOL (WINAPI *dbg_SymGetLineFromAddrW64)(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINEW64 Line); - BOOL (WINAPI *dbg_SymGetModuleInfoW64)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULEW64 ModuleInfo); - - *(FARPROC*)&dbg_SymSetOptions = GetProcAddress(dbghelp, "SymSetOptions"); - *(FARPROC*)&dbg_SymInitializeW = GetProcAddress(dbghelp, "SymInitializeW"); - *(FARPROC*)&dbg_StackWalk64 = GetProcAddress(dbghelp, "StackWalk64"); - *(FARPROC*)&dbg_SymFunctionTableAccess64 = GetProcAddress(dbghelp, "SymFunctionTableAccess64"); - *(FARPROC*)&dbg_SymGetModuleBase64 = GetProcAddress(dbghelp, "SymGetModuleBase64"); - *(FARPROC*)&dbg_SymFromAddrW = GetProcAddress(dbghelp, "SymFromAddrW"); - *(FARPROC*)&dbg_SymGetLineFromAddrW64 = GetProcAddress(dbghelp, "SymGetLineFromAddrW64"); - *(FARPROC*)&dbg_SymGetModuleInfoW64 = GetProcAddress(dbghelp, "SymGetModuleInfoW64"); - - if(dbg_SymSetOptions && dbg_SymInitializeW && dbg_StackWalk64 && dbg_SymFunctionTableAccess64 && dbg_SymGetModuleBase64 && dbg_SymFromAddrW && dbg_SymGetLineFromAddrW64 && dbg_SymGetModuleInfoW64) - { - HANDLE process = GetCurrentProcess(); - HANDLE thread = GetCurrentThread(); - CONTEXT* context = exception_ptrs->ContextRecord; - - dbg_SymSetOptions(SYMOPT_EXACT_SYMBOLS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); - if(dbg_SymInitializeW(process, L"", TRUE)) - { - // check that raddbg.pdb file is good - B32 raddbg_pdb_valid = 0; - { - IMAGEHLP_MODULEW64 module = {0}; - module.SizeOfStruct = sizeof(module); - if(dbg_SymGetModuleInfoW64(process, (DWORD64)&win32_exception_filter, &module)) - { - raddbg_pdb_valid = (module.SymType == SymPdb); - } - } - - if(!raddbg_pdb_valid) - { - buflen += wnsprintfW(buffer + buflen, sizeof(buffer) - buflen, - L"\nraddbg.pdb debug file is not valid or not found. Please rebuild binary to get call stack.\n"); - } - else - { - STACKFRAME64 frame = {0}; - DWORD image_type; -#if defined(_M_AMD64) - image_type = IMAGE_FILE_MACHINE_AMD64; - frame.AddrPC.Offset = context->Rip; - frame.AddrPC.Mode = AddrModeFlat; - frame.AddrFrame.Offset = context->Rbp; - frame.AddrFrame.Mode = AddrModeFlat; - frame.AddrStack.Offset = context->Rsp; - frame.AddrStack.Mode = AddrModeFlat; -#elif defined(_M_ARM64) - image_type = IMAGE_FILE_MACHINE_ARM64; - frame.AddrPC.Offset = context->Pc; - frame.AddrPC.Mode = AddrModeFlat; - frame.AddrFrame.Offset = context->Fp; - frame.AddrFrame.Mode = AddrModeFlat; - frame.AddrStack.Offset = context->Sp; - frame.AddrStack.Mode = AddrModeFlat; -#else -# error Architecture not supported! -#endif - - for(U32 idx=0; ;idx++) - { - const U32 max_frames = 32; - if(idx == max_frames) - { - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"..."); - break; - } - - if(!dbg_StackWalk64(image_type, process, thread, &frame, context, 0, dbg_SymFunctionTableAccess64, dbg_SymGetModuleBase64, 0)) - { - break; - } - - U64 address = frame.AddrPC.Offset; - if(address == 0) - { - break; - } - - if(idx==0) - { - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, - L"\nPress Ctrl+C to copy this text to clipboard, then create a new issue in\n" - L"%S\n\n", RADDBG_GITHUB_ISSUES, RADDBG_GITHUB_ISSUES); - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"Call stack:\n"); - } - - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"%u. [0x%I64x]", idx + 1, address); - - struct { - SYMBOL_INFOW info; - WCHAR name[MAX_SYM_NAME]; - } symbol = {0}; - - symbol.info.SizeOfStruct = sizeof(symbol.info); - symbol.info.MaxNameLen = MAX_SYM_NAME; - - DWORD64 displacement = 0; - if(dbg_SymFromAddrW(process, address, &displacement, &symbol.info)) - { - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L" %s +%u", symbol.info.Name, (DWORD)displacement); - - IMAGEHLP_LINEW64 line = {0}; - line.SizeOfStruct = sizeof(line); - - DWORD line_displacement = 0; - if(dbg_SymGetLineFromAddrW64(process, address, &line_displacement, &line)) - { - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L", %s line %u", PathFindFileNameW(line.FileName), line.LineNumber); - } - } - else - { - IMAGEHLP_MODULEW64 module = {0}; - module.SizeOfStruct = sizeof(module); - if(dbg_SymGetModuleInfoW64(process, address, &module)) - { - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L" %s", module.ModuleName); - } - } - - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\n"); - } - } - } - } - } - - buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\nVersion: %S%S", RADDBG_VERSION_STRING_LITERAL, RADDBG_GIT_STR); - - TASKDIALOGCONFIG dialog = {0}; - dialog.cbSize = sizeof(dialog); - dialog.dwFlags = TDF_SIZE_TO_CONTENT | TDF_ENABLE_HYPERLINKS | TDF_ALLOW_DIALOG_CANCELLATION; - dialog.pszMainIcon = TD_ERROR_ICON; - dialog.dwCommonButtons = TDCBF_CLOSE_BUTTON; - dialog.pszWindowTitle = L"Fatal Exception"; - dialog.pszContent = buffer; - dialog.pfCallback = &win32_dialog_callback; - TaskDialogIndirect(&dialog, 0, 0, 0); - - ExitProcess(1); -} - -int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) -{ - SetUnhandledExceptionFilter(&win32_exception_filter); - HANDLE output_handles[3] = { GetStdHandle(STD_INPUT_HANDLE), @@ -331,36 +138,327 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n SetStdHandle(STD_INPUT_HANDLE, 0); SetStdHandle(STD_OUTPUT_HANDLE, 0); SetStdHandle(STD_ERROR_HANDLE, 0); - static TCTX main_thread_tctx = {0}; - tctx_init_and_equip(&main_thread_tctx); - Arena *perm_arena = arena_alloc(); - WCHAR *command_line = GetCommandLineW(); - int argc; - WCHAR **argv_16 = CommandLineToArgvW(command_line, &argc); - char **argv = push_array(perm_arena, char *, argc); - for(int i = 0; i < argc; i += 1) - { - String16 arg16 = str16_cstring((U16 *)argv_16[i]); - String8 arg8 = str8_from_16(perm_arena, arg16); - if(str8_match(arg8, str8_lit("--quiet"), StringMatchFlag_CaseInsensitive)) - { - g_is_quiet = 1; - } - argv[i] = (char *)arg8.str; - } - entry_point(argc, argv); - return 0; -} - -//- rjf: linux -#elif OS_LINUX - -int main(int argument_count, char **arguments) -{ - static TCTX main_thread_tctx = {0}; - tctx_init_and_equip(&main_thread_tctx); - entry_point(argument_count, arguments); - return 0; -} - #endif + + //- rjf: initialize basic dependencies + os_init(); + ts_init(); + + //- rjf: unpack command line arguments + ExecMode exec_mode = ExecMode_Normal; + String8 user_cfg_path = str8_lit(""); + String8 profile_cfg_path = str8_lit(""); + B32 auto_run = 0; + B32 auto_step = 0; + B32 jit_attach = 0; + U64 jit_pid = 0; + U64 jit_code = 0; + U64 jit_addr = 0; + { + if(cmd_line_has_flag(cmd_line, str8_lit("ipc"))) + { + exec_mode = ExecMode_IPCSender; + } + else if(cmd_line_has_flag(cmd_line, str8_lit("convert"))) + { + exec_mode = ExecMode_Converter; + } + else if(cmd_line_has_flag(cmd_line, str8_lit("?")) || + cmd_line_has_flag(cmd_line, str8_lit("help"))) + { + exec_mode = ExecMode_Help; + } + user_cfg_path = cmd_line_string(cmd_line, str8_lit("user")); + profile_cfg_path = cmd_line_string(cmd_line, str8_lit("profile")); + auto_run = cmd_line_has_flag(cmd_line, str8_lit("auto_run")); + auto_step = cmd_line_has_flag(cmd_line, str8_lit("auto_step")); + String8 jit_pid_string = cmd_line_string(cmd_line, str8_lit("jit_pid")); + String8 jit_code_string = cmd_line_string(cmd_line, str8_lit("jit_code")); + String8 jit_addr_string = cmd_line_string(cmd_line, str8_lit("jit_addr")); + try_u64_from_str8_c_rules(jit_pid_string, &jit_pid); + try_u64_from_str8_c_rules(jit_code_string, &jit_code); + try_u64_from_str8_c_rules(jit_addr_string, &jit_addr); + jit_attach = (jit_addr != 0); + } + + //- rjf: set default user/profile paths + { + String8 user_program_data_path = os_string_from_system_path(scratch.arena, OS_SystemPath_UserProgramData); + String8 user_data_folder = push_str8f(scratch.arena, "%S/%S", user_program_data_path, str8_lit("raddbg")); + os_make_directory(user_data_folder); + if(user_cfg_path.size == 0) + { + user_cfg_path = push_str8f(scratch.arena, "%S/default.raddbg_user", user_data_folder); + } + if(profile_cfg_path.size == 0) + { + profile_cfg_path = push_str8f(scratch.arena, "%S/default.raddbg_profile", user_data_folder); + } + } + + //- rjf: dispatch to top-level codepath based on execution mode + switch(exec_mode) + { + //- rjf: normal execution + default: + case ExecMode_Normal: + { + //- rjf: set up shared memory for ipc + OS_Handle ipc_shared_memory = os_shared_memory_alloc(IPC_SHARED_MEMORY_BUFFER_SIZE, ipc_shared_memory_name); + void *ipc_shared_memory_base = os_shared_memory_view_open(ipc_shared_memory, r1u64(0, IPC_SHARED_MEMORY_BUFFER_SIZE)); + OS_Handle ipc_semaphore = os_semaphore_alloc(1, 1, ipc_semaphore_name); + IPCInfo *ipc_info = (IPCInfo *)ipc_shared_memory_base; + ipc_info->msg_size = 0; + + //- rjf: initialize stuff we depend on + { + hs_init(); + fs_init(); + txt_init(); + dbgi_init(); + txti_init(); + demon_init(); + ctrl_init(wakeup_hook); + dasm_init(); + os_graphical_init(); + fp_init(); + r_init(cmd_line); + tex_init(); + geo_init(); + f_init(); + DF_StateDeltaHistory *hist = df_state_delta_history_alloc(); + df_core_init(user_cfg_path, profile_cfg_path, hist); + df_gfx_init(update_and_render, hist); + os_set_cursor(OS_Cursor_Pointer); + } + + //- rjf: setup initial target from command line args + { + String8List args = cmd_line->inputs; + if(args.node_count > 0 && args.first->string.size != 0) + { + Temp scratch = scratch_begin(0, 0); + DF_Entity *target = df_entity_alloc(0, df_entity_root(), DF_EntityKind_Target); + df_entity_equip_b32(target, 1); + df_entity_equip_cfg_src(target, DF_CfgSrc_CommandLine); + String8List passthrough_args_list = {0}; + for(String8Node *n = args.first->next; n != 0; n = n->next) + { + str8_list_push(scratch.arena, &passthrough_args_list, n->string); + } + + // rjf: equip exe + if(args.first->string.size != 0) + { + DF_Entity *exe = df_entity_alloc(0, target, DF_EntityKind_Executable); + df_entity_equip_name(0, exe, args.first->string); + } + + // rjf: equip path + String8 path_part_of_arg = str8_chop_last_slash(args.first->string); + if(path_part_of_arg.size != 0) + { + String8 path = push_str8f(scratch.arena, "%S/", path_part_of_arg); + DF_Entity *execution_path = df_entity_alloc(0, target, DF_EntityKind_ExecutionPath); + df_entity_equip_name(0, execution_path, path); + } + + // rjf: equip args + StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")}; + String8 args_str = str8_list_join(scratch.arena, &passthrough_args_list, &join); + if(args_str.size != 0) + { + DF_Entity *args_entity = df_entity_alloc(0, target, DF_EntityKind_Arguments); + df_entity_equip_name(0, args_entity, args_str); + } + scratch_end(scratch); + } + } + + //- rjf: main application loop + { + for(;;) + { + //- rjf: get IPC messages & dispatch ui commands from them + { + if(os_semaphore_take(ipc_semaphore, max_U64)) + { + if(ipc_info->msg_size != 0) + { + U8 *buffer = (U8 *)(ipc_info+1); + U64 msg_size = ipc_info->msg_size; + String8 cmd_string = str8(buffer, msg_size); + ipc_info->msg_size = 0; + DF_Window *dst_window = df_gfx_state->first_window; + for(DF_Window *window = dst_window; window != 0; window = window->next) + { + if(os_window_is_focused(window->os)) + { + dst_window = window; + break; + } + } + if(dst_window != 0) + { + Temp scratch = scratch_begin(0, 0); + String8 cmd_spec_string = df_cmd_name_part_from_string(cmd_string); + DF_CmdSpec *cmd_spec = df_cmd_spec_from_string(cmd_spec_string); + if(!df_cmd_spec_is_nil(cmd_spec)) + { + DF_CmdParams params = df_cmd_params_from_gfx(); + DF_CtrlCtx ctrl_ctx = df_ctrl_ctx_from_window(dst_window); + String8 error = df_cmd_params_apply_spec_query(scratch.arena, &ctrl_ctx, ¶ms, cmd_spec, df_cmd_arg_part_from_string(cmd_string)); + if(error.size == 0) + { + df_push_cmd__root(¶ms, cmd_spec); + } + else + { + DF_CmdParams params = df_cmd_params_from_window(dst_window); + params.string = error; + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error)); + } + } + scratch_end(scratch); + } + } + os_semaphore_drop(ipc_semaphore); + } + } + + //- rjf: update & render frame + OS_Handle repaint_window = {0}; + update_and_render(repaint_window, 0); + + //- rjf: auto run + if(auto_run) + { + auto_run = 0; + DF_CmdParams params = df_cmd_params_from_gfx(); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_LaunchAndRun)); + } + + //- rjf: auto step + if(auto_step) + { + auto_step = 0; + DF_CmdParams params = df_cmd_params_from_gfx(); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_StepInto)); + } + + //- rjf: jit attach + if(jit_attach) + { + jit_attach = 0; + DF_CmdParams params = df_cmd_params_from_gfx(); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_ID); + params.id = jit_pid; + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Attach)); + } + + //- rjf: quit if no windows are left + if(df_gfx_state->first_window == 0) + { + break; + } + } + } + + }break; + + //- rjf: inter-process communication message sender + case ExecMode_IPCSender: + { + Temp scratch = scratch_begin(0, 0); + + //- rjf: grab ipc shared memory + OS_Handle ipc_shared_memory = os_shared_memory_open(ipc_shared_memory_name); + void *ipc_shared_memory_base = os_shared_memory_view_open(ipc_shared_memory, r1u64(0, MB(16))); + if(ipc_shared_memory_base != 0) + { + OS_Handle ipc_semaphore = os_semaphore_open(ipc_semaphore_name); + IPCInfo *ipc_info = (IPCInfo *)ipc_shared_memory_base; + if(os_semaphore_take(ipc_semaphore, os_now_microseconds() + Million(6))) + { + U8 *buffer = (U8 *)(ipc_info+1); + U64 buffer_max = IPC_SHARED_MEMORY_BUFFER_SIZE - sizeof(IPCInfo); + StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")}; + String8 msg = str8_list_join(scratch.arena, &cmd_line->inputs, &join); + ipc_info->msg_size = Min(buffer_max, msg.size); + MemoryCopy(buffer, msg.str, ipc_info->msg_size); + os_semaphore_drop(ipc_semaphore); + } + } + + scratch_end(scratch); + }break; + + //- rjf: built-in pdb/dwarf -> raddbg converter mode + case ExecMode_Converter: + { + Temp scratch = scratch_begin(0, 0); + + //- rjf: parse arguments + P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(scratch.arena, cmd_line); + + //- rjf: open output file + String8 output_name = push_str8_copy(scratch.arena, user2convert->output_name); + OS_Handle out_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, output_name); + B32 out_file_is_good = !os_handle_match(out_file, os_handle_zero()); + + //- rjf: convert + P2R_Convert2Bake *convert2bake = 0; + if(out_file_is_good) + { + convert2bake = p2r_convert(scratch.arena, user2convert); + } + + //- rjf: bake + P2R_Bake2Serialize *bake2srlz = 0; + ProfScope("bake") + { + bake2srlz = p2r_bake(scratch.arena, convert2bake); + } + + //- rjf: serialize + String8List serialize_out = rdim_serialized_strings_from_params_bake_section_list(scratch.arena, &convert2bake->bake_params, &bake2srlz->sections); + + //- rjf: write + if(out_file_is_good) + { + U64 off = 0; + for(String8Node *n = serialize_out.first; n != 0; n = n->next) + { + os_file_write(out_file, r1u64(off, off+n->string.size), n->string.str); + off += n->string.size; + } + } + + //- rjf: close output file + os_file_close(out_file); + + scratch_end(scratch); + }break; + + //- rjf: help message box + case ExecMode_Help: + { + os_graphical_message(0, + str8_lit("The RAD Debugger - Help"), + str8_lit("The following options may be used when starting the RAD Debugger from the command line:\n\n" + "--user:\n" + "Use to specify the location of a user file which should be used. User files are used to store settings for users, including window and panel setups, path mapping, and visual settings. If this file does not exist, it will be created as necessary. This file will be autosaved as user-related changes are made.\n\n" + "--profile:\n" + "Use to specify the location of a profile file which should be used. Profile files are used to store settings for users and projects. If this file does not exist, it will be created as necessary. This file will be autosaved as profile-related changes are made.\n\n" + "--auto_step\n" + "This will step into all targets after the debugger initially starts.\n\n" + "--auto_run\n" + "This will run all targets after the debugger initially starts.\n\n" + "--ipc \n" + "This will launch the debugger in the non-graphical IPC mode, which is used to communicate with another running instance of the debugger. The debugger instance will launch, send the specified command, then immediately terminate. This may be used by editors or other programs to control the debugger.\n\n")); + }break; + } + + scratch_end(scratch); +} diff --git a/src/raddbgi_dump/raddbgi_dump_main.c b/src/raddbgi_dump/raddbgi_dump_main.c index 3b154a09..b47f5789 100644 --- a/src/raddbgi_dump/raddbgi_dump_main.c +++ b/src/raddbgi_dump/raddbgi_dump_main.c @@ -4,6 +4,11 @@ //////////////////////////////// //~ rjf: Build Options +#define BUILD_VERSION_MAJOR 0 +#define BUILD_VERSION_MINOR 9 +#define BUILD_VERSION_PATCH 8 +#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#define BUILD_TITLE "raddbgi_dump" #define BUILD_CONSOLE_INTERFACE 1 //////////////////////////////// @@ -28,8 +33,8 @@ //////////////////////////////// //~ rjf: Entry Point -int -main(int argc, char **argv) +internal void +entry_point(CmdLine *cmd_line) { ////////////////////////////// //- rjf: set up @@ -37,8 +42,6 @@ main(int argc, char **argv) local_persist TCTX main_thread_tctx = {0}; tctx_init_and_equip(&main_thread_tctx); Arena *arena = arena_alloc(); - String8List args = os_string_list_from_argcv(arena, argc, argv); - CmdLine cmdline = cmd_line_from_string_list(arena, args); String8List errors = {0}; ////////////////////////////// @@ -69,14 +72,14 @@ main(int argc, char **argv) DumpFlags dump_flags = (U32)0xffffffff; { // rjf: extract input file path & load data - input_name = str8_list_first(&cmdline.inputs); + input_name = str8_list_first(&cmd_line->inputs); if(input_name.size > 0) { input_data = os_data_from_file_path(arena, input_name); } else {str8_list_pushf(arena, &errors, "error (input): No input RADDBGI file specified.");} if(input_name.size != 0 && input_data.size == 0) { str8_list_pushf(arena, &errors, "error (input): No input RADDBGI file successfully loaded; either the path or file contents are invalid."); } // rjf: extract dump options { - String8List dump_options = cmd_line_strings(&cmdline, str8_lit("dump")); + String8List dump_options = cmd_line_strings(cmd_line, str8_lit("dump")); if(dump_options.first != 0) { dump_flags = 0; @@ -413,6 +416,4 @@ main(int argc, char **argv) { fwrite(n->string.str, 1, n->string.size, stdout); } - - return 0; } diff --git a/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c b/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c index dcb6e420..df0454c2 100644 --- a/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c +++ b/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c @@ -4,6 +4,11 @@ //////////////////////////////// //~ rjf: Build Options +#define BUILD_VERSION_MAJOR 0 +#define BUILD_VERSION_MINOR 9 +#define BUILD_VERSION_PATCH 8 +#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#define BUILD_TITLE "raddbgi_from_pdb" #define BUILD_CONSOLE_INTERFACE 1 //////////////////////////////// @@ -42,37 +47,16 @@ //////////////////////////////// //~ rjf: Entry Point -int -main(int argc, char **argv) +internal void +entry_point(CmdLine *cmdline) { - local_persist TCTX main_thread_tctx = {0}; - tctx_init_and_equip(&main_thread_tctx); -#if PROFILE_TELEMETRY - U64 tm_data_size = MB(512); - U8 *tm_data = os_reserve(tm_data_size); - os_commit(tm_data, tm_data_size); - tmLoadLibrary(TM_RELEASE); - tmSetMaxThreadCount(1024); - tmInitialize(tm_data_size, tm_data); -#endif - ThreadNameF("[main]"); - //- rjf: initialize dependencies - os_init(argc, argv); + os_init(); ts_init(); - //- rjf: initialize state, parse command line + //- rjf: initialize state, unpack command line Arena *arena = arena_alloc(); - String8List args = os_string_list_from_argcv(arena, argc, argv); - CmdLine cmdline = cmd_line_from_string_list(arena, args); - B32 should_capture = cmd_line_has_flag(&cmdline, str8_lit("capture")); - P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(arena, &cmdline); - - //- rjf: begin capture - if(should_capture) - { - ProfBeginCapture(argv[0]); - } + P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(arena, cmdline); //- rjf: display errors with input if(user2convert->errors.node_count > 0 && !user2convert->hide_errors.input) @@ -112,12 +96,4 @@ main(int argc, char **argv) } os_file_close(output_file); } - - //- rjf: end capture - if(should_capture) - { - ProfEndCapture(); - } - - return 0; }