rrtrace 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +36 -0
- data/.github/workflows/ci.yml +94 -0
- data/.github/workflows/release.yml +4 -4
- data/Cargo.lock +26 -30
- data/Cargo.toml +8 -4
- data/README.md +77 -16
- data/exe/rrtrace +16 -0
- data/ext/rrtrace/process_manager_posix.h +18 -2
- data/ext/rrtrace/process_manager_windows.h +14 -0
- data/ext/rrtrace/rrtrace.c +153 -44
- data/ext/rrtrace/rrtrace_event.h +6 -15
- data/ext/rrtrace/rrtrace_event_ringbuffer.h +2 -2
- data/ext/rrtrace/shared_memory_posix.h +57 -5
- data/ext/rrtrace/shared_memory_windows.h +38 -3
- data/ext/rrtrace/time_posix.h +29 -0
- data/ext/rrtrace/time_windows.h +33 -0
- data/lib/rrtrace/run.rb +5 -0
- data/lib/rrtrace/version.rb +1 -1
- data/lib/rrtrace.rb +31 -3
- data/libexec/rrtrace +0 -0
- data/mise.toml +5 -2
- data/sig/rrtrace.rbs +3 -0
- data/src/main.rs +114 -32
- data/src/object_scatter.rs +110 -0
- data/src/oneshot_channel.rs +74 -0
- data/src/renderer/vertex_arena.rs +17 -13
- data/src/renderer.rs +268 -37
- data/src/ringbuffer.rs +2 -2
- data/src/shader.wgsl +160 -115
- data/src/trace_state.rs +500 -119
- data/src/universal_notifier.rs +31 -0
- data/taplo.toml +4 -0
- metadata +14 -3
data/ext/rrtrace/rrtrace.c
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#include "rrtrace.h"
|
|
2
2
|
#include "rrtrace_event_ringbuffer.h"
|
|
3
3
|
|
|
4
|
-
#
|
|
4
|
+
#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__)
|
|
5
5
|
#include "process_manager_windows.h"
|
|
6
6
|
#include "shared_memory_windows.h"
|
|
7
7
|
#else
|
|
@@ -20,10 +20,22 @@ typedef struct {
|
|
|
20
20
|
} ThreadData;
|
|
21
21
|
|
|
22
22
|
typedef struct {
|
|
23
|
+
shared_memory_handle shared_memory;
|
|
23
24
|
RRTraceEventRingBuffer *event_ringbuffer;
|
|
24
25
|
process_id visualizer_process_id;
|
|
26
|
+
rb_internal_thread_event_hook_t *thread_start_hook;
|
|
27
|
+
rb_internal_thread_event_hook_t *thread_ready_hook;
|
|
28
|
+
rb_internal_thread_event_hook_t *thread_suspended_hook;
|
|
29
|
+
rb_internal_thread_event_hook_t *thread_resume_hook;
|
|
30
|
+
rb_internal_thread_event_hook_t *thread_exit_hook;
|
|
31
|
+
VALUE trace_call;
|
|
32
|
+
VALUE trace_return;
|
|
33
|
+
VALUE trace_gc_start;
|
|
34
|
+
VALUE trace_gc_end;
|
|
25
35
|
rb_internal_thread_specific_key_t thread_data_key;
|
|
26
36
|
atomic_uint_fast32_t next_thread_id;
|
|
37
|
+
atomic_flag event_ringbuffer_lock;
|
|
38
|
+
int started;
|
|
27
39
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
28
40
|
FILE *log;
|
|
29
41
|
#endif
|
|
@@ -31,12 +43,15 @@ typedef struct {
|
|
|
31
43
|
|
|
32
44
|
static inline void push_event(TraceContext *context, RRTraceEvent event) {
|
|
33
45
|
if (context->event_ringbuffer == NULL) return;
|
|
46
|
+
while (atomic_flag_test_and_set_explicit(&context->event_ringbuffer_lock, memory_order_acquire)) {
|
|
47
|
+
}
|
|
34
48
|
while (!rrtrace_event_ringbuffer_push(context->event_ringbuffer, event)) {
|
|
35
49
|
if (!is_process_running(context->visualizer_process_id)) {
|
|
36
50
|
context->event_ringbuffer = NULL;
|
|
37
51
|
break;
|
|
38
52
|
}
|
|
39
53
|
}
|
|
54
|
+
atomic_flag_clear_explicit(&context->event_ringbuffer_lock, memory_order_release);
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
static uint32_t get_thread_id(TraceContext *context, VALUE thread) {
|
|
@@ -93,80 +108,137 @@ static void tracepoint_gc_end_handler(VALUE tpval, void *data) {
|
|
|
93
108
|
|
|
94
109
|
static void thread_start_handler(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *data) {
|
|
95
110
|
TraceContext *context = (TraceContext *)data;
|
|
96
|
-
|
|
111
|
+
uint32_t thread_id = get_thread_id(context, event_data->thread);
|
|
112
|
+
push_event(context, event_thread_start(thread_id));
|
|
97
113
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
98
|
-
fprintf(context->log, "THREAD START\n");
|
|
114
|
+
fprintf(context->log, "THREAD %02d START\n", thread_id);
|
|
99
115
|
fflush(context->log);
|
|
100
116
|
#endif
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
static void thread_ready_handler(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *data) {
|
|
104
120
|
TraceContext *context = (TraceContext *)data;
|
|
105
|
-
|
|
121
|
+
uint32_t thread_id = get_thread_id(context, event_data->thread);
|
|
122
|
+
push_event(context, event_thread_ready(thread_id));
|
|
106
123
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
107
|
-
fprintf(context->log, "THREAD READY\n");
|
|
124
|
+
fprintf(context->log, "THREAD %02d READY\n", thread_id);
|
|
108
125
|
fflush(context->log);
|
|
109
126
|
#endif
|
|
110
127
|
}
|
|
111
128
|
|
|
112
129
|
static void thread_suspended_handler(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *data) {
|
|
113
130
|
TraceContext *context = (TraceContext *)data;
|
|
114
|
-
|
|
131
|
+
uint32_t thread_id = get_thread_id(context, event_data->thread);
|
|
132
|
+
push_event(context, event_thread_suspended(thread_id));
|
|
115
133
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
116
|
-
fprintf(context->log, "THREAD SUSPENDED\n");
|
|
134
|
+
fprintf(context->log, "THREAD %02d SUSPENDED\n", thread_id);
|
|
117
135
|
fflush(context->log);
|
|
118
136
|
#endif
|
|
119
137
|
}
|
|
120
138
|
|
|
121
139
|
static void thread_resume_handler(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *data) {
|
|
122
140
|
TraceContext *context = (TraceContext *)data;
|
|
123
|
-
|
|
141
|
+
uint32_t thread_id = get_thread_id(context, event_data->thread);
|
|
142
|
+
push_event(context, event_thread_resume(thread_id));
|
|
124
143
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
125
|
-
fprintf(context->log, "THREAD RESUME\n");
|
|
144
|
+
fprintf(context->log, "THREAD %02d RESUME\n", thread_id);
|
|
126
145
|
fflush(context->log);
|
|
127
146
|
#endif
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
static void thread_exit_handler(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *data) {
|
|
131
150
|
TraceContext *context = (TraceContext *)data;
|
|
132
|
-
|
|
151
|
+
uint32_t thread_id = get_thread_id(context, event_data->thread);
|
|
152
|
+
push_event(context, event_thread_exit(thread_id));
|
|
133
153
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
134
|
-
fprintf(context->log, "THREAD EXIT\n");
|
|
154
|
+
fprintf(context->log, "THREAD %02d EXIT\n", thread_id);
|
|
135
155
|
fflush(context->log);
|
|
136
156
|
#endif
|
|
137
157
|
}
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
159
|
+
static TraceContext trace_context;
|
|
160
|
+
|
|
161
|
+
static void unregister_tracepoint(VALUE *tracepoint) {
|
|
162
|
+
if (NIL_P(*tracepoint)) return;
|
|
163
|
+
|
|
164
|
+
rb_tracepoint_disable(*tracepoint);
|
|
165
|
+
rb_gc_unregister_address(tracepoint);
|
|
166
|
+
*tracepoint = Qnil;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static void remove_thread_hook(rb_internal_thread_event_hook_t **hook) {
|
|
170
|
+
if (*hook == NULL) return;
|
|
171
|
+
|
|
172
|
+
rb_internal_thread_remove_event_hook(*hook);
|
|
173
|
+
*hook = NULL;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static void cleanup_context(TraceContext *context) {
|
|
177
|
+
unregister_tracepoint(&context->trace_call);
|
|
178
|
+
unregister_tracepoint(&context->trace_return);
|
|
179
|
+
unregister_tracepoint(&context->trace_gc_start);
|
|
180
|
+
unregister_tracepoint(&context->trace_gc_end);
|
|
181
|
+
|
|
182
|
+
remove_thread_hook(&context->thread_start_hook);
|
|
183
|
+
remove_thread_hook(&context->thread_ready_hook);
|
|
184
|
+
remove_thread_hook(&context->thread_suspended_hook);
|
|
185
|
+
remove_thread_hook(&context->thread_resume_hook);
|
|
186
|
+
remove_thread_hook(&context->thread_exit_hook);
|
|
187
|
+
|
|
188
|
+
context->event_ringbuffer = NULL;
|
|
189
|
+
close_shared_memory(&context->shared_memory);
|
|
190
|
+
|
|
191
|
+
if (context->visualizer_process_id != invalid_process_id()) {
|
|
192
|
+
terminate_process(context->visualizer_process_id);
|
|
193
|
+
close_process(context->visualizer_process_id);
|
|
194
|
+
context->visualizer_process_id = invalid_process_id();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
context->started = 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static VALUE rrtrace_native_started_p(VALUE self) {
|
|
201
|
+
return trace_context.started ? Qtrue : Qfalse;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static VALUE rrtrace_native_stop(VALUE self) {
|
|
205
|
+
if (!trace_context.started) return Qfalse;
|
|
206
|
+
|
|
207
|
+
cleanup_context(&trace_context);
|
|
208
|
+
return Qtrue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static VALUE rrtrace_native_start(VALUE self, VALUE visualizer) {
|
|
212
|
+
TraceContext *context = &trace_context;
|
|
213
|
+
VALUE visualizer_path = rb_str_dup(StringValue(visualizer));
|
|
214
|
+
char *visualizer_path_cstr = StringValueCStr(visualizer_path);
|
|
215
|
+
|
|
216
|
+
if (context->started) return Qfalse;
|
|
217
|
+
|
|
218
|
+
atomic_store_explicit(&context->next_thread_id, 1, memory_order_relaxed);
|
|
148
219
|
|
|
149
220
|
char shm_name[64];
|
|
150
221
|
generate_shared_memory_name(shm_name, sizeof(shm_name));
|
|
151
|
-
|
|
152
|
-
if (
|
|
222
|
+
context->shared_memory = open_shared_memory(shm_name, sizeof(RRTraceEventRingBuffer));
|
|
223
|
+
if (!shared_memory_opened(context->shared_memory)) {
|
|
153
224
|
rb_raise(rb_eRuntimeError, "Failed to create shared memory for rrtrace");
|
|
154
|
-
return;
|
|
225
|
+
return Qfalse;
|
|
155
226
|
}
|
|
227
|
+
|
|
228
|
+
RRTraceEventRingBuffer *ringbuffer = shared_memory_ptr(&context->shared_memory);
|
|
156
229
|
rrtrace_event_ringbuffer_init(ringbuffer);
|
|
157
230
|
context->event_ringbuffer = ringbuffer;
|
|
158
231
|
|
|
159
|
-
VALUE mMyGem = rb_const_get(rb_cObject, rb_intern("Rrtrace"));
|
|
160
|
-
VALUE visualizer = rb_funcall(mMyGem, rb_intern("visualizer_path"), 0);
|
|
161
|
-
char *visualizer_path = rb_string_value_cstr(&visualizer);
|
|
162
232
|
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
163
|
-
fprintf(context->log, "Visualizer: %s\n",
|
|
233
|
+
fprintf(context->log, "Visualizer: %s\n", visualizer_path_cstr);
|
|
164
234
|
fprintf(context->log, "Shared Memory: %s\n", shm_name);
|
|
165
235
|
#endif
|
|
166
|
-
|
|
167
|
-
|
|
236
|
+
init_base_timestamp();
|
|
237
|
+
process_id pid = spawn_process(visualizer_path_cstr, (char * const[]){visualizer_path_cstr, shm_name, NULL});
|
|
238
|
+
if (pid == invalid_process_id()) {
|
|
239
|
+
cleanup_context(context);
|
|
168
240
|
rb_raise(rb_eRuntimeError, "Failed to spawn visualizer process");
|
|
169
|
-
return;
|
|
241
|
+
return Qfalse;
|
|
170
242
|
}
|
|
171
243
|
context->visualizer_process_id = pid;
|
|
172
244
|
|
|
@@ -175,18 +247,55 @@ Init_rrtrace(void)
|
|
|
175
247
|
VALUE thread = rb_thread_current();
|
|
176
248
|
rb_internal_thread_specific_set(thread, context->thread_data_key, main_thread_data);
|
|
177
249
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
rb_internal_thread_add_event_hook(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
250
|
+
context->trace_call = rb_tracepoint_new(RUBY_Qnil, RUBY_EVENT_CALL | RUBY_EVENT_C_CALL, tracepoint_call_handler, context);
|
|
251
|
+
rb_gc_register_address(&context->trace_call);
|
|
252
|
+
context->trace_return = rb_tracepoint_new(RUBY_Qnil, RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN, tracepoint_return_handler, context);
|
|
253
|
+
rb_gc_register_address(&context->trace_return);
|
|
254
|
+
context->trace_gc_start = rb_tracepoint_new(RUBY_Qnil, RUBY_INTERNAL_EVENT_GC_ENTER, tracepoint_gc_start_handler, context);
|
|
255
|
+
rb_gc_register_address(&context->trace_gc_start);
|
|
256
|
+
context->trace_gc_end = rb_tracepoint_new(RUBY_Qnil, RUBY_INTERNAL_EVENT_GC_EXIT, tracepoint_gc_end_handler, context);
|
|
257
|
+
rb_gc_register_address(&context->trace_gc_end);
|
|
258
|
+
context->thread_start_hook = rb_internal_thread_add_event_hook(thread_start_handler, RUBY_INTERNAL_THREAD_EVENT_STARTED, context);
|
|
259
|
+
context->thread_ready_hook = rb_internal_thread_add_event_hook(thread_ready_handler, RUBY_INTERNAL_THREAD_EVENT_READY, context);
|
|
260
|
+
context->thread_suspended_hook = rb_internal_thread_add_event_hook(thread_suspended_handler, RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, context);
|
|
261
|
+
context->thread_resume_hook = rb_internal_thread_add_event_hook(thread_resume_handler, RUBY_INTERNAL_THREAD_EVENT_RESUMED, context);
|
|
262
|
+
context->thread_exit_hook = rb_internal_thread_add_event_hook(thread_exit_handler, RUBY_INTERNAL_THREAD_EVENT_EXITED, context);
|
|
263
|
+
|
|
264
|
+
rb_tracepoint_enable(context->trace_call);
|
|
265
|
+
rb_tracepoint_enable(context->trace_return);
|
|
266
|
+
rb_tracepoint_enable(context->trace_gc_start);
|
|
267
|
+
rb_tracepoint_enable(context->trace_gc_end);
|
|
268
|
+
|
|
269
|
+
context->started = 1;
|
|
270
|
+
return Qtrue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
RUBY_FUNC_EXPORTED void
|
|
274
|
+
Init_rrtrace(void)
|
|
275
|
+
{
|
|
276
|
+
TraceContext *context = &trace_context;
|
|
277
|
+
context->shared_memory = invalid_shared_memory_handle();
|
|
278
|
+
context->event_ringbuffer = NULL;
|
|
279
|
+
context->visualizer_process_id = invalid_process_id();
|
|
280
|
+
context->thread_start_hook = NULL;
|
|
281
|
+
context->thread_ready_hook = NULL;
|
|
282
|
+
context->thread_suspended_hook = NULL;
|
|
283
|
+
context->thread_resume_hook = NULL;
|
|
284
|
+
context->thread_exit_hook = NULL;
|
|
285
|
+
context->trace_call = Qnil;
|
|
286
|
+
context->trace_return = Qnil;
|
|
287
|
+
context->trace_gc_start = Qnil;
|
|
288
|
+
context->trace_gc_end = Qnil;
|
|
289
|
+
context->thread_data_key = rb_internal_thread_specific_key_create();
|
|
290
|
+
atomic_init(&context->next_thread_id, 1);
|
|
291
|
+
atomic_flag_clear(&context->event_ringbuffer_lock);
|
|
292
|
+
context->started = 0;
|
|
293
|
+
#ifdef RRTRACE_WRITE_DEBUG_LOG
|
|
294
|
+
context->log = fopen("rrtrace.log", "w");
|
|
295
|
+
#endif
|
|
296
|
+
|
|
297
|
+
VALUE mRrtrace = rb_const_get(rb_cObject, rb_intern("Rrtrace"));
|
|
298
|
+
rb_define_singleton_method(mRrtrace, "native_start", rrtrace_native_start, 1);
|
|
299
|
+
rb_define_singleton_method(mRrtrace, "native_stop", rrtrace_native_stop, 0);
|
|
300
|
+
rb_define_singleton_method(mRrtrace, "native_started?", rrtrace_native_started_p, 0);
|
|
192
301
|
}
|
data/ext/rrtrace/rrtrace_event.h
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
#ifndef RRTRACE_EVENT_H
|
|
2
2
|
#define RRTRACE_EVENT_H
|
|
3
3
|
|
|
4
|
-
#include <time.h>
|
|
5
4
|
#include <stdatomic.h>
|
|
6
5
|
#include <stdint.h>
|
|
7
6
|
|
|
7
|
+
#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__)
|
|
8
|
+
#include "time_windows.h"
|
|
9
|
+
#else
|
|
10
|
+
#include "time_posix.h"
|
|
11
|
+
#endif
|
|
12
|
+
|
|
8
13
|
#define EVENT_TYPE_CALL 0x0000000000000000ull
|
|
9
14
|
#define EVENT_TYPE_RETURN 0x1000000000000000ull
|
|
10
15
|
#define EVENT_TYPE_GC_START 0x2000000000000000ull
|
|
@@ -22,20 +27,6 @@ typedef struct {
|
|
|
22
27
|
uint64_t data;
|
|
23
28
|
} RRTraceEvent;
|
|
24
29
|
|
|
25
|
-
static inline uint64_t now(void) {
|
|
26
|
-
struct timespec ts;
|
|
27
|
-
timespec_get(&ts, TIME_UTC);
|
|
28
|
-
uint64_t timestamp = (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
|
|
29
|
-
|
|
30
|
-
static atomic_uint_fast64_t base_timestamp = 0;
|
|
31
|
-
uint64_t expected = 0;
|
|
32
|
-
if (atomic_compare_exchange_strong_explicit(&base_timestamp, &expected, timestamp, memory_order_relaxed, memory_order_relaxed)) {
|
|
33
|
-
return 0;
|
|
34
|
-
} else {
|
|
35
|
-
return timestamp - expected;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
30
|
static inline RRTraceEvent event_call(uint64_t method_id) {
|
|
40
31
|
RRTraceEvent event;
|
|
41
32
|
event.timestamp_and_event_type = now() | EVENT_TYPE_CALL;
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
typedef struct {
|
|
10
10
|
RRTraceEvent buffer[SIZE];
|
|
11
|
-
alignas(
|
|
11
|
+
alignas(128) struct {
|
|
12
12
|
atomic_uint_fast64_t write_index;
|
|
13
13
|
uint64_t read_index_cache;
|
|
14
14
|
} writer;
|
|
15
|
-
alignas(
|
|
15
|
+
alignas(128) struct {
|
|
16
16
|
atomic_uint_fast64_t read_index;
|
|
17
17
|
uint64_t write_index_cache;
|
|
18
18
|
} reader;
|
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
#include <unistd.h>
|
|
8
8
|
#include <time.h>
|
|
9
9
|
#include <stdio.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
typedef struct {
|
|
13
|
+
int fd;
|
|
14
|
+
void *ptr;
|
|
15
|
+
size_t size;
|
|
16
|
+
char name[64];
|
|
17
|
+
} shared_memory_handle;
|
|
10
18
|
|
|
11
19
|
static inline void generate_shared_memory_name(char *buffer, size_t size) {
|
|
12
20
|
struct timespec ts;
|
|
@@ -14,13 +22,57 @@ static inline void generate_shared_memory_name(char *buffer, size_t size) {
|
|
|
14
22
|
snprintf(buffer, size, "/rrtrace_shm_%d_%d", getpid(), (int)ts.tv_nsec);
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
static inline
|
|
25
|
+
static inline shared_memory_handle invalid_shared_memory_handle(void) {
|
|
26
|
+
shared_memory_handle shm;
|
|
27
|
+
shm.fd = -1;
|
|
28
|
+
shm.ptr = NULL;
|
|
29
|
+
shm.size = 0;
|
|
30
|
+
shm.name[0] = '\0';
|
|
31
|
+
return shm;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static inline shared_memory_handle open_shared_memory(const char *name, int size) {
|
|
18
35
|
int fd = shm_open(name, O_CREAT | O_RDWR, 0666);
|
|
19
|
-
if (fd == -1) return
|
|
20
|
-
if (ftruncate(fd, size) == -1)
|
|
36
|
+
if (fd == -1) return invalid_shared_memory_handle();
|
|
37
|
+
if (ftruncate(fd, size) == -1) {
|
|
38
|
+
close(fd);
|
|
39
|
+
return invalid_shared_memory_handle();
|
|
40
|
+
}
|
|
21
41
|
void *ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
22
|
-
if (ptr == MAP_FAILED)
|
|
23
|
-
|
|
42
|
+
if (ptr == MAP_FAILED) {
|
|
43
|
+
close(fd);
|
|
44
|
+
return invalid_shared_memory_handle();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
shared_memory_handle shm;
|
|
48
|
+
shm.fd = fd;
|
|
49
|
+
shm.ptr = ptr;
|
|
50
|
+
shm.size = size;
|
|
51
|
+
snprintf(shm.name, sizeof(shm.name), "%s", name);
|
|
52
|
+
return shm;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static inline int shared_memory_opened(shared_memory_handle shm) {
|
|
56
|
+
return shm.ptr != NULL;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static inline void *shared_memory_ptr(shared_memory_handle *shm) {
|
|
60
|
+
return shm->ptr;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static inline void close_shared_memory(shared_memory_handle *shm) {
|
|
64
|
+
if (shm->ptr != NULL) {
|
|
65
|
+
munmap(shm->ptr, shm->size);
|
|
66
|
+
shm->ptr = NULL;
|
|
67
|
+
}
|
|
68
|
+
if (shm->fd != -1) {
|
|
69
|
+
close(shm->fd);
|
|
70
|
+
shm->fd = -1;
|
|
71
|
+
}
|
|
72
|
+
if (shm->name[0] != '\0') {
|
|
73
|
+
shm_unlink(shm->name);
|
|
74
|
+
shm->name[0] = '\0';
|
|
75
|
+
}
|
|
24
76
|
}
|
|
25
77
|
|
|
26
78
|
#endif /* SHARED_MEMORY_POSIX_H */
|
|
@@ -4,11 +4,21 @@
|
|
|
4
4
|
#include <windows.h>
|
|
5
5
|
#include <stdio.h>
|
|
6
6
|
|
|
7
|
+
typedef struct {
|
|
8
|
+
HANDLE handle;
|
|
9
|
+
void *ptr;
|
|
10
|
+
} shared_memory_handle;
|
|
11
|
+
|
|
7
12
|
static inline void generate_shared_memory_name(char *buffer, size_t size) {
|
|
8
13
|
snprintf(buffer, size, "Local\\rrtrace_shm_%lu_%lu", GetCurrentProcessId(), GetTickCount());
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
static inline
|
|
16
|
+
static inline shared_memory_handle invalid_shared_memory_handle(void) {
|
|
17
|
+
shared_memory_handle shm = {0};
|
|
18
|
+
return shm;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static inline shared_memory_handle open_shared_memory(const char *name, int size) {
|
|
12
22
|
HANDLE hMapFile = CreateFileMappingA(
|
|
13
23
|
INVALID_HANDLE_VALUE,
|
|
14
24
|
NULL,
|
|
@@ -18,7 +28,7 @@ static inline void* open_shared_memory(const char *name, int size) {
|
|
|
18
28
|
name);
|
|
19
29
|
|
|
20
30
|
if (hMapFile == NULL) {
|
|
21
|
-
return
|
|
31
|
+
return invalid_shared_memory_handle();
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
void *ptr = MapViewOfFile(
|
|
@@ -28,7 +38,32 @@ static inline void* open_shared_memory(const char *name, int size) {
|
|
|
28
38
|
0,
|
|
29
39
|
size);
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
if (ptr == NULL) {
|
|
42
|
+
CloseHandle(hMapFile);
|
|
43
|
+
return invalid_shared_memory_handle();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
shared_memory_handle shm = {hMapFile, ptr};
|
|
47
|
+
return shm;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static inline int shared_memory_opened(shared_memory_handle shm) {
|
|
51
|
+
return shm.ptr != NULL;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static inline void *shared_memory_ptr(shared_memory_handle *shm) {
|
|
55
|
+
return shm->ptr;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static inline void close_shared_memory(shared_memory_handle *shm) {
|
|
59
|
+
if (shm->ptr != NULL) {
|
|
60
|
+
UnmapViewOfFile(shm->ptr);
|
|
61
|
+
shm->ptr = NULL;
|
|
62
|
+
}
|
|
63
|
+
if (shm->handle != NULL) {
|
|
64
|
+
CloseHandle(shm->handle);
|
|
65
|
+
shm->handle = NULL;
|
|
66
|
+
}
|
|
32
67
|
}
|
|
33
68
|
|
|
34
69
|
#endif /* SHARED_MEMORY_WIN_H */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#ifndef RRTRACE_TIME_POSIX_H
|
|
2
|
+
#define RRTRACE_TIME_POSIX_H
|
|
3
|
+
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <time.h>
|
|
6
|
+
|
|
7
|
+
static struct timespec rrtrace_base_timestamp = {0};
|
|
8
|
+
|
|
9
|
+
static inline void init_base_timestamp(void) {
|
|
10
|
+
clock_gettime(CLOCK_MONOTONIC, &rrtrace_base_timestamp);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static inline uint64_t now(void) {
|
|
14
|
+
struct timespec ts;
|
|
15
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
16
|
+
|
|
17
|
+
uint64_t seconds = (uint64_t)(ts.tv_sec - rrtrace_base_timestamp.tv_sec);
|
|
18
|
+
uint64_t nanoseconds;
|
|
19
|
+
if (ts.tv_nsec >= rrtrace_base_timestamp.tv_nsec) {
|
|
20
|
+
nanoseconds = (uint64_t)(ts.tv_nsec - rrtrace_base_timestamp.tv_nsec);
|
|
21
|
+
} else {
|
|
22
|
+
seconds -= 1;
|
|
23
|
+
nanoseconds = (uint64_t)ts.tv_nsec + 1000000000ull - (uint64_t)rrtrace_base_timestamp.tv_nsec;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return seconds * 1000000000ull + nanoseconds;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#endif /* RRTRACE_TIME_POSIX_H */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#ifndef RRTRACE_TIME_WINDOWS_H
|
|
2
|
+
#define RRTRACE_TIME_WINDOWS_H
|
|
3
|
+
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <windows.h>
|
|
6
|
+
|
|
7
|
+
static LARGE_INTEGER rrtrace_base_counter = {0};
|
|
8
|
+
static LARGE_INTEGER rrtrace_counter_frequency = {0};
|
|
9
|
+
|
|
10
|
+
static inline uint64_t rrtrace_counter_to_ns(uint64_t counter) {
|
|
11
|
+
uint64_t frequency = (uint64_t)rrtrace_counter_frequency.QuadPart;
|
|
12
|
+
uint64_t seconds = counter / frequency;
|
|
13
|
+
uint64_t remainder = counter % frequency;
|
|
14
|
+
return seconds * 1000000000ull + (remainder * 1000000000ull) / frequency;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static inline void init_base_timestamp(void) {
|
|
18
|
+
if (!QueryPerformanceFrequency(&rrtrace_counter_frequency) || rrtrace_counter_frequency.QuadPart <= 0) {
|
|
19
|
+
rrtrace_counter_frequency.QuadPart = 1;
|
|
20
|
+
}
|
|
21
|
+
if (!QueryPerformanceCounter(&rrtrace_base_counter)) {
|
|
22
|
+
rrtrace_base_counter.QuadPart = 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static inline uint64_t now(void) {
|
|
27
|
+
LARGE_INTEGER counter;
|
|
28
|
+
if (!QueryPerformanceCounter(&counter)) return 0;
|
|
29
|
+
|
|
30
|
+
return rrtrace_counter_to_ns((uint64_t)(counter.QuadPart - rrtrace_base_counter.QuadPart));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#endif /* RRTRACE_TIME_WINDOWS_H */
|
data/lib/rrtrace/run.rb
ADDED
data/lib/rrtrace/version.rb
CHANGED
data/lib/rrtrace.rb
CHANGED
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
3
5
|
require_relative "rrtrace/version"
|
|
4
6
|
|
|
5
7
|
module Rrtrace
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
class << self
|
|
9
|
+
def visualizer_path
|
|
10
|
+
@visualizer_path ||= default_visualizer_path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def start
|
|
14
|
+
native_start(visualizer_path)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stop
|
|
18
|
+
native_stop
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def started?
|
|
22
|
+
native_started?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def default_visualizer_path
|
|
28
|
+
exe = "rrtrace#{RbConfig::CONFIG["EXEEXT"]}"
|
|
29
|
+
File.expand_path("../libexec/#{exe}", __dir__)
|
|
30
|
+
end
|
|
9
31
|
end
|
|
10
32
|
end
|
|
11
33
|
|
|
12
34
|
require "rrtrace/rrtrace"
|
|
35
|
+
|
|
36
|
+
module Rrtrace
|
|
37
|
+
private_class_method :native_start, :native_stop, :native_started?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Kernel.at_exit { Rrtrace.stop }
|
data/libexec/rrtrace
CHANGED
|
Binary file
|
data/mise.toml
CHANGED