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.
@@ -1,7 +1,7 @@
1
1
  #include "rrtrace.h"
2
2
  #include "rrtrace_event_ringbuffer.h"
3
3
 
4
- #ifdef _WIN32
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
- push_event(context, event_thread_start(get_thread_id(context, event_data->thread)));
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
- push_event(context, event_thread_ready(get_thread_id(context, event_data->thread)));
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
- push_event(context, event_thread_suspended(get_thread_id(context, event_data->thread)));
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
- push_event(context, event_thread_resume(get_thread_id(context, event_data->thread)));
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
- push_event(context, event_thread_exit(get_thread_id(context, event_data->thread)));
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
- RUBY_FUNC_EXPORTED void
140
- Init_rrtrace(void)
141
- {
142
- TraceContext *context = malloc(sizeof(TraceContext));
143
- context->thread_data_key = rb_internal_thread_specific_key_create();
144
- atomic_init(&context->next_thread_id, 1);
145
- #ifdef RRTRACE_WRITE_DEBUG_LOG
146
- context->log = fopen("rrtrace.log", "w");
147
- #endif
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
- RRTraceEventRingBuffer *ringbuffer = open_shared_memory(shm_name, sizeof(RRTraceEventRingBuffer));
152
- if (ringbuffer == NULL) {
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", visualizer_path);
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
- process_id pid = spawn_process(visualizer_path, (char * const[]){visualizer_path, shm_name, NULL});
167
- if (pid == 0) {
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
- VALUE trace_call = rb_tracepoint_new(RUBY_Qnil, RUBY_EVENT_CALL | RUBY_EVENT_C_CALL, tracepoint_call_handler, context);
179
- VALUE trace_return = rb_tracepoint_new(RUBY_Qnil, RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN, tracepoint_return_handler, context);
180
- VALUE trace_gc_start = rb_tracepoint_new(RUBY_Qnil, RUBY_INTERNAL_EVENT_GC_ENTER, tracepoint_gc_start_handler, context);
181
- VALUE trace_gc_end = rb_tracepoint_new(RUBY_Qnil, RUBY_INTERNAL_EVENT_GC_EXIT, tracepoint_gc_end_handler, context);
182
- rb_internal_thread_add_event_hook(thread_start_handler, RUBY_INTERNAL_THREAD_EVENT_STARTED, context);
183
- rb_internal_thread_add_event_hook(thread_ready_handler, RUBY_INTERNAL_THREAD_EVENT_READY, context);
184
- rb_internal_thread_add_event_hook(thread_suspended_handler, RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, context);
185
- rb_internal_thread_add_event_hook(thread_resume_handler, RUBY_INTERNAL_THREAD_EVENT_RESUMED, context);
186
- rb_internal_thread_add_event_hook(thread_exit_handler, RUBY_INTERNAL_THREAD_EVENT_EXITED, context);
187
-
188
- rb_tracepoint_enable(trace_call);
189
- rb_tracepoint_enable(trace_return);
190
- rb_tracepoint_enable(trace_gc_start);
191
- rb_tracepoint_enable(trace_gc_end);
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
  }
@@ -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(64) struct {
11
+ alignas(128) struct {
12
12
  atomic_uint_fast64_t write_index;
13
13
  uint64_t read_index_cache;
14
14
  } writer;
15
- alignas(64) struct {
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 void* open_shared_memory(const char *name, int size) {
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 NULL;
20
- if (ftruncate(fd, size) == -1) return NULL;
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) return NULL;
23
- return ptr;
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 void* open_shared_memory(const char *name, int size) {
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 NULL;
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
- return ptr;
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 */
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rrtrace"
4
+
5
+ Rrtrace.start
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rrtrace
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
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
- def self.visualizer_path
7
- exe = "rrtrace#{RbConfig::CONFIG["EXEEXT"]}"
8
- File.expand_path("../libexec/#{exe}", __dir__)
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
@@ -1,8 +1,11 @@
1
1
  [tools]
2
- ruby = "4.0.0"
2
+ ruby = "4.0"
3
3
 
4
4
  [tasks.setup]
5
5
  run = "bundle install"
6
6
 
7
7
  [tasks.build]
8
- run = "bundle exec rake build"
8
+ run = """
9
+ rm -rf tmp
10
+ bundle exec rake build
11
+ """
data/sig/rrtrace.rbs CHANGED
@@ -1,4 +1,7 @@
1
1
  module Rrtrace
2
2
  VERSION: String
3
3
  def self.visualizer_path: () -> String
4
+ def self.start: () -> bool
5
+ def self.stop: () -> bool
6
+ def self.started?: () -> bool
4
7
  end