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 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
-