rblineprof 0.2.5 → 0.2.7
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.
- data/README.md +1 -0
- data/ext/extconf.rb +23 -1
- data/ext/rblineprof.c +131 -19
- data/rblineprof.gemspec +2 -2
- data/test.rb +3 -2
- metadata +20 -39
data/README.md
CHANGED
data/ext/extconf.rb
CHANGED
@@ -1,2 +1,24 @@
|
|
1
1
|
require 'mkmf'
|
2
|
-
|
2
|
+
|
3
|
+
if RUBY_VERSION >= "1.9"
|
4
|
+
begin
|
5
|
+
require "debugger/ruby_core_source"
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems/dependency_installer'
|
8
|
+
installer = Gem::DependencyInstaller.new
|
9
|
+
installer.install 'debugger-ruby_core_source'
|
10
|
+
require "debugger/ruby_core_source"
|
11
|
+
end
|
12
|
+
|
13
|
+
hdrs = proc {
|
14
|
+
have_header("vm_core.h") and
|
15
|
+
have_header("iseq.h")
|
16
|
+
}
|
17
|
+
|
18
|
+
unless Debugger::RubyCoreSource::create_makefile_with_core(hdrs, "rblineprof")
|
19
|
+
STDERR.puts "\nDebugger::RubyCoreSource::create_makefile failed"
|
20
|
+
exit(1)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
create_makefile 'rblineprof'
|
24
|
+
end
|
data/ext/rblineprof.c
CHANGED
@@ -1,17 +1,26 @@
|
|
1
|
-
#include <
|
2
|
-
#include <stdlib.h>
|
1
|
+
#include <ruby.h>
|
3
2
|
#include <stdbool.h>
|
4
|
-
#include <string.h>
|
5
3
|
|
6
|
-
#
|
7
|
-
#include <
|
8
|
-
#include <
|
9
|
-
#include <
|
10
|
-
#include <
|
11
|
-
|
4
|
+
#ifdef RUBY_VM
|
5
|
+
#include <ruby/re.h>
|
6
|
+
#include <ruby/intern.h>
|
7
|
+
#include <vm_core.h>
|
8
|
+
#include <iseq.h>
|
9
|
+
|
10
|
+
// There's a compile error on 1.9.3. So:
|
11
|
+
#ifdef RTYPEDDATA_DATA
|
12
|
+
#define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
|
13
|
+
#endif
|
14
|
+
#else
|
15
|
+
#include <st.h>
|
16
|
+
#include <re.h>
|
17
|
+
#include <intern.h>
|
18
|
+
#include <node.h>
|
19
|
+
#include <env.h>
|
20
|
+
typedef rb_event_t rb_event_flag_t;
|
21
|
+
#endif
|
12
22
|
|
13
23
|
typedef uint64_t prof_time_t;
|
14
|
-
|
15
24
|
static VALUE gc_hook;
|
16
25
|
|
17
26
|
/*
|
@@ -42,8 +51,12 @@ typedef struct sourcefile {
|
|
42
51
|
*/
|
43
52
|
typedef struct stackframe {
|
44
53
|
// data emitted from Ruby to our profiler hook
|
45
|
-
|
54
|
+
rb_event_flag_t event;
|
55
|
+
#ifdef RUBY_VM
|
56
|
+
rb_thread_t *thread;
|
57
|
+
#else
|
46
58
|
NODE *node;
|
59
|
+
#endif
|
47
60
|
VALUE self;
|
48
61
|
ID mid;
|
49
62
|
VALUE klass;
|
@@ -76,6 +89,12 @@ static struct {
|
|
76
89
|
// regex mode, store file data in hash table
|
77
90
|
VALUE source_regex;
|
78
91
|
st_table *files;
|
92
|
+
|
93
|
+
// cache
|
94
|
+
struct {
|
95
|
+
char *file;
|
96
|
+
sourcefile_t *srcfile;
|
97
|
+
} cache;
|
79
98
|
}
|
80
99
|
rblineprof = {
|
81
100
|
.enabled = false,
|
@@ -128,7 +147,11 @@ sourcefile_lookup(char *filename)
|
|
128
147
|
sourcefile_t *srcfile = NULL;
|
129
148
|
|
130
149
|
if (rblineprof.source_filename) { // single file mode
|
150
|
+
#ifdef RUBY_VM
|
151
|
+
if (strcmp(rblineprof.source_filename, filename) == 0) {
|
152
|
+
#else
|
131
153
|
if (rblineprof.source_filename == filename) { // compare char*, not contents
|
154
|
+
#endif
|
132
155
|
srcfile = &rblineprof.file;
|
133
156
|
srcfile->filename = filename;
|
134
157
|
} else {
|
@@ -157,8 +180,39 @@ sourcefile_lookup(char *filename)
|
|
157
180
|
return srcfile;
|
158
181
|
}
|
159
182
|
|
183
|
+
#ifdef RUBY_VM
|
184
|
+
/* Find the source of the current method call. This is based on rb_f_caller
|
185
|
+
* in vm_eval.c, and replicates the behavior of `caller.first` from ruby.
|
186
|
+
*
|
187
|
+
* On method calls, ruby 1.9 sends an extra RUBY_EVENT_CALL event with mid=0. The
|
188
|
+
* top-most cfp on the stack in these cases points to the 'def method' line, so we skip
|
189
|
+
* these and grab the second caller instead.
|
190
|
+
*/
|
191
|
+
rb_control_frame_t *
|
192
|
+
rb_vm_get_caller(rb_thread_t *th, rb_control_frame_t *cfp, ID mid)
|
193
|
+
{
|
194
|
+
int level = 0;
|
195
|
+
|
196
|
+
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
|
197
|
+
if (++level == 1 && mid == 0) {
|
198
|
+
// skip method definition line
|
199
|
+
} else if (cfp->iseq != 0 && cfp->pc != 0) {
|
200
|
+
return cfp;
|
201
|
+
}
|
202
|
+
|
203
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
204
|
+
}
|
205
|
+
|
206
|
+
return 0;
|
207
|
+
}
|
208
|
+
#endif
|
209
|
+
|
160
210
|
static void
|
161
|
-
|
211
|
+
#ifdef RUBY_VM
|
212
|
+
profiler_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
|
213
|
+
#else
|
214
|
+
profiler_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
215
|
+
#endif
|
162
216
|
{
|
163
217
|
char *file;
|
164
218
|
long line;
|
@@ -166,13 +220,20 @@ profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
166
220
|
sourcefile_t *srcfile, *curr_srcfile;
|
167
221
|
prof_time_t now = timeofday_usec();
|
168
222
|
|
223
|
+
#if 0
|
169
224
|
/* file profiler: when invoking a method in a new file, account elapsed
|
170
225
|
* time to the current file and start a new timer.
|
171
226
|
*/
|
227
|
+
#ifndef RUBY_VM
|
172
228
|
if (!node) return;
|
173
|
-
|
174
229
|
file = node->nd_file;
|
175
230
|
line = nd_line(node);
|
231
|
+
#else
|
232
|
+
// this returns filename (instead of filepath) on 1.9
|
233
|
+
file = (char *) rb_sourcefile();
|
234
|
+
line = rb_sourceline();
|
235
|
+
#endif
|
236
|
+
|
176
237
|
if (!file) return;
|
177
238
|
if (line <= 0) return;
|
178
239
|
|
@@ -188,6 +249,7 @@ profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
188
249
|
|
189
250
|
rblineprof.curr_srcfile = srcfile;
|
190
251
|
}
|
252
|
+
#endif
|
191
253
|
|
192
254
|
/* line profiler: maintain a stack of CALL events with timestamps. for
|
193
255
|
* each corresponding RETURN, account elapsed time to the calling line.
|
@@ -195,16 +257,41 @@ profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
195
257
|
* we use ruby_current_node here to get the caller's file/line info,
|
196
258
|
* (as opposed to node, which points to the callee method being invoked)
|
197
259
|
*/
|
260
|
+
#ifndef RUBY_VM
|
198
261
|
NODE *caller_node = ruby_frame->node;
|
199
262
|
if (!caller_node) return;
|
200
263
|
|
201
264
|
file = caller_node->nd_file;
|
202
265
|
line = nd_line(caller_node);
|
266
|
+
#else
|
267
|
+
rb_thread_t *th = GET_THREAD();
|
268
|
+
rb_control_frame_t *cfp = rb_vm_get_caller(th, th->cfp, mid);
|
269
|
+
if (!cfp) return;
|
270
|
+
|
271
|
+
if (RTEST(cfp->iseq->filepath))
|
272
|
+
file = StringValueCStr(cfp->iseq->filepath);
|
273
|
+
else
|
274
|
+
file = StringValueCStr(cfp->iseq->filename);
|
275
|
+
line = rb_vm_get_sourceline(cfp);
|
276
|
+
#endif
|
277
|
+
|
203
278
|
if (!file) return;
|
204
279
|
if (line <= 0) return;
|
205
280
|
|
206
|
-
|
281
|
+
/* find the srcfile entry for the current file.
|
282
|
+
*
|
283
|
+
* first check the cache, in case this is the same file as
|
284
|
+
* the previous invocation.
|
285
|
+
*
|
286
|
+
* if no record is found, we don't care about profiling this
|
287
|
+
* file and return early.
|
288
|
+
*/
|
289
|
+
if (rblineprof.cache.file == file)
|
290
|
+
srcfile = rblineprof.cache.srcfile;
|
291
|
+
else
|
207
292
|
srcfile = sourcefile_lookup(file);
|
293
|
+
rblineprof.cache.file = file;
|
294
|
+
rblineprof.cache.srcfile = srcfile;
|
208
295
|
if (!srcfile) return; /* skip line profiling for this file */
|
209
296
|
|
210
297
|
switch (event) {
|
@@ -214,7 +301,12 @@ profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
214
301
|
if (rblineprof.stack_depth > 0 && rblineprof.stack_depth < MAX_STACK_DEPTH) {
|
215
302
|
frame = &rblineprof.stack[rblineprof.stack_depth-1];
|
216
303
|
frame->event = event;
|
304
|
+
|
305
|
+
#ifdef RUBY_VM
|
306
|
+
frame->thread = th;
|
307
|
+
#else
|
217
308
|
frame->node = node;
|
309
|
+
#endif
|
218
310
|
frame->self = self;
|
219
311
|
frame->mid = mid;
|
220
312
|
frame->klass = klass;
|
@@ -232,7 +324,13 @@ profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
232
324
|
else
|
233
325
|
frame = NULL;
|
234
326
|
rblineprof.stack_depth--;
|
235
|
-
} while (frame &&
|
327
|
+
} while (frame &&
|
328
|
+
#ifdef RUBY_VM
|
329
|
+
frame->thread != th &&
|
330
|
+
#endif
|
331
|
+
frame->self != self &&
|
332
|
+
frame->mid != mid &&
|
333
|
+
frame->klass != klass);
|
236
334
|
|
237
335
|
if (frame)
|
238
336
|
stackframe_record(frame, now);
|
@@ -269,7 +367,6 @@ summarize_files(st_data_t key, st_data_t record, st_data_t arg)
|
|
269
367
|
rb_ary_store(ary, 0, ULL2NUM(srcfile->exclusive_time));
|
270
368
|
for (i=1; i<srcfile->nlines; i++)
|
271
369
|
rb_ary_store(ary, i, rb_ary_new3(2, ULL2NUM(srcfile->lines[i].total_time), ULL2NUM(srcfile->lines[i].calls)));
|
272
|
-
|
273
370
|
rb_hash_aset(ret, rb_str_new2(srcfile->filename), ary);
|
274
371
|
|
275
372
|
return ST_CONTINUE;
|
@@ -278,8 +375,9 @@ summarize_files(st_data_t key, st_data_t record, st_data_t arg)
|
|
278
375
|
static VALUE
|
279
376
|
lineprof_ensure(VALUE self)
|
280
377
|
{
|
281
|
-
rb_remove_event_hook(profiler_hook);
|
378
|
+
rb_remove_event_hook((rb_event_hook_func_t) profiler_hook);
|
282
379
|
rblineprof.enabled = false;
|
380
|
+
return self;
|
283
381
|
}
|
284
382
|
|
285
383
|
VALUE
|
@@ -294,7 +392,14 @@ lineprof(VALUE self, VALUE filename)
|
|
294
392
|
VALUE filename_class = rb_obj_class(filename);
|
295
393
|
|
296
394
|
if (filename_class == rb_cString) {
|
395
|
+
#ifdef RUBY_VM
|
396
|
+
rblineprof.source_filename = (char *) (StringValuePtr(filename));
|
397
|
+
#else
|
398
|
+
/* rb_source_filename will return a string we can compare directly against
|
399
|
+
* node->file, without a strcmp()
|
400
|
+
*/
|
297
401
|
rblineprof.source_filename = rb_source_filename(StringValuePtr(filename));
|
402
|
+
#endif
|
298
403
|
} else if (filename_class == rb_cRegexp) {
|
299
404
|
rblineprof.source_regex = filename;
|
300
405
|
rblineprof.source_filename = NULL;
|
@@ -310,9 +415,16 @@ lineprof(VALUE self, VALUE filename)
|
|
310
415
|
rblineprof.file.lines = NULL;
|
311
416
|
rblineprof.file.nlines = 0;
|
312
417
|
}
|
418
|
+
rblineprof.cache.file = NULL;
|
419
|
+
rblineprof.cache.srcfile = NULL;
|
313
420
|
|
314
421
|
rblineprof.enabled = true;
|
315
|
-
|
422
|
+
#ifndef RUBY_VM
|
423
|
+
rb_add_event_hook((rb_event_hook_func_t) profiler_hook, RUBY_EVENT_CALL|RUBY_EVENT_RETURN|RUBY_EVENT_C_CALL|RUBY_EVENT_C_RETURN);
|
424
|
+
#else
|
425
|
+
rb_add_event_hook((rb_event_hook_func_t) profiler_hook, RUBY_EVENT_CALL|RUBY_EVENT_RETURN|RUBY_EVENT_C_CALL|RUBY_EVENT_C_RETURN, Qnil);
|
426
|
+
#endif
|
427
|
+
|
316
428
|
rb_ensure(rb_yield, Qnil, lineprof_ensure, self);
|
317
429
|
|
318
430
|
sourcefile_t *curr_srcfile = rblineprof.curr_srcfile;
|
@@ -348,4 +460,4 @@ Init_rblineprof()
|
|
348
460
|
rb_define_global_function("lineprof", lineprof, 1);
|
349
461
|
}
|
350
462
|
|
351
|
-
/* vim: ts=2
|
463
|
+
/* vim: set ts=2 sw=2 expandtab: */
|
data/rblineprof.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'rblineprof'
|
3
|
-
s.version = '0.2.
|
3
|
+
s.version = '0.2.7'
|
4
4
|
s.homepage = 'http://github.com/tmm1/rblineprof'
|
5
5
|
|
6
6
|
s.authors = 'Aman Gupta'
|
@@ -9,6 +9,6 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.files = `git ls-files`.split("\n")
|
10
10
|
s.extensions = 'ext/extconf.rb'
|
11
11
|
|
12
|
-
s.summary = 'line-profiler for ruby
|
12
|
+
s.summary = 'line-profiler for ruby'
|
13
13
|
s.description = 'rblineprof shows you lines of code that are slow.'
|
14
14
|
end
|
data/test.rb
CHANGED
@@ -38,8 +38,9 @@ profile = lineprof(/./) do
|
|
38
38
|
outer
|
39
39
|
end
|
40
40
|
|
41
|
-
File.
|
42
|
-
|
41
|
+
file = File.expand_path(__FILE__)
|
42
|
+
File.readlines(file).each_with_index do |line, num|
|
43
|
+
time, calls = profile[file][num+1]
|
43
44
|
if calls && calls > 0
|
44
45
|
printf "% 8.1fms (% 5d) | %s", time/1000.0, calls, line
|
45
46
|
else
|
metadata
CHANGED
@@ -1,32 +1,23 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rblineprof
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.7
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 5
|
10
|
-
version: 0.2.5
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Aman Gupta
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2013-01-18 00:00:00 Z
|
12
|
+
date: 2013-02-13 00:00:00.000000000 Z
|
19
13
|
dependencies: []
|
20
|
-
|
21
14
|
description: rblineprof shows you lines of code that are slow.
|
22
15
|
email: aman@tmm1.net
|
23
16
|
executables: []
|
24
|
-
|
25
|
-
extensions:
|
17
|
+
extensions:
|
26
18
|
- ext/extconf.rb
|
27
19
|
extra_rdoc_files: []
|
28
|
-
|
29
|
-
files:
|
20
|
+
files:
|
30
21
|
- README.md
|
31
22
|
- ext/.gitignore
|
32
23
|
- ext/extconf.rb
|
@@ -35,36 +26,26 @@ files:
|
|
35
26
|
- test.rb
|
36
27
|
homepage: http://github.com/tmm1/rblineprof
|
37
28
|
licenses: []
|
38
|
-
|
39
29
|
post_install_message:
|
40
30
|
rdoc_options: []
|
41
|
-
|
42
|
-
require_paths:
|
31
|
+
require_paths:
|
43
32
|
- lib
|
44
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
34
|
none: false
|
46
|
-
requirements:
|
47
|
-
- -
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
|
50
|
-
|
51
|
-
- 0
|
52
|
-
version: "0"
|
53
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
40
|
none: false
|
55
|
-
requirements:
|
56
|
-
- -
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
|
59
|
-
segments:
|
60
|
-
- 0
|
61
|
-
version: "0"
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
62
45
|
requirements: []
|
63
|
-
|
64
46
|
rubyforge_project:
|
65
|
-
rubygems_version: 1.8.
|
47
|
+
rubygems_version: 1.8.23
|
66
48
|
signing_key:
|
67
49
|
specification_version: 3
|
68
|
-
summary: line-profiler for ruby
|
50
|
+
summary: line-profiler for ruby
|
69
51
|
test_files: []
|
70
|
-
|