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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +0 -85
- data/README.md +15 -33
- data/Rakefile +0 -6
- data/ext/pf2/configuration.c +0 -14
- data/ext/pf2/configuration.h +0 -3
- data/ext/pf2/extconf.rb +3 -37
- data/ext/pf2/pf2.c +8 -8
- data/ext/pf2/sample.c +3 -1
- data/ext/pf2/sample.h +4 -5
- data/ext/pf2/serializer.c +38 -121
- data/ext/pf2/serializer.h +4 -8
- data/ext/pf2/session.c +115 -292
- data/ext/pf2/session.h +5 -157
- data/lib/pf2/cli.rb +1 -1
- data/lib/pf2/reporter/stack_weaver.rb +1 -1
- data/lib/pf2/serve.rb +2 -1
- data/lib/pf2/session.rb +9 -0
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +4 -16
- data/vendor/libbacktrace/atomic.c +1 -1
- data/vendor/libbacktrace/configure +4 -12
- data/vendor/libbacktrace/configure.ac +1 -6
- data/vendor/libbacktrace/elf.c +4 -4
- data/vendor/libbacktrace/fileline.c +1 -35
- data/vendor/libbacktrace/filetype.awk +0 -1
- metadata +3 -35
- data/.document +0 -3
- data/.rdoc_options +0 -6
- data/THIRD_PARTY_LICENSES.txt +0 -59
- data/ext/patches/libbacktrace/0001-Support-MACH_O_MH_BUNDLE.patch +0 -32
- data/ext/pf2/khashl.h +0 -506
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
|
-
|
|
9
|
+
int *stack;
|
|
12
10
|
size_t stack_count;
|
|
13
|
-
|
|
11
|
+
int *native_stack;
|
|
14
12
|
size_t native_stack_count;
|
|
15
|
-
|
|
13
|
+
size_t ruby_thread_id;
|
|
16
14
|
uint64_t elapsed_ns;
|
|
17
15
|
};
|
|
18
16
|
|
|
19
17
|
struct pf2_ser_location {
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
235
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
{
|
|
272
|
-
|
|
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
|
-
|
|
291
|
-
|
|
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 (
|
|
379
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
//
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
rb_gc_mark(
|
|
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
|
}
|