mirror of
https://github.com/jlfwong/speedscope.git
synced 2024-11-27 01:53:17 +03:00
24b60dfd4a
Implements import from the [callgrind format](https://www.valgrind.org/docs/manual/cl-format.html). This comes with a big caveat that the call graph information contained with callgrind formatted files don't uniquely define a flamegraph, so the generated flamegraph is a best-effort guess. Here's the comment from the top of the main file for the callgrind importer with an examplataion: ``` // https://www.valgrind.org/docs/manual/cl-format.html // // Larger example files can be found by searching on github: // https://github.com/search?q=cfn%3D&type=code // // Converting callgrind files into flamegraphs is challenging because callgrind // formatted profiles contain call graphs with weighted nodes and edges, and // such a weighted call graph does not uniquely define a flamegraph. // // Consider a program that looks like this: // // // example.js // function backup(read) { // if (read) { // read() // } else { // write() // } // } // // function start() { // backup(true) // } // // function end() { // backup(false) // } // // start() // end() // // Profiling this program might result in a profile that looks like the // following flame graph defined in Brendan Gregg's plaintext format: // // start;backup;read 4 // end;backup;write 4 // // When we convert this execution into a call-graph, we get the following: // // +------------------+ +---------------+ // | start (self: 0) | | end (self: 0) | // +------------------+ +---------------| // \ / // (total: 4) \ / (total: 4) // v v // +------------------+ // | backup (self: 0) | // +------------------+ // / \ // (total: 4) / \ (total: 4) // v v // +----------------+ +-----------------+ // | read (self: 4) | | write (self: 4) | // +----------------+ +-----------------+ // // In the process of the conversion, we've lost information about the ratio of // time spent in read v.s. write in the start call v.s. the end call. The // following flame graph would yield the exact same call-graph, and therefore // the exact sample call-grind formatted profile: // // start;backup;read 3 // start;backup;write 1 // end;backup;read 1 // end;backup;write 3 // // This is unfortunate, since it means we can't produce a flamegraph that isn't // potentially lying about the what the actual execution behavior was. To // produce a flamegraph at all from the call graph representation, we have to // decide how much weight each sub-call should have. Given that we know the // total weight of each node, we'll make the incorrect assumption that every // invocation of a function will have the average distribution of costs among // the sub-function invocations. In the example given, this means we assume that // every invocation of backup() is assumed to spend half its time in read() and // half its time in write(). // // So the flamegraph we'll produce from the given call-graph will actually be: // // start;backup;read 2 // start;backup;write 2 // end;backup;read 2 // end;backup;write 2 // // A particularly bad consequence is that the resulting flamegraph will suggest // that there was at some point a call stack that looked like // strat;backup;write, even though that never happened in the real program // execution. ``` Fixes #18 |
||
---|---|---|
.. | ||
callgrind.example.log | ||
callgrind.multiple-event-types.log | ||
callgrind.name-compression.log |