Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add support for Linux `perf record` output

Add a command-line tool and reader for dealing with perf.data
Based on the original FlameGraph Perl script (with several fixes)
and lib/input-dtrace.

To capture data using perf, you can use:

$ perf record -F 997 -g ./myprogram
$ perf script | cmd/stackcollapse-perf > collapsed.out
  • Loading branch information...
commit d2b85e3b17e899e2b1f9aea5cfe54d55f9b92a53 1 parent 3e5dc09
@ackalker ackalker authored davepacheco committed
Showing with 123 additions and 0 deletions.
  1. +20 −0 cmd/stackcollapse-perf
  2. +103 −0 lib/input-perf.js
View
20 cmd/stackcollapse-perf
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+
+/*
+ * cmd/stackcollapse: emit collapsed stack traces from perf 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('perf');
+var writer = mod_stackvis.writerLookup('collapsed');
+
+mod_stackvis.pipeStacks(log, process.stdin, reader, writer, process.stdout,
+ function () {});
+process.stdin.resume();
View
103 lib/input-perf.js
@@ -0,0 +1,103 @@
+/*
+ * lib/input-perf.js: reads output from a perf profiling script, which emits
+ * stanzas that look like this:
+ *
+ * foo 15150 10062.190770: cycles:
+ * 400675 bar (/tmp/stackvis/foo)
+ * 400603 foo (/tmp/stackvis/foo)
+ * 40071f main (/tmp/stackvis/foo)
+ * 7fb3db1bf76d __libc_start_main (/lib/x86_64-linux-gnu/libc-2.15.so)
+ *
+ * You can generate such output with:
+ *
+ * # perf record -F 997 -g ./myprogram
+ * # perf script > perf.out
+ */
+
+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_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;
+
+ /* Lines beginning with # are always ignored. */
+ if (/^#/.exec(line))
+ return;
+
+ /* Skip summary lines */
+ if (/^\S+/.exec(line))
+ return;
+
+ /*
+ * In general, lines may have leading or trailing whitespace and the
+ * following components:
+ *
+ * loc function (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+/, '');
+ frame = frame.replace(/\s+$/, '');
+
+ if (frame.length === 0) {
+ if (this.dsr_stack.length === 0) {
+ this.dsr_log.warn('line ' + this.dsr_linenum +
+ ': found empty line with no stack');
+ return;
+ }
+
+ this.emit('stack', this.dsr_stack, 1);
+ this.dsr_stack = [];
+ return;
+ }
+
+ frame = frame.replace(/^\w+ /, '');
+ frame = frame.replace(/ \(\S+\)$/, '');
+
+ /*
+ * 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;
+ }
+
+ this.dsr_stack.unshift(frame);
+};
+
+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');
+};
Please sign in to comment.
Something went wrong with that request. Please try again.