move to codebase-defined entry point style, with basic command line argument parsing, capture, thread-ctx, and other boilerplate deduplicated in the base layer's entry point definition

This commit is contained in:
Ryan Fleury
2024-02-25 15:02:05 -08:00
parent dd05eaa21e
commit 8eb0f90f3e
15 changed files with 661 additions and 683 deletions
+1 -1
View File
@@ -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]
+1
View File
@@ -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.
{
+1
View File
@@ -29,6 +29,7 @@ struct CmdLineOptList
typedef struct CmdLine CmdLine;
struct CmdLine
{
String8 exe_name;
CmdLineOptList options;
String8List inputs;
U64 option_table_size;
+42
View File
@@ -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
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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))
+1 -1
View File
@@ -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
{
-13
View File
@@ -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)
+12 -7
View File
@@ -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
+245 -11
View File
@@ -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 <dbghelp.h>
#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside
#include <shlwapi.h>
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"<a href=\"%S\">%S</a>\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
-347
View File
@@ -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, &params, cmd_spec, df_cmd_arg_part_from_string(cmd_string));
if(error.size == 0)
{
df_push_cmd__root(&params, cmd_spec);
}
else
{
DF_CmdParams params = df_cmd_params_from_window(dst_window);
params.string = error;
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_String);
df_push_cmd__root(&params, 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(&params, 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(&params, 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(&params, DF_CmdParamSlot_ID);
params.id = jit_pid;
df_push_cmd__root(&params, 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:<path>\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:<path>\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 <command>\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);
}
-20
View File
@@ -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
+336 -238
View File
@@ -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 <dbghelp.h>
#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside
#include <shlwapi.h>
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"<a href=\"%S\">%S</a>\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, &params, cmd_spec, df_cmd_arg_part_from_string(cmd_string));
if(error.size == 0)
{
df_push_cmd__root(&params, cmd_spec);
}
else
{
DF_CmdParams params = df_cmd_params_from_window(dst_window);
params.string = error;
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_String);
df_push_cmd__root(&params, 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(&params, 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(&params, 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(&params, DF_CmdParamSlot_ID);
params.id = jit_pid;
df_push_cmd__root(&params, 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:<path>\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:<path>\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 <command>\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);
}
+9 -8
View File
@@ -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;
}
+10 -34
View File
@@ -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;
}