1bf215546Sopenharmony_ci#!/usr/bin/env python 2bf215546Sopenharmony_ci# 3bf215546Sopenharmony_ci# Copyright 2012 VMware Inc 4bf215546Sopenharmony_ci# Copyright 2008-2009 Jose Fonseca 5bf215546Sopenharmony_ci# 6bf215546Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a copy 7bf215546Sopenharmony_ci# of this software and associated documentation files (the "Software"), to deal 8bf215546Sopenharmony_ci# in the Software without restriction, including without limitation the rights 9bf215546Sopenharmony_ci# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10bf215546Sopenharmony_ci# copies of the Software, and to permit persons to whom the Software is 11bf215546Sopenharmony_ci# furnished to do so, subject to the following conditions: 12bf215546Sopenharmony_ci# 13bf215546Sopenharmony_ci# The above copyright notice and this permission notice shall be included in 14bf215546Sopenharmony_ci# all copies or substantial portions of the Software. 15bf215546Sopenharmony_ci# 16bf215546Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17bf215546Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18bf215546Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19bf215546Sopenharmony_ci# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20bf215546Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21bf215546Sopenharmony_ci# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22bf215546Sopenharmony_ci# THE SOFTWARE. 23bf215546Sopenharmony_ci# 24bf215546Sopenharmony_ci 25bf215546Sopenharmony_ci"""Perf annotate for JIT code. 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ciLinux `perf annotate` does not work with JIT code. This script takes the data 28bf215546Sopenharmony_ciproduced by `perf script` command, plus the diassemblies outputed by gallivm 29bf215546Sopenharmony_ciinto /tmp/perf-XXXXX.map.asm and produces output similar to `perf annotate`. 30bf215546Sopenharmony_ci 31bf215546Sopenharmony_ciSee docs/llvmpipe.rst for usage instructions. 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ciThe `perf script` output parser was derived from the gprof2dot.py script. 34bf215546Sopenharmony_ci""" 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_ci 37bf215546Sopenharmony_ciimport sys 38bf215546Sopenharmony_ciimport os.path 39bf215546Sopenharmony_ciimport re 40bf215546Sopenharmony_ciimport optparse 41bf215546Sopenharmony_ciimport subprocess 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_ciclass Parser: 45bf215546Sopenharmony_ci """Parser interface.""" 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_ci def __init__(self): 48bf215546Sopenharmony_ci pass 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_ci def parse(self): 51bf215546Sopenharmony_ci raise NotImplementedError 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_ci 54bf215546Sopenharmony_ciclass LineParser(Parser): 55bf215546Sopenharmony_ci """Base class for parsers that read line-based formats.""" 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci def __init__(self, file): 58bf215546Sopenharmony_ci Parser.__init__(self) 59bf215546Sopenharmony_ci self._file = file 60bf215546Sopenharmony_ci self.__line = None 61bf215546Sopenharmony_ci self.__eof = False 62bf215546Sopenharmony_ci self.line_no = 0 63bf215546Sopenharmony_ci 64bf215546Sopenharmony_ci def readline(self): 65bf215546Sopenharmony_ci line = self._file.readline() 66bf215546Sopenharmony_ci if not line: 67bf215546Sopenharmony_ci self.__line = '' 68bf215546Sopenharmony_ci self.__eof = True 69bf215546Sopenharmony_ci else: 70bf215546Sopenharmony_ci self.line_no += 1 71bf215546Sopenharmony_ci self.__line = line.rstrip('\r\n') 72bf215546Sopenharmony_ci 73bf215546Sopenharmony_ci def lookahead(self): 74bf215546Sopenharmony_ci assert self.__line is not None 75bf215546Sopenharmony_ci return self.__line 76bf215546Sopenharmony_ci 77bf215546Sopenharmony_ci def consume(self): 78bf215546Sopenharmony_ci assert self.__line is not None 79bf215546Sopenharmony_ci line = self.__line 80bf215546Sopenharmony_ci self.readline() 81bf215546Sopenharmony_ci return line 82bf215546Sopenharmony_ci 83bf215546Sopenharmony_ci def eof(self): 84bf215546Sopenharmony_ci assert self.__line is not None 85bf215546Sopenharmony_ci return self.__eof 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_ci 88bf215546Sopenharmony_cimapFile = None 89bf215546Sopenharmony_ci 90bf215546Sopenharmony_cidef lookupMap(filename, matchSymbol): 91bf215546Sopenharmony_ci global mapFile 92bf215546Sopenharmony_ci mapFile = filename 93bf215546Sopenharmony_ci stream = open(filename, 'rt') 94bf215546Sopenharmony_ci for line in stream: 95bf215546Sopenharmony_ci start, length, symbol = line.split() 96bf215546Sopenharmony_ci 97bf215546Sopenharmony_ci start = int(start, 16) 98bf215546Sopenharmony_ci length = int(length,16) 99bf215546Sopenharmony_ci 100bf215546Sopenharmony_ci if symbol == matchSymbol: 101bf215546Sopenharmony_ci return start 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_ci return None 104bf215546Sopenharmony_ci 105bf215546Sopenharmony_cidef lookupAsm(filename, desiredFunction): 106bf215546Sopenharmony_ci stream = open(filename + '.asm', 'rt') 107bf215546Sopenharmony_ci while stream.readline() != desiredFunction + ':\n': 108bf215546Sopenharmony_ci pass 109bf215546Sopenharmony_ci 110bf215546Sopenharmony_ci asm = [] 111bf215546Sopenharmony_ci line = stream.readline().strip() 112bf215546Sopenharmony_ci while line: 113bf215546Sopenharmony_ci addr, instr = line.split(':', 1) 114bf215546Sopenharmony_ci addr = int(addr) 115bf215546Sopenharmony_ci asm.append((addr, instr)) 116bf215546Sopenharmony_ci line = stream.readline().strip() 117bf215546Sopenharmony_ci 118bf215546Sopenharmony_ci return asm 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_ci 122bf215546Sopenharmony_cisamples = {} 123bf215546Sopenharmony_ci 124bf215546Sopenharmony_ci 125bf215546Sopenharmony_ciclass PerfParser(LineParser): 126bf215546Sopenharmony_ci """Parser for linux perf callgraph output. 127bf215546Sopenharmony_ci 128bf215546Sopenharmony_ci It expects output generated with 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci perf record -g 131bf215546Sopenharmony_ci perf script 132bf215546Sopenharmony_ci """ 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci def __init__(self, infile, symbol): 135bf215546Sopenharmony_ci LineParser.__init__(self, infile) 136bf215546Sopenharmony_ci self.symbol = symbol 137bf215546Sopenharmony_ci 138bf215546Sopenharmony_ci def readline(self): 139bf215546Sopenharmony_ci # Override LineParser.readline to ignore comment lines 140bf215546Sopenharmony_ci while True: 141bf215546Sopenharmony_ci LineParser.readline(self) 142bf215546Sopenharmony_ci if self.eof() or not self.lookahead().startswith('#'): 143bf215546Sopenharmony_ci break 144bf215546Sopenharmony_ci 145bf215546Sopenharmony_ci def parse(self): 146bf215546Sopenharmony_ci # read lookahead 147bf215546Sopenharmony_ci self.readline() 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci while not self.eof(): 150bf215546Sopenharmony_ci self.parse_event() 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_ci asm = lookupAsm(mapFile, self.symbol) 153bf215546Sopenharmony_ci 154bf215546Sopenharmony_ci addresses = samples.keys() 155bf215546Sopenharmony_ci addresses.sort() 156bf215546Sopenharmony_ci total_samples = 0 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci sys.stdout.write('%s:\n' % self.symbol) 159bf215546Sopenharmony_ci for address, instr in asm: 160bf215546Sopenharmony_ci try: 161bf215546Sopenharmony_ci sample = samples.pop(address) 162bf215546Sopenharmony_ci except KeyError: 163bf215546Sopenharmony_ci sys.stdout.write(6*' ') 164bf215546Sopenharmony_ci else: 165bf215546Sopenharmony_ci sys.stdout.write('%6u' % (sample)) 166bf215546Sopenharmony_ci total_samples += sample 167bf215546Sopenharmony_ci sys.stdout.write('%6u: %s\n' % (address, instr)) 168bf215546Sopenharmony_ci print 'total:', total_samples 169bf215546Sopenharmony_ci assert len(samples) == 0 170bf215546Sopenharmony_ci 171bf215546Sopenharmony_ci sys.exit(0) 172bf215546Sopenharmony_ci 173bf215546Sopenharmony_ci def parse_event(self): 174bf215546Sopenharmony_ci if self.eof(): 175bf215546Sopenharmony_ci return 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci line = self.consume() 178bf215546Sopenharmony_ci assert line 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci callchain = self.parse_callchain() 181bf215546Sopenharmony_ci if not callchain: 182bf215546Sopenharmony_ci return 183bf215546Sopenharmony_ci 184bf215546Sopenharmony_ci def parse_callchain(self): 185bf215546Sopenharmony_ci callchain = [] 186bf215546Sopenharmony_ci while self.lookahead(): 187bf215546Sopenharmony_ci function = self.parse_call(len(callchain) == 0) 188bf215546Sopenharmony_ci if function is None: 189bf215546Sopenharmony_ci break 190bf215546Sopenharmony_ci callchain.append(function) 191bf215546Sopenharmony_ci if self.lookahead() == '': 192bf215546Sopenharmony_ci self.consume() 193bf215546Sopenharmony_ci return callchain 194bf215546Sopenharmony_ci 195bf215546Sopenharmony_ci call_re = re.compile(r'^\s+(?P<address>[0-9a-fA-F]+)\s+(?P<symbol>.*)\s+\((?P<module>[^)]*)\)$') 196bf215546Sopenharmony_ci 197bf215546Sopenharmony_ci def parse_call(self, first): 198bf215546Sopenharmony_ci line = self.consume() 199bf215546Sopenharmony_ci mo = self.call_re.match(line) 200bf215546Sopenharmony_ci assert mo 201bf215546Sopenharmony_ci if not mo: 202bf215546Sopenharmony_ci return None 203bf215546Sopenharmony_ci 204bf215546Sopenharmony_ci if not first: 205bf215546Sopenharmony_ci return None 206bf215546Sopenharmony_ci 207bf215546Sopenharmony_ci function_name = mo.group('symbol') 208bf215546Sopenharmony_ci if not function_name: 209bf215546Sopenharmony_ci function_name = mo.group('address') 210bf215546Sopenharmony_ci 211bf215546Sopenharmony_ci module = mo.group('module') 212bf215546Sopenharmony_ci 213bf215546Sopenharmony_ci function_id = function_name + ':' + module 214bf215546Sopenharmony_ci 215bf215546Sopenharmony_ci address = mo.group('address') 216bf215546Sopenharmony_ci address = int(address, 16) 217bf215546Sopenharmony_ci 218bf215546Sopenharmony_ci if function_name != self.symbol: 219bf215546Sopenharmony_ci return None 220bf215546Sopenharmony_ci 221bf215546Sopenharmony_ci start_address = lookupMap(module, function_name) 222bf215546Sopenharmony_ci address -= start_address 223bf215546Sopenharmony_ci 224bf215546Sopenharmony_ci #print function_name, module, address 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci samples[address] = samples.get(address, 0) + 1 227bf215546Sopenharmony_ci 228bf215546Sopenharmony_ci return True 229bf215546Sopenharmony_ci 230bf215546Sopenharmony_ci 231bf215546Sopenharmony_cidef main(): 232bf215546Sopenharmony_ci """Main program.""" 233bf215546Sopenharmony_ci 234bf215546Sopenharmony_ci optparser = optparse.OptionParser( 235bf215546Sopenharmony_ci usage="\n\t%prog [options] symbol_name") 236bf215546Sopenharmony_ci (options, args) = optparser.parse_args(sys.argv[1:]) 237bf215546Sopenharmony_ci if len(args) != 1: 238bf215546Sopenharmony_ci optparser.error('wrong number of arguments') 239bf215546Sopenharmony_ci 240bf215546Sopenharmony_ci symbol = args[0] 241bf215546Sopenharmony_ci 242bf215546Sopenharmony_ci p = subprocess.Popen(['perf', 'script'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 243bf215546Sopenharmony_ci parser = PerfParser(p.stdout, symbol) 244bf215546Sopenharmony_ci parser.parse() 245bf215546Sopenharmony_ci 246bf215546Sopenharmony_ci 247bf215546Sopenharmony_ciif __name__ == '__main__': 248bf215546Sopenharmony_ci main() 249bf215546Sopenharmony_ci 250bf215546Sopenharmony_ci 251bf215546Sopenharmony_ci# vim: set sw=4 et: 252