pf2 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Cargo.lock +481 -0
- data/Cargo.toml +3 -0
- data/README.md +99 -13
- data/ext/pf2/Cargo.toml +24 -0
- data/ext/pf2/build.rs +3 -0
- data/ext/pf2/extconf.rb +6 -1
- data/ext/pf2/src/lib.rs +14 -0
- data/ext/pf2/src/profile.rs +50 -0
- data/ext/pf2/src/profile_serializer.rs +130 -0
- data/ext/pf2/src/ringbuffer.rs +145 -0
- data/ext/pf2/src/ruby_init.rs +62 -0
- data/ext/pf2/src/sample.rs +45 -0
- data/ext/pf2/src/siginfo_t.c +5 -0
- data/ext/pf2/src/signal_scheduler/configuration.rs +24 -0
- data/ext/pf2/src/signal_scheduler/timer_installer.rs +192 -0
- data/ext/pf2/src/signal_scheduler.rs +242 -0
- data/ext/pf2/src/timer_thread_scheduler.rs +243 -0
- data/ext/pf2/src/util.rs +30 -0
- data/lib/pf2/cli.rb +1 -1
- data/lib/pf2/reporter.rb +36 -11
- data/lib/pf2/version.rb +1 -1
- data/lib/pf2.rb +23 -5
- metadata +34 -5
- data/ext/pf2/pf2.c +0 -246
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pf2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daisuke Aritomo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rb_sys
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.63
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.9.63
|
27
41
|
description:
|
28
42
|
email:
|
29
43
|
- osyoyu@osyoyu.com
|
@@ -34,12 +48,27 @@ extensions:
|
|
34
48
|
extra_rdoc_files: []
|
35
49
|
files:
|
36
50
|
- CHANGELOG.md
|
51
|
+
- Cargo.lock
|
52
|
+
- Cargo.toml
|
37
53
|
- LICENSE.txt
|
38
54
|
- README.md
|
39
55
|
- Rakefile
|
40
56
|
- exe/pf2
|
57
|
+
- ext/pf2/Cargo.toml
|
58
|
+
- ext/pf2/build.rs
|
41
59
|
- ext/pf2/extconf.rb
|
42
|
-
- ext/pf2/
|
60
|
+
- ext/pf2/src/lib.rs
|
61
|
+
- ext/pf2/src/profile.rs
|
62
|
+
- ext/pf2/src/profile_serializer.rs
|
63
|
+
- ext/pf2/src/ringbuffer.rs
|
64
|
+
- ext/pf2/src/ruby_init.rs
|
65
|
+
- ext/pf2/src/sample.rs
|
66
|
+
- ext/pf2/src/siginfo_t.c
|
67
|
+
- ext/pf2/src/signal_scheduler.rs
|
68
|
+
- ext/pf2/src/signal_scheduler/configuration.rs
|
69
|
+
- ext/pf2/src/signal_scheduler/timer_installer.rs
|
70
|
+
- ext/pf2/src/timer_thread_scheduler.rs
|
71
|
+
- ext/pf2/src/util.rs
|
43
72
|
- lib/pf2.rb
|
44
73
|
- lib/pf2/cli.rb
|
45
74
|
- lib/pf2/reporter.rb
|
@@ -60,14 +89,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
89
|
requirements:
|
61
90
|
- - ">="
|
62
91
|
- !ruby/object:Gem::Version
|
63
|
-
version: 3.3.0
|
92
|
+
version: 3.3.0
|
64
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
94
|
requirements:
|
66
95
|
- - ">="
|
67
96
|
- !ruby/object:Gem::Version
|
68
97
|
version: '0'
|
69
98
|
requirements: []
|
70
|
-
rubygems_version: 3.
|
99
|
+
rubygems_version: 3.6.0.dev
|
71
100
|
signing_key:
|
72
101
|
specification_version: 4
|
73
102
|
summary: Yet another Ruby profiler
|
data/ext/pf2/pf2.c
DELETED
@@ -1,246 +0,0 @@
|
|
1
|
-
#include <errno.h>
|
2
|
-
#include <signal.h>
|
3
|
-
#include <stdbool.h>
|
4
|
-
#include <stdio.h>
|
5
|
-
#include <stdlib.h>
|
6
|
-
#include <unistd.h>
|
7
|
-
|
8
|
-
#include <ruby.h>
|
9
|
-
#include <ruby/debug.h>
|
10
|
-
#include <ruby/thread.h>
|
11
|
-
|
12
|
-
#define MAX_BUFFER_SIZE 3000
|
13
|
-
|
14
|
-
struct pf2_buffer_t {
|
15
|
-
VALUE framebuffer[MAX_BUFFER_SIZE];
|
16
|
-
int linebuffer[MAX_BUFFER_SIZE];
|
17
|
-
};
|
18
|
-
|
19
|
-
// Ruby functions
|
20
|
-
void Init_pf2(void);
|
21
|
-
VALUE rb_start(VALUE self, VALUE debug);
|
22
|
-
VALUE rb_stop(VALUE self);
|
23
|
-
|
24
|
-
static void pf2_start(void);
|
25
|
-
static void pf2_stop(void);
|
26
|
-
static void pf2_signal_handler(int signo);
|
27
|
-
static void pf2_postponed_job(void *_);
|
28
|
-
|
29
|
-
static void pf2_record(struct pf2_buffer_t *buffer);
|
30
|
-
static VALUE find_or_create_thread_results(VALUE results, pid_t thread_id);
|
31
|
-
|
32
|
-
// Buffer to record rb_profile_frames() results
|
33
|
-
struct pf2_buffer_t buffer;
|
34
|
-
// The time when the profiler started
|
35
|
-
struct timespec initial_time;
|
36
|
-
// Debug print?
|
37
|
-
bool _debug = false;
|
38
|
-
|
39
|
-
void
|
40
|
-
Init_pf2(void)
|
41
|
-
{
|
42
|
-
VALUE rb_mPf2 = rb_define_module("Pf2");
|
43
|
-
rb_define_module_function(rb_mPf2, "start", rb_start, 1);
|
44
|
-
rb_define_module_function(rb_mPf2, "stop", rb_stop, 0);
|
45
|
-
}
|
46
|
-
|
47
|
-
VALUE
|
48
|
-
rb_start(VALUE self, VALUE debug) {
|
49
|
-
_debug = RTEST(debug);
|
50
|
-
|
51
|
-
/**
|
52
|
-
* {
|
53
|
-
* sequence: 0,
|
54
|
-
* threads: {},
|
55
|
-
* }
|
56
|
-
*/
|
57
|
-
VALUE results = rb_hash_new();
|
58
|
-
rb_hash_aset(results, ID2SYM(rb_intern_const("sequence")), INT2FIX(0));
|
59
|
-
rb_hash_aset(results, ID2SYM(rb_intern_const("threads")), rb_hash_new());
|
60
|
-
|
61
|
-
rb_iv_set(self, "@results", results);
|
62
|
-
|
63
|
-
pf2_start();
|
64
|
-
|
65
|
-
if (_debug) {
|
66
|
-
rb_funcall(rb_mKernel, rb_intern("puts"), 1, rb_str_new_cstr("[debug] Pf2 started"));
|
67
|
-
}
|
68
|
-
|
69
|
-
return results;
|
70
|
-
}
|
71
|
-
|
72
|
-
VALUE
|
73
|
-
rb_stop(VALUE self) {
|
74
|
-
pf2_stop();
|
75
|
-
|
76
|
-
if (_debug) {
|
77
|
-
rb_funcall(rb_mKernel, rb_intern("puts"), 1, rb_str_new_cstr("[debug] Pf2 stopped"));
|
78
|
-
}
|
79
|
-
|
80
|
-
return rb_iv_get(self, "@results");
|
81
|
-
}
|
82
|
-
|
83
|
-
static void
|
84
|
-
pf2_start(void)
|
85
|
-
{
|
86
|
-
clock_gettime(CLOCK_MONOTONIC, &initial_time);
|
87
|
-
|
88
|
-
// Configure timer for every 10 ms
|
89
|
-
// TODO: Make interval configurable
|
90
|
-
struct itimerval timer;
|
91
|
-
timer.it_value.tv_sec = 1;
|
92
|
-
timer.it_value.tv_usec = 0;
|
93
|
-
timer.it_interval.tv_sec = 0;
|
94
|
-
timer.it_interval.tv_usec = 10 * 1000; // 10 ms
|
95
|
-
if (signal(SIGALRM, pf2_signal_handler) == SIG_ERR) {
|
96
|
-
rb_syserr_fail(errno, "Failed to configure profiling timer");
|
97
|
-
};
|
98
|
-
if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
|
99
|
-
rb_syserr_fail(errno, "Failed to configure profiling timer");
|
100
|
-
};
|
101
|
-
}
|
102
|
-
|
103
|
-
static void
|
104
|
-
pf2_stop(void)
|
105
|
-
{
|
106
|
-
struct itimerval timer = { 0 }; // stop
|
107
|
-
setitimer(ITIMER_REAL, &timer, NULL);
|
108
|
-
}
|
109
|
-
|
110
|
-
// async-signal-safe
|
111
|
-
static void
|
112
|
-
pf2_signal_handler(int signo)
|
113
|
-
{
|
114
|
-
rb_postponed_job_register_one(0, pf2_postponed_job, 0);
|
115
|
-
}
|
116
|
-
|
117
|
-
static void
|
118
|
-
pf2_postponed_job(void *_) {
|
119
|
-
pf2_record(&buffer);
|
120
|
-
};
|
121
|
-
|
122
|
-
// Buffer structure
|
123
|
-
static void
|
124
|
-
pf2_record(struct pf2_buffer_t *buffer)
|
125
|
-
{
|
126
|
-
// get the current time
|
127
|
-
struct timespec ts;
|
128
|
-
clock_gettime(CLOCK_MONOTONIC, &ts);
|
129
|
-
|
130
|
-
VALUE rb_mPf2 = rb_const_get(rb_cObject, rb_intern("Pf2"));
|
131
|
-
VALUE results = rb_iv_get(rb_mPf2, "@results");
|
132
|
-
|
133
|
-
// Iterate over all Threads
|
134
|
-
VALUE threads = rb_iv_get(rb_mPf2, "@@threads");
|
135
|
-
for (int i = 0; i < RARRAY_LEN(threads); i++) {
|
136
|
-
VALUE thread = rb_ary_entry(threads, i);
|
137
|
-
VALUE thread_status = rb_funcall(thread, rb_intern("status"), 0);
|
138
|
-
if (NIL_P(thread) || thread_status == Qfalse) {
|
139
|
-
// Thread is dead, just ignore
|
140
|
-
continue;
|
141
|
-
}
|
142
|
-
|
143
|
-
pid_t thread_id = NUM2INT(rb_funcall(thread, rb_intern("native_thread_id"), 0));
|
144
|
-
VALUE thread_results = find_or_create_thread_results(results, thread_id);
|
145
|
-
assert(!NIL_P(thread_results));
|
146
|
-
|
147
|
-
// The actual querying
|
148
|
-
int stack_depth = rb_profile_thread_frames(thread, 0, MAX_BUFFER_SIZE, buffer->framebuffer, buffer->linebuffer);
|
149
|
-
|
150
|
-
// TODO: Reimplement Pf2-internal data structures without CRuby
|
151
|
-
// (which will allow us to release the GVL at this point)
|
152
|
-
// rb_thread_call_without_gvl(...);
|
153
|
-
|
154
|
-
VALUE frames_table = rb_hash_lookup(thread_results, ID2SYM(rb_intern_const("frames")));
|
155
|
-
assert(!NIL_P(frames_table));
|
156
|
-
VALUE samples = rb_hash_lookup(thread_results, ID2SYM(rb_intern_const("samples")));
|
157
|
-
assert(!NIL_P(samples));
|
158
|
-
|
159
|
-
// Dig down the stack (top of call stack -> bottom (root))
|
160
|
-
VALUE stack_tree_p = rb_hash_lookup(thread_results, ID2SYM(rb_intern_const("stack_tree")));
|
161
|
-
for (int i = stack_depth - 1; i >= 0; i--) {
|
162
|
-
assert(NIL_P(buffer->framebuffer[i]));
|
163
|
-
|
164
|
-
// Collect & record frame information
|
165
|
-
VALUE frame_obj_id = rb_obj_id(buffer->framebuffer[i]);
|
166
|
-
VALUE frame_table_entry = rb_hash_aref(frames_table, frame_obj_id);
|
167
|
-
if (NIL_P(frame_table_entry)) {
|
168
|
-
frame_table_entry = rb_hash_new();
|
169
|
-
rb_hash_aset(frame_table_entry, ID2SYM(rb_intern_const("full_label")), rb_profile_frame_full_label(buffer->framebuffer[i]));
|
170
|
-
rb_hash_aset(frames_table, frame_obj_id, frame_table_entry);
|
171
|
-
}
|
172
|
-
|
173
|
-
VALUE children = rb_hash_aref(stack_tree_p, ID2SYM(rb_intern_const("children")));
|
174
|
-
VALUE next_node = rb_hash_lookup(children, frame_obj_id);
|
175
|
-
// If this is the first time we see this frame, register it to the stack tree
|
176
|
-
if (NIL_P(next_node)) { // not found
|
177
|
-
next_node = rb_hash_new();
|
178
|
-
|
179
|
-
// Increment sequence
|
180
|
-
VALUE next =
|
181
|
-
rb_funcall(
|
182
|
-
rb_hash_lookup(results, ID2SYM(rb_intern_const("sequence"))),
|
183
|
-
rb_intern("+"),
|
184
|
-
1,
|
185
|
-
INT2FIX(1)
|
186
|
-
);
|
187
|
-
rb_hash_aset(results, ID2SYM(rb_intern_const("sequence")), next);
|
188
|
-
|
189
|
-
rb_hash_aset(next_node, ID2SYM(rb_intern_const("node_id")), INT2FIX(next));
|
190
|
-
rb_hash_aset(next_node, ID2SYM(rb_intern_const("frame_id")), frame_obj_id);
|
191
|
-
rb_hash_aset(next_node, ID2SYM(rb_intern_const("full_label")), rb_profile_frame_full_label(buffer->framebuffer[i]));
|
192
|
-
rb_hash_aset(next_node, ID2SYM(rb_intern_const("children")), rb_hash_new());
|
193
|
-
|
194
|
-
rb_hash_aset(children, frame_obj_id, next_node);
|
195
|
-
}
|
196
|
-
|
197
|
-
VALUE stack_tree_id = rb_hash_aref(next_node, ID2SYM(rb_intern_const("node_id")));
|
198
|
-
|
199
|
-
// If on leaf
|
200
|
-
if (i == 0) {
|
201
|
-
// Record sample
|
202
|
-
VALUE sample = rb_hash_new();
|
203
|
-
rb_hash_aset(sample, ID2SYM(rb_intern_const("stack_tree_id")), stack_tree_id);
|
204
|
-
unsigned long long nsec = (ts.tv_sec - initial_time.tv_sec) * 1000000000 + ts.tv_nsec - initial_time.tv_nsec;
|
205
|
-
rb_hash_aset(sample, ID2SYM(rb_intern_const("timestamp")), ULL2NUM(nsec));
|
206
|
-
rb_ary_push(samples, sample);
|
207
|
-
}
|
208
|
-
|
209
|
-
stack_tree_p = next_node;
|
210
|
-
}
|
211
|
-
}
|
212
|
-
}
|
213
|
-
|
214
|
-
static VALUE
|
215
|
-
find_or_create_thread_results(VALUE results, pid_t thread_id) {
|
216
|
-
assert(!NIL_P(results));
|
217
|
-
assert(!NIL_P(thread));
|
218
|
-
|
219
|
-
VALUE threads = rb_hash_aref(results, ID2SYM(rb_intern_const("threads")));
|
220
|
-
VALUE thread_results = rb_hash_aref(threads, INT2NUM(thread_id));
|
221
|
-
if (NIL_P(thread_results)) {
|
222
|
-
/**
|
223
|
-
* {
|
224
|
-
* thread_id: 1,
|
225
|
-
* frames: [],
|
226
|
-
* stack_tree: {
|
227
|
-
* node_id: ...,
|
228
|
-
* children: {}
|
229
|
-
* },
|
230
|
-
* samples: [],
|
231
|
-
* }
|
232
|
-
*/
|
233
|
-
thread_results = rb_hash_new();
|
234
|
-
rb_hash_aset(thread_results, ID2SYM(rb_intern_const("thread_id")), INT2NUM(thread_id));
|
235
|
-
|
236
|
-
rb_hash_aset(thread_results, ID2SYM(rb_intern_const("frames")), rb_hash_new());
|
237
|
-
VALUE stack_tree = rb_hash_aset(thread_results, ID2SYM(rb_intern_const("stack_tree")), rb_hash_new());
|
238
|
-
rb_hash_aset(stack_tree, ID2SYM(rb_intern_const("node_id")), ID2SYM(rb_intern_const("root")));
|
239
|
-
rb_hash_aset(stack_tree, ID2SYM(rb_intern_const("children")), rb_hash_new());
|
240
|
-
rb_hash_aset(thread_results, ID2SYM(rb_intern_const("samples")), rb_ary_new());
|
241
|
-
rb_hash_aset(thread_results, ID2SYM(rb_intern_const("gvl_timings")), rb_ary_new());
|
242
|
-
|
243
|
-
rb_hash_aset(threads, INT2NUM(thread_id), thread_results);
|
244
|
-
}
|
245
|
-
return thread_results;
|
246
|
-
}
|