Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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