File indexing completed on 2024-04-21 16:27:17

0001 #
0002 # A script to convert perf data files into the heaptrack format.
0003 #
0004 # perf script -s perf-heaptrack.py -i perf.data | gzip > perf.heaptrack.gz
0005 # heaptrack_gui perf.heaptrack.gz
0006 #
0007 # This is mostly a proof-of-concept to show how this could be used
0008 # in the future to visualize perf results.
0009 #
0010 # SPDX-FileCopyrightText: 2016-2017 Milian Wolff <mail@milianw.de>
0011 #
0012 # SPDX-License-Identifier: LGPL-2.1-or-later
0013 #
0014 
0015 import os
0016 import sys
0017 import json
0018 import subprocess
0019 from collections import defaultdict
0020 
0021 sys.path.append(os.environ["PERF_EXEC_PATH"] + "/scripts/python/Perf-Trace-Util/lib/Perf/Trace")
0022 
0023 from perf_trace_context import *
0024 
0025 try:
0026     from subprocess import DEVNULL # py3k
0027 except ImportError:
0028     import os
0029     DEVNULL = open(os.devnull, 'wb')
0030 
0031 class FileInfo:
0032     def __init__(self, file, line):
0033         self.file = file
0034         self.line = line
0035 
0036 class InternMap:
0037     def __init__(self):
0038         self.map = dict()
0039         self.map[""] = 0;
0040 
0041     def add(self, string):
0042         nextIndex = len(self.map)
0043         index = self.map.get(string, nextIndex)
0044         if index == nextIndex:
0045             print("s %s" % string)
0046             self.map[string] = nextIndex
0047             return nextIndex
0048         return index
0049 
0050 strings = InternMap()
0051 
0052 def addr2line(dsoName, address):
0053     try:
0054         output = subprocess.check_output(["addr2line", "-e", dsoName, hex(address)], stderr=DEVNULL).split(':')
0055         file = output[0]
0056         if not file or file == "??":
0057             raise "error"
0058         line = int(output[1])
0059         return FileInfo(file, line)
0060     except:
0061         return FileInfo("???", 0)
0062 
0063 nextIpIndex = 1
0064 class InstructionPointerMap:
0065     def __init__(self):
0066         self.map = dict()
0067 
0068     def add(self, ip, dsoName, name, sym):
0069         ipEntry = self.map.get(ip, None)
0070         if ipEntry == None:
0071             global nextIpIndex
0072             ipEntry = nextIpIndex
0073             nextIpIndex += 1
0074             fileInfo = addr2line(dsoName, ip)
0075             print("i %x %x %x %x %d" % (ip, strings.add(dsoName), strings.add(name), strings.add(fileInfo.file), fileInfo.line))
0076             self.map[ip] = ipEntry
0077         return ipEntry
0078 ipMap = InstructionPointerMap()
0079 
0080 nextTraceIndex = 1
0081 class TraceEntry:
0082     def __init__(self, traceIndex):
0083         self.index = traceIndex
0084         self.children = dict()
0085 
0086     def add(self, ipIndex):
0087         child = self.children.get(ipIndex, None)
0088         if child == None:
0089             global nextTraceIndex
0090             child = TraceEntry(nextTraceIndex)
0091             nextTraceIndex += 1
0092             print("t %x %x" % (ipIndex, self.index))
0093             self.children[ipIndex] = child
0094         return child
0095 
0096 traceRoot = TraceEntry(0)
0097 
0098 nextSampleIndex = 0
0099 class SampleMap:
0100     def __init__(self):
0101         self.map = dict()
0102 
0103     def add(self, traceIndex):
0104         sampleIndex = self.map.get(traceIndex, None)
0105         if sampleIndex == None:
0106             global nextSampleIndex
0107             sampleIndex = nextSampleIndex
0108             nextSampleIndex += 1
0109             print("a 1 %x" % (traceIndex))
0110             self.map[traceIndex] = sampleIndex
0111         return sampleIndex
0112 samples = SampleMap()
0113 
0114 # write the callgrind file format to stdout
0115 def trace_begin():
0116     print("v 10000")
0117     print("# perf.data converted using perf-heaptrack.py")
0118 
0119 # this function gets called for every sample
0120 # the event dict contains information about the symbol, time, callchain, ...
0121 # print it out to peek inside!
0122 startTime = 0
0123 lastTime = 0
0124 finalTime = 0
0125 
0126 def trace_end():
0127     print("c %x" % (finalTime + 1))
0128 
0129 def process_event(event):
0130     global startTime, lastTime, finalTime
0131     if startTime == 0:
0132         startTime = event["sample"]["time"]
0133     elapsed = (event["sample"]["time"] - startTime) / 10000000
0134     if (lastTime + 1) <= elapsed:
0135         print("c %x" % elapsed)
0136         lastTime = elapsed
0137     finalTime = elapsed
0138 
0139     global ipMap, traceRoot, samples
0140     trace = traceRoot
0141     if not event["callchain"]:
0142         dsoName = event.get("dso", "???")
0143         name = event.get("symbol", "???")
0144         ipIndex = ipMap.add(event["sample"]["ip"], dsoName, name, None)
0145         traceIndex = trace.add(ipIndex).index
0146     else:
0147         # add a function for every frame in the callchain
0148         for item in reversed(event["callchain"]):
0149             dsoName = item.get("dso", "???")
0150             name = "???"
0151             if "sym" in item:
0152                 name = item["sym"]["name"]
0153             ipIndex = ipMap.add(item["ip"], dsoName, name, item.get("sym", None))
0154             trace = trace.add(ipIndex)
0155             traceIndex = trace.index
0156     sampleIndex = samples.add(traceIndex)
0157     print("+ %x" % sampleIndex)