Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add support for SystemTap profile script output, add process name prefix #4

Merged
merged 3 commits into from

2 participants

Alain Kalker David Pacheco
Alain Kalker

This adds a command-line tool and reader for dealing with output of SystemTap profiling scripts.

To capture data using SystemTap, you can use:

# stap -e "global ubt; \
           probe timer.profile { ubt[execname(), sprint_ubacktrace()] += 1 }; \
           probe timer.s(30) { exit() }" \
       -o stap.out
$ cmd/stackcollapse-stap < stap.out > collapsed.out

Please see commit message for dealing with stap warnings about missing unwind data.

ackalker added some commits
Alain Kalker ackalker lib/input-perf.js: Add process name prefix
Add process name as a prefix to each stack frame. This is
useful to better distinguish between different processes calling
functions in the same shared library:

    bar (in process 'foo')

becomes:

    foo`bar

This brings the output of this reader more in line with the others.
c7b7437
Alain Kalker ackalker Add support for SystemTap profile script output
Add a command-line tool and reader for dealing with output of
SystemTap profiling scripts.

To capture data using SystemTap, you can use:

$ stap -e "global ubt; \
           probe timer.profile { ubt[execname(), sprint_ubacktrace()] += 1 }; \
           probe timer.s(30) { exit() }" \
       -o stap.out
$ cmd/stackcollapse-stap < stap.out > collapsed.out

If stap warns about missing unwind data for a module, and stap
suggests adding '-d /lib/libquux.so', which you know to be a shared
library used by the 'foo' binary, add the following to the stap
command above:

       -d /path/to/foo $(ldd /path/to/foo | awk 'NF==4 { print "-d", $3 }')

You can add other stap expressions as keys to the ubt[] array, as
long as sprint_ubacktrace() remains the last key in the list.
Values of the other expressions will be added as prefixes to the
frames in the collapsed stacks, for example:

ubt[execname(), sprint_ubacktrace()]

and running some binary 'foo' which calls functions 'bar' and 'baz',
will output foo's stack frames as:

foo`bar,foo`baz

in the collapsed stacks output.
0d4c65a
Alain Kalker ackalker Add cmd/stackcollapse-perf and cmd/stackcollapse-stap to package.json 9ac49ca
David Pacheco davepacheco merged commit be2d8bf into from
Alain Kalker

Thanks for merging :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 20, 2012
  1. Alain Kalker

    lib/input-perf.js: Add process name prefix

    ackalker authored
    Add process name as a prefix to each stack frame. This is
    useful to better distinguish between different processes calling
    functions in the same shared library:
    
        bar (in process 'foo')
    
    becomes:
    
        foo`bar
    
    This brings the output of this reader more in line with the others.
  2. Alain Kalker

    Add support for SystemTap profile script output

    ackalker authored
    Add a command-line tool and reader for dealing with output of
    SystemTap profiling scripts.
    
    To capture data using SystemTap, you can use:
    
    $ stap -e "global ubt; \
               probe timer.profile { ubt[execname(), sprint_ubacktrace()] += 1 }; \
               probe timer.s(30) { exit() }" \
           -o stap.out
    $ cmd/stackcollapse-stap < stap.out > collapsed.out
    
    If stap warns about missing unwind data for a module, and stap
    suggests adding '-d /lib/libquux.so', which you know to be a shared
    library used by the 'foo' binary, add the following to the stap
    command above:
    
           -d /path/to/foo $(ldd /path/to/foo | awk 'NF==4 { print "-d", $3 }')
    
    You can add other stap expressions as keys to the ubt[] array, as
    long as sprint_ubacktrace() remains the last key in the list.
    Values of the other expressions will be added as prefixes to the
    frames in the collapsed stacks, for example:
    
    ubt[execname(), sprint_ubacktrace()]
    
    and running some binary 'foo' which calls functions 'bar' and 'baz',
    will output foo's stack frames as:
    
    foo`bar,foo`baz
    
    in the collapsed stacks output.
  3. Alain Kalker
