stackprof 0.2.3 → 0.2.4
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.
- 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
|