pf2 0.13.0 → 1.0.0.alpha1

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.
data/ext/pf2/serializer.h CHANGED
@@ -1,23 +1,21 @@
1
1
  #ifndef PF2C_SERIALIZER_H
2
2
  #define PF2C_SERIALIZER_H
3
3
 
4
- #include <stdint.h>
5
-
6
4
  #include <ruby.h>
7
5
 
8
6
  #include "session.h"
9
7
 
10
8
  struct pf2_ser_sample {
11
- size_t *stack; // array of location_indexes
9
+ int *stack;
12
10
  size_t stack_count;
13
- size_t *native_stack; // array of location_indexes
11
+ int *native_stack;
14
12
  size_t native_stack_count;
15
- uintptr_t ruby_thread_id;
13
+ size_t ruby_thread_id;
16
14
  uint64_t elapsed_ns;
17
15
  };
18
16
 
19
17
  struct pf2_ser_location {
20
- size_t function_index;
18
+ int function_index;
21
19
  int32_t lineno;
22
20
  size_t address;
23
21
  };
@@ -38,8 +36,6 @@ struct pf2_ser_function {
38
36
  struct pf2_ser {
39
37
  uint64_t start_timestamp_ns;
40
38
  uint64_t duration_ns;
41
- uint64_t collected_sample_count;
42
- uint64_t dropped_sample_count;
43
39
 
44
40
  struct pf2_ser_sample *samples;
45
41
  size_t samples_count;
data/ext/pf2/session.c CHANGED
@@ -1,10 +1,10 @@
1
+ #include <bits/time.h>
1
2
  #include <pthread.h>
2
3
  #include <signal.h>
3
4
  #include <stdatomic.h>
4
5
  #include <stdbool.h>
5
6
  #include <stdio.h>
6
7
  #include <stdlib.h>
7
- #include <string.h>
8
8
  #include <sys/time.h>
9
9
  #include <time.h>
10
10
 
@@ -20,17 +20,14 @@
20
20
  #include "session.h"
21
21
  #include "serializer.h"
22
22
 
23
- // Pointer to current active session, for access from signal handlers
23
+ #ifndef HAVE_TIMER_CREATE
24
+ // Global session pointer for setitimer fallback
24
25
  static struct pf2_session *global_current_session = NULL;
26
+ #endif
25
27
 
26
28
  static void *sample_collector_thread(void *arg);
27
- static void drain_ringbuffer(struct pf2_session *session);
28
29
  static void sigprof_handler(int sig, siginfo_t *info, void *ucontext);
29
- static void pf2_session_stop(struct pf2_session *session);
30
- static size_t intern_location(struct pf2_session *session, VALUE cme, int lineno);
31
- static size_t intern_stack(struct pf2_session *session, const size_t *frames, size_t depth);
32
- static size_t intern_native_stack(struct pf2_session *session, const uintptr_t *frames, size_t depth);
33
- static bool insert_sample(struct pf2_session *session, const struct pf2_sample *sample);
30
+ bool ensure_sample_capacity(struct pf2_session *session);
34
31
 
35
32
  VALUE
36
33
  rb_pf2_session_initialize(int argc, VALUE *argv, VALUE self)
@@ -43,11 +40,10 @@ rb_pf2_session_initialize(int argc, VALUE *argv, VALUE self)
43
40
  rb_scan_args(argc, argv, ":", &kwargs);
44
41
  ID kwarg_labels[] = {
45
42
  rb_intern("interval_ms"),
46
- rb_intern("time_mode"),
47
- rb_intern("_test_no_install_timer")
43
+ rb_intern("time_mode")
48
44
  };
49
45
  VALUE *kwarg_values = NULL;
50
- rb_get_kwargs(kwargs, kwarg_labels, 0, 3, kwarg_values);
46
+ rb_get_kwargs(kwargs, kwarg_labels, 0, 2, kwarg_values);
51
47
 
52
48
  session->configuration = pf2_configuration_new_from_options_hash(kwargs);
53
49
 
@@ -60,9 +56,6 @@ rb_pf2_session_start(VALUE self)
60
56
  struct pf2_session *session;
61
57
  TypedData_Get_Struct(self, struct pf2_session, &pf2_session_type, session);
62
58
 
63
- // Store pointer to current session for access from signal handlers
64
- global_current_session = session;
65
-
66
59
  session->is_running = true;
67
60
 
68
61
  // Record start time
@@ -94,60 +87,58 @@ rb_pf2_session_start(VALUE self)
94
87
  }
95
88
  #endif
96
89
 
97
- global_current_session = session;
98
-
99
- if (!session->configuration->_test_no_install_timer) {
100
90
  #ifdef HAVE_TIMER_CREATE
101
- // Configure a kernel timer to send SIGPROF periodically
102
- struct sigevent sev;
103
- sev.sigev_notify = SIGEV_SIGNAL;
104
- sev.sigev_signo = SIGPROF;
105
- if (timer_create(
106
- session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
107
- ? CLOCK_PROCESS_CPUTIME_ID
108
- : CLOCK_MONOTONIC,
109
- &sev,
110
- &session->timer
111
- ) == -1) {
112
- rb_raise(rb_eRuntimeError, "Failed to create timer");
113
- }
114
- struct itimerspec its = {
115
- .it_value = {
116
- .tv_sec = 0,
117
- .tv_nsec = session->configuration->interval_ms * 1000000,
118
- },
119
- .it_interval = {
120
- .tv_sec = 0,
121
- .tv_nsec = session->configuration->interval_ms * 1000000,
122
- },
123
- };
124
- if (timer_settime(session->timer, 0, &its, NULL) == -1) {
125
- rb_raise(rb_eRuntimeError, "Failed to start timer");
126
- }
91
+ // Configure a kernel timer to send SIGPROF periodically
92
+ struct sigevent sev;
93
+ sev.sigev_notify = SIGEV_SIGNAL;
94
+ sev.sigev_signo = SIGPROF;
95
+ sev.sigev_value.sival_ptr = session; // Passed as info->si_value.sival_ptr
96
+ if (timer_create(
97
+ session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
98
+ ? CLOCK_PROCESS_CPUTIME_ID
99
+ : CLOCK_MONOTONIC,
100
+ &sev,
101
+ &session->timer
102
+ ) == -1) {
103
+ rb_raise(rb_eRuntimeError, "Failed to create timer");
104
+ }
105
+ struct itimerspec its = {
106
+ .it_value = {
107
+ .tv_sec = 0,
108
+ .tv_nsec = session->configuration->interval_ms * 1000000,
109
+ },
110
+ .it_interval = {
111
+ .tv_sec = 0,
112
+ .tv_nsec = session->configuration->interval_ms * 1000000,
113
+ },
114
+ };
115
+ if (timer_settime(session->timer, 0, &its, NULL) == -1) {
116
+ rb_raise(rb_eRuntimeError, "Failed to start timer");
117
+ }
127
118
  #else
128
- // Use setitimer as fallback
129
- // Some platforms (e.g. macOS) do not have timer_create(3).
130
- // setitimer(3) can be used as a alternative, but has limited functionality.
131
-
132
- struct itimerval itv = {
133
- .it_value = {
134
- .tv_sec = 0,
135
- .tv_usec = session->configuration->interval_ms * 1000,
136
- },
137
- .it_interval = {
138
- .tv_sec = 0,
139
- .tv_usec = session->configuration->interval_ms * 1000,
140
- },
141
- };
142
- int which_timer = session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
143
- ? ITIMER_PROF // CPU time (sends SIGPROF)
144
- : ITIMER_REAL; // Wall time (sends SIGALRM)
145
-
146
- if (setitimer(which_timer, &itv, NULL) == -1) {
147
- rb_raise(rb_eRuntimeError, "Failed to start timer");
148
- }
119
+ // Use setitimer as fallback
120
+ // Some platforms (e.g. macOS) do not have timer_create(3).
121
+ // setitimer(3) can be used as a alternative, but has limited functionality.
122
+ global_current_session = session;
123
+
124
+ struct itimerval itv = {
125
+ .it_value = {
126
+ .tv_sec = 0,
127
+ .tv_usec = session->configuration->interval_ms * 1000,
128
+ },
129
+ .it_interval = {
130
+ .tv_sec = 0,
131
+ .tv_usec = session->configuration->interval_ms * 1000,
132
+ },
133
+ };
134
+ int which_timer = session->configuration->time_mode == PF2_TIME_MODE_CPU_TIME
135
+ ? ITIMER_PROF // CPU time (sends SIGPROF)
136
+ : ITIMER_REAL; // Wall time (sends SIGALRM)
137
+
138
+ if (setitimer(which_timer, &itv, NULL) == -1) {
139
+ rb_raise(rb_eRuntimeError, "Failed to start timer");
140
+ }
149
141
  #endif
150
- } // if !__test_no_install_timer
151
142
 
152
143
  return Qtrue;
153
144
  }
