rblineprof 0.2.7 → 0.3.2
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/ext/extconf.rb +3 -8
- data/ext/rblineprof.c +231 -75
- data/rblineprof.gemspec +3 -1
- data/test.rb +72 -5
- metadata +54 -20
data/ext/extconf.rb
CHANGED
@@ -1,16 +1,11 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
3
|
if RUBY_VERSION >= "1.9"
|
4
|
-
|
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
|
4
|
+
require "debugger/ruby_core_source"
|
12
5
|
|
13
6
|
hdrs = proc {
|
7
|
+
have_type("rb_iseq_location_t", "vm_core.h")
|
8
|
+
|
14
9
|
have_header("vm_core.h") and
|
15
10
|
have_header("iseq.h")
|
16
11
|
}
|
data/ext/rblineprof.c
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <stdbool.h>
|
3
|
+
#include <time.h>
|
4
|
+
#include <sys/time.h>
|
5
|
+
#include <sys/resource.h>
|
3
6
|
|
4
7
|
#ifdef RUBY_VM
|
5
8
|
#include <ruby/re.h>
|
@@ -20,17 +23,27 @@
|
|
20
23
|
typedef rb_event_t rb_event_flag_t;
|
21
24
|
#endif
|
22
25
|
|
23
|
-
typedef uint64_t prof_time_t;
|
24
26
|
static VALUE gc_hook;
|
25
27
|
|
26
28
|
/*
|
27
|
-
*
|
28
|
-
|
29
|
+
* Time in microseconds
|
30
|
+
*/
|
31
|
+
typedef uint64_t prof_time_t;
|
32
|
+
|
33
|
+
/*
|
34
|
+
* Profiling snapshot
|
35
|
+
*/
|
36
|
+
typedef struct snapshot {
|
37
|
+
prof_time_t wall_time;
|
38
|
+
prof_time_t cpu_time;
|
39
|
+
} snapshot_t;
|
40
|
+
|
41
|
+
/*
|
42
|
+
* A line of Ruby source code
|
29
43
|
*/
|
30
44
|
typedef struct sourceline {
|
31
|
-
uint64_t calls; // total number of
|
32
|
-
|
33
|
-
prof_time_t max_time;
|
45
|
+
uint64_t calls; // total number of call/c_call events
|
46
|
+
snapshot_t total;
|
34
47
|
} sourceline_t;
|
35
48
|
|
36
49
|
/*
|
@@ -38,11 +51,17 @@ typedef struct sourceline {
|
|
38
51
|
*/
|
39
52
|
typedef struct sourcefile {
|
40
53
|
char *filename;
|
54
|
+
|
55
|
+
/* per line timing */
|
41
56
|
long nlines;
|
42
57
|
sourceline_t *lines;
|
43
58
|
|
44
|
-
|
45
|
-
|
59
|
+
/* overall file timing */
|
60
|
+
snapshot_t total;
|
61
|
+
snapshot_t child;
|
62
|
+
uint64_t depth;
|
63
|
+
snapshot_t exclusive_start;
|
64
|
+
snapshot_t exclusive;
|
46
65
|
} sourcefile_t;
|
47
66
|
|
48
67
|
/*
|
@@ -64,7 +83,7 @@ typedef struct stackframe {
|
|
64
83
|
char *filename;
|
65
84
|
long line;
|
66
85
|
|
67
|
-
|
86
|
+
snapshot_t start;
|
68
87
|
sourcefile_t *srcfile;
|
69
88
|
} stackframe_t;
|
70
89
|
|
@@ -79,9 +98,6 @@ static struct {
|
|
79
98
|
stackframe_t stack[MAX_STACK_DEPTH];
|
80
99
|
uint64_t stack_depth;
|
81
100
|
|
82
|
-
// current file
|
83
|
-
sourcefile_t *curr_srcfile;
|
84
|
-
|
85
101
|
// single file mode, store filename and line data directly
|
86
102
|
char *source_filename;
|
87
103
|
sourcefile_t file;
|
@@ -102,7 +118,30 @@ rblineprof = {
|
|
102
118
|
};
|
103
119
|
|
104
120
|
static prof_time_t
|
105
|
-
|
121
|
+
cputime_usec()
|
122
|
+
{
|
123
|
+
#if defined(__linux__)
|
124
|
+
struct timespec ts;
|
125
|
+
|
126
|
+
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
|
127
|
+
return (prof_time_t)ts.tv_sec*1e6 +
|
128
|
+
(prof_time_t)ts.tv_nsec*1e-3;
|
129
|
+
}
|
130
|
+
#endif
|
131
|
+
|
132
|
+
#if defined(RUSAGE_SELF)
|
133
|
+
struct rusage usage;
|
134
|
+
|
135
|
+
getrusage(RUSAGE_SELF, &usage);
|
136
|
+
return (prof_time_t)usage.ru_utime.tv_sec*1e6 +
|
137
|
+
(prof_time_t)usage.ru_utime.tv_usec;
|
138
|
+
#endif
|
139
|
+
|
140
|
+
return 0;
|
141
|
+
}
|
142
|
+
|
143
|
+
static prof_time_t
|
144
|
+
walltime_usec()
|
106
145
|
{
|
107
146
|
struct timeval tv;
|
108
147
|
gettimeofday(&tv, NULL);
|
@@ -110,8 +149,26 @@ timeofday_usec()
|
|
110
149
|
(prof_time_t)tv.tv_usec;
|
111
150
|
}
|
112
151
|
|
152
|
+
static inline snapshot_t
|
153
|
+
snapshot_diff(snapshot_t *t1, snapshot_t *t2)
|
154
|
+
{
|
155
|
+
snapshot_t diff = {
|
156
|
+
.wall_time = t1->wall_time - t2->wall_time,
|
157
|
+
.cpu_time = t1->cpu_time - t2->cpu_time,
|
158
|
+
};
|
159
|
+
|
160
|
+
return diff;
|
161
|
+
}
|
162
|
+
|
113
163
|
static inline void
|
114
|
-
|
164
|
+
snapshot_increment(snapshot_t *s, snapshot_t *inc)
|
165
|
+
{
|
166
|
+
s->wall_time += inc->wall_time;
|
167
|
+
s->cpu_time += inc->cpu_time;
|
168
|
+
}
|
169
|
+
|
170
|
+
static inline void
|
171
|
+
stackframe_record(stackframe_t *frame, snapshot_t now, stackframe_t *caller_frame)
|
115
172
|
{
|
116
173
|
sourcefile_t *srcfile = frame->srcfile;
|
117
174
|
long line = frame->line;
|
@@ -132,13 +189,35 @@ stackframe_record(stackframe_t *frame, prof_time_t now)
|
|
132
189
|
MEMZERO(srcfile->lines + prev_nlines, sourceline_t, srcfile->nlines - prev_nlines);
|
133
190
|
}
|
134
191
|
|
135
|
-
|
136
|
-
prof_time_t diff = now - frame->start;
|
192
|
+
snapshot_t diff = snapshot_diff(&now, &frame->start);
|
137
193
|
sourceline_t *srcline = &(srcfile->lines[line]);
|
194
|
+
|
195
|
+
/* Line profiler metrics.
|
196
|
+
*/
|
197
|
+
|
138
198
|
srcline->calls++;
|
139
|
-
|
140
|
-
|
141
|
-
|
199
|
+
|
200
|
+
/* Increment current line's total_time.
|
201
|
+
*
|
202
|
+
* Skip the special case where the stack frame we're returning to
|
203
|
+
* had the same file/line. This fixes double counting on crazy one-liners.
|
204
|
+
*/
|
205
|
+
if (!(caller_frame && caller_frame->srcfile == frame->srcfile && caller_frame->line == frame->line))
|
206
|
+
snapshot_increment(&srcline->total, &diff);
|
207
|
+
|
208
|
+
/* File profiler metrics.
|
209
|
+
*/
|
210
|
+
|
211
|
+
/* Increment the caller file's child_time.
|
212
|
+
*/
|
213
|
+
if (caller_frame && caller_frame->srcfile != srcfile)
|
214
|
+
snapshot_increment(&caller_frame->srcfile->child, &diff);
|
215
|
+
|
216
|
+
/* Increment current file's total_time, but only when we return
|
217
|
+
* to the outermost stack frame when we first entered the file.
|
218
|
+
*/
|
219
|
+
if (srcfile->depth == 0)
|
220
|
+
snapshot_increment(&srcfile->total, &diff);
|
142
221
|
}
|
143
222
|
|
144
223
|
static inline sourcefile_t*
|
@@ -165,7 +244,12 @@ sourcefile_lookup(char *filename)
|
|
165
244
|
return NULL;
|
166
245
|
|
167
246
|
if (!srcfile) { // unknown file, check against regex
|
168
|
-
|
247
|
+
VALUE backref = rb_backref_get();
|
248
|
+
rb_match_busy(backref);
|
249
|
+
long rc = rb_reg_search(rblineprof.source_regex, rb_str_new2(filename), 0, 0);
|
250
|
+
rb_backref_set(backref);
|
251
|
+
|
252
|
+
if (rc >= 0) {
|
169
253
|
srcfile = ALLOC_N(sourcefile_t, 1);
|
170
254
|
MEMZERO(srcfile, sourcefile_t, 1);
|
171
255
|
srcfile->filename = strdup(filename);
|
@@ -188,6 +272,7 @@ sourcefile_lookup(char *filename)
|
|
188
272
|
* top-most cfp on the stack in these cases points to the 'def method' line, so we skip
|
189
273
|
* these and grab the second caller instead.
|
190
274
|
*/
|
275
|
+
static inline
|
191
276
|
rb_control_frame_t *
|
192
277
|
rb_vm_get_caller(rb_thread_t *th, rb_control_frame_t *cfp, ID mid)
|
193
278
|
{
|
@@ -205,6 +290,26 @@ rb_vm_get_caller(rb_thread_t *th, rb_control_frame_t *cfp, ID mid)
|
|
205
290
|
|
206
291
|
return 0;
|
207
292
|
}
|
293
|
+
|
294
|
+
#ifdef HAVE_TYPE_RB_ISEQ_LOCATION_T
|
295
|
+
inline static int
|
296
|
+
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
297
|
+
{
|
298
|
+
return rb_iseq_line_no(iseq, pc - iseq->iseq_encoded);
|
299
|
+
}
|
300
|
+
|
301
|
+
int
|
302
|
+
rb_vm_get_sourceline(const rb_control_frame_t *cfp)
|
303
|
+
{
|
304
|
+
int lineno = 0;
|
305
|
+
const rb_iseq_t *iseq = cfp->iseq;
|
306
|
+
|
307
|
+
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
308
|
+
lineno = calc_lineno(cfp->iseq, cfp->pc);
|
309
|
+
}
|
310
|
+
return lineno;
|
311
|
+
}
|
312
|
+
#endif
|
208
313
|
#endif
|
209
314
|
|
210
315
|
static void
|
@@ -216,40 +321,8 @@ profiler_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass
|
|
216
321
|
{
|
217
322
|
char *file;
|
218
323
|
long line;
|
219
|
-
stackframe_t *frame = NULL;
|
220
|
-
sourcefile_t *srcfile
|
221
|
-
prof_time_t now = timeofday_usec();
|
222
|
-
|
223
|
-
#if 0
|
224
|
-
/* file profiler: when invoking a method in a new file, account elapsed
|
225
|
-
* time to the current file and start a new timer.
|
226
|
-
*/
|
227
|
-
#ifndef RUBY_VM
|
228
|
-
if (!node) return;
|
229
|
-
file = node->nd_file;
|
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
|
-
|
237
|
-
if (!file) return;
|
238
|
-
if (line <= 0) return;
|
239
|
-
|
240
|
-
srcfile = sourcefile_lookup(file);
|
241
|
-
curr_srcfile = rblineprof.curr_srcfile;
|
242
|
-
|
243
|
-
if (curr_srcfile != srcfile) {
|
244
|
-
if (curr_srcfile)
|
245
|
-
curr_srcfile->exclusive_time += now - curr_srcfile->exclusive_start;
|
246
|
-
|
247
|
-
if (srcfile)
|
248
|
-
srcfile->exclusive_start = now;
|
249
|
-
|
250
|
-
rblineprof.curr_srcfile = srcfile;
|
251
|
-
}
|
252
|
-
#endif
|
324
|
+
stackframe_t *frame = NULL, *prev = NULL;
|
325
|
+
sourcefile_t *srcfile;
|
253
326
|
|
254
327
|
/* line profiler: maintain a stack of CALL events with timestamps. for
|
255
328
|
* each corresponding RETURN, account elapsed time to the calling line.
|
@@ -264,14 +337,21 @@ profiler_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass
|
|
264
337
|
file = caller_node->nd_file;
|
265
338
|
line = nd_line(caller_node);
|
266
339
|
#else
|
267
|
-
rb_thread_t *th =
|
340
|
+
rb_thread_t *th = ruby_current_thread;
|
268
341
|
rb_control_frame_t *cfp = rb_vm_get_caller(th, th->cfp, mid);
|
269
342
|
if (!cfp) return;
|
270
343
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
344
|
+
#ifdef HAVE_TYPE_RB_ISEQ_LOCATION_T
|
345
|
+
if (RTEST(cfp->iseq->location.absolute_path))
|
346
|
+
file = StringValueCStr(cfp->iseq->location.absolute_path);
|
347
|
+
else
|
348
|
+
file = StringValueCStr(cfp->iseq->location.path);
|
349
|
+
#else
|
350
|
+
if (RTEST(cfp->iseq->filepath))
|
351
|
+
file = StringValueCStr(cfp->iseq->filepath);
|
352
|
+
else
|
353
|
+
file = StringValueCStr(cfp->iseq->filename);
|
354
|
+
#endif
|
275
355
|
line = rb_vm_get_sourceline(cfp);
|
276
356
|
#endif
|
277
357
|
|
@@ -294,46 +374,115 @@ profiler_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass
|
|
294
374
|
rblineprof.cache.srcfile = srcfile;
|
295
375
|
if (!srcfile) return; /* skip line profiling for this file */
|
296
376
|
|
377
|
+
snapshot_t now = {
|
378
|
+
.wall_time = walltime_usec(),
|
379
|
+
.cpu_time = cputime_usec(),
|
380
|
+
};
|
381
|
+
|
297
382
|
switch (event) {
|
298
383
|
case RUBY_EVENT_CALL:
|
299
384
|
case RUBY_EVENT_C_CALL:
|
385
|
+
/* Create a stack frame entry with this event,
|
386
|
+
* the current file, and a snapshot of metrics.
|
387
|
+
*
|
388
|
+
* On a corresponding RETURN event later, we can
|
389
|
+
* pop this stack frame and accumulate metrics to the
|
390
|
+
* associated file and line.
|
391
|
+
*/
|
300
392
|
rblineprof.stack_depth++;
|
301
393
|
if (rblineprof.stack_depth > 0 && rblineprof.stack_depth < MAX_STACK_DEPTH) {
|
302
394
|
frame = &rblineprof.stack[rblineprof.stack_depth-1];
|
303
395
|
frame->event = event;
|
304
|
-
|
305
|
-
#ifdef RUBY_VM
|
306
|
-
frame->thread = th;
|
307
|
-
#else
|
308
|
-
frame->node = node;
|
309
|
-
#endif
|
310
396
|
frame->self = self;
|
311
397
|
frame->mid = mid;
|
312
398
|
frame->klass = klass;
|
313
399
|
frame->line = line;
|
314
400
|
frame->start = now;
|
315
401
|
frame->srcfile = srcfile;
|
402
|
+
#ifdef RUBY_VM
|
403
|
+
frame->thread = th;
|
404
|
+
#else
|
405
|
+
frame->node = node;
|
406
|
+
#endif
|
407
|
+
}
|
408
|
+
|
409
|
+
/* Record when we entered this file for the first time.
|
410
|
+
* The difference is later accumulated into exclusive_time,
|
411
|
+
* e.g. on the next event if the file changes.
|
412
|
+
*/
|
413
|
+
if (srcfile->depth == 0)
|
414
|
+
srcfile->exclusive_start = now;
|
415
|
+
srcfile->depth++;
|
416
|
+
|
417
|
+
if (rblineprof.stack_depth > 1) { // skip if outermost frame
|
418
|
+
prev = &rblineprof.stack[rblineprof.stack_depth-2];
|
419
|
+
|
420
|
+
/* If we just switched files, record time that was spent in
|
421
|
+
* the previous file.
|
422
|
+
*/
|
423
|
+
if (prev->srcfile != frame->srcfile) {
|
424
|
+
snapshot_t diff = snapshot_diff(&now, &prev->srcfile->exclusive_start);
|
425
|
+
snapshot_increment(&prev->srcfile->exclusive, &diff);
|
426
|
+
prev->srcfile->exclusive_start = now;
|
427
|
+
}
|
316
428
|
}
|
317
429
|
break;
|
318
430
|
|
319
431
|
case RUBY_EVENT_RETURN:
|
320
432
|
case RUBY_EVENT_C_RETURN:
|
433
|
+
/* Find the corresponding CALL for this event.
|
434
|
+
*
|
435
|
+
* We loop here instead of a simple pop, because in the event of a
|
436
|
+
* raise/rescue several stack frames could have disappeared.
|
437
|
+
*/
|
321
438
|
do {
|
322
|
-
if (rblineprof.stack_depth > 0 && rblineprof.stack_depth < MAX_STACK_DEPTH)
|
439
|
+
if (rblineprof.stack_depth > 0 && rblineprof.stack_depth < MAX_STACK_DEPTH) {
|
323
440
|
frame = &rblineprof.stack[rblineprof.stack_depth-1];
|
324
|
-
|
441
|
+
if (frame->srcfile->depth > 0)
|
442
|
+
frame->srcfile->depth--;
|
443
|
+
} else
|
325
444
|
frame = NULL;
|
445
|
+
|
326
446
|
rblineprof.stack_depth--;
|
327
447
|
} while (frame &&
|
328
448
|
#ifdef RUBY_VM
|
329
449
|
frame->thread != th &&
|
330
450
|
#endif
|
451
|
+
/* Break when we find a matching CALL/C_CALL.
|
452
|
+
*/
|
453
|
+
frame->event != (event == RUBY_EVENT_CALL ? RUBY_EVENT_RETURN : RUBY_EVENT_C_RETURN) &&
|
331
454
|
frame->self != self &&
|
332
455
|
frame->mid != mid &&
|
333
456
|
frame->klass != klass);
|
334
457
|
|
458
|
+
if (rblineprof.stack_depth > 0) {
|
459
|
+
// The new top of the stack (that we're returning to)
|
460
|
+
prev = &rblineprof.stack[rblineprof.stack_depth-1];
|
461
|
+
|
462
|
+
/* If we're leaving this frame to go back to a different file,
|
463
|
+
* accumulate time we spent in this file.
|
464
|
+
*
|
465
|
+
* Note that we do this both when entering a new file and leaving to
|
466
|
+
* a new file to ensure we only count time spent exclusively in that file.
|
467
|
+
* Consider the following scenario:
|
468
|
+
*
|
469
|
+
* call (a.rb:1)
|
470
|
+
* call (b.rb:1) <-- leaving a.rb, increment into exclusive_time
|
471
|
+
* call (a.rb:5)
|
472
|
+
* return <-- leaving a.rb, increment into exclusive_time
|
473
|
+
* return
|
474
|
+
* return
|
475
|
+
*/
|
476
|
+
if (frame->srcfile != prev->srcfile) {
|
477
|
+
snapshot_t diff = snapshot_diff(&now, &frame->srcfile->exclusive_start);
|
478
|
+
snapshot_increment(&frame->srcfile->exclusive, &diff);
|
479
|
+
frame->srcfile->exclusive_start = now;
|
480
|
+
prev->srcfile->exclusive_start = now;
|
481
|
+
}
|
482
|
+
}
|
483
|
+
|
335
484
|
if (frame)
|
336
|
-
stackframe_record(frame, now);
|
485
|
+
stackframe_record(frame, now, prev);
|
337
486
|
|
338
487
|
break;
|
339
488
|
}
|
@@ -364,9 +513,21 @@ summarize_files(st_data_t key, st_data_t record, st_data_t arg)
|
|
364
513
|
VALUE ary = rb_ary_new();
|
365
514
|
long i;
|
366
515
|
|
367
|
-
rb_ary_store(ary, 0,
|
516
|
+
rb_ary_store(ary, 0, rb_ary_new3(6,
|
517
|
+
ULL2NUM(srcfile->total.wall_time),
|
518
|
+
ULL2NUM(srcfile->child.wall_time),
|
519
|
+
ULL2NUM(srcfile->exclusive.wall_time),
|
520
|
+
ULL2NUM(srcfile->total.cpu_time),
|
521
|
+
ULL2NUM(srcfile->child.cpu_time),
|
522
|
+
ULL2NUM(srcfile->exclusive.cpu_time)
|
523
|
+
));
|
524
|
+
|
368
525
|
for (i=1; i<srcfile->nlines; i++)
|
369
|
-
rb_ary_store(ary, i, rb_ary_new3(
|
526
|
+
rb_ary_store(ary, i, rb_ary_new3(3,
|
527
|
+
ULL2NUM(srcfile->lines[i].total.wall_time),
|
528
|
+
ULL2NUM(srcfile->lines[i].total.cpu_time),
|
529
|
+
ULL2NUM(srcfile->lines[i].calls)
|
530
|
+
));
|
370
531
|
rb_hash_aset(ret, rb_str_new2(srcfile->filename), ary);
|
371
532
|
|
372
533
|
return ST_CONTINUE;
|
@@ -408,7 +569,6 @@ lineprof(VALUE self, VALUE filename)
|
|
408
569
|
}
|
409
570
|
|
410
571
|
// reset state
|
411
|
-
rblineprof.curr_srcfile = NULL;
|
412
572
|
st_foreach(rblineprof.files, cleanup_files, 0);
|
413
573
|
if (rblineprof.file.lines) {
|
414
574
|
xfree(rblineprof.file.lines);
|
@@ -427,10 +587,6 @@ lineprof(VALUE self, VALUE filename)
|
|
427
587
|
|
428
588
|
rb_ensure(rb_yield, Qnil, lineprof_ensure, self);
|
429
589
|
|
430
|
-
sourcefile_t *curr_srcfile = rblineprof.curr_srcfile;
|
431
|
-
if (curr_srcfile)
|
432
|
-
curr_srcfile->exclusive_time += timeofday_usec() - curr_srcfile->exclusive_start;
|
433
|
-
|
434
590
|
VALUE ret = rb_hash_new();
|
435
591
|
VALUE ary = Qnil;
|
436
592
|
|
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.3.2'
|
4
4
|
s.homepage = 'http://github.com/tmm1/rblineprof'
|
5
5
|
|
6
6
|
s.authors = 'Aman Gupta'
|
@@ -11,4 +11,6 @@ Gem::Specification.new do |s|
|
|
11
11
|
|
12
12
|
s.summary = 'line-profiler for ruby'
|
13
13
|
s.description = 'rblineprof shows you lines of code that are slow.'
|
14
|
+
|
15
|
+
s.add_dependency 'debugger-ruby_core_source', '~> 1.2'
|
14
16
|
end
|
data/test.rb
CHANGED
@@ -9,6 +9,40 @@ class Obj
|
|
9
9
|
def another(options={})
|
10
10
|
sleep 0.001
|
11
11
|
end
|
12
|
+
|
13
|
+
def out=(*)
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_defaults(arg=self.object_id.to_s)
|
17
|
+
another
|
18
|
+
list = [1,2,3]
|
19
|
+
# for cookie in list
|
20
|
+
# self.out=(
|
21
|
+
dummy(
|
22
|
+
1, "str
|
23
|
+
ing")
|
24
|
+
dummy <<-EOS
|
25
|
+
hi
|
26
|
+
EOS
|
27
|
+
dummy \
|
28
|
+
1234
|
29
|
+
dummy :a => 'b',
|
30
|
+
:c => 'd',
|
31
|
+
:e => 1024**1024,
|
32
|
+
'something' => dummy(:ok)
|
33
|
+
# )
|
34
|
+
# end
|
35
|
+
end
|
36
|
+
|
37
|
+
def dummy(*args)
|
38
|
+
args.inspect
|
39
|
+
end
|
40
|
+
|
41
|
+
class_eval <<-RUBY, 'otherfile.rb', 1
|
42
|
+
def other_file
|
43
|
+
another
|
44
|
+
end
|
45
|
+
RUBY
|
12
46
|
end
|
13
47
|
|
14
48
|
def inner
|
@@ -22,10 +56,29 @@ def inner
|
|
22
56
|
o = Obj.new
|
23
57
|
o.inner_block
|
24
58
|
o.another
|
59
|
+
o.other_file
|
60
|
+
o.with_defaults
|
25
61
|
end
|
26
62
|
|
27
63
|
def outer
|
28
|
-
sleep 0.
|
64
|
+
sleep 0.01
|
65
|
+
|
66
|
+
3000.times{ 2**1024 }
|
67
|
+
for i in 1..3000 do 2**1024 end
|
68
|
+
|
69
|
+
for i in 1..3000
|
70
|
+
2**1024
|
71
|
+
end
|
72
|
+
|
73
|
+
(fibonacci = Hash.new{ |h,k| h[k] = k < 2 ? k : h[k-1] + h[k-2] })[500]
|
74
|
+
|
75
|
+
(fibonacci = Hash.new{ |h,k|
|
76
|
+
h[k] = k < 2 ?
|
77
|
+
k :
|
78
|
+
h[k-1] +
|
79
|
+
h[k-2]
|
80
|
+
})
|
81
|
+
fibonacci[500]
|
29
82
|
|
30
83
|
100.times do
|
31
84
|
inner
|
@@ -34,16 +87,30 @@ def outer
|
|
34
87
|
inner
|
35
88
|
end
|
36
89
|
|
90
|
+
file = RUBY_VERSION > '1.9' ? File.expand_path(__FILE__) : __FILE__
|
91
|
+
|
92
|
+
# profile = lineprof(file) do
|
37
93
|
profile = lineprof(/./) do
|
38
94
|
outer
|
39
95
|
end
|
40
96
|
|
41
|
-
file = File.expand_path(__FILE__)
|
42
97
|
File.readlines(file).each_with_index do |line, num|
|
43
|
-
|
98
|
+
wall, cpu, calls = profile[file][num+1]
|
44
99
|
if calls && calls > 0
|
45
|
-
printf "% 8.1fms (% 5d) | %s",
|
100
|
+
printf "% 8.1fms + % 8.1fms (% 5d) | %s", cpu/1000.0, (wall-cpu)/1000.0, calls, line
|
101
|
+
# printf "% 8.1fms (% 5d) | %s", wall/1000.0, calls, line
|
46
102
|
else
|
47
|
-
printf "
|
103
|
+
printf " | %s", line
|
104
|
+
# printf " | %s", line
|
48
105
|
end
|
49
106
|
end
|
107
|
+
|
108
|
+
puts
|
109
|
+
profile.each do |file, data|
|
110
|
+
total, child, exclusive = data[0]
|
111
|
+
puts file
|
112
|
+
printf " % 10.1fms in this file\n", exclusive/1000.0
|
113
|
+
printf " % 10.1fms in this file + children\n", total/1000.0
|
114
|
+
printf " % 10.1fms in children\n", child/1000.0
|
115
|
+
puts
|
116
|
+
end
|
metadata
CHANGED
@@ -1,23 +1,46 @@
|
|
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
|
+
hash: 23
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 2
|
10
|
+
version: 0.3.2
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Aman Gupta
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
17
|
+
|
18
|
+
date: 2013-04-19 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: debugger-ruby_core_source
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 11
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 2
|
32
|
+
version: "1.2"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
14
35
|
description: rblineprof shows you lines of code that are slow.
|
15
36
|
email: aman@tmm1.net
|
16
37
|
executables: []
|
17
|
-
|
38
|
+
|
39
|
+
extensions:
|
18
40
|
- ext/extconf.rb
|
19
41
|
extra_rdoc_files: []
|
20
|
-
|
42
|
+
|
43
|
+
files:
|
21
44
|
- README.md
|
22
45
|
- ext/.gitignore
|
23
46
|
- ext/extconf.rb
|
@@ -26,26 +49,37 @@ files:
|
|
26
49
|
- test.rb
|
27
50
|
homepage: http://github.com/tmm1/rblineprof
|
28
51
|
licenses: []
|
52
|
+
|
29
53
|
post_install_message:
|
30
54
|
rdoc_options: []
|
31
|
-
|
55
|
+
|
56
|
+
require_paths:
|
32
57
|
- lib
|
33
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
59
|
none: false
|
35
|
-
requirements:
|
36
|
-
- -
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
|
39
|
-
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
68
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
45
76
|
requirements: []
|
77
|
+
|
46
78
|
rubyforge_project:
|
47
|
-
rubygems_version: 1.8.
|
79
|
+
rubygems_version: 1.8.24
|
48
80
|
signing_key:
|
49
81
|
specification_version: 3
|
50
82
|
summary: line-profiler for ruby
|
51
83
|
test_files: []
|
84
|
+
|
85
|
+
has_rdoc:
|