pf2 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.1.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: 2023-10-04 00:00:00.000000000 Z
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/pf2.c
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.dev
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.5.0.dev
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
- }