@@ -159,9 +150,19 @@ sample_collector_thread(void *arg)
159
150
 
160
151
  while (session->is_running == true) {
161
152
  // Take samples from the ring buffer
162
- drain_ringbuffer(session);
153
+ struct pf2_sample sample;
154
+ while (pf2_ringbuffer_pop(session->rbuf, &sample) == true) {
155
+ // Ensure we have capacity before adding a new sample
156
+ if (!ensure_sample_capacity(session)) {
157
+ // Failed to expand buffer
158
+ PF2_DEBUG_LOG("Failed to expand sample buffer. Dropping sample\n");
159
+ break;
160
+ }
161
+
162
+ session->samples[session->samples_index++] = sample;
163
+ }
163
164
 
164
- // Sleep for 10 ms
165
+ // Sleep for 100 ms
165
166
  // TODO: Replace with high watermark callback
166
167
  struct timespec ts = { .tv_sec = 0, .tv_nsec = 10 * 1000000, }; // 10 ms
167
168
  nanosleep(&ts, NULL);
@@ -170,20 +171,6 @@ sample_collector_thread(void *arg)
170
171
  return NULL;
171
172
  }
172
173
 
173
- static void
174
- drain_ringbuffer(struct pf2_session *session)
175
- {
176
- struct pf2_sample sample;
177
- while (pf2_ringbuffer_pop(session->rbuf, &sample) == true) {
178
- if (!insert_sample(session, &sample)) {
179
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
180
- PF2_DEBUG_LOG("Failed to record sample. Dropping sample\n");
181
- } else {
182
- atomic_fetch_add_explicit(&session->collected_sample_count, 1, memory_order_relaxed);
183
- }
184
- }
185
- }
186
-
187
174
  // async-signal-safe
