raddbg_server: checkpoint

This commit is contained in:
Ryan Fleury committed 2026-10-02 20:57:47 -07:00
1 parent bf000289db
commit 34a0ff4e80
6 files changed
+424

No files matched your search

+1
View File
@@ -195,6 +195,7 @@ pushd build
if "%raddbg%"=="1" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg.exe || exit /b 1
if "%raddbg_non_graphical%"=="1" set didbuild=1 && %compile% -DWM_STUB=1 -DR_BACKEND=R_BACKEND_STUB ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg_non_graphical.exe || exit /b 1
if "%com_shim%"=="1" set didbuild=1 && %compile% ..\src\com_shim\com_shim_main.c %compile_link% %out%com_shim.exe || exit /b 1
if "%raddbg_server%"=="1" set didbuild=1 && %compile% ..\src\raddbg_server\raddbg_server_main.c %compile_link% %out%raddbg_server.exe || exit /b 1
if "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %linker% /NOIMPLIB %linker% /NATVIS:"%~dp0\src\linker\linker.natvis" %out%radlink.exe || exit /b 1
if "%radbin%"=="1" set didbuild=1 && %compile% ..\src\radbin\radbin_main.c %compile_link% %out%radbin.exe || exit /b 1
if "%raddump%"=="1" set didbuild=1 && %compile% ..\src\raddump\raddump_main.c %compile_link% %out%raddump.exe || exit /b 1
+2
View File
@@ -0,0 +1,2 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_SERVER_H
#define RADDBG_SERVER_H
////////////////////////////////
//~ rjf: Messages (Client -> Server)
#define RDS_MSG_MAGIC 0x7273
typedef enum RDS_MsgKind
{
RDS_MsgKind_Null,
//- rjf: debuggee attaching/detaching ops
RDS_MsgKind_Launch,
RDS_MsgKind_Attach,
RDS_MsgKind_Kill,
RDS_MsgKind_Detach,
//- rjf: running ops
RDS_MsgKind_Run,
RDS_MsgKind_SingleStep,
RDS_MsgKind_Halt,
//- rjf: memory ops
RDS_MsgKind_MemoryReserve,
RDS_MsgKind_MemoryCommit,
RDS_MsgKind_MemoryDecommit,
RDS_MsgKind_MemoryRelease,
RDS_MsgKind_MemoryProtect,
RDS_MsgKind_MemoryRead,
RDS_MsgKind_MemoryWrite,
//- rjf: thread ops
RDS_MsgKind_ThreadRegBlockRead,
RDS_MsgKind_ThreadRegBlockWrite,
RDS_MsgKind_ThreadGetModuleTLSVAddr,
//- rjf: system ops
RDS_MsgKind_ListSystemProcesses,
}
RDS_MsgKind;
typedef U32 RDS_MsgFlags;
enum
{
RDS_MsgFlag_InheritEnv = (1<<0),
RDS_MsgFlag_DebugSubprocesses = (1<<1),
RDS_MsgFlag_IgnorePreviousException = (1<<2),
RDS_MsgFlag_RunEntitiesAreUnfrozen = (1<<3),
RDS_MsgFlag_RunEntitiesAreProcesses = (1<<4),
};
typedef struct RDS_Msg RDS_Msg;
struct RDS_Msg
{
RDS_MsgKind kind;
RDS_MsgFlags flags;
U64 id;
//- rjf: general parameters
Rng1U64 vaddr_range;
AccessFlags access_flags;
DMN_Handle entity;
U64 pid;
//- rjf: process launching parameters
String8List command_line;
String8 path;
String8List env;
//- rjf: running parameters
DMN_TrapChunkList traps;
DMN_HandleArray run_entities;
};
////////////////////////////////
//~ rjf: Responses (Server -> Client responses to messages)
typedef struct RDS_Response RDS_Response;
struct RDS_Response
{
U64 id;
String8 data;
DMN_Handle entity;
U64 pid;
U64 demon_event_count;
};
#endif // RADDBG_SERVER_H
+300
View File
@@ -0,0 +1,300 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_TITLE "The RAD Debugger Server"
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: [h]
#include "base/base_inc.h"
#include "x64/x64_inc.h"
#include "arch/arch_inc.h"
#include "socket/socket_inc.h"
#include "coff/coff.h"
#include "coff/coff_parse.h"
#include "pe/pe.h"
#include "elf/elf.h"
#include "gnu/gnu.h"
#include "gnu/gnu_parse.h"
#include "elf/elf_parse.h"
#include "demon/demon_inc.h"
#include "raddbg_server/raddbg_server.h"
//- rjf: [c]
#include "base/base_inc.c"
#include "x64/x64_inc.c"
#include "arch/arch_inc.c"
#include "socket/socket_inc.c"
#include "coff/coff.c"
#include "coff/coff_parse.c"
#include "pe/pe.c"
#include "elf/elf.c"
#include "gnu/gnu.c"
#include "gnu/gnu_parse.c"
#include "elf/elf_parse.c"
#include "demon/demon_inc.c"
#include "raddbg_server/raddbg_server.c"
////////////////////////////////
//~ rjf: Globals
typedef struct RDS_State RDS_State;
struct RDS_State
{
Arena *arena;
SOCK_Session sock_session;
//- rjf: client -> demon messages
RWMutex c2d_rw_mutex;
CondVar c2d_cv;
Arena *c2d_arena;
String8List c2d_msgs;
//- rjf: demon run state
B32 demon_is_running;
};
global RDS_State *rds_state = 0;
////////////////////////////////
//~ rjf: Demon Thread
internal void
dmn_thread__entry_point(void *p)
{
ThreadNameF("dmn_thread");
DMN_CtrlCtx *ctrl_ctx = dmn_ctrl_begin();
for(;;)
{
Temp scratch = scratch_begin(0, 0);
////////////////////////////
//- rjf: get next messages
//
String8List msgs = {0};
RWMutexScope(rds_state->c2d_rw_mutex, 1) for(;;)
{
if(rds_state->c2d_msgs.node_count != 0)
{
msgs = str8_list_copy(scratch.arena, &rds_state->c2d_msgs);
MemoryZeroStruct(&rds_state->c2d_msgs);
arena_clear(rds_state->c2d_arena);
break;
}
cond_var_wait_rw_w(rds_state->c2d_cv, rds_state->c2d_rw_mutex, max_U64);
}
////////////////////////////
//- rjf: do messages
//
for(String8Node *msg_n = msgs.first; msg_n != 0; msg_n = msg_n->next)
{
//////////////////////////
//- rjf: unpack message
//
String8 data = msg_n->string;
U16 magic_maybe = 0;
str8_deserial_read_struct(data, 0, &magic_maybe);
//////////////////////////
//- rjf: parse message
//
RDS_Msg msg = {0};
if(magic_maybe == RDS_MSG_MAGIC)
{
U64 off = sizeof(magic_maybe);
// rjf: read flat parts
off += str8_deserial_read_struct(data, off, &msg.kind);
off += str8_deserial_read_struct(data, off, &msg.flags);
off += str8_deserial_read_struct(data, off, &msg.id);
off += str8_deserial_read_struct(data, off, &msg.vaddr_range);
off += str8_deserial_read_struct(data, off, &msg.access_flags);
off += str8_deserial_read_struct(data, off, &msg.entity);
off += str8_deserial_read_struct(data, off, &msg.pid);
// rjf: read command line strings
{
U64 cmd_line_string_count = 0;
off += str8_deserial_read_struct(data, off, &cmd_line_string_count);
for EachIndex(idx, cmd_line_string_count)
{
String8 string = {0};
off += str8_deserial_read_struct(data, off, &string.size);
string = str8_substr(data, r1u64(off, off+string.size));
str8_list_push(scratch.arena, &msg.command_line, string);
}
}
// rjf: read path
{
String8 string = {0};
off += str8_deserial_read_struct(data, off, &string.size);
string = str8_substr(data, r1u64(off, off+string.size));
msg.path = string;
}
// rjf: read environment strings
{
U64 env_string_count = 0;
off += str8_deserial_read_struct(data, off, &env_string_count);
for EachIndex(idx, env_string_count)
{
String8 string = {0};
off += str8_deserial_read_struct(data, off, &string.size);
string = str8_substr(data, r1u64(off, off+string.size));
str8_list_push(scratch.arena, &msg.env, string);
}
}
// rjf: read traps
{
U64 trap_count = 0;
off += str8_deserial_read_struct(data, off, &trap_count);
for EachIndex(idx, trap_count)
{
DMN_Trap trap = {0};
off += str8_deserial_read_struct(data, off, &trap.process);
off += str8_deserial_read_struct(data, off, &trap.vaddr);
off += str8_deserial_read_struct(data, off, &trap.id);
off += str8_deserial_read_struct(data, off, &trap.flags);
off += str8_deserial_read_struct(data, off, &trap.size);
dmn_trap_chunk_list_push(scratch.arena, &msg.traps, trap_count, &trap);
}
}
}
//////////////////////////
//- rjf: do message
//
switch(msg.kind)
{
default:{}break;
//- rjf: debuggee attaching/detaching ops
case RDS_MsgKind_Launch:{}break;
case RDS_MsgKind_Attach:{}break;
case RDS_MsgKind_Kill:{}break;
case RDS_MsgKind_Detach:{}break;
//- rjf: running ops
case RDS_MsgKind_Run:{}break;
case RDS_MsgKind_SingleStep:{}break;
case RDS_MsgKind_Halt:{}break;
//- rjf: memory ops
case RDS_MsgKind_MemoryReserve:{}break;
case RDS_MsgKind_MemoryCommit:{}break;
case RDS_MsgKind_MemoryDecommit:{}break;
case RDS_MsgKind_MemoryRelease:{}break;
case RDS_MsgKind_MemoryProtect:{}break;
case RDS_MsgKind_MemoryRead:{}break;
case RDS_MsgKind_MemoryWrite:{}break;
//- rjf: thread ops
case RDS_MsgKind_ThreadRegBlockRead:{}break;
case RDS_MsgKind_ThreadRegBlockWrite:{}break;
case RDS_MsgKind_ThreadGetModuleTLSVAddr:{}break;
//- rjf: system ops
case RDS_MsgKind_ListSystemProcesses:
{
}break;
}
}
scratch_end(scratch);
}
}
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmd_line)
{
//- rjf: get port argument
U64 port = 7273;
{
U64 port_maybe = 0;
String8 port_arg_string = cmd_line_string(cmd_line, s("port"));
if(try_u64_from_str8_c_rules(port_arg_string, &port_maybe))
{
port = port_maybe;
}
}
//- rjf: set up shared state
Arena *arena = arena_alloc();
rds_state = push_array(arena, RDS_State, 1);
rds_state->arena = arena;
rds_state->sock_session = sock_session_open((U16)port, 0);
rds_state->c2d_rw_mutex = rw_mutex_alloc();
rds_state->c2d_cv = cond_var_alloc();
rds_state->c2d_arena = arena_alloc();
//- rjf: launch demon thread
thread_launch(dmn_thread__entry_point, 0);
//- rjf: loop: wait for messages, issue commands to demon
for(;;)
{
Temp scratch = scratch_begin(0, 0);
//- rjf: get next message from client
fprintf(stderr, "[waiting for message]\n");
SOCK_Protocol protocol = SOCK_Protocol_TCP;
SOCK_Endpoint endpoint = {0};
String8 data = {0};
sock_recv(scratch.arena, rds_state->sock_session, &protocol, &endpoint, &data, max_U64);
//- rjf: parse magic prefix
U16 magic_maybe = 0;
str8_deserial_read_struct(data, 0, &magic_maybe);
//- rjf: log message info
{
String8 endpoint_string = sock_string_from_endpoint(scratch.arena, endpoint);
fprintf(stderr, "[got message]\n");
fprintf(stderr, " sender: %.*s\n", str8_varg(endpoint_string));
fprintf(stderr, " magic: 0x%x\n", (int)magic_maybe);
}
//- rjf: the only kind of message we handle on the main thread is halting,
// since we need to interrupt the actual controller. just look for that one
// code here, and handle it - otherwise, all we need to do is send the message
// blob to the demon thread.
RDS_MsgKind peek_kind = RDS_MsgKind_Null;
if(magic_maybe == RDS_MSG_MAGIC)
{
str8_deserial_read_struct(data, 2, &peek_kind);
if(peek_kind == RDS_MsgKind_Halt && rds_state->demon_is_running)
{
dmn_halt(0, 0);
}
}
//- rjf: not halt? -> okay, send to demon thread, if it's ready.
if(magic_maybe == RDS_MSG_MAGIC && peek_kind != RDS_MsgKind_Halt && !rds_state->demon_is_running)
{
RWMutexScope(rds_state->c2d_rw_mutex, 1)
{
str8_list_push(rds_state->c2d_arena, &rds_state->c2d_msgs, data);
}
cond_var_broadcast(rds_state->c2d_cv);
}
scratch_end(scratch);
}
}
+28
View File
@@ -96,3 +96,31 @@ sock_endpoint_from_string(String8 address_and_port)
SOCK_Endpoint ep = sock_endpoint_from_string_port(addr, port);
return ep;
}
internal String8
sock_string_from_endpoint(Arena *arena, SOCK_Endpoint endpoint)
{
String8 result = {0};
switch(endpoint.kind)
{
default:{}break;
case SOCK_EndpointKind_IPv4:
{
result = str8f(arena, "%i.%i.%i.%i:%i", (int)endpoint.address_u8[0], (int)endpoint.address_u8[1], (int)endpoint.address_u8[2], (int)endpoint.address_u8[3], (int)endpoint.port);
}break;
case SOCK_EndpointKind_IPv6:
{
result = str8f(arena, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%i",
(int)endpoint.address_u16[0],
(int)endpoint.address_u16[1],
(int)endpoint.address_u16[2],
(int)endpoint.address_u16[3],
(int)endpoint.address_u16[4],
(int)endpoint.address_u16[5],
(int)endpoint.address_u16[6],
(int)endpoint.address_u16[7],
(int)endpoint.port);
}break;
}
return result;
}
+1
View File
@@ -59,6 +59,7 @@ typedef SOCK_WAKEUP_FUNCTION_DEF(SOCK_WakeupFunctionType);
internal SOCK_Endpoint sock_endpoint_from_string_port(String8 address, U16 port);
internal SOCK_Endpoint sock_endpoint_from_string(String8 address_and_port);
internal String8 sock_string_from_endpoint(Arena *arena, SOCK_Endpoint endpoint);
////////////////////////////////
//~ rjf: @per_os_impl Top-Level Layer Calls