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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/README.md +11 -0
  4. data/Rakefile +9 -2
  5. data/doc/development.md +11 -0
  6. data/examples/mandelbrot.rb +69 -0
  7. data/examples/mandelbrot_ractor.rb +77 -0
  8. data/ext/pf2/build.rs +7 -0
  9. data/ext/pf2/src/ruby_c_api_helper.c +6 -0
  10. data/ext/pf2/src/serialization/profile.rs +1 -0
  11. data/ext/pf2/src/serialization/serializer.rs +4 -0
  12. data/ext/pf2/src/signal_scheduler.rs +1 -1
  13. data/ext/pf2/src/util.rs +2 -1
  14. data/ext/pf2c/backtrace_state.c +10 -0
  15. data/ext/pf2c/backtrace_state.h +10 -0
  16. data/ext/pf2c/configuration.c +90 -0
  17. data/ext/pf2c/configuration.h +23 -0
  18. data/ext/pf2c/extconf.rb +21 -0
  19. data/ext/pf2c/pf2.c +17 -0
  20. data/ext/pf2c/pf2.h +8 -0
  21. data/ext/pf2c/ringbuffer.c +74 -0
  22. data/ext/pf2c/ringbuffer.h +24 -0
  23. data/ext/pf2c/sample.c +70 -0
  24. data/ext/pf2c/sample.h +22 -0
  25. data/ext/pf2c/serializer.c +377 -0
  26. data/ext/pf2c/serializer.h +58 -0
  27. data/ext/pf2c/session.c +344 -0
  28. data/ext/pf2c/session.h +51 -0
  29. data/lib/pf2/cli.rb +33 -2
  30. data/lib/pf2/reporter/annotate.rb +101 -0
  31. data/lib/pf2/reporter/firefox_profiler.rb +1 -1
  32. data/lib/pf2/reporter/firefox_profiler_ser2.rb +308 -0
  33. data/lib/pf2/reporter.rb +2 -0
  34. data/lib/pf2/version.rb +1 -1
  35. data/vendor/libbacktrace/.gitignore +5 -0
  36. data/vendor/libbacktrace/Isaac.Newton-Opticks.txt +9286 -0
  37. data/vendor/libbacktrace/LICENSE +29 -0
  38. data/vendor/libbacktrace/Makefile.am +708 -0
  39. data/vendor/libbacktrace/Makefile.in +2820 -0
  40. data/vendor/libbacktrace/README.md +46 -0
  41. data/vendor/libbacktrace/aclocal.m4 +864 -0
  42. data/vendor/libbacktrace/alloc.c +167 -0
  43. data/vendor/libbacktrace/allocfail.c +136 -0
  44. data/vendor/libbacktrace/allocfail.sh +104 -0
  45. data/vendor/libbacktrace/atomic.c +113 -0
  46. data/vendor/libbacktrace/backtrace-supported.h.in +66 -0
  47. data/vendor/libbacktrace/backtrace.c +129 -0
  48. data/vendor/libbacktrace/backtrace.h +189 -0
  49. data/vendor/libbacktrace/btest.c +517 -0
  50. data/vendor/libbacktrace/compile +348 -0
  51. data/vendor/libbacktrace/config/enable.m4 +38 -0
  52. data/vendor/libbacktrace/config/lead-dot.m4 +31 -0
  53. data/vendor/libbacktrace/config/libtool.m4 +7545 -0
  54. data/vendor/libbacktrace/config/ltoptions.m4 +369 -0
  55. data/vendor/libbacktrace/config/ltsugar.m4 +123 -0
  56. data/vendor/libbacktrace/config/ltversion.m4 +23 -0
  57. data/vendor/libbacktrace/config/lt~obsolete.m4 +98 -0
  58. data/vendor/libbacktrace/config/multi.m4 +68 -0
  59. data/vendor/libbacktrace/config/override.m4 +117 -0
  60. data/vendor/libbacktrace/config/unwind_ipinfo.m4 +37 -0
  61. data/vendor/libbacktrace/config/warnings.m4 +227 -0
  62. data/vendor/libbacktrace/config.guess +1700 -0
  63. data/vendor/libbacktrace/config.h.in +185 -0
  64. data/vendor/libbacktrace/config.sub +1885 -0
  65. data/vendor/libbacktrace/configure +15952 -0
  66. data/vendor/libbacktrace/configure.ac +642 -0
  67. data/vendor/libbacktrace/dwarf.c +4593 -0
  68. data/vendor/libbacktrace/edtest.c +120 -0
  69. data/vendor/libbacktrace/edtest2.c +43 -0
  70. data/vendor/libbacktrace/elf.c +7471 -0
  71. data/vendor/libbacktrace/fileline.c +407 -0
  72. data/vendor/libbacktrace/filenames.h +52 -0
  73. data/vendor/libbacktrace/filetype.awk +13 -0
  74. data/vendor/libbacktrace/install-debuginfo-for-buildid.sh.in +65 -0
  75. data/vendor/libbacktrace/install-sh +501 -0
  76. data/vendor/libbacktrace/instrumented_alloc.c +114 -0
  77. data/vendor/libbacktrace/internal.h +428 -0
  78. data/vendor/libbacktrace/ltmain.sh +8636 -0
  79. data/vendor/libbacktrace/macho.c +1361 -0
  80. data/vendor/libbacktrace/missing +215 -0
  81. data/vendor/libbacktrace/mmap.c +331 -0
  82. data/vendor/libbacktrace/mmapio.c +110 -0
  83. data/vendor/libbacktrace/move-if-change +83 -0
  84. data/vendor/libbacktrace/mtest.c +410 -0
  85. data/vendor/libbacktrace/nounwind.c +66 -0
  86. data/vendor/libbacktrace/pecoff.c +1123 -0
  87. data/vendor/libbacktrace/posix.c +104 -0
  88. data/vendor/libbacktrace/print.c +117 -0
  89. data/vendor/libbacktrace/read.c +110 -0
  90. data/vendor/libbacktrace/simple.c +108 -0
  91. data/vendor/libbacktrace/sort.c +108 -0
  92. data/vendor/libbacktrace/state.c +72 -0
  93. data/vendor/libbacktrace/stest.c +137 -0
  94. data/vendor/libbacktrace/test-driver +148 -0
  95. data/vendor/libbacktrace/test_format.c +55 -0
  96. data/vendor/libbacktrace/testlib.c +234 -0
  97. data/vendor/libbacktrace/testlib.h +110 -0
  98. data/vendor/libbacktrace/ttest.c +161 -0
  99. data/vendor/libbacktrace/unittest.c +92 -0
  100. data/vendor/libbacktrace/unknown.c +65 -0
  101. data/vendor/libbacktrace/xcoff.c +1617 -0
  102. data/vendor/libbacktrace/xztest.c +508 -0
  103. data/vendor/libbacktrace/zstdtest.c +523 -0
  104. data/vendor/libbacktrace/ztest.c +541 -0
  105. 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