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 CHANGED
@@ -38,3 +38,4 @@
38
38
  * [method_profiler](https://github.com/change/method_profiler)
39
39
  * [ruby-prof](https://github.com/rdp/ruby-prof)
40
40
  * [perftools.rb](https://github.com/tmm1/perftools.rb)
41
+ * [zenprofile](https://github.com/seattlerb/zenprofile)
data/ext/extconf.rb CHANGED
@@ -1,2 +1,24 @@
1
1
  require 'mkmf'
2
- create_makefile 'rblineprof'
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 <stdio.h>
2
- #include <stdlib.h>
1
+ #include <ruby.h>
3
2
  #include <stdbool.h>
4
- #include <string.h>
5
3
 
6
- #include <ruby.h>
7
- #include <node.h>
8
- #include <env.h>
9
- #include <intern.h>
10
- #include <st.h>
11
- #include <re.h>
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
- rb_event_t event;
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
- profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
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
- if (caller_node->nd_file != node->nd_file)
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 && frame->self != self && frame->mid != mid && frame->klass != klass);
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
- rb_add_event_hook(profiler_hook, RUBY_EVENT_CALL|RUBY_EVENT_RETURN|RUBY_EVENT_C_CALL|RUBY_EVENT_C_RETURN);
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,sw=2,expandtab */
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.5'
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 1.8'
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.readlines(__FILE__).each_with_index do |line, num|
42
- time, calls = profile[__FILE__][num+1]
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
- hash: 29
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
- hash: 3
50
- segments:
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
- hash: 3
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.24
47
+ rubygems_version: 1.8.23
66
48
  signing_key:
67
49
  specification_version: 3
68
- summary: line-profiler for ruby 1.8
50
+ summary: line-profiler for ruby
69
51
  test_files: []
70
-