188
175
  static void
189
176
  sigprof_handler(int sig, siginfo_t *info, void *ucontext)
@@ -193,12 +180,16 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
193
180
  clock_gettime(CLOCK_MONOTONIC, &sig_start_time);
194
181
  #endif
195
182
 
196
- struct pf2_session *session = global_current_session;
183
+ struct pf2_session *session;
184
+ #ifdef HAVE_TIMER_CREATE
185
+ session = info->si_value.sival_ptr;
186
+ #else
187
+ session = global_current_session;
188
+ #endif
197
189
 
198
190
  // If garbage collection is in progress, don't collect samples.
199
191
  if (atomic_load_explicit(&session->is_marking, memory_order_acquire)) {
200
192
  PF2_DEBUG_LOG("Dropping sample: Garbage collection is in progress\n");
201
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
202
193
  return;
203
194
  }
204
195
 
@@ -206,7 +197,6 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
206
197
 
207
198
  if (pf2_sample_capture(&sample) == false) {
208
199
  PF2_DEBUG_LOG("Dropping sample: Failed to capture sample\n");
209
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
210
200
  return;
211
201
  }
212
202
 
@@ -214,7 +204,6 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
214
204
  if (pf2_ringbuffer_push(session->rbuf, &sample) == false) {
215
205
  // Copy failed. The sample buffer is full.
216
206
  PF2_DEBUG_LOG("Dropping sample: Sample buffer is full\n");
217
- atomic_fetch_add_explicit(&session->dropped_sample_count, 1, memory_order_relaxed);
218
207
  return;
219
208
  }
