stackprof 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/bin/stackprof +7 -4
- data/bin/{stackprof-flamegraph → stackprof-flamegraph.pl} +0 -0
- data/bin/{stackprof-gprof2dot → stackprof-gprof2dot.py} +0 -0
- data/ext/stackprof.c +78 -42
- data/lib/stackprof/report.rb +65 -0
- data/stackprof.gemspec +5 -5
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9391ce548ee344fce9ee6e1f2ff86a59f7e6837
|
4
|
+
data.tar.gz: 961fb781cadaa07df721ed11d60df39d10bd0680
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcefc33f597c6ee462651a4242b0251e7a7b8595e5869df0110f59ab9f9174ad86ac3243378f971c1ed0fe96700e36b382ecc325d65a0304ba542e5344c462d2
|
7
|
+
data.tar.gz: d5af5d2e2da5082c32b14b2b255e9d70d10d9958d86605b14ac53b2b91239d617e8f7d29006d06f8513e6facb7cd3904589321908e9d0c6e65cb7e42bcc31966
|
data/Gemfile.lock
CHANGED
data/bin/stackprof
CHANGED
@@ -16,10 +16,11 @@ parser = OptionParser.new(ARGV) do |o|
|
|
16
16
|
o.on('--limit [num]', Integer, 'Limit --text or --files output to N lines'){ |n| options[:limit] = n }
|
17
17
|
o.on('--sort-total', "Sort --text or --files output on total samples\n\n"){ options[:sort] = true }
|
18
18
|
o.on('--method [grep]', 'Zoom into specified method'){ |f| options[:format] = :method; options[:filter] = f }
|
19
|
-
o.on('--file [grep]',
|
20
|
-
o.on('--
|
21
|
-
o.on('--
|
22
|
-
o.on('--
|
19
|
+
o.on('--file [grep]', "Show annotated code for specified file\n\n"){ |f| options[:format] = :file; options[:filter] = f }
|
20
|
+
o.on('--callgrind', 'Callgrind output (use with kcachegrind, stackprof-gprof2dot.py)'){ options[:format] = :callgrind }
|
21
|
+
o.on('--graphviz', "Graphviz output (use with dot)"){ options[:format] = :graphviz }
|
22
|
+
o.on('--stackcollapse', 'stackcollapse.pl compatible output (use with stackprof-flamegraph.pl)'){ options[:format] = :stackcollapse }
|
23
|
+
o.on('--flamegraph', "timeline-flamegraph output (js)\n\n"){ options[:format] = :flamegraph }
|
23
24
|
o.on('--dump', 'Print marshaled profile dump (combine multiple profiles)'){ options[:format] = :dump }
|
24
25
|
o.on('--debug', 'Pretty print raw profile data'){ options[:format] = :debug }
|
25
26
|
end
|
@@ -51,6 +52,8 @@ when :graphviz
|
|
51
52
|
report.print_graphviz
|
52
53
|
when :stackcollapse
|
53
54
|
report.print_stackcollapse
|
55
|
+
when :flamegraph
|
56
|
+
report.print_flamegraph
|
54
57
|
when :method
|
55
58
|
report.print_method(options[:filter])
|
56
59
|
when :file
|
File without changes
|
File without changes
|
data/ext/stackprof.c
CHANGED
@@ -26,12 +26,18 @@ typedef struct {
|
|
26
26
|
|
27
27
|
static struct {
|
28
28
|
int running;
|
29
|
+
int raw;
|
30
|
+
int aggregate;
|
31
|
+
|
29
32
|
VALUE mode;
|
30
33
|
VALUE interval;
|
31
|
-
VALUE raw;
|
32
|
-
size_t raw_sample_index;
|
33
34
|
VALUE out;
|
34
35
|
|
36
|
+
VALUE *raw_samples;
|
37
|
+
size_t raw_samples_len;
|
38
|
+
size_t raw_samples_capa;
|
39
|
+
size_t raw_sample_index;
|
40
|
+
|
35
41
|
size_t overall_signals;
|
36
42
|
size_t overall_samples;
|
37
43
|
size_t during_gc;
|
@@ -43,7 +49,7 @@ static struct {
|
|
43
49
|
|
44
50
|
static VALUE sym_object, sym_wall, sym_cpu, sym_custom, sym_name, sym_file, sym_line;
|
45
51
|
static VALUE sym_samples, sym_total_samples, sym_missed_samples, sym_edges, sym_lines;
|
46
|
-
static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out;
|
52
|
+
static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out, sym_aggregate;
|
47
53
|
static VALUE sym_gc_samples, objtracer;
|
48
54
|
static VALUE gc_hook;
|
49
55
|
static VALUE rb_mStackProf;
|
@@ -56,7 +62,8 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
|
|
56
62
|
{
|
57
63
|
struct sigaction sa;
|
58
64
|
struct itimerval timer;
|
59
|
-
VALUE opts = Qnil, mode = Qnil, interval = Qnil,
|
65
|
+
VALUE opts = Qnil, mode = Qnil, interval = Qnil, out = Qfalse;
|
66
|
+
int raw = 0, aggregate = 1;
|
60
67
|
|
61
68
|
if (_stackprof.running)
|
62
69
|
return Qfalse;
|
@@ -69,7 +76,9 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
|
|
69
76
|
out = rb_hash_aref(opts, sym_out);
|
70
77
|
|
71
78
|
if (RTEST(rb_hash_aref(opts, sym_raw)))
|
72
|
-
raw =
|
79
|
+
raw = 1;
|
80
|
+
if (rb_hash_lookup2(opts, sym_aggregate, Qundef) == Qfalse)
|
81
|
+
aggregate = 0;
|
73
82
|
}
|
74
83
|
if (!RTEST(mode)) mode = sym_wall;
|
75
84
|
|
@@ -106,6 +115,7 @@ stackprof_start(int argc, VALUE *argv, VALUE self)
|
|
106
115
|
|
107
116
|
_stackprof.running = 1;
|
108
117
|
_stackprof.raw = raw;
|
118
|
+
_stackprof.aggregate = aggregate;
|
109
119
|
_stackprof.mode = mode;
|
110
120
|
_stackprof.interval = interval;
|
111
121
|
_stackprof.out = out;
|
@@ -234,9 +244,27 @@ stackprof_results(int argc, VALUE *argv, VALUE self)
|
|
234
244
|
st_free_table(_stackprof.frames);
|
235
245
|
_stackprof.frames = NULL;
|
236
246
|
|
237
|
-
if (
|
238
|
-
|
239
|
-
|
247
|
+
if (_stackprof.raw && _stackprof.raw_samples_len) {
|
248
|
+
size_t len, n, o;
|
249
|
+
VALUE raw_samples = rb_ary_new_capa(_stackprof.raw_samples_len);
|
250
|
+
|
251
|
+
for (n = 0; n < _stackprof.raw_samples_len; n++) {
|
252
|
+
len = (size_t)_stackprof.raw_samples[n];
|
253
|
+
rb_ary_push(raw_samples, SIZET2NUM(len));
|
254
|
+
|
255
|
+
for (o = 0, n++; o < len; n++, o++)
|
256
|
+
rb_ary_push(raw_samples, rb_obj_id(_stackprof.raw_samples[n]));
|
257
|
+
rb_ary_push(raw_samples, SIZET2NUM((size_t)_stackprof.raw_samples[n]));
|
258
|
+
}
|
259
|
+
|
260
|
+
free(_stackprof.raw_samples);
|
261
|
+
_stackprof.raw_samples = NULL;
|
262
|
+
_stackprof.raw_samples_len = 0;
|
263
|
+
_stackprof.raw_samples_capa = 0;
|
264
|
+
_stackprof.raw_sample_index = 0;
|
265
|
+
_stackprof.raw = 0;
|
266
|
+
|
267
|
+
rb_hash_aset(results, sym_raw, raw_samples);
|
240
268
|
}
|
241
269
|
|
242
270
|
if (argc == 1)
|
@@ -315,37 +343,44 @@ void
|
|
315
343
|
stackprof_record_sample()
|
316
344
|
{
|
317
345
|
int num, i, n;
|
318
|
-
int raw_mode = RTEST(_stackprof.raw);
|
319
346
|
VALUE prev_frame = Qnil;
|
320
|
-
size_t raw_len;
|
321
347
|
|
322
348
|
_stackprof.overall_samples++;
|
323
349
|
num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer), _stackprof.frames_buffer, _stackprof.lines_buffer);
|
324
350
|
|
325
|
-
if (
|
351
|
+
if (_stackprof.raw) {
|
326
352
|
int found = 0;
|
327
|
-
raw_len = RARRAY_LEN(_stackprof.raw);
|
328
353
|
|
329
|
-
if (
|
354
|
+
if (!_stackprof.raw_samples) {
|
355
|
+
_stackprof.raw_samples_capa = num * 100;
|
356
|
+
_stackprof.raw_samples = malloc(sizeof(VALUE) * _stackprof.raw_samples_capa);
|
357
|
+
}
|
358
|
+
|
359
|
+
if (_stackprof.raw_samples_capa <= _stackprof.raw_samples_len + num) {
|
360
|
+
_stackprof.raw_samples_capa *= 2;
|
361
|
+
_stackprof.raw_samples = realloc(_stackprof.raw_samples, _stackprof.raw_samples_capa);
|
362
|
+
}
|
363
|
+
|
364
|
+
if (_stackprof.raw_samples_len > 0 && _stackprof.raw_samples[_stackprof.raw_sample_index] == (VALUE)num) {
|
330
365
|
for (i = num-1, n = 0; i >= 0; i--, n++) {
|
331
366
|
VALUE frame = _stackprof.frames_buffer[i];
|
332
|
-
if (
|
367
|
+
if (_stackprof.raw_samples[_stackprof.raw_sample_index + 1 + n] != frame)
|
333
368
|
break;
|
334
369
|
}
|
335
370
|
if (i == -1) {
|
336
|
-
|
371
|
+
_stackprof.raw_samples[_stackprof.raw_samples_len-1] += 1;
|
337
372
|
found = 1;
|
338
373
|
}
|
339
374
|
}
|
340
375
|
|
341
376
|
if (!found) {
|
342
|
-
_stackprof.raw_sample_index =
|
343
|
-
|
377
|
+
_stackprof.raw_sample_index = _stackprof.raw_samples_len;
|
378
|
+
_stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)num;
|
344
379
|
for (i = num-1; i >= 0; i--) {
|
345
380
|
VALUE frame = _stackprof.frames_buffer[i];
|
346
|
-
|
381
|
+
_stackprof.raw_samples[_stackprof.raw_samples_len++] = frame;
|
347
382
|
}
|
348
|
-
|
383
|
+
_stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)1;
|
349
384
|
}
|
350
385
|
}
|
351
386
|
|
@@ -358,13 +393,13 @@ stackprof_record_sample()
|
|
358
393
|
|
359
394
|
if (i == 0) {
|
360
395
|
frame_data->caller_samples++;
|
361
|
-
} else {
|
396
|
+
} else if (_stackprof.aggregate) {
|
362
397
|
if (!frame_data->edges)
|
363
398
|
frame_data->edges = st_init_numtable();
|
364
399
|
st_numtable_increment(frame_data->edges, (st_data_t)prev_frame, 1);
|
365
400
|
}
|
366
401
|
|
367
|
-
if (line > 0) {
|
402
|
+
if (_stackprof.aggregate && line > 0) {
|
368
403
|
if (!frame_data->lines)
|
369
404
|
frame_data->lines = st_init_numtable();
|
370
405
|
size_t half = (size_t)1<<(8*SIZEOF_SIZE_T/2);
|
@@ -428,8 +463,6 @@ frame_mark_i(st_data_t key, st_data_t val, st_data_t arg)
|
|
428
463
|
static void
|
429
464
|
stackprof_gc_mark(void *data)
|
430
465
|
{
|
431
|
-
if (RTEST(_stackprof.raw))
|
432
|
-
rb_gc_mark(_stackprof.raw);
|
433
466
|
if (RTEST(_stackprof.out))
|
434
467
|
rb_gc_mark(_stackprof.out);
|
435
468
|
|
@@ -472,25 +505,28 @@ stackprof_atfork_child(void)
|
|
472
505
|
void
|
473
506
|
Init_stackprof(void)
|
474
507
|
{
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
508
|
+
#define S(name) sym_##name = ID2SYM(rb_intern(#name));
|
509
|
+
S(object);
|
510
|
+
S(custom);
|
511
|
+
S(wall);
|
512
|
+
S(cpu);
|
513
|
+
S(name);
|
514
|
+
S(file);
|
515
|
+
S(line);
|
516
|
+
S(total_samples);
|
517
|
+
S(gc_samples);
|
518
|
+
S(missed_samples);
|
519
|
+
S(samples);
|
520
|
+
S(edges);
|
521
|
+
S(lines);
|
522
|
+
S(version);
|
523
|
+
S(mode);
|
524
|
+
S(interval);
|
525
|
+
S(raw);
|
526
|
+
S(out);
|
527
|
+
S(frames);
|
528
|
+
S(aggregate);
|
529
|
+
#undef S
|
494
530
|
|
495
531
|
gc_hook = Data_Wrap_Struct(rb_cObject, stackprof_gc_mark, NULL, NULL);
|
496
532
|
rb_global_variable(&gc_hook);
|
data/lib/stackprof/report.rb
CHANGED
@@ -79,6 +79,71 @@ module StackProf
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
def print_flamegraph(f=STDOUT, skip_common=true)
|
83
|
+
raise "profile does not include raw samples" unless raw = data[:raw]
|
84
|
+
|
85
|
+
stacks = []
|
86
|
+
max_y = 0
|
87
|
+
while len = raw.shift
|
88
|
+
max_y = len if len > max_y
|
89
|
+
stacks << raw.slice!(0, len+1)
|
90
|
+
end
|
91
|
+
max_x = stacks.inject(0){ |sum, (*stack, weight)| sum + weight }
|
92
|
+
|
93
|
+
f.puts 'flamegraph(['
|
94
|
+
max_y.times do |y|
|
95
|
+
row_prev = nil
|
96
|
+
row_width = 0
|
97
|
+
x = 0
|
98
|
+
|
99
|
+
stacks.each do |*stack, weight|
|
100
|
+
cell = stack[y]
|
101
|
+
|
102
|
+
if cell.nil?
|
103
|
+
if row_prev
|
104
|
+
flamegraph_row(x - row_width, y, row_width, row_prev)
|
105
|
+
end
|
106
|
+
|
107
|
+
row_prev = nil
|
108
|
+
x += weight
|
109
|
+
next
|
110
|
+
end
|
111
|
+
|
112
|
+
if row_prev.nil? # start new row with this cell
|
113
|
+
row_width = weight
|
114
|
+
row_prev = cell
|
115
|
+
x += weight
|
116
|
+
|
117
|
+
elsif row_prev == cell # grow current row along x-axis
|
118
|
+
row_width += weight
|
119
|
+
x += weight
|
120
|
+
|
121
|
+
else # end current row and start new row
|
122
|
+
flamegraph_row(x - row_width, y, row_width, row_prev)
|
123
|
+
x += weight
|
124
|
+
row_prev = cell
|
125
|
+
row_width = weight
|
126
|
+
end
|
127
|
+
|
128
|
+
row_prev = cell
|
129
|
+
end
|
130
|
+
|
131
|
+
if row_prev
|
132
|
+
next if skip_common && row_width == max_x
|
133
|
+
|
134
|
+
flamegraph_row(x - row_width, y, row_width, row_prev)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
f.puts '])'
|
138
|
+
end
|
139
|
+
|
140
|
+
def flamegraph_row(x, y, weight, addr)
|
141
|
+
frame = frames[addr]
|
142
|
+
print ',' if @rows_started
|
143
|
+
@rows_started = true
|
144
|
+
f.puts %{{"x":#{x},"y":#{y},"width":#{weight},"frame_id":#{addr},"frame":#{frame[:name].dump},"file":#{frame[:file].dump}}}
|
145
|
+
end
|
146
|
+
|
82
147
|
def print_graphviz(filter = nil, f = STDOUT)
|
83
148
|
if filter
|
84
149
|
mark_stack = []
|
data/stackprof.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'stackprof'
|
3
|
-
s.version = '0.2.
|
3
|
+
s.version = '0.2.4'
|
4
4
|
s.homepage = 'http://github.com/tmm1/stackprof'
|
5
5
|
|
6
6
|
s.authors = 'Aman Gupta'
|
@@ -11,14 +11,14 @@ Gem::Specification.new do |s|
|
|
11
11
|
|
12
12
|
s.bindir = 'bin'
|
13
13
|
s.executables << 'stackprof'
|
14
|
-
s.executables << 'stackprof-flamegraph'
|
15
|
-
s.executables << 'stackprof-gprof2dot'
|
14
|
+
s.executables << 'stackprof-flamegraph.pl'
|
15
|
+
s.executables << 'stackprof-gprof2dot.py'
|
16
16
|
|
17
17
|
s.summary = 'sampling callstack-profiler for ruby 2.1+'
|
18
18
|
s.description = 'stackprof is a fast sampling profiler for ruby code, with cpu, wallclock and object allocation samplers.'
|
19
19
|
|
20
20
|
s.license = 'MIT'
|
21
21
|
|
22
|
-
s.add_development_dependency 'rake-compiler'
|
23
|
-
s.add_development_dependency 'mocha'
|
22
|
+
s.add_development_dependency 'rake-compiler', '~> 0.9'
|
23
|
+
s.add_development_dependency 'mocha', '~> 0.14'
|
24
24
|
end
|
metadata
CHANGED
@@ -1,50 +1,50 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackprof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Gupta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '0.9'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
26
|
+
version: '0.9'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mocha
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
33
|
+
version: '0.14'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
40
|
+
version: '0.14'
|
41
41
|
description: stackprof is a fast sampling profiler for ruby code, with cpu, wallclock
|
42
42
|
and object allocation samplers.
|
43
43
|
email: aman@tmm1.net
|
44
44
|
executables:
|
45
45
|
- stackprof
|
46
|
-
- stackprof-flamegraph
|
47
|
-
- stackprof-gprof2dot
|
46
|
+
- stackprof-flamegraph.pl
|
47
|
+
- stackprof-gprof2dot.py
|
48
48
|
extensions:
|
49
49
|
- ext/extconf.rb
|
50
50
|
extra_rdoc_files: []
|
@@ -55,8 +55,8 @@ files:
|
|
55
55
|
- README.md
|
56
56
|
- Rakefile
|
57
57
|
- bin/stackprof
|
58
|
-
- bin/stackprof-flamegraph
|
59
|
-
- bin/stackprof-gprof2dot
|
58
|
+
- bin/stackprof-flamegraph.pl
|
59
|
+
- bin/stackprof-gprof2dot.py
|
60
60
|
- ext/extconf.rb
|
61
61
|
- ext/stackprof.c
|
62
62
|
- lib/stackprof/middleware.rb
|