This page is out of date. Refresh to see the latest.
20 cmd/stackcollapse-stap
View
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+
+/*
+ * cmd/stackcollapse: emit collapsed stack traces from stap output
+ */
+
+var mod_bunyan = require('bunyan');
+var mod_stackvis = require('../lib/stackvis');
+
+var log = new mod_bunyan({
+ 'name': 'stackcollapse',
+ 'stream': process.stderr
+});
+
+var reader = mod_stackvis.readerLookup('stap');
+var writer = mod_stackvis.writerLookup('collapsed');
+
+mod_stackvis.pipeStacks(log, process.stdin, reader, writer, process.stdout,
+ function () {});
+process.stdin.resume();
14 lib/input-perf.js
View
@@ -25,6 +25,7 @@ function PerfStreamReader(input, log)
{
this.dsr_log = log;
this.dsr_linenum = 0;
+ this.dsr_prefix = '';
this.dsr_stack = [];
this.dsr_carrier = mod_carrier.carry(input);
this.dsr_carrier.on('line', this.onLine.bind(this));
@@ -43,9 +44,12 @@ PerfStreamReader.prototype.onLine = function (line)
if (/^#/.exec(line))
return;
- /* Skip summary lines */
- if (/^\S+/.exec(line))
+ /* Get process name from summary line, to use as prefix */
+ var match = /(^\w+)\s+/.exec(line);
+ if (match) {
+ this.dsr_prefix = match[1];
return;
+ }
/*
* In general, lines may have leading or trailing whitespace and the
@@ -68,6 +72,7 @@ PerfStreamReader.prototype.onLine = function (line)
}
this.emit('stack', this.dsr_stack, 1);
+ this.dsr_prefix = '';
this.dsr_stack = [];
return;
}
@@ -90,6 +95,11 @@ PerfStreamReader.prototype.onLine = function (line)
return;
}
+ /* Add prefix */
+ if (this.dsr_prefix.length > 0) {
+ frame = this.dsr_prefix + '`' + frame;
+ }
+
this.dsr_stack.unshift(frame);
};
145 lib/input-stap.js
View
@@ -0,0 +1,145 @@
+/*
+ * lib/input-stap.js: reads output from a stap profiling script, which emits
+ * stanzas that look like this:
+ *
+ * ubt["bar+0x32 [foo]
+ * foo+0x57 [foo]
+ * main+0x48 [foo]
+ * __libc_start_main+0xed [libc-2.15.so]
+ * _start+0x29 [foo]"]=0x77
+ *
+ * You can generate such output with:
+ *
+ * # stap \
+ * -e "global ubt; \
+ * probe timer.profile { ubt[sprint_ubacktrace()] += 1 }; \
+ * probe timer.s(30) { exit() }" \
+ * -o stap.out
+ *
+ * If stap warns about missing unwind data for a module, and stap
+ * suggests adding '-d /lib/libquux.so', which you know to be a shared
+ * library used by the 'foo' binary, add the following to the above
+ * command:
+ *
+ * -d /path/to/foo $(ldd /path/to/foo | awk 'NF==4 { print "-d", $3 }')
+ *
+ * to deal with all warnings related to shared libraries used by 'foo',
+ * all at once.
+ */
+
+var mod_util = require('util');
+var mod_events = require('events');
+
+var mod_carrier = require('carrier');
+
+exports.reader = PerfStreamReader;
+
+function PerfStreamReader(input, log)
+{
+ this.dsr_log = log;
+ this.dsr_linenum = 0;
+ this.dsr_addingframes = false;
+ this.dsr_prefixes = [];
+ this.dsr_stack = [];
+ this.dsr_carrier = mod_carrier.carry(input);
+ this.dsr_carrier.on('line', this.onLine.bind(this));
+ this.dsr_carrier.on('end', this.onEnd.bind(this));
+
+ mod_events.EventEmitter.call(this);
+}
+
+mod_util.inherits(PerfStreamReader, mod_events.EventEmitter);
+
+PerfStreamReader.prototype.onLine = function (line)
+{
+ ++this.dsr_linenum;
+
+ var match;
+ if (!this.dsr_addingframes) {
+ /* Skip array name */
+ line.replace(/^\w+\[/, '');
+
+ /* Find and add prefixes */
+ while (true) {
+ /* JSSTYLED */
+ match = /(?:"([^"]*)",)(.*$)/.exec(line);
+ if (!match)
+ break;
+ this.dsr_prefixes.push(match[1]);
+ line = match[2];
+ }
+
+ /* Find first frame */
+ /* JSSTYLED */
+ match = /(?:"(.*$))/.exec(line);
+ if (!match) {
+ this.dsr_log.warn('line ' + this.dsr_linenum +
+ ': no first frame found');
+ return;
+ }
+ line = match[1];
+ this.dsr_addingframes = true;
+ }
+
+ /* Look for count */
+ var count;
+ /* JSSTYLED */
+ match = /(^.*)"\]=(\w+$)/.exec(line);
+ if (match) {
+ line = match[1];
+ count = parseInt(match[2], 16);
+ this.dsr_addingframes = false;
+ }
+
+ /*
+ * In general, frames have one of the following sets of components:
+ *
+ * address
+ * address [module+offset]
+ * function+offset [module]
+ *
+ * We try to avoid assuming too much about the form in order to support
+ * various annotations provided by ustack helpers.
+ */
+ var frame = line;
+ frame = frame.replace(/ \[(\S+)\]$/, '');
+ /* JSSTYLED */
+ frame = frame.replace(/\+.*/, '');
+
+ /*
+ * Remove both function and template parameters from demangled C++
+ * frames, but skip the first two characters because they're used by the
+ * Node.js ustack helper as separators.
+ */
+ /* JSSTYLED */
+ frame = frame.replace(/(..)[(<].*/, '$1');
+
+ if (line.length === 0) {
+ if (this.dsr_stack.length !== 0)
+ this.dsr_log.warn('line ' + this.dsr_linenum +
+ ': unexpected blank line');
+ return;
+ }
+
+ /* Add prefixes */
+ if (this.dsr_prefixes.length > 0) {
+ frame = this.dsr_prefixes.join('`') + '`' + frame;
+ }
+
+ this.dsr_stack.unshift(frame);
+
+ if (!this.dsr_addingframes) {
+ this.emit('stack', this.dsr_stack, count);
+ this.dsr_prefixes = [];
+ this.dsr_stack = [];
+ }
+};
+
+PerfStreamReader.prototype.onEnd = function ()
+{
+ if (this.dsr_stack.length !== 0)
+ this.dsr_log.warn('line ' + this.dsr_linenum +
+ ': unexpected end of stream');
+
+ this.emit('end');
+};
2  package.json
View
@@ -6,6 +6,8 @@
"bin": {
"flamegraph": "./cmd/flamegraph",
"stackcollapse": "./cmd/stackcollapse",
+ "stackcollapse-perf": "./cmd/stackcollapse-perf",
+ "stackcollapse-stap": "./cmd/stackcollapse-stap",
"stackvis": "./cmd/stackvis"
},
Something went wrong with that request. Please try again.