220
209
 
@@ -231,117 +220,27 @@ sigprof_handler(int sig, siginfo_t *info, void *ucontext)
231
220
  #endif
232
221
  }
233
222
 
234
- static size_t
235
- intern_location(struct pf2_session *session, VALUE cme, int lineno)
223
+ // Ensures that the session's sample array has capacity for at least one more sample
224
+ // Returns true if successful, false if memory allocation failed
225
+ bool
226
+ ensure_sample_capacity(struct pf2_session *session)
236
227
  {
237
- struct pf2_location_key key = { .cme = cme, .lineno = lineno };
238
- int absent;
239
- khint_t k = pf2_location_table_put(session->location_table, key, &absent);
240
- if (k == kh_end(session->location_table)) { return (size_t)-1; }
241
- if (absent) {
242
- kh_val(session->location_table, k) = kh_size(session->location_table) - 1;
228
+ // Check if we need to expand
229
+ if (session->samples_index < session->samples_capacity) {
230
+ return true;
243
231
  }
244
- return kh_val(session->location_table, k);
245
- }
246
232
 
247
- static size_t
248
- intern_stack(struct pf2_session *session, const size_t *frames, size_t depth)
249
- {
250
- struct pf2_stack_key skey = { .frames = frames, .depth = depth };
251
- int absent;
252
- khint_t k = pf2_stack_table_put(session->stack_table, skey, &absent);
253
- if (k == kh_end(session->stack_table)) { return (size_t)-1; }
254
- if (absent) {
255
- size_t *copy = NULL;
256
- if (depth > 0) {
257
- copy = malloc(sizeof(size_t) * depth);
258
- // TODO: if allocation fails, remove stack_table entry to avoid dangling stack-local key.
259
- if (copy == NULL) return (size_t)-1;
260
- memcpy(copy, frames, sizeof(size_t) * depth);
261
- }
262
- kh_key(session->stack_table, k).frames = copy;
263
- kh_key(session->stack_table, k).depth = depth;
264
- kh_val(session->stack_table, k) = kh_size(session->stack_table) - 1;
265
- }
266
- return kh_val(session->stack_table, k);
267
- }
233
+ // Calculate new size (double the current size)
234
+ size_t new_capacity = session->samples_capacity * 2;
268
235
 
269
- static size_t
270
- intern_native_stack(struct pf2_session *session, const uintptr_t *frames, size_t depth)
271
- {
272
- struct pf2_native_stack_key skey = { .frames = frames, .depth = depth };
273
- int absent;
274
- khint_t k = pf2_native_stack_table_put(session->native_stack_table, skey, &absent);
275
- if (k == kh_end(session->native_stack_table)) { return (size_t)-1; }
276
- if (absent) {
277
- uintptr_t *copy = NULL;
278
- if (depth > 0) {
279
- copy = malloc(sizeof(uintptr_t) * depth);
280
- if (copy == NULL) return (size_t)-1;
281
- memcpy(copy, frames, sizeof(uintptr_t) * depth);
282
- }
283
- kh_key(session->native_stack_table, k).frames = copy;
284
- kh_key(session->native_stack_table, k).depth = depth;
285
- kh_val(session->native_stack_table, k) = kh_size(session->native_stack_table) - 1;
236
+ // Reallocate the array
237
+ struct pf2_sample *new_samples = realloc(session->samples, new_capacity * sizeof(struct pf2_sample));
238
+ if (new_samples == NULL) {
239
+ return false;
286
240
  }
287
- return kh_val(session->native_stack_table, k);
288
- }
289
241
 
290
- static bool
291
- insert_sample(struct pf2_session *session, const struct pf2_sample *sample)
292
- {
293
- size_t frame_ids[PF2_SAMPLE_MAX_RUBY_DEPTH];
294
-
295
- // Convert each frame to a location
296
- for (int i = 0; i < sample->depth; i++) {
297
- frame_ids[i] = intern_location(session, sample->cmes[i], sample->linenos[i]);
298
- if (frame_ids[i] == (size_t)-1) { return false; }
299
- }
300
-
301
- // Obtain stack_id for the array of locations
302
- size_t stack_id = intern_stack(session, frame_ids, (size_t)sample->depth);
303
- if (stack_id == (size_t)-1) { return false; }
304
-
305
- size_t native_stack_id = intern_native_stack(session, sample->native_stack, sample->native_stack_depth);
306
- if (native_stack_id == (size_t)-1) { return false; }
307
-
308
- // Increment the observation count for this stack_id
309
- int absent;
310
- struct pf2_combined_stack_key ckey = {
311
- .ruby_stack_id = stack_id,
312
- .native_stack_id = native_stack_id
313
- };
314
- khint_t k = pf2_sample_table_put(session->sample_table, ckey, &absent);
315
- if (k == kh_end(session->sample_table)) { return false; }
316
- struct pf2_sample_stats *stats = &kh_val(session->sample_table, k);
317
- if (absent) {
318
- // This is the first time this stack was observed. Initialize stats.
319
- stats->count = 0;
320
- stats->timestamps = NULL;
321
- stats->thread_ids = NULL;
322
- stats->timestamps_count = 0;
323
- stats->timestamps_capacity = 0;
324
- }
325
-
326
- // count
327
- stats->count += 1;
328
- // timestamps
329
- if (stats->timestamps_count == stats->timestamps_capacity) {
330
- size_t new_cap = stats->timestamps_capacity ? stats->timestamps_capacity * 2 : 16;
331
- uint64_t *new_ts = realloc(stats->timestamps, sizeof(uint64_t) * new_cap);
332
- uintptr_t *new_threads = realloc(stats->thread_ids, sizeof(uintptr_t) * new_cap);
333
- if (new_ts == NULL || new_threads == NULL) {
334
- free(new_ts);
335
- free(new_threads);
336
- return false;
337
- }
338
- stats->timestamps = new_ts;
339
- stats->thread_ids = new_threads;
340
- stats->timestamps_capacity = new_cap;
341
- }
342
- stats->timestamps[stats->timestamps_count] = sample->timestamp_ns;
343
- stats->thread_ids[stats->timestamps_count] = (uintptr_t)sample->context_pthread;
344
- stats->timestamps_count++;
242
+ session->samples = new_samples;
243
+ session->samples_capacity = new_capacity;
345
244
 
346
245
  return true;
347
246
  }
@@ -352,20 +251,6 @@ rb_pf2_session_stop(VALUE self)
352
251
  struct pf2_session *session;
353
252
  TypedData_Get_Struct(self, struct pf2_session, &pf2_session_type, session);
354
253
 
355
- pf2_session_stop(session);
356
-
357
- // Create serializer and serialize
358
- struct pf2_ser *serializer = pf2_ser_new();
359
- pf2_ser_prepare(serializer, session);
360
- VALUE result = pf2_ser_to_ruby_hash(serializer);
361
- pf2_ser_free(serializer);
362
-
363
- return result;
364
- }
365
-
366
- static void
367
- pf2_session_stop(struct pf2_session *session)
368
- {
369
254
  // Calculate duration
370
255
  struct timespec end_time;
371
256
  clock_gettime(CLOCK_MONOTONIC, &end_time);
@@ -375,10 +260,8 @@ pf2_session_stop(struct pf2_session *session)
375
260
 
376
261
  // Disarm and delete the timer.
377
262
  #ifdef HAVE_TIMER_CREATE
378
- if (!session->configuration->_test_no_install_timer) {
379
- if (timer_delete(session->timer) == -1) {
380
- rb_raise(rb_eRuntimeError, "Failed to delete timer");
381
- }
263
+ if (timer_delete(session->timer) == -1) {
264
+ rb_raise(rb_eRuntimeError, "Failed to delete timer");
382
265
  }
383
266
  #else
384
267
  struct itimerval zero_timer = {{0, 0}, {0, 0}};
@@ -394,7 +277,14 @@ pf2_session_stop(struct pf2_session *session)
394
277
  // Terminate the collector thread
395
278
  session->is_running = false;
396
279
  pthread_join(*session->collector_thread, NULL);
397
- drain_ringbuffer(session);
280
+
281
+ // Create serializer and serialize
282
+ struct pf2_ser *serializer = pf2_ser_new();
283
+ pf2_ser_prepare(serializer, session);
284
+ VALUE result = pf2_ser_to_ruby_hash(serializer);
285
+ pf2_ser_free(serializer);
286
+
287
+ return result;
398
288
  }
399
289
 
400
290
  VALUE
@@ -410,7 +300,7 @@ pf2_session_alloc(VALUE self)
410
300
  {
411
301
  // Initialize state for libbacktrace
412
302
  if (global_backtrace_state == NULL) {
413
- global_backtrace_state = backtrace_create_state(NULL, 1, pf2_backtrace_print_error, NULL);
303
+ global_backtrace_state = backtrace_create_state("pf2", 1, pf2_backtrace_print_error, NULL);
414
304
  if (global_backtrace_state == NULL) {
415
305
  rb_raise(rb_eRuntimeError, "Failed to initialize libbacktrace");
416
306
  }
@@ -421,61 +311,26 @@ pf2_session_alloc(VALUE self)
421
311
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
422
312
  }
423
313
 
424
- // is_running
425
- session->is_running = false;
426
-
427
- // timer
428
- #ifdef HAVE_TIMER_CREATE
429
- session->timer = (timer_t)0;
430
- #else
431
- session->timer = (struct itimerval){0};
432
- #endif
433
-
434
- // rbuf
435
314
  session->rbuf = pf2_ringbuffer_new(1000);
436
315
  if (session->rbuf == NULL) {
437
316
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
438
317
  }
439
318
 
440
- // is_marking
441
319
  atomic_store_explicit(&session->is_marking, false, memory_order_relaxed);
442
-
443
- // collector_thread
444
320
  session->collector_thread = malloc(sizeof(pthread_t));
445
321
  if (session->collector_thread == NULL) {
446
322
  rb_raise(rb_eNoMemError, "Failed to allocate memory");
447
323
  }
448
324
 
449
- // location_table, stack_table, native_stack_table, sample_table
450
- session->location_table = pf2_location_table_init();
451
- if (session->location_table == NULL) {
452
- rb_raise(rb_eNoMemError, "Failed to allocate location table");
453
- }
454
- session->stack_table = pf2_stack_table_init();
455
- if (session->stack_table == NULL) {
456
- rb_raise(rb_eNoMemError, "Failed to allocate stack table");
457
- }
458
- session->native_stack_table = pf2_native_stack_table_init();
459
- if (session->native_stack_table == NULL) {
460
- rb_raise(rb_eNoMemError, "Failed to allocate native stack table");
461
- }
462
- session->sample_table = pf2_sample_table_init();
463
- if (session->sample_table == NULL) {
464
- rb_raise(rb_eNoMemError, "Failed to allocate stack sample table");
465
- }
466
-
467
- // collected_sample_count, dropped_sample_count
468
- atomic_store_explicit(&session->collected_sample_count, 0, memory_order_relaxed);
469
- atomic_store_explicit(&session->dropped_sample_count, 0, memory_order_relaxed);
470
-
471
- // start_time_realtime, start_time
472
- session->start_time_realtime = (struct timespec){0};
473
- session->start_time = (struct timespec){0};
474
-
475
- // duration_ns
476
325
  session->duration_ns = 0;
477
326
 
478
- // configuration
327
+ session->samples_index = 0;
328
+ session->samples_capacity = 500; // 10 seconds worth of samples at 50 Hz
329
+ session->samples = malloc(sizeof(struct pf2_sample) * session->samples_capacity);
330
+ if (session->samples == NULL) {
331
+ rb_raise(rb_eNoMemError, "Failed to allocate memory");
332
+ }
333
+
479
334
  session->configuration = NULL;
480
335
 
481
336
  return TypedData_Wrap_Struct(self, &pf2_session_type, session);
@@ -503,11 +358,11 @@ pf2_session_dmark(void *sess)
503
358
  head = (head + 1) % rbuf->size;
504
359
  }
505
360
 
506
- // Mark Ruby VALUEs stored in location_table keys
507
- if (session->location_table) {
508
- khint_t k;
509
- kh_foreach(session->location_table, k) {
510
- rb_gc_mark(kh_key(session->location_table, k).cme);
361
+ // Iterate over all samples in the samples array and mark them
362
+ for (size_t i = 0; i < session->samples_index; i++) {
363
+ sample = &session->samples[i];
364
+ for (int i = 0; i < sample->depth; i++) {
365
+ rb_gc_mark(sample->cmes[i]);
511
366
  }
512
367
  }
513
368
 
@@ -518,44 +373,11 @@ pf2_session_dmark(void *sess)
518
373
  void
519
374
  pf2_session_dfree(void *sess)
520
375
  {
376
+ // TODO: Ensure the uninstall process is complete before freeing the session
521
377
  struct pf2_session *session = sess;
522
-
523
- assert(session->is_running == false || session->is_running == true);
524
-
525
- // Stop the session if it's still running
526
- if (session->is_running) {
527
- pf2_session_stop(session);
528
- }
529
-
530
378
  pf2_configuration_free(session->configuration);
531
379
  pf2_ringbuffer_free(session->rbuf);
532
-
533
- if (session->sample_table) {
534
- khint_t k;
535
- kh_foreach(session->sample_table, k) {
536
- free(kh_val(session->sample_table, k).timestamps);
537
- free(kh_val(session->sample_table, k).thread_ids);
538
- }
539
- pf2_sample_table_destroy(session->sample_table);
540
- }
541
- if (session->stack_table) {
542
- khint_t k;
543
- kh_foreach(session->stack_table, k) {
544
- free((void *)kh_key(session->stack_table, k).frames);
545
- }
546
- pf2_stack_table_destroy(session->stack_table);
547
- }
548
- if (session->native_stack_table) {
549
- khint_t k;
550
- kh_foreach(session->native_stack_table, k) {
551
- free((void *)kh_key(session->native_stack_table, k).frames);
552
- }
553
- pf2_native_stack_table_destroy(session->native_stack_table);
554
- }
555
- if (session->location_table) {
556
- pf2_location_table_destroy(session->location_table);
557
- }
558
-
380
+ free(session->samples);
559
381
  free(session->collector_thread);
560
382
  free(session);
561
383
  }
@@ -566,6 +388,7 @@ pf2_session_dsize(const void *sess)
566
388
  const struct pf2_session *session = sess;
567
389
  return (
568
390
  sizeof(struct pf2_session)
391
+ + sizeof(struct pf2_sample) * session->samples_capacity
569
392
  + sizeof(struct pf2_sample) * session->rbuf->size
570
393
  );
571
394
  }