o3-pipeview.py revision 8471:18e560ba1539
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    num_lines = ((inst['retire'] - inst['fetch']) / time_width) + 1
137    curr_color = termcap.Normal
138    for i in range(num_lines):
139        start_tick = base_tick + i * time_width
140        end_tick = start_tick + time_width
141        if num_lines == 1:  # compact form
142            end_tick += (inst['fetch'] - base_tick)
143        events = []
144        for stage_idx in range(len(stages)):
145            tick = inst[stages[stage_idx]['name']]
146            if tick >= start_tick and tick < end_tick:
147                events.append((tick % time_width,
148                               stages[stage_idx]['name'],
149                               stage_idx))
150        events.sort()
151        outfile.write('[')
152        pos = 0
153        if num_lines == 1 and events[0][2] != 0:  # event is not fetch
154            curr_color = stages[events[0][2] - 1]['color']
155        for event in events:
156            if (stages[event[2]]['name'] == 'dispatch' and
157                inst['dispatch'] == inst['issue']):
158                continue
159            outfile.write(curr_color + '.' * ((event[0] / cycle_time) - pos))
160            outfile.write(stages[event[2]]['color'] +
161                          stages[event[2]]['shorthand'])
162            if event[2] != len(stages) - 1:  # event is not retire
163                curr_color = stages[event[2]]['color']
164            else:
165                curr_color = termcap.Normal
166            pos = (event[0] / cycle_time) + 1
167        outfile.write(curr_color + '.' * (width - pos) + termcap.Normal +
168                      ']-(' + str(base_tick + i * time_width).rjust(15) + ') ')
169        if i == 0:
170            outfile.write('%s.%s  %s [%s]' % (
171                    inst['pc'].rjust(10),
172                    inst['upc'],
173                    inst['disasm'].ljust(25),
174                    str(inst['sn']).rjust(15)))
175            if timestamps:
176                outfile.write('  f=%s, r=%s' % (inst['fetch'], inst['retire']))
177            outfile.write('\n')
178        else:
179            outfile.write('...'.center(12) + '\n')
180
181
182def validate_range(my_range):
183    my_range = [int(i) for i in my_range.split(':')]
184    if (len(my_range) != 2 or
185        my_range[0] < 0 or
186        my_range[1] > 0 and my_range[0] >= my_range[1]):
187        return None
188    return my_range
189
190
191def main():
192    # Parse options
193    usage = ('%prog [OPTION]... TRACE_FILE')
194    parser = optparse.OptionParser(usage=usage)
195    parser.add_option(
196        '-o',
197        dest='outfile',
198        default=os.path.join(os.getcwd(), 'o3-pipeview.out'),
199        help="output file (default: '%default')")
200    parser.add_option(
201        '-t',
202        dest='tick_range',
203        default='0:-1',
204        help="tick range (default: '%default'; -1 == inf.)")
205    parser.add_option(
206        '-i',
207        dest='inst_range',
208        default='0:-1',
209        help="instruction range (default: '%default'; -1 == inf.)")
210    parser.add_option(
211        '-w',
212        dest='width',
213        type='int', default=80,
214        help="timeline width (default: '%default')")
215    parser.add_option(
216        '--color',
217        action='store_true', default=False,
218        help="enable colored output (default: '%default')")
219    parser.add_option(
220        '-c', '--cycle-time',
221        type='int', default=1000,
222        help="CPU cycle time in ticks (default: '%default')")
223    parser.add_option(
224        '--timestamps',
225        action='store_true', default=False,
226        help="print fetch and retire timestamps (default: '%default')")
227    (options, args) = parser.parse_args()
228    if len(args) != 1:
229        parser.error('incorrect number of arguments')
230        sys.exit(1)
231    tick_range = validate_range(options.tick_range)
232    if not tick_range:
233        parser.error('invalid range')
234        sys.exit(1)
235    inst_range = validate_range(options.inst_range)
236    if not inst_range:
237        parser.error('invalid range')
238        sys.exit(1)
239    # Process trace
240    print 'Processing trace... ',
241    with open(args[0], 'r') as trace:
242        with open(options.outfile, 'w') as out:
243            process_trace(trace, out, options.cycle_time, options.width,
244                          options.color, options.timestamps,
245                          *(tick_range + inst_range))
246    print 'done!'
247
248
249if __name__ == '__main__':
250    sys.path.append(os.path.join(
251            os.path.dirname(os.path.abspath(__file__)),
252            '..', 'src', 'python'))
253    main()
254