pf2 0.7.1 → 0.9.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/CHANGELOG.md +19 -0
- data/README.md +11 -0
- data/Rakefile +9 -2
- data/doc/development.md +11 -0
- data/examples/mandelbrot.rb +69 -0
- data/examples/mandelbrot_ractor.rb +77 -0
- data/ext/pf2/build.rs +7 -0
- data/ext/pf2/src/ruby_c_api_helper.c +6 -0
- data/ext/pf2/src/serialization/profile.rs +1 -0
- data/ext/pf2/src/serialization/serializer.rs +4 -0
- data/ext/pf2/src/signal_scheduler.rs +1 -1
- data/ext/pf2/src/util.rs +2 -1
- data/ext/pf2c/backtrace_state.c +10 -0
- data/ext/pf2c/backtrace_state.h +10 -0
- data/ext/pf2c/configuration.c +90 -0
- data/ext/pf2c/configuration.h +23 -0
- data/ext/pf2c/extconf.rb +21 -0
- data/ext/pf2c/pf2.c +17 -0
- data/ext/pf2c/pf2.h +8 -0
- data/ext/pf2c/ringbuffer.c +74 -0
- data/ext/pf2c/ringbuffer.h +24 -0
- data/ext/pf2c/sample.c +70 -0
- data/ext/pf2c/sample.h +22 -0
- data/ext/pf2c/serializer.c +377 -0
- data/ext/pf2c/serializer.h +58 -0
- data/ext/pf2c/session.c +344 -0
- data/ext/pf2c/session.h +51 -0
- data/lib/pf2/cli.rb +33 -2
- data/lib/pf2/reporter/annotate.rb +101 -0
- data/lib/pf2/reporter/firefox_profiler.rb +1 -1
- data/lib/pf2/reporter/firefox_profiler_ser2.rb +308 -0
- data/lib/pf2/reporter.rb +2 -0
- data/lib/pf2/version.rb +1 -1
- data/vendor/libbacktrace/.gitignore +5 -0
- data/vendor/libbacktrace/Isaac.Newton-Opticks.txt +9286 -0
- data/vendor/libbacktrace/LICENSE +29 -0
- data/vendor/libbacktrace/Makefile.am +708 -0
- data/vendor/libbacktrace/Makefile.in +2820 -0
- data/vendor/libbacktrace/README.md +46 -0
- data/vendor/libbacktrace/aclocal.m4 +864 -0
- data/vendor/libbacktrace/alloc.c +167 -0
- data/vendor/libbacktrace/allocfail.c +136 -0
- data/vendor/libbacktrace/allocfail.sh +104 -0
- data/vendor/libbacktrace/atomic.c +113 -0
- data/vendor/libbacktrace/backtrace-supported.h.in +66 -0
- data/vendor/libbacktrace/backtrace.c +129 -0
- data/vendor/libbacktrace/backtrace.h +189 -0
- data/vendor/libbacktrace/btest.c +517 -0
- data/vendor/libbacktrace/compile +348 -0
- data/vendor/libbacktrace/config/enable.m4 +38 -0
- data/vendor/libbacktrace/config/lead-dot.m4 +31 -0
- data/vendor/libbacktrace/config/libtool.m4 +7545 -0
- data/vendor/libbacktrace/config/ltoptions.m4 +369 -0
- data/vendor/libbacktrace/config/ltsugar.m4 +123 -0
- data/vendor/libbacktrace/config/ltversion.m4 +23 -0
- data/vendor/libbacktrace/config/lt~obsolete.m4 +98 -0
- data/vendor/libbacktrace/config/multi.m4 +68 -0
- data/vendor/libbacktrace/config/override.m4 +117 -0
- data/vendor/libbacktrace/config/unwind_ipinfo.m4 +37 -0
- data/vendor/libbacktrace/config/warnings.m4 +227 -0
- data/vendor/libbacktrace/config.guess +1700 -0
- data/vendor/libbacktrace/config.h.in +185 -0
- data/vendor/libbacktrace/config.sub +1885 -0
- data/vendor/libbacktrace/configure +15952 -0
- data/vendor/libbacktrace/configure.ac +642 -0
- data/vendor/libbacktrace/dwarf.c +4593 -0
- data/vendor/libbacktrace/edtest.c +120 -0
- data/vendor/libbacktrace/edtest2.c +43 -0
- data/vendor/libbacktrace/elf.c +7471 -0
- data/vendor/libbacktrace/fileline.c +407 -0
- data/vendor/libbacktrace/filenames.h +52 -0
- data/vendor/libbacktrace/filetype.awk +13 -0
- data/vendor/libbacktrace/install-debuginfo-for-buildid.sh.in +65 -0
- data/vendor/libbacktrace/install-sh +501 -0
- data/vendor/libbacktrace/instrumented_alloc.c +114 -0
- data/vendor/libbacktrace/internal.h +428 -0
- data/vendor/libbacktrace/ltmain.sh +8636 -0
- data/vendor/libbacktrace/macho.c +1361 -0
- data/vendor/libbacktrace/missing +215 -0
- data/vendor/libbacktrace/mmap.c +331 -0
- data/vendor/libbacktrace/mmapio.c +110 -0
- data/vendor/libbacktrace/move-if-change +83 -0
- data/vendor/libbacktrace/mtest.c +410 -0
- data/vendor/libbacktrace/nounwind.c +66 -0
- data/vendor/libbacktrace/pecoff.c +1123 -0
- data/vendor/libbacktrace/posix.c +104 -0
- data/vendor/libbacktrace/print.c +117 -0
- data/vendor/libbacktrace/read.c +110 -0
- data/vendor/libbacktrace/simple.c +108 -0
- data/vendor/libbacktrace/sort.c +108 -0
- data/vendor/libbacktrace/state.c +72 -0
- data/vendor/libbacktrace/stest.c +137 -0
- data/vendor/libbacktrace/test-driver +148 -0
- data/vendor/libbacktrace/test_format.c +55 -0
- data/vendor/libbacktrace/testlib.c +234 -0
- data/vendor/libbacktrace/testlib.h +110 -0
- data/vendor/libbacktrace/ttest.c +161 -0
- data/vendor/libbacktrace/unittest.c +92 -0
- data/vendor/libbacktrace/unknown.c +65 -0
- data/vendor/libbacktrace/xcoff.c +1617 -0
- data/vendor/libbacktrace/xztest.c +508 -0
- data/vendor/libbacktrace/zstdtest.c +523 -0
- data/vendor/libbacktrace/ztest.c +541 -0
- metadata +122 -3
data/ext/pf2c/sample.h
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#ifndef PF2_SAMPLE_H
|
2
|
+
#define PF2_SAMPLE_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
|
6
|
+
extern const int PF2_SAMPLE_MAX_NATIVE_DEPTH;
|
7
|
+
|
8
|
+
struct pf2_sample {
|
9
|
+
int depth;
|
10
|
+
VALUE cmes[200];
|
11
|
+
int linenos[200];
|
12
|
+
|
13
|
+
size_t native_stack_depth;
|
14
|
+
uintptr_t native_stack[200];
|
15
|
+
|
16
|
+
uint64_t consumed_time_ns;
|
17
|
+
uint64_t timestamp_ns;
|
18
|
+
};
|
19
|
+
|
20
|
+
bool pf2_sample_capture(struct pf2_sample *sample);
|
21
|
+
|
22
|
+
#endif // PF2_SAMPLE_H
|
@@ -0,0 +1,377 @@
|
|
1
|
+
#include <time.h>
|
2
|
+
#include <stdint.h>
|
3
|
+
#include <string.h>
|
4
|
+
|
5
|
+
#include <ruby.h>
|
6
|
+
#include <ruby/debug.h>
|
7
|
+
|
8
|
+
#include <backtrace.h>
|
9
|
+
|
10
|
+
#include "backtrace_state.h"
|
11
|
+
#include "serializer.h"
|
12
|
+
#include "session.h"
|
13
|
+
#include "sample.h"
|
14
|
+
|
15
|
+
static struct pf2_ser_function extract_function_from_ruby_frame(VALUE frame);
|
16
|
+
static struct pf2_ser_function extract_function_from_native_pc(uintptr_t pc);
|
17
|
+
// static int backtrace_pcinfo_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function);
|
18
|
+
static void pf2_backtrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize);
|
19
|
+
static int function_index_for(struct pf2_ser *serializer, struct pf2_ser_function *function);
|
20
|
+
static int location_index_for(struct pf2_ser *serializer, int function_index, int32_t lineno);
|
21
|
+
static void ensure_samples_capacity(struct pf2_ser *serializer);
|
22
|
+
static void ensure_locations_capacity(struct pf2_ser *serializer);
|
23
|
+
static void ensure_functions_capacity(struct pf2_ser *serializer);
|
24
|
+
|
25
|
+
struct pf2_ser *
|
26
|
+
pf2_ser_new(void) {
|
27
|
+
struct pf2_ser *ser = malloc(sizeof(struct pf2_ser));
|
28
|
+
if (ser == NULL) { goto err; }
|
29
|
+
ser->start_timestamp_ns = 0;
|
30
|
+
ser->duration_ns = 0;
|
31
|
+
|
32
|
+
ser->samples = NULL;
|
33
|
+
ser->samples_count = 0;
|
34
|
+
ser->samples_capacity = 0;
|
35
|
+
|
36
|
+
ser->locations = NULL;
|
37
|
+
ser->locations_count = 0;
|
38
|
+
ser->locations_capacity = 0;
|
39
|
+
|
40
|
+
ser->functions = NULL;
|
41
|
+
ser->functions_count = 0;
|
42
|
+
ser->functions_capacity = 0;
|
43
|
+
|
44
|
+
return ser;
|
45
|
+
|
46
|
+
err:
|
47
|
+
return NULL;
|
48
|
+
}
|
49
|
+
|
50
|
+
void
|
51
|
+
pf2_ser_free(struct pf2_ser *serializer) {
|
52
|
+
// Free samples
|
53
|
+
for (size_t i = 0; i < serializer->samples_count; i++) {
|
54
|
+
free(serializer->samples[i].stack);
|
55
|
+
free(serializer->samples[i].native_stack);
|
56
|
+
}
|
57
|
+
free(serializer->samples);
|
58
|
+
|
59
|
+
// Free functions
|
60
|
+
for (size_t i = 0; i < serializer->functions_count; i++) {
|
61
|
+
free(serializer->functions[i].name); // strdup'ed
|
62
|
+
free(serializer->functions[i].filename); // strdup'ed
|
63
|
+
}
|
64
|
+
free(serializer->functions);
|
65
|
+
|
66
|
+
// Free locations
|
67
|
+
free(serializer->locations);
|
68
|
+
|
69
|
+
free(serializer);
|
70
|
+
}
|
71
|
+
|
72
|
+
void
|
73
|
+
pf2_ser_prepare(struct pf2_ser *serializer, struct pf2_session *session) {
|
74
|
+
// Set metadata
|
75
|
+
serializer->start_timestamp_ns =
|
76
|
+
(uint64_t)session->start_time_realtime.tv_sec * 1000000000ULL +
|
77
|
+
(uint64_t)session->start_time_realtime.tv_nsec;
|
78
|
+
serializer->duration_ns = session->duration_ns;
|
79
|
+
|
80
|
+
// Process samples
|
81
|
+
for (size_t i = 0; i < session->samples_index; i++) {
|
82
|
+
struct pf2_sample *sample = &session->samples[i];
|
83
|
+
ensure_samples_capacity(serializer);
|
84
|
+
|
85
|
+
struct pf2_ser_sample *ser_sample = &serializer->samples[serializer->samples_count++];
|
86
|
+
ser_sample->ruby_thread_id = 0; // TODO: Add thread ID support
|
87
|
+
ser_sample->elapsed_ns = sample->timestamp_ns - serializer->start_timestamp_ns;
|
88
|
+
|
89
|
+
// Copy and process Ruby stack frames
|
90
|
+
ser_sample->stack = malloc(sizeof(size_t) * sample->depth);
|
91
|
+
ser_sample->stack_count = sample->depth;
|
92
|
+
for (int j = 0; j < sample->depth; j++) {
|
93
|
+
VALUE frame = sample->cmes[j];
|
94
|
+
int32_t lineno = sample->linenos[j];
|
95
|
+
|
96
|
+
struct pf2_ser_function func = extract_function_from_ruby_frame(frame);
|
97
|
+
size_t function_index = function_index_for(serializer, &func);
|
98
|
+
size_t location_index = location_index_for(serializer, function_index, lineno);
|
99
|
+
|
100
|
+
ser_sample->stack[j] = location_index;
|
101
|
+
}
|
102
|
+
|
103
|
+
// Copy and process native stack frames, if any
|
104
|
+
if (sample->native_stack_depth > 0) {
|
105
|
+
ser_sample->native_stack = malloc(sizeof(size_t) * sample->native_stack_depth);
|
106
|
+
ser_sample->native_stack_count = sample->native_stack_depth;
|
107
|
+
|
108
|
+
for (size_t j = 0; j < sample->native_stack_depth; j++) {
|
109
|
+
struct pf2_ser_function func = extract_function_from_native_pc(sample->native_stack[j]);
|
110
|
+
size_t function_index = function_index_for(serializer, &func);
|
111
|
+
size_t location_index = location_index_for(serializer, function_index, 0);
|
112
|
+
|
113
|
+
ser_sample->native_stack[j] = location_index;
|
114
|
+
}
|
115
|
+
} else {
|
116
|
+
ser_sample->native_stack = NULL;
|
117
|
+
ser_sample->native_stack_count = 0;
|
118
|
+
}
|
119
|
+
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
VALUE
|
124
|
+
pf2_ser_to_ruby_hash(struct pf2_ser *serializer) {
|
125
|
+
VALUE hash = rb_hash_new();
|
126
|
+
|
127
|
+
// Add metadata
|
128
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("start_timestamp_ns")), ULL2NUM(serializer->start_timestamp_ns));
|
129
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("duration_ns")), ULL2NUM(serializer->duration_ns));
|
130
|
+
|
131
|
+
// Add samples
|
132
|
+
VALUE samples = rb_ary_new_capa(serializer->samples_count);
|
133
|
+
for (size_t i = 0; i < serializer->samples_count; i++) {
|
134
|
+
struct pf2_ser_sample *sample = &serializer->samples[i];
|
135
|
+
VALUE sample_hash = rb_hash_new();
|
136
|
+
|
137
|
+
// Add Ruby stack
|
138
|
+
VALUE stack = rb_ary_new_capa(sample->stack_count);
|
139
|
+
for (size_t j = 0; j < sample->stack_count; j++) {
|
140
|
+
rb_ary_push(stack, SIZET2NUM(sample->stack[j]));
|
141
|
+
}
|
142
|
+
rb_hash_aset(sample_hash, ID2SYM(rb_intern("stack")), stack);
|
143
|
+
|
144
|
+
// Add native stack frames
|
145
|
+
VALUE native_stack = rb_ary_new_capa(sample->native_stack_count);
|
146
|
+
if (sample->native_stack != NULL) {
|
147
|
+
for (size_t j = 0; j < sample->native_stack_count; j++) {
|
148
|
+
rb_ary_push(native_stack, ULL2NUM(sample->native_stack[j]));
|
149
|
+
}
|
150
|
+
}
|
151
|
+
rb_hash_aset(sample_hash, ID2SYM(rb_intern("native_stack")), native_stack);
|
152
|
+
|
153
|
+
// Add thread ID and elapsed time
|
154
|
+
rb_hash_aset(
|
155
|
+
sample_hash,
|
156
|
+
ID2SYM(rb_intern("ruby_thread_id")),
|
157
|
+
sample->ruby_thread_id ? SIZET2NUM(sample->ruby_thread_id) : Qnil
|
158
|
+
);
|
159
|
+
rb_hash_aset(sample_hash, ID2SYM(rb_intern("elapsed_ns")), ULL2NUM(sample->elapsed_ns));
|
160
|
+
|
161
|
+
rb_ary_push(samples, sample_hash);
|
162
|
+
}
|
163
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("samples")), samples);
|
164
|
+
|
165
|
+
// Add locations
|
166
|
+
VALUE locations = rb_ary_new_capa(serializer->locations_count);
|
167
|
+
for (size_t i = 0; i < serializer->locations_count; i++) {
|
168
|
+
struct pf2_ser_location *location = &serializer->locations[i];
|
169
|
+
VALUE location_hash = rb_hash_new();
|
170
|
+
|
171
|
+
rb_hash_aset(
|
172
|
+
location_hash,
|
173
|
+
ID2SYM(rb_intern("function_index")),
|
174
|
+
SIZET2NUM(location->function_index)
|
175
|
+
);
|
176
|
+
rb_hash_aset(
|
177
|
+
location_hash,
|
178
|
+
ID2SYM(rb_intern("lineno")),
|
179
|
+
INT2NUM(location->lineno)
|
180
|
+
);
|
181
|
+
rb_hash_aset(location_hash, ID2SYM(rb_intern("address")), Qnil); // TODO: C functions
|
182
|
+
|
183
|
+
rb_ary_push(locations, location_hash);
|
184
|
+
}
|
185
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("locations")), locations);
|
186
|
+
|
187
|
+
// Add functions
|
188
|
+
VALUE functions = rb_ary_new_capa(serializer->functions_count);
|
189
|
+
for (size_t i = 0; i < serializer->functions_count; i++) {
|
190
|
+
struct pf2_ser_function *function = &serializer->functions[i];
|
191
|
+
VALUE function_hash = rb_hash_new();
|
192
|
+
|
193
|
+
rb_hash_aset(
|
194
|
+
function_hash,
|
195
|
+
ID2SYM(rb_intern("implementation")),
|
196
|
+
function->implementation == IMPLEMENTATION_RUBY ? ID2SYM(rb_intern("ruby")) : ID2SYM(rb_intern("native"))
|
197
|
+
); // TODO: C functions
|
198
|
+
rb_hash_aset(
|
199
|
+
function_hash,
|
200
|
+
ID2SYM(rb_intern("name")),
|
201
|
+
function->name ? rb_str_new_cstr(function->name) : Qnil
|
202
|
+
);
|
203
|
+
rb_hash_aset(
|
204
|
+
function_hash,
|
205
|
+
ID2SYM(rb_intern("filename")),
|
206
|
+
function->filename ? rb_str_new_cstr(function->filename) : Qnil
|
207
|
+
);
|
208
|
+
rb_hash_aset(
|
209
|
+
function_hash,
|
210
|
+
ID2SYM(rb_intern("start_lineno")),
|
211
|
+
function->start_lineno >= 0 ? INT2NUM(function->start_lineno) : Qnil
|
212
|
+
);
|
213
|
+
rb_hash_aset(function_hash, ID2SYM(rb_intern("start_address")), Qnil); // TODO: C functions
|
214
|
+
|
215
|
+
rb_ary_push(functions, function_hash);
|
216
|
+
}
|
217
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("functions")), functions);
|
218
|
+
|
219
|
+
return hash;
|
220
|
+
}
|
221
|
+
|
222
|
+
static struct pf2_ser_function
|
223
|
+
extract_function_from_ruby_frame(VALUE frame) {
|
224
|
+
struct pf2_ser_function func;
|
225
|
+
|
226
|
+
VALUE frame_full_label = rb_profile_frame_full_label(frame);
|
227
|
+
if (RTEST(frame_full_label)) {
|
228
|
+
const char *label = StringValueCStr(frame_full_label);
|
229
|
+
func.name = strdup(label);
|
230
|
+
} else {
|
231
|
+
func.name = NULL;
|
232
|
+
}
|
233
|
+
|
234
|
+
VALUE frame_path = rb_profile_frame_path(frame);
|
235
|
+
if (RTEST(frame_path)) {
|
236
|
+
const char *path = StringValueCStr(frame_path);
|
237
|
+
func.filename = strdup(path);
|
238
|
+
} else {
|
239
|
+
func.filename = NULL;
|
240
|
+
}
|
241
|
+
|
242
|
+
VALUE frame_first_lineno = rb_profile_frame_first_lineno(frame);
|
243
|
+
if (RTEST(frame_first_lineno)) {
|
244
|
+
func.start_lineno = NUM2INT(frame_first_lineno);
|
245
|
+
} else {
|
246
|
+
func.start_lineno = -1;
|
247
|
+
}
|
248
|
+
|
249
|
+
func.implementation = IMPLEMENTATION_RUBY;
|
250
|
+
func.start_address = 0; // For C functions
|
251
|
+
|
252
|
+
return func;
|
253
|
+
}
|
254
|
+
|
255
|
+
static struct pf2_ser_function
|
256
|
+
extract_function_from_native_pc(uintptr_t pc) {
|
257
|
+
struct pf2_ser_function func;
|
258
|
+
func.implementation = IMPLEMENTATION_NATIVE;
|
259
|
+
|
260
|
+
func.start_address = 0;
|
261
|
+
func.name = NULL;
|
262
|
+
func.filename = NULL;
|
263
|
+
func.start_lineno = 0;
|
264
|
+
|
265
|
+
// Use libbacktrace to get function details
|
266
|
+
struct backtrace_state *state = global_backtrace_state;
|
267
|
+
assert(state != NULL);
|
268
|
+
backtrace_syminfo(state, pc, pf2_backtrace_syminfo_callback, pf2_backtrace_print_error, &func);
|
269
|
+
|
270
|
+
// TODO: backtrace_pcinfo could give us more information, such as filenames and linenos.
|
271
|
+
// Maybe try pcinfo first, then use syminfo as a fallback?
|
272
|
+
|
273
|
+
return func;
|
274
|
+
}
|
275
|
+
|
276
|
+
// Full callback for syminfo
|
277
|
+
static void
|
278
|
+
pf2_backtrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize) {
|
279
|
+
struct pf2_ser_function *func = (struct pf2_ser_function *)data;
|
280
|
+
|
281
|
+
if (symname != NULL) {
|
282
|
+
func->name = strdup(symname);
|
283
|
+
}
|
284
|
+
func->start_address = symval;
|
285
|
+
|
286
|
+
return;
|
287
|
+
}
|
288
|
+
|
289
|
+
// static int
|
290
|
+
// backtrace_pcinfo_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
|
291
|
+
// struct pf2_ser_function *func = (struct pf2_ser_function *)data;
|
292
|
+
|
293
|
+
// if (function) {
|
294
|
+
// func->name = strdup(function);
|
295
|
+
// }
|
296
|
+
// func->start_lineno = lineno;
|
297
|
+
// if (filename) {
|
298
|
+
// func->filename = strdup(filename);
|
299
|
+
// }
|
300
|
+
|
301
|
+
// // Return non-zero to stop after first match
|
302
|
+
// return 1;
|
303
|
+
// }
|
304
|
+
|
305
|
+
// Returns the index of the function in `functions`.
|
306
|
+
// Calling this method will modify `serializer->profile` in place.
|
307
|
+
static int
|
308
|
+
function_index_for(struct pf2_ser *serializer, struct pf2_ser_function *function) {
|
309
|
+
for (size_t i = 0; i < serializer->functions_count; i++) {
|
310
|
+
struct pf2_ser_function *existing = &serializer->functions[i];
|
311
|
+
if (
|
312
|
+
(existing->name == NULL && function->name == NULL) ||
|
313
|
+
(existing->name != NULL && function->name != NULL && strcmp(existing->name, function->name) == 0)
|
314
|
+
) {
|
315
|
+
if (
|
316
|
+
(existing->filename == NULL && function->filename == NULL) ||
|
317
|
+
(existing->filename != NULL && function->filename != NULL && strcmp(existing->filename, function->filename) == 0)
|
318
|
+
) {
|
319
|
+
if (existing->start_lineno == function->start_lineno) {
|
320
|
+
return i;
|
321
|
+
}
|
322
|
+
}
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
// No matching function was found, add the function to the profile.
|
327
|
+
ensure_functions_capacity(serializer);
|
328
|
+
size_t new_index = serializer->functions_count++;
|
329
|
+
serializer->functions[new_index] = *function;
|
330
|
+
return new_index;
|
331
|
+
}
|
332
|
+
|
333
|
+
// Returns the index of the location in `locations`.
|
334
|
+
// Calling this method will modify `self.profile` in place.
|
335
|
+
static int
|
336
|
+
location_index_for(struct pf2_ser *serializer, int function_index, int32_t lineno) {
|
337
|
+
for (size_t i = 0; i < serializer->locations_count; i++) {
|
338
|
+
struct pf2_ser_location *existing = &serializer->locations[i];
|
339
|
+
if (existing->function_index == function_index && existing->lineno == lineno) {
|
340
|
+
return i;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
ensure_locations_capacity(serializer);
|
345
|
+
size_t new_index = serializer->locations_count++;
|
346
|
+
serializer->locations[new_index].function_index = function_index;
|
347
|
+
serializer->locations[new_index].lineno = lineno;
|
348
|
+
serializer->locations[new_index].address = 0; // Not handling addresses in this version
|
349
|
+
return new_index;
|
350
|
+
}
|
351
|
+
|
352
|
+
static void
|
353
|
+
ensure_samples_capacity(struct pf2_ser *serializer) {
|
354
|
+
if (serializer->samples_count >= serializer->samples_capacity) {
|
355
|
+
size_t new_capacity = serializer->samples_capacity == 0 ? 16 : serializer->samples_capacity * 2;
|
356
|
+
serializer->samples = realloc(serializer->samples, new_capacity * sizeof(struct pf2_ser_sample));
|
357
|
+
serializer->samples_capacity = new_capacity;
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
static void
|
362
|
+
ensure_locations_capacity(struct pf2_ser *serializer) {
|
363
|
+
if (serializer->locations_count >= serializer->locations_capacity) {
|
364
|
+
size_t new_capacity = serializer->locations_capacity == 0 ? 16 : serializer->locations_capacity * 2;
|
365
|
+
serializer->locations = realloc(serializer->locations, new_capacity * sizeof(struct pf2_ser_location));
|
366
|
+
serializer->locations_capacity = new_capacity;
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
static void
|
371
|
+
ensure_functions_capacity(struct pf2_ser *serializer) {
|
372
|
+
if (serializer->functions_count >= serializer->functions_capacity) {
|
373
|
+
size_t new_capacity = serializer->functions_capacity == 0 ? 16 : serializer->functions_capacity * 2;
|
374
|
+
serializer->functions = realloc(serializer->functions, new_capacity * sizeof(struct pf2_ser_function));
|
375
|
+
serializer->functions_capacity = new_capacity;
|
376
|
+
}
|
377
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#ifndef PF2C_SERIALIZER_H
|
2
|
+
#define PF2C_SERIALIZER_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
|
6
|
+
#include "session.h"
|
7
|
+
|
8
|
+
struct pf2_ser_sample {
|
9
|
+
int *stack;
|
10
|
+
size_t stack_count;
|
11
|
+
int *native_stack;
|
12
|
+
size_t native_stack_count;
|
13
|
+
size_t ruby_thread_id;
|
14
|
+
uint64_t elapsed_ns;
|
15
|
+
};
|
16
|
+
|
17
|
+
struct pf2_ser_location {
|
18
|
+
int function_index;
|
19
|
+
int32_t lineno;
|
20
|
+
size_t address;
|
21
|
+
};
|
22
|
+
|
23
|
+
enum function_implementation {
|
24
|
+
IMPLEMENTATION_RUBY,
|
25
|
+
IMPLEMENTATION_NATIVE
|
26
|
+
};
|
27
|
+
|
28
|
+
struct pf2_ser_function {
|
29
|
+
enum function_implementation implementation;
|
30
|
+
char *name;
|
31
|
+
char *filename;
|
32
|
+
int32_t start_lineno;
|
33
|
+
size_t start_address;
|
34
|
+
};
|
35
|
+
|
36
|
+
struct pf2_ser {
|
37
|
+
uint64_t start_timestamp_ns;
|
38
|
+
uint64_t duration_ns;
|
39
|
+
|
40
|
+
struct pf2_ser_sample *samples;
|
41
|
+
size_t samples_count;
|
42
|
+
size_t samples_capacity;
|
43
|
+
|
44
|
+
struct pf2_ser_location *locations;
|
45
|
+
size_t locations_count;
|
46
|
+
size_t locations_capacity;
|
47
|
+
|
48
|
+
struct pf2_ser_function *functions;
|
49
|
+
size_t functions_count;
|
50
|
+
size_t functions_capacity;
|
51
|
+
};
|
52
|
+
|
53
|
+
struct pf2_ser *pf2_ser_new(void);
|
54
|
+
void pf2_ser_free(struct pf2_ser *serializer);
|
55
|
+
void pf2_ser_prepare(struct pf2_ser *serializer, struct pf2_session *session);
|
56
|
+
VALUE pf2_ser_to_ruby_hash(struct pf2_ser *serializer);
|
57
|
+
|
58
|
+
#endif // PF2C_SERIALIZER_H
|