rblineprof 0.2.5 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|