o3-pipeview.py revision 9188:b91e4bec7a76
1#! /usr/bin/env python
2
3# Copyright (c) 2011 ARM Limited
4# All rights reserved
5#
6# The license below extends only to copyright in the software and shall
7# not be construed as granting a license to any other intellectual
8# property including but not limited to intellectual property relating
9# to a hardware implementation of the functionality of the software
10# licensed hereunder.  You may use the software subject to the license
11# terms below provided that you ensure that this notice is replicated
12# unmodified and in its entirety in all distributions of the software,
13# modified or unmodified, in source code or in binary form.
14#
15# Redistribution and use in source and binary forms, with or without
16# modification, are permitted provided that the following conditions are
17# met: redistributions of source code must retain the above copyright
18# notice, this list of conditions and the following disclaimer;
19# redistributions in binary form must reproduce the above copyright
20# notice, this list of conditions and the following disclaimer in the
21# documentation and/or other materials provided with the distribution;
22# neither the name of the copyright holders nor the names of its
23# contributors may be used to endorse or promote products derived from
24# this software without specific prior written permission.
25#
26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37#
38# Authors: Giacomo Gabrielli
39
40# Pipeline activity viewer for the O3 CPU model.
41
42import optparse
43import os
44import sys
45
46
47def process_trace(trace, outfile, cycle_time, width, color, timestamps,
48                  start_tick, stop_tick, start_sn, stop_sn):
49    line = None
50    fields = None
51    # Skip lines up to region of interest
52    if start_tick != 0:
53        while True:
54            line = trace.readline()
55            if not line: return
56            fields = line.split(':')
57            if fields[0] != 'O3PipeView': continue
58            if int(fields[2]) >= start_tick: break
59    elif start_sn != 0:
60        while True:
61            line = trace.readline()
62            if not line: return
63            fields = line.split(':')
64            if fields[0] != 'O3PipeView': continue
65            if fields[1] == 'fetch' and int(fields[5]) >= start_sn: break
66    else:
67        line = trace.readline()
68        if not line: return
69        fields = line.split(':')
70    # Skip lines up to next instruction fetch
71    while fields[0] != 'O3PipeView' or fields[1] != 'fetch':
72        line = trace.readline()
73        if not line: return
74        fields = line.split(':')
75    # Print header
76    outfile.write('// f = fetch, d = decode, n = rename, p = dispatch, '
77                  'i = issue, c = complete, r = retire\n\n')
78    outfile.write(' ' + 'timeline'.center(width) +
79                  '   ' + 'tick'.center(15) +
80                  '  ' + 'pc.upc'.center(12) +
81                  '  ' + 'disasm'.ljust(25) +
82                  '  ' + 'seq_num'.center(15))
83    if timestamps:
84        outfile.write('timestamps'.center(25))
85    outfile.write('\n')
86    # Region of interest
87    curr_inst = {}
88    while True:
89        if fields[0] == 'O3PipeView':
90            curr_inst[fields[1]] = int(fields[2])
91            if fields[1] == 'fetch':
92                if ((stop_tick > 0 and int(fields[2]) > stop_tick) or
93                    (stop_sn > 0 and int(fields[5]) > stop_sn)):
94                    return
95                (curr_inst['pc'], curr_inst['upc']) = fields[3:5]
96                curr_inst['sn'] = int(fields[5])
97                curr_inst['disasm'] = ' '.join(fields[6][:-1].split())
98            elif fields[1] == 'retire':
99                print_inst(outfile, curr_inst, cycle_time, width, color,
100                           timestamps)
101        line = trace.readline()
102        if not line: return
103        fields = line.split(':')
104
105
106def print_inst(outfile, inst, cycle_time, width, color, timestamps):
107    if color:
108        from m5.util.terminal import termcap
109    else:
110        from m5.util.terminal import no_termcap as termcap
111    # Pipeline stages
112    stages = [{'name': 'fetch',
113               'color': termcap.Blue + termcap.Reverse,
114               'shorthand': 'f'},
115              {'name': 'decode',
116               'color': termcap.Yellow + termcap.Reverse,
117               'shorthand': 'd'},
118              {'name': 'rename',
119               'color': termcap.Magenta + termcap.Reverse,
120               'shorthand': 'n'},
121              {'name': 'dispatch',
122               'color': termcap.Green + termcap.Reverse,
123               'shorthand': 'p'},
124              {'name': 'issue',
125               'color': termcap.Red + termcap.Reverse,
126               'shorthand': 'i'},
127              {'name': 'complete',
128               'color': termcap.Cyan + termcap.Reverse,
129               'shorthand': 'c'},
130              {'name': 'retire',
131               'color': termcap.Blue + termcap.Reverse,
132               'shorthand': 'r'}]
133    # Print
134    time_width = width * cycle_time
135    base_tick = (inst['fetch'] / time_width) * time_width
136    # Timeline shorter then time_width is printed in compact form where
137    # the print continues at the start of the same line.
138    if ((inst['retire'] - inst['fetch']) < time_width):
139        num_lines = 1 # compact form
140    else:
141        num_lines = ((inst['retire'] - base_tick) / time_width) + 1
142
143    curr_color = termcap.Normal
144    for i in range(num_lines):
145        start_tick = base_tick + i * time_width
146        end_tick = start_tick + time_width
147        if num_lines == 1:  # compact form
148            end_tick += (inst['fetch'] - base_tick)
149        events = []
150        for stage_idx in range(len(stages)):
151            tick = inst[stages[stage_idx]['name']]
152            if tick >= start_tick and tick < end_tick:
153                events.append((tick % time_width,
154                               stages[stage_idx]['name'],
155                               stage_idx))
156        events.sort()
157        outfile.write('[')
158        pos = 0
159        if num_lines == 1 and events[0][2] != 0:  # event is not fetch
160            curr_color = stages[events[0][2] - 1]['color']
161        for event in events:
162            if (stages[event[2]]['name'] == 'dispatch' and
163                inst['dispatch'] == inst['issue']):
164                continue
165            outfile.write(curr_color + '.' * ((event[0] / cycle_time) - pos))
166            outfile.write(stages[event[2]]['color'] +
167                          stages[event[2]]['shorthand'])
168            if event[2] != len(stages) - 1:  # event is not retire
169                curr_color = stages[event[2]]['color']
170            else:
171                curr_color = termcap.Normal
172            pos = (event[0] / cycle_time) + 1
173        outfile.write(curr_color + '.' * (width - pos) + termcap.Normal +
174                      ']-(' + str(base_tick + i * time_width).rjust(15) + ') ')
175        if i == 0:
176            outfile.write('%s.%s  %s [%s]' % (
177                    inst['pc'].rjust(10),
178                    inst['upc'],
179                    inst['disasm'].ljust(25),
180                    str(inst['sn']).rjust(15)))
181            if timestamps:
182                outfile.write('  f=%s, r=%s' % (inst['fetch'], inst['retire']))
183            outfile.write('\n')
184        else:
185            outfile.write('...'.center(12) + '\n')
186
187
188def validate_range(my_range):
189    my_range = [int(i) for i in my_range.split(':')]
190    if (len(my_range) != 2 or
191        my_range[0] < 0 or
192        my_range[1] > 0 and my_range[0] >= my_range[1]):
193        return None
194    return my_range
195
196
197def main():
198    # Parse options
199    usage = ('%prog [OPTION]... TRACE_FILE')
200    parser = optparse.OptionParser(usage=usage)
201    parser.add_option(
202        '-o',
203        dest='outfile',
204        default=os.path.join(os.getcwd(), 'o3-pipeview.out'),
205        help="output file (default: '%default')")
206    parser.add_option(
207        '-t',
208        dest='tick_range',
209        default='0:-1',
210        help="tick range (default: '%default'; -1 == inf.)")
211    parser.add_option(
212        '-i',
213        dest='inst_range',
214        default='0:-1',
215        help="instruction range (default: '%default'; -1 == inf.)")
216    parser.add_option(
217        '-w',
218        dest='width',
219        type='int', default=80,
220        help="timeline width (default: '%default')")
221    parser.add_option(
222        '--color',
223        action='store_true', default=False,
224        help="enable colored output (default: '%default')")
225    parser.add_option(
226        '-c', '--cycle-time',
227        type='int', default=1000,
228        help="CPU cycle time in ticks (default: '%default')")
229    parser.add_option(
230        '--timestamps',
231        action='store_true', default=False,
232        help="print fetch and retire timestamps (default: '%default')")
233    (options, args) = parser.parse_args()
234    if len(args) != 1:
235        parser.error('incorrect number of arguments')
236        sys.exit(1)
237    tick_range = validate_range(options.tick_range)
238    if not tick_range:
239        parser.error('invalid range')
240        sys.exit(1)
241    inst_range = validate_range(options.inst_range)
242    if not inst_range:
243        parser.error('invalid range')
244        sys.exit(1)
245    # Process trace
246    print 'Processing trace... ',
247    with open(args[0], 'r') as trace:
248        with open(options.outfile, 'w') as out:
249            process_trace(trace, out, options.cycle_time, options.width,
250                          options.color, options.timestamps,
251                          *(tick_range + inst_range))
252    print 'done!'
253
254
255if __name__ == '__main__':
256    sys.path.append(os.path.join(
257            os.path.dirname(os.path.abspath(__file__)),
258            '..', 'src', 'python'))
259    main()
260