From f990dccd8db35b9076ec3f6cdd6656c973f4e41b Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 16 Nov 2024 22:48:44 -0500 Subject: [PATCH] Windows 515.1644-1646 support, Linux 515.1643 support, use C11 atomics/threading --- compile_flags.txt | 1 + prof.c | 325 +++++++++++++++++++++++----------------------- readme.md | 3 + 3 files changed, 167 insertions(+), 162 deletions(-) create mode 100644 compile_flags.txt diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..13fc3ed --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1 @@ +-m32 diff --git a/prof.c b/prof.c index ada9232..ba83c4b 100644 --- a/prof.c +++ b/prof.c @@ -122,16 +122,14 @@ _Static_assert(sizeof(long long) == 8, "incorrect size"); #include #include #include +#include +#include #if (__STDC_HOSTED__ == 1) # include # include #endif -#if !defined(__STDC_NO_ATOMICS__) -# include -#endif - #if (__STDC_HOSTED__ == 0) void *memset(void *const a, int value, size_t len) { for(size_t i=0; i= 4, "incorrect size"); # define atomic_store_release(a, b) atomic_store_explicit((a), (b), memory_order_release) #endif -#if defined(UTRACY_WINDOWS) -typedef HANDLE event_pipe_t; -#elif defined(UTRACY_LINUX) -typedef int event_pipe_t; -#endif +// Event pipe structure using C11 primitives +typedef struct event_pipe { + atomic_bool signaled; + mtx_t mutex; + cnd_t condition; + bool manual_reset; +} event_pipe_t; struct event_zone_begin { int unsigned tid; @@ -399,12 +399,8 @@ static struct { long long exec_time; } info; -#if defined(UTRACY_WINDOWS) - HANDLE thread; -#elif defined(UTRACY_LINUX) - pthread_t thread; -#endif - event_pipe_t quit; + thrd_t thread; + event_pipe_t* quit; FILE* fstream; @@ -413,134 +409,153 @@ static struct { int unsigned consumer_head_cache; struct event events[EVENT_QUEUE_CAPACITY]; -#if defined(__STDC_NO_ATOMICS__) - long volatile head; - long volatile tail; -#else _Alignas(64) atomic_uint head; - _Alignas(64) atomic_uint tail; - _Alignas(64) int padding; -#endif + _Alignas(64) atomic_uint tail; + _Alignas(64) int padding; } queue; } utracy; -#if defined(UTRACY_WINDOWS) -UTRACY_INTERNAL -event_pipe_t create_event_pipe(bool manual_reset, int initial_state, const char* name) { - return CreateEvent(NULL, manual_reset, initial_state, name ? TEXT(name) : NULL); -} - -UTRACY_INTERNAL -void close_event_pipe(event_pipe_t event_pipe) { - CloseHandle(event_pipe); -} - -UTRACY_INTERNAL -int wait_for_event_pipe(event_pipe_t event_pipe, int timeout_ms) { - DWORD result = WaitForSingleObject(event_pipe, timeout_ms); - switch (result) { - case WAIT_OBJECT_0: return 0; - case WAIT_TIMEOUT: return 1; - default: return -1; - } +event_pipe_t* create_event_pipe(bool manual_reset, bool initial_state, const char* name) { + event_pipe_t* pipe = malloc(sizeof(event_pipe_t)); + if (!pipe) { + return NULL; + } + + if (mtx_init(&pipe->mutex, mtx_plain) != thrd_success) { + free(pipe); + return NULL; + } + + if (cnd_init(&pipe->condition) != thrd_success) { + mtx_destroy(&pipe->mutex); + free(pipe); + return NULL; + } + + atomic_init(&pipe->signaled, initial_state); + pipe->manual_reset = manual_reset; + return pipe; } -UTRACY_INTERNAL -void set_event_pipe(event_pipe_t event_pipe) { - SetEvent(event_pipe); -} -#elif defined(UTRACY_LINUX) -UTRACY_INTERNAL -event_pipe_t create_event_pipe(bool manual_reset, int initial_state, const char* name) { - (void)manual_reset; // Unused on Linux - (void)name; // Unused on Linux - return eventfd(initial_state ? 1 : 0, EFD_CLOEXEC); +void close_event_pipe(event_pipe_t* pipe) { + if (pipe) { + cnd_destroy(&pipe->condition); + mtx_destroy(&pipe->mutex); + free(pipe); + } } -UTRACY_INTERNAL -void close_event_pipe(event_pipe_t event_pipe) { - close(event_pipe); +int wait_for_event_pipe(event_pipe_t* pipe, int timeout_ms) { + if (!pipe) { + return -1; + } + + int result = -1; + mtx_lock(&pipe->mutex); + + if (!atomic_load(&pipe->signaled)) { + if (timeout_ms < 0) { + // Wait indefinitely + while (!atomic_load(&pipe->signaled)) { + if (cnd_wait(&pipe->condition, &pipe->mutex) != thrd_success) { + goto cleanup; + } + } + result = 0; + } else { + // Wait with timeout + struct timespec ts; + timespec_get(&ts, TIME_UTC); + ts.tv_sec += timeout_ms / 1000; + ts.tv_nsec += (timeout_ms % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec += 1; + ts.tv_nsec -= 1000000000; + } + + while (!atomic_load(&pipe->signaled)) { + int wait_result = cnd_timedwait(&pipe->condition, &pipe->mutex, &ts); + if (wait_result == thrd_timedout) { + result = 1; // Timeout + goto cleanup; + } else if (wait_result != thrd_success) { + goto cleanup; + } + } + result = 0; + } + } else { + result = 0; + } + + if (result == 0 && !pipe->manual_reset) { + atomic_store(&pipe->signaled, false); + } + +cleanup: + mtx_unlock(&pipe->mutex); + return result; } -UTRACY_INTERNAL -int wait_for_event_pipe(event_pipe_t event_pipe, int timeout_ms) { - fd_set fds; - struct timeval tv; - uint64_t value; - - FD_ZERO(&fds); - FD_SET(event_pipe, &fds); +void set_event_pipe(event_pipe_t* pipe) { + if (!pipe) { + return; + } - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - - int result = select(event_pipe + 1, &fds, NULL, NULL, timeout_ms >= 0 ? &tv : NULL); - if (result > 0) { - if (read(event_pipe, &value, sizeof(value)) == sizeof(value)) { - return 0; - } - } else if (result == 0) { - return 1; // Timeout - } - return -1; // Error + mtx_lock(&pipe->mutex); + atomic_store(&pipe->signaled, true); + cnd_broadcast(&pipe->condition); + mtx_unlock(&pipe->mutex); } -UTRACY_INTERNAL -void set_event_pipe(event_pipe_t event_pipe) { - uint64_t value = 1; - (void) write(event_pipe, &value, sizeof(value)); -} -#endif - - /* queue api */ UTRACY_INTERNAL UTRACY_INLINE int event_queue_init(void) { - utracy.queue.producer_tail_cache = 0; - utracy.queue.consumer_head_cache = 0; + utracy.queue.producer_tail_cache = 0; + utracy.queue.consumer_head_cache = 0; atomic_store_seqcst(&utracy.queue.head, 1); atomic_store_seqcst(&utracy.queue.tail, 0); - return 0; + return 0; } UTRACY_INTERNAL UTRACY_INLINE void event_queue_push(struct event const *const event) { - int unsigned store = atomic_load_relaxed(&utracy.queue.head); - int unsigned next_store = store + 1; + int unsigned store = atomic_load_relaxed(&utracy.queue.head); + int unsigned next_store = store + 1; - if(next_store == EVENT_QUEUE_CAPACITY) { - next_store = 0; - } + if(next_store == EVENT_QUEUE_CAPACITY) { + next_store = 0; + } - while(unlikely(next_store == utracy.queue.producer_tail_cache)) { - utracy.queue.producer_tail_cache = atomic_load_acquire(&utracy.queue.tail); - } + while(unlikely(next_store == utracy.queue.producer_tail_cache)) { + utracy.queue.producer_tail_cache = atomic_load_acquire(&utracy.queue.tail); + } - utracy.queue.events[store] = *event; + utracy.queue.events[store] = *event; - atomic_store_release(&utracy.queue.head, next_store); + atomic_store_release(&utracy.queue.head, next_store); } UTRACY_INTERNAL UTRACY_INLINE int event_queue_pop(struct event *const event) { - int unsigned load = atomic_load_relaxed(&utracy.queue.tail); - int unsigned next_load = load + 1; + int unsigned load = atomic_load_relaxed(&utracy.queue.tail); + int unsigned next_load = load + 1; - if(load == utracy.queue.consumer_head_cache) { - utracy.queue.consumer_head_cache = atomic_load_acquire(&utracy.queue.head); - if(load == utracy.queue.consumer_head_cache) { - return -1; - } - } + if(load == utracy.queue.consumer_head_cache) { + utracy.queue.consumer_head_cache = atomic_load_acquire(&utracy.queue.head); + if(load == utracy.queue.consumer_head_cache) { + return -1; + } + } - *event = utracy.queue.events[load]; + *event = utracy.queue.events[load]; - if(next_load == EVENT_QUEUE_CAPACITY) { - next_load = 0; - } + if(next_load == EVENT_QUEUE_CAPACITY) { + next_load = 0; + } - atomic_store_release(&utracy.queue.tail, next_load); - return 0; + atomic_store_release(&utracy.queue.tail, next_load); + return 0; } /* profiler */ @@ -841,12 +856,8 @@ static int utracy_write(void const *const buf, size_t size) { #endif UTRACY_INTERNAL -#if defined(UTRACY_WINDOWS) -DWORD WINAPI utracy_server_thread_start(void* arg) { -#elif defined(UTRACY_LINUX) -void *utracy_server_thread_start(void* arg) { -#endif - event_pipe_t profiler_connected_event = (event_pipe_t)arg; +int utracy_server_thread_start(void* arg) { + event_pipe_t* profiler_connected_event = (event_pipe_t*) arg; if (profiler_connected_event) { set_event_pipe(profiler_connected_event); @@ -954,15 +965,8 @@ void *utracy_server_thread_start(void* arg) { } -#if defined(UTRACY_WINDOWS) - CloseHandle(utracy.thread); - CloseHandle(utracy.quit); - - ExitThread(0); -#elif defined(UTRACY_LINUX) - close(utracy.quit); - pthread_exit(NULL); -#endif + close_event_pipe(utracy.quit); + return 0; } /* byond hooks */ @@ -1101,7 +1105,7 @@ void *hook(char *const restrict dst, char *const restrict src, char unsigned siz } #if defined(UTRACY_WINDOWS) -# define BYOND_MAX_BUILD 1642 +# define BYOND_MAX_BUILD 1646 # define BYOND_MIN_BUILD 1543 # define BYOND_VERSION_ADJUSTED(a) ((a) - BYOND_MIN_BUILD) @@ -1205,12 +1209,16 @@ static int unsigned const byond_offsets[][9] = { [BYOND_VERSION_ADJUSTED(1640)] = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, [BYOND_VERSION_ADJUSTED(1641)] = {0x00409614, 0x00409618, 0x00409624, 0x00409634, 0x001C002C, 0x00130B20, 0x0020BA10, 0x001C3890, 0x00050606}, [BYOND_VERSION_ADJUSTED(1642)] = {0x00409614, 0x00409618, 0x00409624, 0x00409634, 0x001C002C, 0x00130B20, 0x0020BAE0, 0x001C3940, 0x00050606}, + [BYOND_VERSION_ADJUSTED(1643)] = {0x0040961C, 0x00409620, 0x0040962C, 0x0040963C, 0x001C002C, 0x00130B20, 0x0020BAD0, 0x001C38E0, 0x00050606}, + [BYOND_VERSION_ADJUSTED(1644)] = {0x004096BC, 0x004096C0, 0x004096CC, 0x004096DC, 0x001C002C, 0x00130D60, 0x0020BD20, 0x001C3B90, 0x00050606}, + [BYOND_VERSION_ADJUSTED(1645)] = {0x0040A6C4, 0x0040A6C8, 0x0040A6D4, 0x0040A6E4, 0x001C002C, 0x00131240, 0x0020C260, 0x001C4060, 0x00050606}, + [BYOND_VERSION_ADJUSTED(1646)] = {0x0040A6C4, 0x0040A6C8, 0x0040A6D4, 0x0040A6E4, 0x001C002C, 0x00131310, 0x0020C300, 0x001C4160, 0x00050606}, /* strings strings_len miscs procdefs procdef exec_proc server_tick send_maps prologue */ }; #elif defined(UTRACY_LINUX) -# define BYOND_MAX_BUILD 1642 +# define BYOND_MAX_BUILD 1643 # define BYOND_MIN_BUILD 1543 # define BYOND_VERSION_ADJUSTED(a) ((a) - BYOND_MIN_BUILD) @@ -1312,6 +1320,7 @@ static int unsigned const byond_offsets[][9] = { [BYOND_VERSION_ADJUSTED(1640)] = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, [BYOND_VERSION_ADJUSTED(1641)] = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, [BYOND_VERSION_ADJUSTED(1642)] = {0x006DB818, 0x006DB81C, 0x006DB830, 0x006DB86C, 0x001C002C, 0x00323000, 0x003105C0, 0x00306FC0, 0x00050505}, + [BYOND_VERSION_ADJUSTED(1643)] = {0x006DB618, 0x006DB61C, 0x006DB630, 0x006DB66C, 0x001C002C, 0x00322E10, 0x00310430, 0x00306E30, 0x00050505}, }; #endif @@ -1562,7 +1571,7 @@ char *UTRACY_WINDOWS_CDECL UTRACY_LINUX_CDECL init(int argc, char **argv) { #define MAX_PATH 260 // same as windows #endif - event_pipe_t profiler_connected_event = 0; + event_pipe_t* profiler_connected_event = NULL; if (block_start) { profiler_connected_event = create_event_pipe(true, 0, "ProfilerConnectedEvent"); if (profiler_connected_event == 0) { @@ -1587,27 +1596,25 @@ char *UTRACY_WINDOWS_CDECL UTRACY_LINUX_CDECL init(int argc, char **argv) { utracy.info.exec_time = unix_timestamp(); utracy.info.init_end = utracy_tsc(); -#if defined(UTRACY_WINDOWS) - if(NULL == (utracy.thread = CreateThread(NULL, 0, utracy_server_thread_start, (void*)profiler_connected_event, 0, NULL))) { - LOG_DEBUG_ERROR; - fclose(utracy.fstream); - return "CreateThread failed"; - } -#elif defined(UTRACY_LINUX) - if (0 != pthread_create(&utracy.thread, NULL, utracy_server_thread_start, (void*)profiler_connected_event)) { - LOG_DEBUG_ERROR; - return "pthread_create failed"; - } -#endif + thrd_t thread; + if (thrd_create(&thread, utracy_server_thread_start, (void*)profiler_connected_event) != thrd_success) { + LOG_DEBUG_ERROR; + if (profiler_connected_event) { + close_event_pipe(profiler_connected_event); + } + return "thread creation failed"; + } + + utracy.thread = thread; initialized = true; if (block_start) { printf("blocking until initialized\n"); - wait_for_event_pipe(profiler_connected_event, -1); + wait_for_event_pipe(profiler_connected_event, -1); printf("initialized, unblocking\n"); - close_event_pipe(profiler_connected_event); - } + close_event_pipe(profiler_connected_event); + } return ffilename; } @@ -1617,29 +1624,23 @@ char *UTRACY_WINDOWS_CDECL UTRACY_LINUX_CDECL destroy(int argc, char **argv) { (void) argc; (void) argv; - /* not yet implemented */ - if(!initialized) { - return "not initialized"; - } + if (!initialized) { + return "not initialized"; + } - set_event_pipe(utracy.quit); -#if defined(UTRACY_WINDOWS) - switch(WaitForSingleObject(utracy.thread, INFINITE)) { - case WAIT_OBJECT_0: - break; - default: - LOG_DEBUG_ERROR; - } -#elif defined(UTRACY_LINUX) - void* thread_return; - pthread_join(utracy.thread, &thread_return); -#endif - close_event_pipe(utracy.quit); + set_event_pipe(utracy.quit); + + int thread_result; + if (thrd_join(utracy.thread, &thread_result) != thrd_success) { + LOG_DEBUG_ERROR; + return "thread join failed"; + } - fclose(utracy.fstream); - initialized = false; + close_event_pipe(utracy.quit); + fclose(utracy.fstream); + initialized = false; - return "0"; + return "0"; } UTRACY_EXTERNAL diff --git a/readme.md b/readme.md index 71062c7..35db70d 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,9 @@ A massive thanks to `mafemergency` for even making this possible. The below read | windows | linux | | -------- | -------- | +| 515.1646 | | +| 515.1645 | | +| 515.1644 | | | 515.1643 | 515.1643 | | 515.1642 | 515.1642 | | 515.1641 | |