diff --git a/project.4coder b/project.4coder index 0b10da9c..8819400a 100644 --- a/project.4coder +++ b/project.4coder @@ -49,7 +49,7 @@ commands = // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, - .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [raddbg wsl] // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/src/base/base_core.c b/src/base/base_core.c index c450648a..34c5d6e2 100644 --- a/src/base/base_core.c +++ b/src/base/base_core.c @@ -623,10 +623,10 @@ date_time_from_unix_time(U64 unix_time) } //////////////////////////////// -//~ rjf: Non-Fancy Ring Buffer Reads/Writes +//~ rjf: Wrapped Ring Buffer Reads/Writes internal U64 -ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size) +wrapped_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size) { Assert(src_data_size <= ring_size); { @@ -643,7 +643,7 @@ ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_d } internal U64 -ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size) +wrapped_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size) { Assert(read_size <= ring_size); { diff --git a/src/base/base_core.h b/src/base/base_core.h index 2bad9a82..ebef4649 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -1107,12 +1107,12 @@ internal void sleep_ms(U32 ms); internal Guid make_guid(void); //////////////////////////////// -//~ rjf: Non-Fancy Ring Buffer Reads/Writes +//~ rjf: Wrapped Ring Buffer Reads/Writes -internal U64 ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size); -internal U64 ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size); -#define ring_write_struct(ring_base, ring_size, ring_pos, ptr) ring_write((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr))) -#define ring_read_struct(ring_base, ring_size, ring_pos, ptr) ring_read((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr))) +internal U64 wrapped_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size); +internal U64 wrapped_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size); +#define wrapped_write_struct(ring_base, ring_size, ring_pos, ptr) wrapped_write((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr))) +#define wrapped_read_struct(ring_base, ring_size, ring_pos, ptr) wrapped_read((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr))) //////////////////////////////// //~ rjf: Sorts diff --git a/src/base/base_entry_point.c b/src/base/base_entry_point.c index a3ab9aa8..f05a9e39 100644 --- a/src/base/base_entry_point.c +++ b/src/base/base_entry_point.c @@ -58,6 +58,12 @@ main_thread_base_entry_point(int arguments_count, char **arguments) } //- rjf: initialize all included layers +#if defined(HTTP_H) && !defined(HTTP_INIT_MANUAL) + http_init(); +#endif +#if defined(SYMBOL_SERVER_H) && !defined(SMSV_INIT_MANUAL) + smsv_init(); +#endif #if defined(ARTIFACT_CACHE_H) && !defined(AC_INIT_MANUAL) ac_init(); #endif @@ -215,6 +221,9 @@ async_thread_entry_point(void *params) #if defined(FILE_STREAM_H) fs_async_tick(); #endif +#if defined(HTTP_H) + http_async_tick(); +#endif #if defined(DBG_INFO_H) di_async_tick(); #endif diff --git a/src/base/base_files.c b/src/base/base_files.c index 9a500f81..ca63d390 100644 --- a/src/base/base_files.c +++ b/src/base/base_files.c @@ -78,7 +78,7 @@ write_data_list_to_file_path(String8 path, String8List list) else { U64 bytes_to_copy = Min(write_buffer_available_size, n->string.size - n_off); - write_buffer_write_pos += ring_write(write_buffer, write_buffer_size, write_buffer_write_pos, n->string.str + n_off, bytes_to_copy); + write_buffer_write_pos += wrapped_write(write_buffer, write_buffer_size, write_buffer_write_pos, n->string.str + n_off, bytes_to_copy); n_off += bytes_to_copy; } } diff --git a/src/base/base_inc.c b/src/base/base_inc.c index 90467069..5a102437 100644 --- a/src/base/base_inc.c +++ b/src/base/base_inc.c @@ -17,6 +17,7 @@ #include "base_hash.c" #include "base_system.c" #include "base_threads.c" +#include "base_ring.c" #include "base_thread_context.c" #include "base_files.c" #include "base_shared_memory.c" diff --git a/src/base/base_inc.h b/src/base/base_inc.h index 3a378ec0..f83f8858 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -19,6 +19,7 @@ #include "base_hash.h" #include "base_system.h" #include "base_threads.h" +#include "base_ring.h" #include "base_thread_context.h" #include "base_files.h" #include "base_shared_memory.h" diff --git a/src/base/base_processes.h b/src/base/base_processes.h index 21e33cf1..33d3bb13 100644 --- a/src/base/base_processes.h +++ b/src/base/base_processes.h @@ -15,7 +15,6 @@ struct ProcessInfo String8 user_program_config_data_path; String8 user_program_cache_data_path; String8 user_program_logs_data_path; - String8 symbol_cache_path; String8List module_load_paths; String8List environment; }; diff --git a/src/base/base_ring.c b/src/base/base_ring.c new file mode 100644 index 00000000..d43f0955 --- /dev/null +++ b/src/base/base_ring.c @@ -0,0 +1,135 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Ring Functions + +internal Ring * +make_ring(Arena *arena, U64 size) +{ + Ring *ring = push_array(arena, Ring, 1); + ring->size = size; + ring->base = push_array(arena, U8, ring->size); + return ring; +} + +internal B32 +ring_try_write(Ring *ring, U64 size, void *ptr) +{ + U64 bytes_unconsumed = (ring->write_pos - ring->read_pos); + U64 bytes_available = ring->size - bytes_unconsumed; + B32 result = 0; + if(bytes_available >= size) + { + result = 1; + ring->write_pos += wrapped_write(ring->base, ring->size, ring->write_pos, ptr, size); + } + return result; +} + +internal B32 +ring_try_read(Ring *ring, U64 size, void *ptr) +{ + U64 bytes_unconsumed = (ring->write_pos - ring->read_pos); + B32 result = 0; + if(bytes_unconsumed >= size) + { + result = 1; + ring->read_pos += wrapped_read(ring->base, ring->size, ring->read_pos, ptr, size); + } + return result; +} + +//////////////////////////////// +//~ rjf: Guarded Ring Functions + +internal GuardedRing * +guarded_ring_alloc(Arena *arena, U64 size) +{ + GuardedRing *gr = push_array(arena, GuardedRing, 1); + gr->ring = make_ring(arena, size); + gr->mutex = mutex_alloc(); + gr->cv = cond_var_alloc(); + return gr; +} + +internal void +guarded_ring_release(GuardedRing *ring) +{ + mutex_release(ring->mutex); + cond_var_release(ring->cv); +} + +internal RingGuard +guarded_ring_open(GuardedRing *ring) +{ + RingGuard guard = {ring}; + mutex_take(ring->mutex); + return guard; +} + +internal void +guarded_ring_close(RingGuard *guard) +{ + mutex_drop(guard->r->mutex); +} + +internal B32 +guarded_ring_try_write(RingGuard *guard, U64 size, void *ptr) +{ + B32 result = ring_try_write(guard->r->ring, size, ptr); + if(result) + { + cond_var_broadcast(guard->r->cv); + } + return result; +} + +internal B32 +guarded_ring_try_read(RingGuard *guard, U64 size, void *ptr) +{ + B32 result = ring_try_read(guard->r->ring, size, ptr); + if(result) + { + cond_var_broadcast(guard->r->cv); + } + return result; +} + +internal B32 +guarded_ring_write_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us) +{ + B32 write_good = 0; + for(;!write_good;) + { + write_good = guarded_ring_try_write(guard, size, ptr); + if(now_time_us() >= endt_us) + { + break; + } + if(!write_good) + { + cond_var_wait(guard->r->cv, guard->r->mutex, endt_us); + } + } + return write_good; +} + +internal B32 +guarded_ring_read_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us) +{ + B32 read_good = 0; + for(;!read_good;) + { + read_good = guarded_ring_try_read(guard, size, ptr); + if(now_time_us() >= endt_us) + { + break; + } + if(!read_good) + { + cond_var_wait(guard->r->cv, guard->r->mutex, endt_us); + } + } + return read_good; +} diff --git a/src/base/base_ring.h b/src/base/base_ring.h new file mode 100644 index 00000000..59b07b86 --- /dev/null +++ b/src/base/base_ring.h @@ -0,0 +1,56 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_RING_H +#define BASE_RING_H + +typedef struct Ring Ring; +struct Ring +{ + U8 *base; + U64 size; + U64 write_pos; + U64 read_pos; +}; + +typedef struct GuardedRing GuardedRing; +struct GuardedRing +{ + Ring *ring; + Mutex mutex; + CondVar cv; +}; + +typedef struct RingGuard RingGuard; +struct RingGuard +{ + GuardedRing *r; +}; + +//////////////////////////////// +//~ rjf: Ring Functions + +internal Ring *make_ring(Arena *arena, U64 size); +internal B32 ring_try_write(Ring *ring, U64 size, void *ptr); +internal B32 ring_try_read(Ring *ring, U64 size, void *ptr); +#define ring_try_write_struct(ring, ptr) ring_try_write((ring), sizeof(*(ptr)), (ptr)) +#define ring_try_read_struct(ring, ptr) ring_try_read((ring), sizeof(*(ptr)), (ptr)) + +//////////////////////////////// +//~ rjf: Guarded Ring Functions + +internal GuardedRing *guarded_ring_alloc(Arena *arena, U64 size); +internal void guarded_ring_release(GuardedRing *ring); +internal RingGuard guarded_ring_open(GuardedRing *ring); +internal void guarded_ring_close(RingGuard *guard); +internal B32 guarded_ring_try_write(RingGuard *guard, U64 size, void *ptr); +internal B32 guarded_ring_try_read(RingGuard *guard, U64 size, void *ptr); +#define guarded_ring_try_write_struct(ring, ptr) guarded_ring_try_write((ring), sizeof(*(ptr)), (ptr)) +#define guarded_ring_try_read_struct(ring, ptr) guarded_ring_try_read((ring), sizeof(*(ptr)), (ptr)) +internal B32 guarded_ring_write_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us); +internal B32 guarded_ring_read_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us); +#define guarded_ring_write_struct_or_wait(ring, ptr, endt_us) guarded_ring_write_or_wait((ring), sizeof(*(ptr)), (ptr), (endt_us)) +#define guarded_ring_read_struct_or_wait(ring, ptr, endt_us) guarded_ring_read_or_wait((ring), sizeof(*(ptr)), (ptr), (endt_us)) +#define guarded_ring_write_string_or_wait(ring, string, endt_us) guarded_ring_write_or_wait((ring), (string).size, (string).str, (endt_us)) + +#endif // BASE_RING_H diff --git a/src/dbg_engine/dbg_engine_ctrl.c b/src/dbg_engine/dbg_engine_ctrl.c index 7b2ff413..1b4081a2 100644 --- a/src/dbg_engine/dbg_engine_ctrl.c +++ b/src/dbg_engine/dbg_engine_ctrl.c @@ -2144,8 +2144,8 @@ d_u2c_push_msgs(D_MsgList *msgs, U64 endt_us) U64 needed_size = sizeof(msgs_srlzed_baked.size) + msgs_srlzed_baked.size; if(available_size >= needed_size) { - d_ctrl_state->u2c_ring_write_pos += ring_write_struct(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_write_pos, &msgs_srlzed_baked.size); - d_ctrl_state->u2c_ring_write_pos += ring_write(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_write_pos, msgs_srlzed_baked.str, msgs_srlzed_baked.size); + d_ctrl_state->u2c_ring_write_pos += wrapped_write_struct(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_write_pos, &msgs_srlzed_baked.size); + d_ctrl_state->u2c_ring_write_pos += wrapped_write(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_write_pos, msgs_srlzed_baked.str, msgs_srlzed_baked.size); good = 1; break; } @@ -2174,10 +2174,10 @@ d_u2c_pop_msgs(Arena *arena) if(unconsumed_size >= sizeof(U64)) { U64 size_to_decode = 0; - d_ctrl_state->u2c_ring_read_pos += ring_read_struct(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_read_pos, &size_to_decode); + d_ctrl_state->u2c_ring_read_pos += wrapped_read_struct(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_read_pos, &size_to_decode); msgs_srlzed_baked.size = size_to_decode; msgs_srlzed_baked.str = push_array_no_zero(scratch.arena, U8, msgs_srlzed_baked.size); - d_ctrl_state->u2c_ring_read_pos += ring_read(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_read_pos, msgs_srlzed_baked.str, size_to_decode); + d_ctrl_state->u2c_ring_read_pos += wrapped_read(d_ctrl_state->u2c_ring_base, d_ctrl_state->u2c_ring_size, d_ctrl_state->u2c_ring_read_pos, msgs_srlzed_baked.str, size_to_decode); break; } cond_var_wait(d_ctrl_state->u2c_ring_cv, d_ctrl_state->u2c_ring_mutex, max_U64); @@ -2210,8 +2210,8 @@ d_c2u_push_events(D_EventList *events) U64 needed_size = sizeof(event_srlzed.size) + event_srlzed.size; if(available_size >= needed_size) { - d_ctrl_state->c2u_ring_write_pos += ring_write_struct(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_write_pos, &event_srlzed.size); - d_ctrl_state->c2u_ring_write_pos += ring_write(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_write_pos, event_srlzed.str, event_srlzed.size); + d_ctrl_state->c2u_ring_write_pos += wrapped_write_struct(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_write_pos, &event_srlzed.size); + d_ctrl_state->c2u_ring_write_pos += wrapped_write(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_write_pos, event_srlzed.str, event_srlzed.size); break; } cond_var_wait(d_ctrl_state->c2u_ring_cv, d_ctrl_state->c2u_ring_mutex, now_time_us()+100); @@ -2238,11 +2238,11 @@ d_c2u_pop_events(Arena *arena) if(unconsumed_size >= sizeof(U64)) { U64 size_to_decode = 0; - d_ctrl_state->c2u_ring_read_pos += ring_read_struct(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_read_pos, &size_to_decode); + d_ctrl_state->c2u_ring_read_pos += wrapped_read_struct(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_read_pos, &size_to_decode); String8 event_srlzed = {0}; event_srlzed.size = size_to_decode; event_srlzed.str = push_array_no_zero(scratch.arena, U8, event_srlzed.size); - d_ctrl_state->c2u_ring_read_pos += ring_read(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_read_pos, event_srlzed.str, event_srlzed.size); + d_ctrl_state->c2u_ring_read_pos += wrapped_read(d_ctrl_state->c2u_ring_base, d_ctrl_state->c2u_ring_size, d_ctrl_state->c2u_ring_read_pos, event_srlzed.str, event_srlzed.size); D_Event *new_event = d_event_list_push(arena, &events); *new_event = d_event_from_serialized_string(arena, event_srlzed); } @@ -2683,28 +2683,11 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DM } ////////////////////////////// - //- rjf: determine debug info unique identifier + //- rjf: no found debug info path -> try to fall back on symbol server // - String8 debug_info_unique_identifier = {0}; - if(module_info->debug_info_path.size != 0) + if(initial_debug_info_path.size == 0) { - Guid guid = module_info->debug_info_guid; - debug_info_unique_identifier = str8f(scratch.arena, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", - guid.data1, guid.data2, guid.data3, guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3], guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7], module_info->debug_info_age); - } - - ////////////////////////////// - //- rjf: build local server cache debug info path - // - String8 local_server_cache_debug_info_path = {0}; - if(debug_info_unique_identifier.size != 0 && module_info->debug_info_path.size != 0) - { - // TODO(rjf): this is not complete - assuming stripped etc. - just following pattern from ntdll.dll for now - String8 symbol_cache_path = get_process_info()->symbol_cache_path; - String8 module_name = str8_skip_last_slash(module_info->module_path); - String8 dbg_name = str8_skip_last_slash(module_info->debug_info_path); - String8 path = str8f(scratch.arena, "%S/%S/%S/stripped/%S", symbol_cache_path, module_name, debug_info_unique_identifier, dbg_name); - local_server_cache_debug_info_path = path_normalized_from_string(arena, path); + initial_debug_info_path = smsv_local_path_from_key(arena, str8_skip_last_slash(module_info->module_path), str8_skip_last_slash(module_info->debug_info_path), module_info->debug_info_guid, module_info->debug_info_age); } ////////////////////////////// @@ -2725,7 +2708,6 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DM info.unwinder = unwinder; info.unwind_info = unwind_info_opaque; info.local_debug_info_path = initial_debug_info_path; - info.debug_info_unique_identifier = debug_info_unique_identifier; info.raddbg_attached_marker_voff = module_info->raddbg_is_attached_marker_voff; info.raddbg_data = raddbg_data; } diff --git a/src/dbg_engine/dbg_engine_ctrl.h b/src/dbg_engine/dbg_engine_ctrl.h index 48616ca3..1617dfb6 100644 --- a/src/dbg_engine/dbg_engine_ctrl.h +++ b/src/dbg_engine/dbg_engine_ctrl.h @@ -169,8 +169,6 @@ struct D_ModuleInfo UWND_Unwinder unwinder; void *unwind_info; String8 local_debug_info_path; - String8 local_server_cache_debug_info_path; - String8 debug_info_unique_identifier; U64 raddbg_attached_marker_voff; String8 raddbg_data; }; diff --git a/src/http/http.c b/src/http/http.c new file mode 100644 index 00000000..db9ec6ca --- /dev/null +++ b/src/http/http.c @@ -0,0 +1,33 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////////////////////////////////////// +//~ rjf: Basic Type Functions + +internal HTTP_StatusKind +http_status_kind_from_code(HTTP_StatusCode code) +{ + HTTP_StatusKind kind = HTTP_StatusKind_Null; + if(0){} + else if(HTTP_StatusCode_FirstInformational <= code && code <= HTTP_StatusCode_LastInformational) + { + kind = HTTP_StatusKind_Informational; + } + else if(HTTP_StatusCode_FirstSuccessful <= code && code <= HTTP_StatusCode_LastSuccessful) + { + kind = HTTP_StatusKind_Successful; + } + else if(HTTP_StatusCode_FirstRedirection <= code && code <= HTTP_StatusCode_LastRedirection) + { + kind = HTTP_StatusKind_Redirection; + } + else if(HTTP_StatusCode_FirstClientError <= code && code <= HTTP_StatusCode_LastClientError) + { + kind = HTTP_StatusKind_ClientError; + } + else if(HTTP_StatusCode_FirstServerError <= code && code <= HTTP_StatusCode_LastServerError) + { + kind = HTTP_StatusKind_ServerError; + } + return kind; +} diff --git a/src/http/http.h b/src/http/http.h new file mode 100644 index 00000000..d52179cd --- /dev/null +++ b/src/http/http.h @@ -0,0 +1,170 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef HTTP_H +#define HTTP_H + +//////////////////////////////////////////////////////////////// +//~ rjf: Status Codes + +typedef enum HTTP_StatusKind +{ + HTTP_StatusKind_Null, + HTTP_StatusKind_Informational, + HTTP_StatusKind_Successful, + HTTP_StatusKind_Redirection, + HTTP_StatusKind_ClientError, + HTTP_StatusKind_ServerError, + HTTP_StatusKind_COUNT +} +HTTP_StatusKind; + +typedef U32 HTTP_StatusCode; +typedef enum HTTP_StatusCodeEnum +{ + //- rjf: informational messages + HTTP_StatusCode_FirstInformational = 100, + HTTP_StatusCode_LastInformational = 199, + HTTP_StatusCode_Continue = 100, + HTTP_StatusCode_SwitchingProtocols = 101, + HTTP_StatusCode_Processing = 102, + HTTP_StatusCode_EarlyHints = 103, + + //- rjf: successful messages + HTTP_StatusCode_FirstSuccessful = 200, + HTTP_StatusCode_LastSuccessful = 299, + HTTP_StatusCode_OK = 200, + HTTP_StatusCode_Created = 201, + HTTP_StatusCode_Accepted = 202, + HTTP_StatusCode_NonAuthoritativeInformation = 203, + HTTP_StatusCode_NoContent = 204, + HTTP_StatusCode_ResetContent = 205, + HTTP_StatusCode_PartialContent = 206, + HTTP_StatusCode_MultiStatus = 207, + HTTP_StatusCode_AlreadyReported = 208, + HTTP_StatusCode_IMUsed = 226, + + //- rjf: redirection messages + HTTP_StatusCode_FirstRedirection = 300, + HTTP_StatusCode_LastRedirection = 399, + HTTP_StatusCode_MultipleChoices = 300, + HTTP_StatusCode_MovedPermanently = 301, + HTTP_StatusCode_Found = 302, + HTTP_StatusCode_SeeOther = 303, + HTTP_StatusCode_NotModified = 304, + HTTP_StatusCode_UseProxy = 305, + HTTP_StatusCode_TemporaryRedirect = 307, + HTTP_StatusCode_PermanentRedirect = 308, + + //- rjf: client error responses + HTTP_StatusCode_FirstClientError = 400, + HTTP_StatusCode_LastClientError = 499, + HTTP_StatusCode_BadRequest = 400, + HTTP_StatusCode_Unauthorized = 401, + HTTP_StatusCode_PaymentRequired = 402, + HTTP_StatusCode_Forbidden = 403, + HTTP_StatusCode_NotFound = 404, + HTTP_StatusCode_MethodNotAllowed = 405, + HTTP_StatusCode_NotAcceptable = 406, + HTTP_StatusCode_ProxyAuthenticationRequired = 407, + HTTP_StatusCode_RequestTimeout = 408, + HTTP_StatusCode_Conflict = 409, + HTTP_StatusCode_Gone = 410, + HTTP_StatusCode_LengthRequired = 411, + HTTP_StatusCode_PreconditionFailed = 412, + HTTP_StatusCode_PayloadTooLarge = 413, + HTTP_StatusCode_URITooLong = 414, + HTTP_StatusCode_UnsupportedMediaType = 415, + HTTP_StatusCode_RangeNotSatisfiable = 416, + HTTP_StatusCode_ExpectationFailed = 417, + HTTP_StatusCode_ImATeapot = 418, + HTTP_StatusCode_MisdirectedRequest = 421, + HTTP_StatusCode_UnprocessableContent = 422, + HTTP_StatusCode_Locked = 423, + HTTP_StatusCode_FailedDependency = 424, + HTTP_StatusCode_TooEarly = 425, + HTTP_StatusCode_UpgradeRequired = 426, + HTTP_StatusCode_PreconditionRequired = 428, + HTTP_StatusCode_TooManyRequests = 429, + HTTP_StatusCode_RequestHeaderFieldsTooLarge = 431, + HTTP_StatusCode_UnavailableForLegalReasons = 451, + + //- rjf: servor error responses + HTTP_StatusCode_FirstServerError = 500, + HTTP_StatusCode_LastServerError = 599, + HTTP_StatusCode_InternalServerError = 500, + HTTP_StatusCode_NotImplemented = 501, + HTTP_StatusCode_BadGateway = 502, + HTTP_StatusCode_ServiceUnavailable = 503, + HTTP_StatusCode_GatewayTimeout = 504, + HTTP_StatusCode_HTTPVersionNotSupported = 505, + HTTP_StatusCode_VariantAlsoNegotiates = 506, + HTTP_StatusCode_InsufficientStorage = 507, + HTTP_StatusCode_LoopDetected = 508, + HTTP_StatusCode_NotExtended = 510, + HTTP_StatusCode_NetworkAuthenticationRequired = 511, +} +HTTP_StatusCodeEnum; + +//////////////////////////////////////////////////////////////// +//~ rjf: Method Kinds + +typedef enum HTTP_Method +{ + HTTP_Method_Get, + HTTP_Method_Head, + HTTP_Method_Post, + HTTP_Method_Put, + HTTP_Method_Delete, + HTTP_Method_Connect, + HTTP_Method_Options, + HTTP_Method_Trace, + HTTP_Method_Patch, + HTTP_Method_COUNT +} +HTTP_Method; + +//////////////////////////////////////////////////////////////// +//~ rjf: Request Parameters Bundle + +typedef struct HTTP_RequestParams HTTP_RequestParams; +struct HTTP_RequestParams +{ + U64 id; + HTTP_Method method; + String8 url; + String8 body; + String8 user_agent; + String8 authorization; + String8 content_type; +}; + +//////////////////////////////////////////////////////////////// +//~ rjf: Response + +typedef struct HTTP_Response HTTP_Response; +struct HTTP_Response +{ + U64 id; + HTTP_StatusCode code; + String8 body; +}; + +//////////////////////////////////////////////////////////////// +//~ rjf: Basic Type Functions + +internal HTTP_StatusKind http_status_kind_from_code(HTTP_StatusCode code); + +//////////////////////////////////////////////////////////////// +//~ rjf: @per_os_impl Top-Level Layer Calls + +internal void http_init(void); +internal void http_async_tick(void); + +//////////////////////////////////////////////////////////////// +//~ rjf: @per_os_impl I/O Ring Functions + +internal B32 http_push_request(GuardedRing *out_ring, HTTP_RequestParams *params, U64 endt_us); +internal B32 http_pop_response(Arena *arena, GuardedRing *out_ring, HTTP_Response *response_out, U64 endt_us); + +#endif // HTTP_H diff --git a/src/http/http_inc.c b/src/http/http_inc.c new file mode 100644 index 00000000..b8242b35 --- /dev/null +++ b/src/http/http_inc.c @@ -0,0 +1,9 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "http.c" +#if OS_WINDOWS +# include "win32/http/win32_http.c" +#else +# include "http_stub.c" +#endif diff --git a/src/http/http_inc.h b/src/http/http_inc.h new file mode 100644 index 00000000..a839a7f1 --- /dev/null +++ b/src/http/http_inc.h @@ -0,0 +1,14 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef HTTP_INC_H +#define HTTP_INC_H + +#include "http.h" +#if OS_WINDOWS +# include "win32/http/win32_http.h" +#else +# include "http_stub.h" +#endif + +#endif // HTTP_INC_H diff --git a/src/http/http_stub.c b/src/http/http_stub.c new file mode 100644 index 00000000..5074dcd8 --- /dev/null +++ b/src/http/http_stub.c @@ -0,0 +1,22 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////////////////////////////////////// +//~ rjf: @per_os_impl Top-Level Layer Calls + +internal void http_init(void){} + +//////////////////////////////////////////////////////////////// +//~ rjf: @per_os_impl I/O Ring Functions + +internal B32 +http_push_request(GuardedRing *out_ring, HTTP_RequestParams *params, U64 endt_us) +{ + return 0; +} + +internal B32 +http_pop_response(Arena *arena, GuardedRing *out_ring, HTTP_Response *response_out, U64 endt_us) +{ + return 0; +} diff --git a/src/http/http_stub.h b/src/http/http_stub.h new file mode 100644 index 00000000..d644b7d0 --- /dev/null +++ b/src/http/http_stub.h @@ -0,0 +1,7 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef HTTP_STUB_H +#define HTTP_STUB_H + +#endif // HTTP_STUB_H diff --git a/src/mutable_text/mutable_text.c b/src/mutable_text/mutable_text.c index 420c5e94..ecaf58e5 100644 --- a/src/mutable_text/mutable_text.c +++ b/src/mutable_text/mutable_text.c @@ -59,10 +59,10 @@ mtx_enqueue_op(MTX_MutThread *thread, C_Key buffer_key, MTX_Op op) U64 needed_size = sizeof(buffer_key) + sizeof(op.range) + sizeof(op.replace.size) + op.replace.size; if(available_size >= needed_size) { - thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &buffer_key); - thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.range); - thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.replace.size); - thread->ring_write_pos += ring_write(thread->ring_base, thread->ring_size, thread->ring_write_pos, op.replace.str, op.replace.size); + thread->ring_write_pos += wrapped_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &buffer_key); + thread->ring_write_pos += wrapped_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.range); + thread->ring_write_pos += wrapped_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.replace.size); + thread->ring_write_pos += wrapped_write(thread->ring_base, thread->ring_size, thread->ring_write_pos, op.replace.str, op.replace.size); break; } cond_var_wait(thread->cv, thread->mutex, max_U64); @@ -78,11 +78,11 @@ mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, C_Key *buffer_key_out, MTX_O U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos; if(unconsumed_size >= sizeof(*buffer_key_out) + sizeof(op_out->range) + sizeof(op_out->replace.size)) { - thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, buffer_key_out); - thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->range); - thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->replace.size); + thread->ring_read_pos += wrapped_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, buffer_key_out); + thread->ring_read_pos += wrapped_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->range); + thread->ring_read_pos += wrapped_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->replace.size); op_out->replace.str = push_array_no_zero(arena, U8, op_out->replace.size); - thread->ring_read_pos += ring_read(thread->ring_base, thread->ring_size, thread->ring_read_pos, op_out->replace.str, op_out->replace.size); + thread->ring_read_pos += wrapped_read(thread->ring_base, thread->ring_size, thread->ring_read_pos, op_out->replace.str, op_out->replace.size); break; } cond_var_wait(thread->cv, thread->mutex, max_U64); diff --git a/src/raddbg/generated/raddbg.meta.c b/src/raddbg/generated/raddbg.meta.c index 15a90e62..bafabca8 100644 --- a/src/raddbg/generated/raddbg.meta.c +++ b/src/raddbg/generated/raddbg.meta.c @@ -626,7 +626,7 @@ RD_CmdKindInfo rd_cmd_kind_info_table[255] = { str8_lit_comp("open_crash_dump"), str8_lit_comp("Opens a crash dump file and recreates debuggee state at the time of the crash."), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*0)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*1)|(RD_QueryFlag_Required*1), RD_RegSlot_FilePath, str8_lit_comp("folder:\"$input\""), str8_lit_comp(""), D_EntityKind_Null}}, { str8_lit_comp("exit"), str8_lit_comp("Exits the debugger."), str8_lit_comp("quit,close,abort"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}}, { str8_lit_comp("open_palette"), str8_lit_comp("Opens the palette."), str8_lit_comp("help,cmd,lister"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}}, -{ str8_lit_comp("run_command"), str8_lit_comp("Runs a command from the command palette."), str8_lit_comp("help,cmd"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*1)|(RD_QueryFlag_Required*1), RD_RegSlot_CmdName, str8_lit_comp("query:commands"), str8_lit_comp("commands"), D_EntityKind_Null}}, +{ str8_lit_comp("run_command"), str8_lit_comp("Runs a command from the command palette."), str8_lit_comp("help,cmd"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*0)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*1)|(RD_QueryFlag_Required*1), RD_RegSlot_CmdName, str8_lit_comp("query:commands"), str8_lit_comp("commands"), D_EntityKind_Null}}, { str8_lit_comp("run_ext_driver_text_command"), str8_lit_comp(""), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*0)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}}, { str8_lit_comp("state"), str8_lit_comp(""), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}}, { str8_lit_comp("eval"), str8_lit_comp(""), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0)|(RD_CmdKindFlag_ListInEval*0)|(RD_CmdKindFlag_ListInMemory*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Expr, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}}, @@ -845,7 +845,7 @@ RD_CmdKindInfo rd_cmd_kind_info_table[255] = { str8_lit_comp("geo3d"), str8_lit_comp("Opens a Geometry (3D) tab."), {0}, {0}, RD_CmdKindFlag_ListInUI|RD_CmdKindFlag_ListInIPCDocs|RD_CmdKindFlag_ListInTab}, }; -struct {String8 string; CFG_Binding binding;} rd_default_binding_table[114] = +struct {String8 string; CFG_Binding binding;} rd_default_binding_table[116] = { {str8_lit_comp("kill_all"), {WM_Key_F5, 0 |WM_Modifier_Shift }}, {str8_lit_comp("step_into_inst"), {WM_Key_F11, 0 |WM_Modifier_Alt}}, @@ -944,7 +944,9 @@ struct {String8 string; CFG_Binding binding;} rd_default_binding_table[114] = {str8_lit_comp("goto_line"), {WM_Key_G, 0 |WM_Modifier_Ctrl }}, {str8_lit_comp("goto_address"), {WM_Key_G, 0 |WM_Modifier_Alt}}, {str8_lit_comp("search"), {WM_Key_F, 0 |WM_Modifier_Ctrl }}, +{str8_lit_comp("search"), {WM_Key_Slash, 0 }}, {str8_lit_comp("search_backwards"), {WM_Key_R, 0 |WM_Modifier_Ctrl }}, +{str8_lit_comp("search_backwards"), {WM_Key_Slash, 0 |WM_Modifier_Ctrl }}, {str8_lit_comp("find_next"), {WM_Key_F3, 0 }}, {str8_lit_comp("find_prev"), {WM_Key_F3, 0 |WM_Modifier_Ctrl }}, {str8_lit_comp("find_selected_thread"), {WM_Key_F4, 0 }}, diff --git a/src/raddbg/raddbg.mdesk b/src/raddbg/raddbg.mdesk index 70e376b3..aa6a68b4 100644 --- a/src/raddbg/raddbg.mdesk +++ b/src/raddbg/raddbg.mdesk @@ -949,7 +949,7 @@ RD_CmdTable: // | | | | {OpenPalette 1 1 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 List "open_palette" "Open Palette" "Opens the palette." "help,cmd,lister" "" } //- rjf: command fast-path - {RunCommand 1 1 0 0 0 0 "query:commands" CmdName commands Nil Null 0 0 0 0 0 1 1 Null "run_command" "Run Command" "Runs a command from the command palette." "help,cmd" "" } + {RunCommand 0 0 0 0 0 0 "query:commands" CmdName commands Nil Null 0 0 0 0 0 1 1 Null "run_command" "Run Command" "Runs a command from the command palette." "help,cmd" "" } //- rjf: external drivers {RunExternalDriverTextCommand 0 0 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Null "run_ext_driver_text_command" "Run External Driver Text Command" "" "" "" } @@ -1415,7 +1415,9 @@ RD_DefaultBindingTable: { "goto_line" G ctrl 0 0 } { "goto_address" G 0 0 alt } { "search" F ctrl 0 0 } + { "search" Slash 0 0 0 } { "search_backwards" R ctrl 0 0 } + { "search_backwards" Slash ctrl 0 0 } { "find_next" F3 0 0 0 } { "find_prev" F3 shift 0 0 } diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 0d94229b..edb3c81f 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -1685,7 +1685,7 @@ rd_view_ui(Rng2F32 rect) //- rjf: build container UI_Box *search_row = &ui_nil_box; - UI_PrefHeight(ui_px(search_row_height, 1.f)) UI_TagF("focus") + UI_PrefHeight(ui_px(search_row_height, 1.f)) // UI_TagF("focus") { if(!vs->contents_are_focused) { @@ -6633,12 +6633,19 @@ rd_window_frame(void) UI_PermissionFlag_KeyboardSecondary|UI_PermissionFlag_Clicks|UI_PermissionFlag_ScrollX|UI_PermissionFlag_ScrollY : UI_PermissionFlag_All) { + B32 has_footer = (is_lister && !is_anchored); + F32 container_corner_top_radius_px = is_lister ? ui_top_font_size()*1.f : ui_top_font_size()*0.15f; + F32 container_corner_bottom_radius_px = has_footer ? ui_top_font_size()*1.f : ui_top_font_size()*0.15f; + // rjf: build top-level container box UI_Box *container = &ui_nil_box; UI_Rect(rect) UI_ChildLayoutAxis(Axis2_Y) UI_Squish(0.1f-0.1f*open_t) UI_Transparency(1.f-open_t) - UI_CornerRadius(ui_top_font_size()*0.25f) + UI_CornerRadius00(container_corner_top_radius_px) + UI_CornerRadius10(container_corner_top_radius_px) + UI_CornerRadius01(container_corner_bottom_radius_px) + UI_CornerRadius11(container_corner_bottom_radius_px) { container = ui_build_box_from_stringf(UI_BoxFlag_Clickable| UI_BoxFlag_DrawBorder| @@ -6680,7 +6687,7 @@ rd_window_frame(void) // rjf: build contents F32 total_height_px = dim_2f32(rect).y; F32 footer_height_px = 0; - if(is_lister && !is_anchored) + if(has_footer) { footer_height_px = ui_top_font_size()*4.f; } @@ -7573,6 +7580,29 @@ rd_window_frame(void) ui_label(str8_lit(BUILD_TITLE_STRING_LITERAL)); } + // rjf: update available? + if(rd_state->newer_update_available) + UI_FontSize(ui_top_font_size()*0.85f) + UI_PrefWidth(ui_text_dim(10, 1)) + UI_TextAlignment(UI_TextAlign_Center) + { + DR_FStrList fstrs = {0}; + ui_set_next_hover_cursor(WM_Cursor_HandPoint); + UI_Box *label = ui_build_box_from_stringf(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, "##update"); + UI_Signal sig = ui_signal_from_box(label); + DR_FStrParams p = {ui_top_font(), ui_top_text_raster_flags(), ui_color_from_name(s("text")), ui_top_font_size()}; + if(ui_key_match(ui_hot_key(), label->key)) + { + p.underline_thickness = ui_top_font_size()*0.2f; + } + dr_fstrs_push_new(scratch.arena, &fstrs, &p, s("Update Available")); + ui_box_equip_display_fstrs(label, &fstrs); + if(ui_clicked(sig)) + { + wm_open_in_browser(s("https://github.com/EpicGamesExt/raddebugger/releases/latest")); + } + } + scratch_end(scratch); } } @@ -7910,6 +7940,7 @@ rd_window_frame(void) // if(content_rect.x1 > content_rect.x0 && content_rect.y1 > content_rect.y0) { + B32 any_disasm_open = 0; ProfScope("leaf panel UI") for(CFG_PanelNode *panel = panel_tree.root; panel != &cfg_nil_panel_node; @@ -7924,6 +7955,10 @@ rd_window_frame(void) panel_tree.focused == panel); CFG_Node *selected_tab = panel->selected_tab; RD_ViewState *selected_tab_view_state = rd_view_state_from_cfg(selected_tab); + if(str8_match(selected_tab->string, s("disasm"), 0)) + { + any_disasm_open = 1; + } ProfScope("leaf panel UI work - %.*s: %.*s", str8_varg(selected_tab->string), str8_varg(rd_expr_from_cfg(selected_tab))) UI_Focus(panel_is_focused ? UI_FocusKind_Null : UI_FocusKind_Off) { @@ -8752,6 +8787,10 @@ rd_window_frame(void) } } } + if(!any_disasm_open && rd_regs()->prefer_disasm) + { + rd_regs()->prefer_disasm = 0; + } } //////////////////////////// @@ -9342,6 +9381,8 @@ rd_window_frame(void) FNT_Tag font = rd_font_from_slot(RD_FontSlot_Code); Vec2F32 p = ui_mouse(); dr_rect(hover_debug_box->rect, v4f32(1, 1, 1, 0.2f), 0, 0, 0); + R_Rect2DInst *border = dr_rect(hover_debug_box->rect, v4f32(1, 0, 0, 1.f), 0, 0, 0); + MemoryCopyArray(border->corner_radii, hover_debug_box->corner_radii); dr_text(font, 12.f, 0, 0, FNT_RasterFlag_Hinted, p, v4f32(1, 1, 1, 1), push_str8f(scratch.arena, "key: 0x%I64x", hover_debug_box->key.u64[0])); p.y += 20.f; dr_text(font, 12.f, 0, 0, FNT_RasterFlag_Hinted, p, v4f32(1, 1, 1, 1), push_str8f(scratch.arena, "string: '%S'", hover_debug_box->string)); @@ -10641,6 +10682,19 @@ rd_init(CmdLine *cmdln) rd_state->drag_drop_regs = push_array(rd_state->drag_drop_arena, RD_Regs, 1); rd_state->top_regs = &rd_state->base_regs; + // rjf: set up update check http ring & send request + { + rd_state->update_check_http_ring = guarded_ring_alloc(arena, KB(8)); + HTTP_RequestParams p = + { + .id = 1, + .method = HTTP_Method_Get, + .url = s("https://api.github.com/repos/EpicGamesExt/raddebugger/releases/latest"), + .user_agent = s("raddebugger"), + }; + http_push_request(rd_state->update_check_http_ring, &p, 0); + } + // rjf: set up schemas { rd_state->cfg_schema_table = push_array(rd_state->arena, CFG_SchemaTable, 1); @@ -11043,6 +11097,33 @@ rd_frame(void) MemoryZeroStruct(&rd_state->cmd_outputs); } + ////////////////////////////// + //- rjf: check for updates + // + if(!rd_state->got_update_check) + { + Temp scratch = scratch_begin(0, 0); + HTTP_Response response = {0}; + if(http_pop_response(scratch.arena, rd_state->update_check_http_ring, &response, 0)) + { + rd_state->got_update_check = 1; + MD_Node *root = md_tree_from_string(scratch.arena, response.body)->first; + MD_Node *html_url = md_child_from_string(root, s("html_url"), 0); + String8 newest_release_url = html_url->first->string; + U64 tag_pos = str8_find_needle(newest_release_url, 0, s("tag/v"), 0); + String8 tag_name = str8_skip(newest_release_url, tag_pos); + U64 suffix_pos = str8_find_needle(tag_name, 0, s("-"), 0); + String8 version_name = str8_prefix(tag_name, suffix_pos); + U64 release_version = version_from_str8(version_name); + U64 current_version = Version(BUILD_VERSION_MAJOR, BUILD_VERSION_MINOR, BUILD_VERSION_PATCH); + if(current_version < release_version) + { + rd_state->newer_update_available = 1; + } + } + scratch_end(scratch); + } + ////////////////////////////// //- rjf: iterate all tabs, touch their view-states // @@ -11082,6 +11163,7 @@ rd_frame(void) ////////////////////////////// //- rjf: apply debug info config trees -> loaded debug info cache // + ProfScope("apply debug info config trees -> loaded debug info cache") { U64 current_update_tick_idx = update_tick_idx(); @@ -12883,7 +12965,8 @@ rd_frame(void) { rd_cmd(RD_CmdKind_PushQuery, .do_implicit_root = 1, - .do_lister = info->query.expr.size != 0); + .do_lister = (info->query.expr.size != 0), + .expr = info->query.expr); } }break; diff --git a/src/raddbg/raddbg_core.h b/src/raddbg/raddbg_core.h index 235c122c..e592b2e6 100644 --- a/src/raddbg/raddbg_core.h +++ b/src/raddbg/raddbg_core.h @@ -394,6 +394,11 @@ struct RD_State S32 frame_depth; U64 frame_eval_memread_endt_us; + // rjf: update checker + B32 got_update_check; + B32 newer_update_available; + GuardedRing *update_check_http_ring; + // rjf: config bucket paths Arena *user_path_arena; String8 user_path; diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 5d4f8d49..8f1326eb 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -4,6 +4,7 @@ //////////////////////////////// //~ rjf: post-0.9.26 TODO notes // +// [ ] unwinding crash - f5 on bp in w32_http_status_callback to repro // [ ] jeff stepping bug // [ ] wassim memory bug // [ ] switch off spoofs for step-over @@ -309,6 +310,8 @@ #include "minidump/minidump_parse.h" #include "mdesk/mdesk.h" #include "window_manager/window_manager_inc.h" +#include "http/http_inc.h" +#include "symbol_server/symbol_server_inc.h" #include "config/config_inc.h" #include "content/content.h" #include "file_stream/file_stream.h" @@ -366,6 +369,8 @@ #include "minidump/minidump_parse.c" #include "mdesk/mdesk.c" #include "window_manager/window_manager_inc.c" +#include "http/http_inc.c" +#include "symbol_server/symbol_server_inc.c" #include "config/config_inc.c" #include "content/content.c" #include "file_stream/file_stream.c" @@ -468,8 +473,8 @@ ipc_signaler_thread__entry_point(void *p) U64 available_size = (sizeof(ipc_s2m_ring_buffer) - unconsumed_size); if(available_size >= sizeof(U64)+sizeof(msg.size)) { - ipc_s2m_ring_write_pos += ring_write_struct(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_write_pos, &msg.size); - ipc_s2m_ring_write_pos += ring_write(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_write_pos, msg.str, msg.size); + ipc_s2m_ring_write_pos += wrapped_write_struct(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_write_pos, &msg.size); + ipc_s2m_ring_write_pos += wrapped_write(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_write_pos, msg.str, msg.size); break; } cond_var_wait(ipc_s2m_ring_cv, ipc_s2m_ring_mutex, max_U64); @@ -659,10 +664,10 @@ entry_point(CmdLine *cmd_line) { consumed = 1; ipc_command_frame = 1; - ipc_s2m_ring_read_pos += ring_read_struct(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_read_pos, &msg.size); + ipc_s2m_ring_read_pos += wrapped_read_struct(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_read_pos, &msg.size); msg.size = Min(msg.size, unconsumed_size); msg.str = push_array(scratch.arena, U8, msg.size); - ipc_s2m_ring_read_pos += ring_read(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_read_pos, msg.str, msg.size); + ipc_s2m_ring_read_pos += wrapped_read(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_read_pos, msg.str, msg.size); } } if(consumed) diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index 85189195..6dd2aab3 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -2543,12 +2543,12 @@ RD_VIEW_UI_FUNCTION_DEF(disasm) } ////////////////////////////// - //- rjf: unpack cursor info + //- rjf: unpack cursor info / fill regs // + rd_regs()->prefer_disasm = 1; if(!is_loading && has_disasm) { U64 off = dasm_line_array_code_off_from_idx(&dasm_info.lines, rd_regs()->cursor.line-1); - rd_regs()->prefer_disasm = 1; rd_regs()->vaddr = range.min+off; rd_regs()->vaddr_range = r1u64(range.min+off, range.min+off); rd_regs()->voff_range = d_voff_range_from_vaddr_range(dasm_module, rd_regs()->vaddr_range); diff --git a/src/symbol_server/symbol_server.c b/src/symbol_server/symbol_server.c new file mode 100644 index 00000000..ae447aab --- /dev/null +++ b/src/symbol_server/symbol_server.c @@ -0,0 +1,2 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) diff --git a/src/symbol_server/symbol_server.h b/src/symbol_server/symbol_server.h new file mode 100644 index 00000000..bc8ef932 --- /dev/null +++ b/src/symbol_server/symbol_server.h @@ -0,0 +1,10 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef SYMBOL_SERVER_H +#define SYMBOL_SERVER_H + +internal void smsv_init(void); +internal String8 smsv_local_path_from_key(Arena *arena, String8 module_name, String8 dbg_name, Guid guid, U64 age); + +#endif // SYMBOL_SERVER_H diff --git a/src/symbol_server/symbol_server_inc.c b/src/symbol_server/symbol_server_inc.c new file mode 100644 index 00000000..ca6cf968 --- /dev/null +++ b/src/symbol_server/symbol_server_inc.c @@ -0,0 +1,9 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "symbol_server.c" +#if OS_WINDOWS +# include "win32/symbol_server/win32_symbol_server.c" +#else +# include "symbol_server_stub.c" +#endif diff --git a/src/symbol_server/symbol_server_inc.h b/src/symbol_server/symbol_server_inc.h new file mode 100644 index 00000000..8edabf00 --- /dev/null +++ b/src/symbol_server/symbol_server_inc.h @@ -0,0 +1,14 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef SYMBOL_SERVER_INC_H +#define SYMBOL_SERVER_INC_H + +#include "symbol_server.h" +#if OS_WINDOWS +# include "win32/symbol_server/win32_symbol_server.h" +#else +# include "symbol_server_stub.h" +#endif + +#endif // SYMBOL_SERVER_INC_H diff --git a/src/symbol_server/symbol_server_stub.c b/src/symbol_server/symbol_server_stub.c new file mode 100644 index 00000000..4284f3ee --- /dev/null +++ b/src/symbol_server/symbol_server_stub.c @@ -0,0 +1,13 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +smsv_init(void) +{ +} + +internal String8 +smsv_local_path_from_key(Arena *arena, String8 module_name, String8 dbg_name, Guid guid, U64 age) +{ + return s(""); +} diff --git a/src/symbol_server/symbol_server_stub.h b/src/symbol_server/symbol_server_stub.h new file mode 100644 index 00000000..61ddc1c9 --- /dev/null +++ b/src/symbol_server/symbol_server_stub.h @@ -0,0 +1,7 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef SYMBOL_SERVER_STUB_H +#define SYMBOL_SERVER_STUB_H + +#endif // SYMBOL_SERVER_STUB_H diff --git a/src/ui/ui_basic_widgets.c b/src/ui/ui_basic_widgets.c index 8864f285..dffd3c34 100644 --- a/src/ui/ui_basic_widgets.c +++ b/src/ui/ui_basic_widgets.c @@ -1289,6 +1289,8 @@ ui_scroll_bar(Axis2 axis, UI_Size off_axis_size, UI_ScrollPt pt, Rng1S64 idx_ran UI_Flags(UI_BoxFlag_DrawBorder|disabled_flags) UI_TextAlignment(UI_TextAlign_Center) UI_Font(ui_icon_font()) + UI_TextRasterFlags(FNT_RasterFlag_Smooth) + UI_TagF("weak") { String8 arrow_string = ui_icon_string_from_kind(axis == Axis2_X ? UI_IconKind_LeftArrow : UI_IconKind_UpArrow); min_scroll_sig = ui_buttonf("%S##_min_scroll_%i", arrow_string, axis); @@ -1339,6 +1341,8 @@ ui_scroll_bar(Axis2 axis, UI_Size off_axis_size, UI_ScrollPt pt, Rng1S64 idx_ran UI_Flags(UI_BoxFlag_DrawBorder|disabled_flags) UI_TextAlignment(UI_TextAlign_Center) UI_Font(ui_icon_font()) + UI_TextRasterFlags(FNT_RasterFlag_Smooth) + UI_TagF("weak") { String8 arrow_string = ui_icon_string_from_kind(axis == Axis2_X ? UI_IconKind_RightArrow : UI_IconKind_DownArrow); max_scroll_sig = ui_buttonf("%S##_max_scroll_%i", arrow_string, axis); diff --git a/src/win32/base/win32_base.c b/src/win32/base/win32_base.c index 1d9f8e1e..79945203 100644 --- a/src/win32/base/win32_base.c +++ b/src/win32/base/win32_base.c @@ -1942,30 +1942,12 @@ w32_entry_point_caller(int argc, WCHAR **wargv) { String16 string16 = str16((U16 *)this_proc_env + start_idx, idx - start_idx); String8 string = str8_from_16(arena, string16); - if(str8_match(string, s("_NT_SYMBOL_PATH="), StringMatchFlag_RightSideSloppy)) - { - info->symbol_cache_path = str8_skip(string, 16); - info->symbol_cache_path = str8_skip_chop_whitespace(info->symbol_cache_path); - } str8_list_push(arena, &info->environment, string); start_idx = idx+1; } } } } - if(info->symbol_cache_path.size == 0) - { - Temp scratch = scratch_begin(0, 0); - U64 size = KB(32); - U16 *buffer = push_array_no_zero(scratch.arena, U16, size); - if(SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, (WCHAR*)buffer))) - { - String8 local_appdata = str8_from_16(scratch.arena, str16_cstring(buffer)); - info->symbol_cache_path = str8f(arena, "%S\\Temp\\SymbolCache", local_appdata); - - } - scratch_end(scratch); - } } //- rjf: set up entity storage diff --git a/src/win32/http/win32_http.c b/src/win32/http/win32_http.c new file mode 100644 index 00000000..59cebb50 --- /dev/null +++ b/src/win32/http/win32_http.c @@ -0,0 +1,501 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Status Callback + +internal void +w32_http_status_callback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, void *lpvStatusInformation, DWORD dwStatusInformationLength) +{ + W32_HTTP_Request *req = (W32_HTTP_Request *)dwContext; + B32 reloop = 1; + switch(dwInternetStatus) + { + default:{reloop = 0;}break; + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + { + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_Failed); + }break; + case WINHTTP_CALLBACK_STATUS_REQUEST_SENT: + { + ins_atomic_u32_eval_cond_assign(&req->status, W32_HTTP_RequestStatus_Sent, W32_HTTP_RequestStatus_PendingSend); + }break; + case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: + { + req->next_body_piece = 0; + req->next_body_piece_size = 0; + ins_atomic_u32_eval_cond_assign(&req->status, W32_HTTP_RequestStatus_Recvd, W32_HTTP_RequestStatus_PendingRecv); + }break; + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + { + DWORD *size_ptr = (DWORD *)lpvStatusInformation; + req->next_body_piece_size = size_ptr[0]; + if(size_ptr[0] != 0) + { + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_DataSized); + } + else + { + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_Done); + } + }break; + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + { + req->next_body_piece_size = dwStatusInformationLength; + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_Recvd); + }break; + } + if(reloop) + { + ins_atomic_u32_eval_assign(&async_loop_again, 1); + cond_var_broadcast(async_tick_start_cond_var); + } +} + +//////////////////////////////// +//~ rjf: @per_os_impl Top-Level Layer Calls + +internal void +http_init(void) +{ + Arena *arena = arena_alloc(); + w32_http_state = push_array(arena, W32_HTTP_State, 1); + w32_http_state->arena = arena; + w32_http_state->request_ring = guarded_ring_alloc(arena, MB(1)); + for EachElement(idx, w32_http_state->active_requests) + { + w32_http_state->active_requests[idx].arena = arena_alloc(); + } + w32_http_state->hSession = WinHttpOpen(0, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); + WinHttpSetStatusCallback(w32_http_state->hSession, w32_http_status_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); +} + +internal void +http_async_tick(void) +{ + //- rjf: pop requests, fill active request slots + if(lane_idx() == 0) + { + for(;;) + { + Temp scratch = scratch_begin(0, 0); + + //- rjf: find a free active request slot + U64 free_slot_num = 0; + for EachElement(idx, w32_http_state->active_requests) + { + if(!w32_http_state->active_requests[idx].active) + { + free_slot_num = idx+1; + break; + } + } + + //- rjf: if we got one -> pop request data + String8 request_data = {0}; + if(free_slot_num != 0) + { + RingGuard guard = guarded_ring_open(w32_http_state->request_ring); + guarded_ring_try_read_struct(&guard, &request_data.size); + if(request_data.size != 0) + { + request_data.str = push_array(scratch.arena, U8, request_data.size); + guarded_ring_read_or_wait(&guard, request_data.size, request_data.str, max_U64); + } + guarded_ring_close(&guard); + } + + //- rjf: deserialize + GuardedRing *out_ring = 0; + HTTP_RequestParams params = {0}; + if(request_data.size != 0) + { + U64 off = 0; + off += str8_deserial_read_struct(request_data, off, &out_ring); + off += str8_deserial_read_struct(request_data, off, ¶ms.id); + off += str8_deserial_read_struct(request_data, off, ¶ms.method); + off += str8_deserial_read_struct(request_data, off, ¶ms.url.size); + params.url = str8_substr(request_data, r1u64(off, off+params.url.size)); + off += params.url.size; + off += str8_deserial_read_struct(request_data, off, ¶ms.body.size); + params.body = str8_substr(request_data, r1u64(off, off+params.body.size)); + off += params.body.size; + off += str8_deserial_read_struct(request_data, off, ¶ms.user_agent.size); + params.user_agent = str8_substr(request_data, r1u64(off, off+params.user_agent.size)); + off += params.user_agent.size; + off += str8_deserial_read_struct(request_data, off, ¶ms.authorization.size); + params.authorization = str8_substr(request_data, r1u64(off, off+params.authorization.size)); + off += params.authorization.size; + off += str8_deserial_read_struct(request_data, off, ¶ms.content_type.size); + params.content_type = str8_substr(request_data, r1u64(off, off+params.content_type.size)); + off += params.content_type.size; + } + + //- rjf: fill active request slot + W32_HTTP_Request *req = 0; + if(free_slot_num != 0 && request_data.size != 0) + { + req = &w32_http_state->active_requests[free_slot_num-1]; + req->active = 1; + arena_clear(req->arena); + req->out_ring = out_ring; + req->id = params.id; + req->status = W32_HTTP_RequestStatus_Null; + } + + //- rjf: kick off request + if(req != 0) + { + B32 good = 0; + + //- rjf: unpack + HTTP_Method method = params.method; + String8 url = params.url; + String8 body = params.body; + String8 user_agent = params.user_agent; + String8 authorization = params.authorization; + String8 content_type = params.content_type; + + //- rjf: extract URL info + String8 url_protocol_part = {0}; + String8 url_port_part = {0}; + String8 url_hostname_part = {0}; + String8 url_path_part = {0}; + { + U64 protocol_delimiter_pos = str8_find_needle(url, 0, s("://"), 0); + U64 post_protocol_pos = 0; + if(protocol_delimiter_pos < url.size) + { + post_protocol_pos = protocol_delimiter_pos+3; + url_protocol_part = str8_prefix(url, post_protocol_pos); + } + U64 last_colon_pos = str8_find_needle(url, post_protocol_pos, s(":"), 0); + if(last_colon_pos < url.size) + { + url_port_part = str8_skip(url, last_colon_pos+1); + } + U64 first_non_protocol_slash_pos = str8_find_needle(url, post_protocol_pos, s("/"), 0); + if(first_non_protocol_slash_pos < url.size) + { + url_hostname_part = str8_prefix(url, first_non_protocol_slash_pos); + url_hostname_part = str8_skip(url_hostname_part, post_protocol_pos); + url_path_part = str8_skip(url, first_non_protocol_slash_pos); + } + else + { + url_hostname_part = str8_skip(url, post_protocol_pos); + } + } + + //- rjf: convert url parts to connection parameters + U16 port = INTERNET_DEFAULT_HTTPS_PORT; + String16 hostname16 = {0}; + { + if(url_port_part.size != 0) + { + port = (U16)u64_from_str8(url_port_part, 10); + } + else if(str8_match(url_protocol_part, s("https://"), StringMatchFlag_CaseInsensitive)) + { + port = INTERNET_DEFAULT_HTTPS_PORT; + } + else if(str8_match(url_protocol_part, s("http://"), StringMatchFlag_CaseInsensitive)) + { + port = INTERNET_DEFAULT_HTTP_PORT; + } + else if(str8_match(url_protocol_part, s("ftp://"), StringMatchFlag_CaseInsensitive)) + { + port = 21; + } + else if(str8_match(url_protocol_part, s("ssh://"), StringMatchFlag_CaseInsensitive)) + { + port = 22; + } + hostname16 = str16_from_8(scratch.arena, url_hostname_part); + } + + //- rjf: convert method to verb wchar + WCHAR *verb = L"GET"; + switch(method) + { + default: + case HTTP_Method_Get: {verb = L"GET";}break; + case HTTP_Method_Head: {verb = L"HEAD";}break; + case HTTP_Method_Post: {verb = L"POST";}break; + case HTTP_Method_Put: {verb = L"PUT";}break; + case HTTP_Method_Delete: {verb = L"DELETE";}break; + case HTTP_Method_Connect: {verb = L"CONNECT";}break; + case HTTP_Method_Options: {verb = L"OPTIONS";}break; + case HTTP_Method_Trace: {verb = L"TRACE";}break; + case HTTP_Method_Patch: {verb = L"PATCH";}break; + } + + //- rjf: convert url parts to request parameters + WCHAR *path_name = L""; + if(url_path_part.size != 0) + { + String16 url_path_part16 = str16_from_8(scratch.arena, url_path_part); + path_name = (WCHAR *)url_path_part16.str; + } + + //- rjf: unpack body params + String16 header16 = {0}; + { + String8List header_strings = {0}; + if(user_agent.size != 0) { str8_list_pushf(scratch.arena, &header_strings, "User-Agent: %S\n", user_agent); } + if(authorization.size != 0) { str8_list_pushf(scratch.arena, &header_strings, "Authorization: %S\n", authorization); } + if(content_type.size != 0) { str8_list_pushf(scratch.arena, &header_strings, "Content-Type: %S\n", content_type); } + String8 header = str8_list_join(scratch.arena, &header_strings, 0); + header16 = str16_from_8(scratch.arena, header); + } + + //- rjf: convert body/header info to winhttp expected params + WCHAR *header = WINHTTP_NO_ADDITIONAL_HEADERS; + U64 header_size = 0; + void *optional = WINHTTP_NO_REQUEST_DATA; + U64 optional_size = 0; + if(header16.size != 0) + { + header = (WCHAR *)header16.str; + header_size = header16.size; + } + if(body.size != 0) + { + String8 body_copy = str8_copy(req->arena, body); + optional = body_copy.str; + optional_size = body_copy.size; + } + + //- rjf: open connection + HINTERNET hConnect = 0; + if(w32_http_state->hSession != 0) + { + hConnect = WinHttpConnect(w32_http_state->hSession, (WCHAR *)hostname16.str, port, 0); + good = (hConnect != 0); + } + + //- rjf: open request + HINTERNET hRequest = 0; + if(good) + { + hRequest = WinHttpOpenRequest(hConnect, verb, path_name, 0, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); + good = (hRequest != 0); + } + + //- rjf: fill info + if(good) + { + req->status = W32_HTTP_RequestStatus_PendingSend; + } + else + { + req->status = W32_HTTP_RequestStatus_Failed; + } + req->hConnect = hConnect; + req->hRequest = hRequest; + + //- rjf: send request + if(good) + { + WinHttpSendRequest(hRequest, header, header_size, optional, optional_size, optional_size, (DWORD_PTR)req); + } + } + + scratch_end(scratch); + if(free_slot_num == 0 || request_data.size == 0) + { + break; + } + } + } + lane_sync(); + + //- rjf: kick off receives + { + Rng1U64 range = lane_range(ArrayCount(w32_http_state->active_requests)); + for EachInRange(idx, range) + { + W32_HTTP_Request *req = &w32_http_state->active_requests[idx]; + if(req->active && ins_atomic_u32_eval(&req->status) == W32_HTTP_RequestStatus_Sent) + { + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_PendingRecv); + WinHttpReceiveResponse(req->hRequest, 0); + } + } + } + + //- rjf: kick off body size queries + { + Rng1U64 range = lane_range(ArrayCount(w32_http_state->active_requests)); + for EachInRange(idx, range) + { + W32_HTTP_Request *req = &w32_http_state->active_requests[idx]; + if(req->active && ins_atomic_u32_eval(&req->status) == W32_HTTP_RequestStatus_Recvd) + { + if(req->next_body_piece_size != 0) + { + str8_list_push(req->arena, &req->finished_body_pieces, str8((U8 *)req->next_body_piece, req->next_body_piece_size)); + } + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_PendingDataSize); + WinHttpQueryDataAvailable(req->hRequest, 0); + } + } + } + + //- rjf: kick off body reads + { + Rng1U64 range = lane_range(ArrayCount(w32_http_state->active_requests)); + for EachInRange(idx, range) + { + W32_HTTP_Request *req = &w32_http_state->active_requests[idx]; + if(req->active && ins_atomic_u32_eval(&req->status) == W32_HTTP_RequestStatus_DataSized) + { + req->next_body_piece = push_array(req->arena, U8, req->next_body_piece_size); + ins_atomic_u32_eval_assign(&req->status, W32_HTTP_RequestStatus_PendingRead); + WinHttpReadData(req->hRequest, req->next_body_piece, req->next_body_piece_size, 0); + } + } + } + + //- rjf: complete requests + { + Rng1U64 range = lane_range(ArrayCount(w32_http_state->active_requests)); + for EachInRange(idx, range) + { + W32_HTTP_Request *req = &w32_http_state->active_requests[idx]; + W32_HTTP_RequestStatus status = ins_atomic_u32_eval(&req->status); + if(req->active && + (status == W32_HTTP_RequestStatus_Done || + status == W32_HTTP_RequestStatus_Failed)) + { + Temp scratch = scratch_begin(0, 0); + B32 good = (status == W32_HTTP_RequestStatus_Done); + + //- rjf: unpack + HINTERNET hRequest = req->hRequest; + + //- rjf: read response code + HTTP_StatusCode code = 0; + if(good) + { + DWORD status_code = 0; + DWORD status_code_size = sizeof(status_code); + WinHttpQueryHeaders(hRequest, + WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX); + code = status_code; + } + + //- rjf: serialize response + String8 response_data = {0}; + U64 response_data_min_size = sizeof(U64) + sizeof(U64) + sizeof(HTTP_StatusCode) + sizeof(U64); + U64 response_data_max_size = req->out_ring->ring->size; + B32 response_is_possible = (response_data_min_size < response_data_max_size); + if(response_is_possible) + { + U64 body_size = req->finished_body_pieces.total_size; + String8List pieces = {0}; + str8_list_push(scratch.arena, &pieces, str8_struct(&req->id)); + str8_list_push(scratch.arena, &pieces, str8_struct(&code)); + str8_list_push(scratch.arena, &pieces, str8_struct(&body_size)); + str8_list_concat_in_place(&pieces, &req->finished_body_pieces); + U64 data_size = pieces.total_size; + str8_list_push_front(scratch.arena, &pieces, str8_struct(&data_size)); + if(data_size + sizeof(U64) > response_data_max_size) + { + U64 overkill = data_size + sizeof(U64) - response_data_max_size; + data_size -= overkill; + } + response_data = str8_list_join(scratch.arena, &pieces, 0); + response_data.size = data_size + sizeof(U64); + } + + //- rjf: write response + if(response_is_possible) + { + RingGuard g = guarded_ring_open(req->out_ring); + guarded_ring_write_string_or_wait(&g, response_data, max_U64); + guarded_ring_close(&g); + } + + //- rjf: free this request slot + req->active = 0; + arena_clear(req->arena); + MemoryZeroStruct(&req->finished_body_pieces); + req->next_body_piece = 0; + req->next_body_piece_size = 0; + WinHttpCloseHandle(req->hConnect); + WinHttpCloseHandle(req->hRequest); + + scratch_end(scratch); + } + } + } +} + +//////////////////////////////// +//~ rjf: @per_os_impl I/O Ring Functions + +internal B32 +http_push_request(GuardedRing *out_ring, HTTP_RequestParams *params, U64 endt_us) +{ + Temp scratch = scratch_begin(0, 0); + B32 result = 0; + + // rjf: serialize + String8 data = {0}; + { + String8List pieces = {0}; + str8_list_push(scratch.arena, &pieces, str8_struct(&out_ring)); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->id)); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->method)); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->url.size)); + str8_list_push(scratch.arena, &pieces, params->url); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->body.size)); + str8_list_push(scratch.arena, &pieces, params->body); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->user_agent.size)); + str8_list_push(scratch.arena, &pieces, params->user_agent); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->authorization.size)); + str8_list_push(scratch.arena, &pieces, params->authorization); + str8_list_push(scratch.arena, &pieces, str8_struct(¶ms->content_type.size)); + str8_list_push(scratch.arena, &pieces, params->content_type); + U64 data_size = pieces.total_size; + str8_list_push_front(scratch.arena, &pieces, str8_struct(&data_size)); + data = str8_list_join(scratch.arena, &pieces, 0); + } + + // rjf: write + if(data.size < w32_http_state->request_ring->ring->size) + { + RingGuard g = guarded_ring_open(w32_http_state->request_ring); + result = guarded_ring_write_string_or_wait(&g, data, endt_us); + guarded_ring_close(&g); + } + + scratch_end(scratch); + return result; +} + +internal B32 +http_pop_response(Arena *arena, GuardedRing *out_ring, HTTP_Response *response_out, U64 endt_us) +{ + U64 data_size = 0; + RingGuard g = guarded_ring_open(out_ring); + B32 result = guarded_ring_read_struct_or_wait(&g, &data_size, endt_us); + if(result) + { + String8 data = {0}; + data.size = data_size; + data.str = push_array(arena, U8, data.size); + guarded_ring_read_or_wait(&g, data.size, data.str, max_U64); + U64 off = 0; + off += str8_deserial_read_struct(data, off, &response_out->id); + off += str8_deserial_read_struct(data, off, &response_out->code); + off += str8_deserial_read_struct(data, off, &response_out->body.size); + response_out->body.str = push_array(arena, U8, response_out->body.size); + off += str8_deserial_read(data, off, response_out->body.str, response_out->body.size, 1); + } + guarded_ring_close(&g); + return result; +} diff --git a/src/win32/http/win32_http.h b/src/win32/http/win32_http.h new file mode 100644 index 00000000..337c37b8 --- /dev/null +++ b/src/win32/http/win32_http.h @@ -0,0 +1,58 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef WIN32_HTTP_H +#define WIN32_HTTP_H + +#include +#pragma comment(lib, "winhttp") + +typedef enum W32_HTTP_RequestStatus +{ + W32_HTTP_RequestStatus_Null, + W32_HTTP_RequestStatus_PendingSend, + W32_HTTP_RequestStatus_Sent, + W32_HTTP_RequestStatus_PendingRecv, + W32_HTTP_RequestStatus_Recvd, + W32_HTTP_RequestStatus_PendingDataSize, + W32_HTTP_RequestStatus_DataSized, + W32_HTTP_RequestStatus_PendingRead, + W32_HTTP_RequestStatus_Done, + W32_HTTP_RequestStatus_Failed, +} +W32_HTTP_RequestStatus; + +typedef struct W32_HTTP_Request W32_HTTP_Request; +struct W32_HTTP_Request +{ + B32 active; + Arena *arena; + GuardedRing *out_ring; + U64 id; + W32_HTTP_RequestStatus status; + HINTERNET hConnect; + HINTERNET hRequest; + void *optional; // NOTE(rjf): must persist with in-flight requests + U64 optional_size; + String8List finished_body_pieces; + void *next_body_piece; + U64 next_body_piece_size; +}; + +typedef struct W32_HTTP_State W32_HTTP_State; +struct W32_HTTP_State +{ + Arena *arena; + HINTERNET hSession; + GuardedRing *request_ring; + W32_HTTP_Request active_requests[16]; +}; + +global W32_HTTP_State *w32_http_state = 0; + +//////////////////////////////// +//~ rjf: Status Callback + +internal void w32_http_status_callback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, void *lpvStatusInformation, DWORD dwStatusInformationLength); + +#endif // WIN32_HTTP_H diff --git a/src/win32/symbol_server/win32_symbol_server.c b/src/win32/symbol_server/win32_symbol_server.c new file mode 100644 index 00000000..0fa06df6 --- /dev/null +++ b/src/win32/symbol_server/win32_symbol_server.c @@ -0,0 +1,114 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +smsv_init(void) +{ + Arena *arena = arena_alloc(); + w32_smsv_state = push_array(arena, W32_SMSV_State, 1); + w32_smsv_state->arena = arena; + { + // rjf: extract parameterizations from _NT_SYMBOL_PATH + { + // rjf: get rules string from environment + String8 rules = {0}; + for EachNode(n, String8Node, get_process_info()->environment.first) + { + String8 string = n->string; + if(str8_match(string, s("_NT_SYMBOL_PATH="), StringMatchFlag_RightSideSloppy)) + { + rules = str8_skip(string, 16); + } + } + + // rjf: parse rules string + { + Temp scratch = scratch_begin(0, 0); + if(str8_match(rules, s("srv*"), StringMatchFlag_RightSideSloppy)) + { + String8List parts = str8_split(scratch.arena, rules, (U8 *)"*", 1, 0); + for EachNode(n, String8Node, parts.first) + { + PathStyle style = path_style_from_str8(n->string); + if(w32_smsv_state->symbol_cache_path.size != 0 && style == PathStyle_WindowsAbsolute) + { + w32_smsv_state->symbol_cache_path = n->string; + } + else if(str8_match(n->string, s("https://"), StringMatchFlag_RightSideSloppy) || + str8_match(n->string, s("http://"), StringMatchFlag_RightSideSloppy)) + { + str8_list_push(arena, &w32_smsv_state->symbol_server_urls, n->string); + } + else if(str8_match(n->string, s("\\\\"), StringMatchFlag_RightSideSloppy)) + { + // TODO(rjf): network share; currently unsupported + } + } + } + else + { + w32_smsv_state->symbol_cache_path = str8_skip_chop_whitespace(rules); + } + scratch_end(scratch); + } + } + + // rjf: always fall back to public MS server + { + String8 public_ms_server = s("https://msdl.microsoft.com/download/symbols"); + B32 public_ms_server_already_present = 0; + for EachNode(n, String8Node, w32_smsv_state->symbol_server_urls.first) + { + if(str8_match(public_ms_server, n->string, 0)) + { + public_ms_server_already_present = 1; + break; + } + } + if(!public_ms_server_already_present) + { + str8_list_push(arena, &w32_smsv_state->symbol_server_urls, public_ms_server); + } + } + + // rjf fall back to using local appdata temp symbol cache folder + if(w32_smsv_state->symbol_cache_path.size == 0) + { + Temp scratch = scratch_begin(0, 0); + U64 size = KB(32); + U16 *buffer = push_array_no_zero(scratch.arena, U16, size); + if(SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, (WCHAR*)buffer))) + { + String8 local_appdata = str8_from_16(scratch.arena, str16_cstring(buffer)); + w32_smsv_state->symbol_cache_path = str8f(arena, "%S\\Temp\\SymbolCache", local_appdata); + } + scratch_end(scratch); + } + } +} + +internal String8 +smsv_local_path_from_key(Arena *arena, String8 module_name, String8 dbg_name, Guid guid, U64 age) +{ + String8 result = {0}; + Temp scratch = scratch_begin(&arena, 1); + { + // rjf: form unique identifier based on guid + String8 debug_info_unique_identifier = {0}; + { + debug_info_unique_identifier = str8f(scratch.arena, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", + guid.data1, guid.data2, guid.data3, guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3], guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7], age); + } + + // rjf: form full path + if(debug_info_unique_identifier.size != 0) + { + // TODO(rjf): assuming 'stripped' for now + String8 symbol_cache_path = w32_smsv_state->symbol_cache_path; + String8 path = str8f(scratch.arena, "%S/%S/%S/stripped/%S", symbol_cache_path, module_name, debug_info_unique_identifier, dbg_name); + result = path_normalized_from_string(arena, path); + } + } + scratch_end(scratch); + return result; +} diff --git a/src/win32/symbol_server/win32_symbol_server.h b/src/win32/symbol_server/win32_symbol_server.h new file mode 100644 index 00000000..fbab9932 --- /dev/null +++ b/src/win32/symbol_server/win32_symbol_server.h @@ -0,0 +1,17 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef WIN32_SYMBOL_SERVER_H +#define WIN32_SYMBOL_SERVER_H + +typedef struct W32_SMSV_State W32_SMSV_State; +struct W32_SMSV_State +{ + Arena *arena; + String8 symbol_cache_path; + String8List symbol_server_urls; +}; + +global W32_SMSV_State *w32_smsv_state = 0; + +#endif // WIN32_SYMBOL_SERVER_H diff --git a/test2.project b/test2.project new file mode 100644 index 00000000..efb3fb81 --- /dev/null +++ b/test2.project @@ -0,0 +1,2 @@ +// raddbg 0.9.27 project file +