Make it possible to create flamegraph compatible stack files.
authorMilian Wolff <mail@milianw.de>
Thu, 20 Aug 2015 21:09:30 +0000 (23:09 +0200)
committerMilian Wolff <mail@milianw.de>
Thu, 20 Aug 2015 21:11:00 +0000 (23:11 +0200)
heaptrack_print gained the following new feature:

 -F [ --print-flamegraph ] arg
Path to output file where a flame-graph compatible stack file will
be written to.
To visualize the resulting file, use flamegraph.pl from
https://github.com/brendangregg/FlameGraph:
    heaptrack_print heaptrack.someapp.PID.gz -F stacks.txt
    # optionally pass --reverse to flamegraph.pl
    flamegraph.pl --title "heaptrack: allocations" --colors mem \
      --countname allocations < stacks.txt > heaptrack.someapp.PID.svg
    [firefox|chromium] heaptrack.someapp.PID.svg

heaptrack_print.cpp

index d04091b..ed1514b 100644 (file)
@@ -164,7 +164,7 @@ struct Printer final : public AccumulatedTraceData
         printIp(findIp(ip), out, indent);
     }
 
-    void printIp(const InstructionPointer& ip, ostream& out, const size_t indent = 0) const
+    void printIp(const InstructionPointer& ip, ostream& out, const size_t indent = 0, bool flameGraph = false) const
     {
         printIndent(out, indent);
 
@@ -174,6 +174,17 @@ struct Printer final : public AccumulatedTraceData
             out << "0x" << hex << ip.instructionPointer << dec;
         }
 
+        if (flameGraph) {
+            // only print the file name but nothing else
+            if (ip.fileIndex) {
+                const auto& file = stringify(ip.fileIndex);
+                auto idx = file.find_last_of('/') + 1;
+                out << " (" << file.substr(idx) << ")";
+            }
+            out << ';';
+            return;
+        }
+
         out << '\n';
         printIndent(out, indent + 1);
 
@@ -216,6 +227,25 @@ struct Printer final : public AccumulatedTraceData
         };
     }
 
+    /**
+     * recursive top-down printer in the format
+     *
+     * func1;func2 (file);func2 (file);
+     */
+    void printFlamegraph(TraceNode node, ostream& out) const
+    {
+        if (!node.ipIndex) {
+            return;
+        }
+
+        const auto& ip = findIp(node.ipIndex);
+
+        if (!isStopIndex(ip.functionIndex)) {
+            printFlamegraph(findTrace(node.parentIndex), out);
+        }
+        printIp(ip, out, 0, true);
+    }
+
     template<typename T, typename LabelPrinter, typename SubLabelPrinter>
     void printAllocations(T AllocationData::* member, LabelPrinter label, SubLabelPrinter sublabel)
     {
@@ -458,6 +488,14 @@ int main(int argc, char** argv)
             "Print top overall allocators, ignoring memory frees.")
         ("print-histogram,H", po::value<string>()->default_value(string()),
             "Path to output file where an allocation size histogram will be written to.")
+        ("print-flamegraph,F", po::value<string>()->default_value(string()),
+            "Path to output file where a flame-graph compatible stack file will be written to.\n"
+            "To visualize the resulting file, use flamegraph.pl from https://github.com/brendangregg/FlameGraph:\n"
+            "  heaptrack_print heaptrack.someapp.PID.gz -F stacks.txt\n"
+            "  # optionally pass --reverse to flamegraph.pl\n"
+            "  flamegraph.pl --title \"heaptrack: allocations\" --colors mem \\\n"
+            "    --countname allocations < stacks.txt > heaptrack.someapp.PID.svg\n"
+            "  [firefox|chromium] heaptrack.someapp.PID.svg\n")
         ("print-massif,M", po::value<string>()->default_value(string()),
             "Path to output file where a massif compatible data file will be written to.")
         ("massif-threshold", po::value<double>()->default_value(1.),
@@ -514,6 +552,7 @@ int main(int argc, char** argv)
     data.filterBtFunction = vm["filter-bt-function"].as<string>();
     const string printHistogram = vm["print-histogram"].as<string>();
     data.printHistogram = !printHistogram.empty();
+    const string printFlamegraph = vm["print-flamegraph"].as<string>();
     const string printMassif = vm["print-massif"].as<string>();
     if (!printMassif.empty()) {
         data.massifOut.open(printMassif, ios_base::out);
@@ -609,5 +648,21 @@ int main(int argc, char** argv)
         }
     }
 
+    if (!printFlamegraph.empty()) {
+        ofstream flamegraph(printFlamegraph, ios_base::out);
+        if (!flamegraph.is_open()) {
+            cerr << "Failed to open flamegraph output file \"" << printFlamegraph << "\"." << endl;
+        } else {
+            for (const auto& allocation : data.allocations) {
+                if (!allocation.traceIndex) {
+                    flamegraph << "??";
+                } else {
+                    data.printFlamegraph(data.findTrace(allocation.traceIndex), flamegraph);
+                }
+                flamegraph << ' ' << allocation.allocations << '\n';
+            }
+        }
+    }
+
     return 0;
 }