110259SAndrew.Bardsley@arm.com# Copyright (c) 2013 ARM Limited
210259SAndrew.Bardsley@arm.com# All rights reserved
310259SAndrew.Bardsley@arm.com#
410259SAndrew.Bardsley@arm.com# The license below extends only to copyright in the software and shall
510259SAndrew.Bardsley@arm.com# not be construed as granting a license to any other intellectual
610259SAndrew.Bardsley@arm.com# property including but not limited to intellectual property relating
710259SAndrew.Bardsley@arm.com# to a hardware implementation of the functionality of the software
810259SAndrew.Bardsley@arm.com# licensed hereunder.  You may use the software subject to the license
910259SAndrew.Bardsley@arm.com# terms below provided that you ensure that this notice is replicated
1010259SAndrew.Bardsley@arm.com# unmodified and in its entirety in all distributions of the software,
1110259SAndrew.Bardsley@arm.com# modified or unmodified, in source code or in binary form.
1210259SAndrew.Bardsley@arm.com#
1310259SAndrew.Bardsley@arm.com# Redistribution and use in source and binary forms, with or without
1410259SAndrew.Bardsley@arm.com# modification, are permitted provided that the following conditions are
1510259SAndrew.Bardsley@arm.com# met: redistributions of source code must retain the above copyright
1610259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer;
1710259SAndrew.Bardsley@arm.com# redistributions in binary form must reproduce the above copyright
1810259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer in the
1910259SAndrew.Bardsley@arm.com# documentation and/or other materials provided with the distribution;
2010259SAndrew.Bardsley@arm.com# neither the name of the copyright holders nor the names of its
2110259SAndrew.Bardsley@arm.com# contributors may be used to endorse or promote products derived from
2210259SAndrew.Bardsley@arm.com# this software without specific prior written permission.
2310259SAndrew.Bardsley@arm.com#
2410259SAndrew.Bardsley@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2510259SAndrew.Bardsley@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2610259SAndrew.Bardsley@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2710259SAndrew.Bardsley@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2810259SAndrew.Bardsley@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2910259SAndrew.Bardsley@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3010259SAndrew.Bardsley@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3110259SAndrew.Bardsley@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3210259SAndrew.Bardsley@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3310259SAndrew.Bardsley@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3410259SAndrew.Bardsley@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3510259SAndrew.Bardsley@arm.com#
3610259SAndrew.Bardsley@arm.com# Authors: Andrew Bardsley
3710259SAndrew.Bardsley@arm.com
3810259SAndrew.Bardsley@arm.comimport parse
3910259SAndrew.Bardsley@arm.comimport colours
4010259SAndrew.Bardsley@arm.comfrom colours import unknownColour
4110259SAndrew.Bardsley@arm.comfrom point import Point
4210259SAndrew.Bardsley@arm.comimport re
4310259SAndrew.Bardsley@arm.comimport blobs
4410259SAndrew.Bardsley@arm.comfrom time import time as wall_time
4510259SAndrew.Bardsley@arm.comimport os
4610259SAndrew.Bardsley@arm.com
4710259SAndrew.Bardsley@arm.comid_parts = "TSPLFE"
4810259SAndrew.Bardsley@arm.com
4910259SAndrew.Bardsley@arm.comall_ids = set(id_parts)
5010259SAndrew.Bardsley@arm.comno_ids = set([])
5110259SAndrew.Bardsley@arm.com
5210259SAndrew.Bardsley@arm.comclass BlobDataSelect(object):
5310259SAndrew.Bardsley@arm.com    """Represents which data is displayed for Ided object"""
5410259SAndrew.Bardsley@arm.com    def __init__(self):
5510259SAndrew.Bardsley@arm.com        # Copy all_ids
5610259SAndrew.Bardsley@arm.com        self.ids = set(all_ids)
5710259SAndrew.Bardsley@arm.com
5810259SAndrew.Bardsley@arm.com    def __and__(self, rhs):
5910259SAndrew.Bardsley@arm.com        """And for filtering"""
6010259SAndrew.Bardsley@arm.com        ret = BlobDataSelect()
6110259SAndrew.Bardsley@arm.com        ret.ids = self.ids.intersection(rhs.ids)
6210259SAndrew.Bardsley@arm.com        return ret
6310259SAndrew.Bardsley@arm.com
6410259SAndrew.Bardsley@arm.comclass BlobVisualData(object):
6510259SAndrew.Bardsley@arm.com    """Super class for block data colouring"""
6610259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
6710259SAndrew.Bardsley@arm.com        """Return an array of colours to use for a striped block"""
6810259SAndrew.Bardsley@arm.com        return unknownColour
6910259SAndrew.Bardsley@arm.com
7010259SAndrew.Bardsley@arm.com    def get_inst(self):
7110259SAndrew.Bardsley@arm.com        """Get an instruction Id (if any) from this data"""
7210259SAndrew.Bardsley@arm.com        return None
7310259SAndrew.Bardsley@arm.com
7410259SAndrew.Bardsley@arm.com    def get_line(self):
7510259SAndrew.Bardsley@arm.com        """Get a line Id (if any) from this data"""
7610259SAndrew.Bardsley@arm.com        return None
7710259SAndrew.Bardsley@arm.com
7810259SAndrew.Bardsley@arm.com    def __repr__(self):
7910259SAndrew.Bardsley@arm.com        return self.__class__.__name__ + '().from_string(' + \
8010259SAndrew.Bardsley@arm.com            self.__str__() + ')'
8110259SAndrew.Bardsley@arm.com
8210259SAndrew.Bardsley@arm.com    def __str__(self):
8310259SAndrew.Bardsley@arm.com        return ''
8410259SAndrew.Bardsley@arm.com
8510259SAndrew.Bardsley@arm.comclass Id(BlobVisualData):
8610259SAndrew.Bardsley@arm.com    """A line or instruction id"""
8710259SAndrew.Bardsley@arm.com    def __init__(self):
8810259SAndrew.Bardsley@arm.com        self.isFault = False
8910259SAndrew.Bardsley@arm.com        self.threadId = 0
9010259SAndrew.Bardsley@arm.com        self.streamSeqNum = 0
9110259SAndrew.Bardsley@arm.com        self.predictionSeqNum = 0
9210259SAndrew.Bardsley@arm.com        self.lineSeqNum = 0
9310259SAndrew.Bardsley@arm.com        self.fetchSeqNum = 0
9410259SAndrew.Bardsley@arm.com        self.execSeqNum = 0
9510259SAndrew.Bardsley@arm.com
9610259SAndrew.Bardsley@arm.com    def as_list(self):
9710259SAndrew.Bardsley@arm.com        return [self.threadId, self.streamSeqNum, self.predictionSeqNum,
9810259SAndrew.Bardsley@arm.com            self.lineSeqNum, self.fetchSeqNum, self.execSeqNum]
9910259SAndrew.Bardsley@arm.com
10010259SAndrew.Bardsley@arm.com    def __cmp__(self, right):
10110259SAndrew.Bardsley@arm.com        return cmp(self.as_list(), right.as_list())
10210259SAndrew.Bardsley@arm.com
10310259SAndrew.Bardsley@arm.com    def from_string(self, string):
10410259SAndrew.Bardsley@arm.com        m = re.match('^(F;)?(\d+)/(\d+)\.(\d+)/(\d+)(/(\d+)(\.(\d+))?)?',
10510259SAndrew.Bardsley@arm.com            string)
10610259SAndrew.Bardsley@arm.com
10710259SAndrew.Bardsley@arm.com        def seqnum_from_string(string):
10810259SAndrew.Bardsley@arm.com            if string is None:
10910259SAndrew.Bardsley@arm.com                return 0
11010259SAndrew.Bardsley@arm.com            else:
11110259SAndrew.Bardsley@arm.com                return int(string)
11210259SAndrew.Bardsley@arm.com
11310259SAndrew.Bardsley@arm.com        if m is None:
11410259SAndrew.Bardsley@arm.com            print 'Invalid Id string', string
11510259SAndrew.Bardsley@arm.com        else:
11610259SAndrew.Bardsley@arm.com            elems = m.groups()
11710259SAndrew.Bardsley@arm.com
11810259SAndrew.Bardsley@arm.com            if elems[0] is not None:
11910259SAndrew.Bardsley@arm.com                self.isFault = True
12010259SAndrew.Bardsley@arm.com            else:
12110259SAndrew.Bardsley@arm.com                self.isFault = False
12210259SAndrew.Bardsley@arm.com
12310259SAndrew.Bardsley@arm.com            self.threadId = seqnum_from_string(elems[1])
12410259SAndrew.Bardsley@arm.com            self.streamSeqNum = seqnum_from_string(elems[2])
12510259SAndrew.Bardsley@arm.com            self.predictionSeqNum = seqnum_from_string(elems[3])
12610259SAndrew.Bardsley@arm.com            self.lineSeqNum = seqnum_from_string(elems[4])
12710259SAndrew.Bardsley@arm.com            self.fetchSeqNum = seqnum_from_string(elems[6])
12810259SAndrew.Bardsley@arm.com            self.execSeqNum = seqnum_from_string(elems[8])
12910259SAndrew.Bardsley@arm.com        return self
13010259SAndrew.Bardsley@arm.com
13110259SAndrew.Bardsley@arm.com    def get_inst(self):
13210259SAndrew.Bardsley@arm.com        if self.fetchSeqNum != 0:
13310259SAndrew.Bardsley@arm.com            return self
13410259SAndrew.Bardsley@arm.com        else:
13510259SAndrew.Bardsley@arm.com            return None
13610259SAndrew.Bardsley@arm.com
13710259SAndrew.Bardsley@arm.com    def get_line(self):
13810259SAndrew.Bardsley@arm.com        return self
13910259SAndrew.Bardsley@arm.com
14010259SAndrew.Bardsley@arm.com    def __str__(self):
14110259SAndrew.Bardsley@arm.com        """Returns the usual id T/S.P/L/F.E string"""
14210259SAndrew.Bardsley@arm.com        return (
14310259SAndrew.Bardsley@arm.com            str(self.threadId) + '/' +
14410259SAndrew.Bardsley@arm.com            str(self.streamSeqNum) + '.' +
14510259SAndrew.Bardsley@arm.com            str(self.predictionSeqNum) + '/' +
14610259SAndrew.Bardsley@arm.com            str(self.lineSeqNum) + '/' +
14710259SAndrew.Bardsley@arm.com            str(self.fetchSeqNum) + '.' +
14810259SAndrew.Bardsley@arm.com            str(self.execSeqNum))
14910259SAndrew.Bardsley@arm.com
15010259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
15110259SAndrew.Bardsley@arm.com        ret = []
15210259SAndrew.Bardsley@arm.com
15310259SAndrew.Bardsley@arm.com        if self.isFault:
15410259SAndrew.Bardsley@arm.com            ret.append(colours.faultColour)
15510259SAndrew.Bardsley@arm.com
15610259SAndrew.Bardsley@arm.com        if 'T' in select.ids:
15710259SAndrew.Bardsley@arm.com            ret.append(colours.number_to_colour(self.threadId))
15810259SAndrew.Bardsley@arm.com        if 'S' in select.ids:
15910259SAndrew.Bardsley@arm.com            ret.append(colours.number_to_colour(self.streamSeqNum))
16010259SAndrew.Bardsley@arm.com        if 'P' in select.ids:
16110259SAndrew.Bardsley@arm.com            ret.append(colours.number_to_colour(self.predictionSeqNum))
16210259SAndrew.Bardsley@arm.com        if 'L' in select.ids:
16310259SAndrew.Bardsley@arm.com            ret.append(colours.number_to_colour(self.lineSeqNum))
16410259SAndrew.Bardsley@arm.com        if self.fetchSeqNum != 0 and 'F' in select.ids:
16510259SAndrew.Bardsley@arm.com            ret.append(colours.number_to_colour(self.fetchSeqNum))
16610259SAndrew.Bardsley@arm.com        if self.execSeqNum != 0 and 'E' in select.ids:
16710259SAndrew.Bardsley@arm.com            ret.append(colours.number_to_colour(self.execSeqNum))
16810259SAndrew.Bardsley@arm.com
16910259SAndrew.Bardsley@arm.com        if len(ret) == 0:
17010259SAndrew.Bardsley@arm.com            ret = [colours.unknownColour]
17110259SAndrew.Bardsley@arm.com
17210259SAndrew.Bardsley@arm.com        if self.isFault:
17310259SAndrew.Bardsley@arm.com            ret.append(colours.faultColour)
17410259SAndrew.Bardsley@arm.com
17510259SAndrew.Bardsley@arm.com        return ret
17610259SAndrew.Bardsley@arm.com
17710259SAndrew.Bardsley@arm.comclass Branch(BlobVisualData):
17810259SAndrew.Bardsley@arm.com    """Branch data new stream and prediction sequence numbers, a branch
17910259SAndrew.Bardsley@arm.com    reason and a new PC"""
18010259SAndrew.Bardsley@arm.com    def __init__(self):
18110259SAndrew.Bardsley@arm.com        self.newStreamSeqNum = 0
18210259SAndrew.Bardsley@arm.com        self.newPredictionSeqNum = 0
18310259SAndrew.Bardsley@arm.com        self.newPC = 0
18410259SAndrew.Bardsley@arm.com        self.reason = "NoBranch"
18510259SAndrew.Bardsley@arm.com        self.id = Id()
18610259SAndrew.Bardsley@arm.com
18710259SAndrew.Bardsley@arm.com    def from_string(self, string):
18810259SAndrew.Bardsley@arm.com        m = re.match('^(\w+);(\d+)\.(\d+);([0-9a-fA-Fx]+);(.*)$', string)
18910259SAndrew.Bardsley@arm.com
19010259SAndrew.Bardsley@arm.com        if m is not None:
19110259SAndrew.Bardsley@arm.com            self.reason, newStreamSeqNum, newPredictionSeqNum, \
19210259SAndrew.Bardsley@arm.com                newPC, id = m.groups()
19310259SAndrew.Bardsley@arm.com
19410259SAndrew.Bardsley@arm.com            self.newStreamSeqNum = int(newStreamSeqNum)
19510259SAndrew.Bardsley@arm.com            self.newPredictionSeqNum = int(newPredictionSeqNum)
19610259SAndrew.Bardsley@arm.com            self.newPC = int(newPC, 0)
19710259SAndrew.Bardsley@arm.com            self.id = special_view_decoder(Id)(id)
19810259SAndrew.Bardsley@arm.com            # self.branch = special_view_decoder(Branch)(branch)
19910259SAndrew.Bardsley@arm.com        else:
20010259SAndrew.Bardsley@arm.com            print "Bad Branch data:", string
20110259SAndrew.Bardsley@arm.com        return self
20210259SAndrew.Bardsley@arm.com
20310259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
20410259SAndrew.Bardsley@arm.com        return [colours.number_to_colour(self.newStreamSeqNum),
20510259SAndrew.Bardsley@arm.com            colours.number_to_colour(self.newPredictionSeqNum),
20610259SAndrew.Bardsley@arm.com            colours.number_to_colour(self.newPC)]
20710259SAndrew.Bardsley@arm.com
20810259SAndrew.Bardsley@arm.comclass Counts(BlobVisualData):
20910259SAndrew.Bardsley@arm.com    """Treat the input data as just a /-separated list of count values (or
21010259SAndrew.Bardsley@arm.com    just a single value)"""
21110259SAndrew.Bardsley@arm.com    def __init__(self):
21210259SAndrew.Bardsley@arm.com        self.counts = []
21310259SAndrew.Bardsley@arm.com
21410259SAndrew.Bardsley@arm.com    def from_string(self, string):
21510259SAndrew.Bardsley@arm.com        self.counts = map(int, re.split('/', string))
21610259SAndrew.Bardsley@arm.com        return self
21710259SAndrew.Bardsley@arm.com
21810259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
21910259SAndrew.Bardsley@arm.com        return map(colours.number_to_colour, self.counts)
22010259SAndrew.Bardsley@arm.com
22110259SAndrew.Bardsley@arm.comclass Colour(BlobVisualData):
22210259SAndrew.Bardsley@arm.com    """A fixed colour block, used for special colour decoding"""
22310259SAndrew.Bardsley@arm.com    def __init__(self, colour):
22410259SAndrew.Bardsley@arm.com        self.colour = colour
22510259SAndrew.Bardsley@arm.com
22610259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
22710259SAndrew.Bardsley@arm.com        return [self.colour]
22810259SAndrew.Bardsley@arm.com
22910259SAndrew.Bardsley@arm.comclass DcacheAccess(BlobVisualData):
23010259SAndrew.Bardsley@arm.com    """Data cache accesses [RW];id"""
23110259SAndrew.Bardsley@arm.com    def __init__(self):
23210259SAndrew.Bardsley@arm.com        self.direc = 'R'
23310259SAndrew.Bardsley@arm.com        self.id = Id()
23410259SAndrew.Bardsley@arm.com
23510259SAndrew.Bardsley@arm.com    def from_string(self, string):
23610259SAndrew.Bardsley@arm.com        self.direc, id = re.match('^([RW]);([^;]*);.*$', string).groups()
23710259SAndrew.Bardsley@arm.com        self.id.from_string(id)
23810259SAndrew.Bardsley@arm.com        return self
23910259SAndrew.Bardsley@arm.com
24010259SAndrew.Bardsley@arm.com    def get_inst(self):
24110259SAndrew.Bardsley@arm.com        return self.id
24210259SAndrew.Bardsley@arm.com
24310259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
24410259SAndrew.Bardsley@arm.com        if self.direc == 'R':
24510259SAndrew.Bardsley@arm.com            direc_colour = colours.readColour
24610259SAndrew.Bardsley@arm.com        elif self.direc == 'R':
24710259SAndrew.Bardsley@arm.com            direc_colour = colours.writeColour
24810259SAndrew.Bardsley@arm.com        else:
24910259SAndrew.Bardsley@arm.com            direc_colour = colours.errorColour
25010259SAndrew.Bardsley@arm.com        return [direc_colour] + self.id.to_striped_block(select)
25110259SAndrew.Bardsley@arm.com
25210259SAndrew.Bardsley@arm.comclass ColourPattern(object):
25310259SAndrew.Bardsley@arm.com    """Super class for decoders that make 2D grids rather than just single
25410259SAndrew.Bardsley@arm.com    striped blocks"""
25510259SAndrew.Bardsley@arm.com    def elems(self):
25610259SAndrew.Bardsley@arm.com        return []
25710259SAndrew.Bardsley@arm.com
25810259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
25910259SAndrew.Bardsley@arm.com        return [[[colours.errorColour]]]
26010259SAndrew.Bardsley@arm.com
26110259SAndrew.Bardsley@arm.comdef special_view_decoder(class_):
26210259SAndrew.Bardsley@arm.com    """Generate a decode function that checks for special character
26310259SAndrew.Bardsley@arm.com    arguments first (and generates a fixed colour) before building a
26410259SAndrew.Bardsley@arm.com    BlobVisualData of the given class"""
26510259SAndrew.Bardsley@arm.com    def decode(symbol):
26610259SAndrew.Bardsley@arm.com        if symbol in special_state_colours:
26710259SAndrew.Bardsley@arm.com            return Colour(special_state_colours[symbol])
26810259SAndrew.Bardsley@arm.com        else:
26910259SAndrew.Bardsley@arm.com            return class_().from_string(symbol)
27010259SAndrew.Bardsley@arm.com    return decode
27110259SAndrew.Bardsley@arm.com
27210259SAndrew.Bardsley@arm.comclass TwoDColours(ColourPattern):
27310259SAndrew.Bardsley@arm.com    """A 2D grid pattern decoder"""
27410259SAndrew.Bardsley@arm.com    def __init__(self, blockss):
27510259SAndrew.Bardsley@arm.com        self.blockss = blockss
27610259SAndrew.Bardsley@arm.com
27710259SAndrew.Bardsley@arm.com    @classmethod
27810259SAndrew.Bardsley@arm.com    def decoder(class_, elemClass, dataName):
27910259SAndrew.Bardsley@arm.com        """Factory for making decoders for particular block types"""
28010259SAndrew.Bardsley@arm.com        def decode(pairs):
28110259SAndrew.Bardsley@arm.com            if dataName not in pairs:
28210259SAndrew.Bardsley@arm.com                print 'TwoDColours: no event data called:', \
28310259SAndrew.Bardsley@arm.com                    dataName, 'in:', pairs
28410259SAndrew.Bardsley@arm.com                return class_([[Colour(colours.errorColour)]])
28510259SAndrew.Bardsley@arm.com            else:
28610259SAndrew.Bardsley@arm.com                parsed = parse.list_parser(pairs[dataName])
28710259SAndrew.Bardsley@arm.com                return class_(parse.map2(special_view_decoder(elemClass), \
28810259SAndrew.Bardsley@arm.com                    parsed))
28910259SAndrew.Bardsley@arm.com        return decode
29010259SAndrew.Bardsley@arm.com
29110259SAndrew.Bardsley@arm.com    @classmethod
29210259SAndrew.Bardsley@arm.com    def indexed_decoder(class_, elemClass, dataName, picPairs):
29310259SAndrew.Bardsley@arm.com        """Factory for making decoders for particular block types but
29410259SAndrew.Bardsley@arm.com        where the list elements are pairs of (index, data) and
29510259SAndrew.Bardsley@arm.com        strip and stripelems counts are picked up from the pair
29610259SAndrew.Bardsley@arm.com        data on the decoder's picture file.  This gives a 2D layout
29710259SAndrew.Bardsley@arm.com        of the values with index 0 at strip=0, elem=0 and index 1
29810259SAndrew.Bardsley@arm.com        at strip=0, elem=1"""
29910259SAndrew.Bardsley@arm.com        def decode(pairs):
30010259SAndrew.Bardsley@arm.com            if dataName not in pairs:
30110259SAndrew.Bardsley@arm.com                print 'TwoDColours: no event data called:', \
30210259SAndrew.Bardsley@arm.com                    dataName, 'in:', pairs
30310259SAndrew.Bardsley@arm.com                return class_([[Colour(colours.errorColour)]])
30410259SAndrew.Bardsley@arm.com            else:
30510259SAndrew.Bardsley@arm.com                strips = int(picPairs['strips'])
30610259SAndrew.Bardsley@arm.com                strip_elems = int(picPairs['stripelems'])
30710259SAndrew.Bardsley@arm.com
30810259SAndrew.Bardsley@arm.com                raw_iv_pairs = pairs[dataName]
30910259SAndrew.Bardsley@arm.com
31010259SAndrew.Bardsley@arm.com                parsed = parse.parse_indexed_list(raw_iv_pairs)
31110259SAndrew.Bardsley@arm.com
31210259SAndrew.Bardsley@arm.com                array = [[Colour(colours.emptySlotColour)
31310259SAndrew.Bardsley@arm.com                    for i in xrange(0, strip_elems)]
31410259SAndrew.Bardsley@arm.com                    for j in xrange(0, strips)]
31510259SAndrew.Bardsley@arm.com
31610259SAndrew.Bardsley@arm.com                for index, value in parsed:
31710259SAndrew.Bardsley@arm.com                    try:
31810259SAndrew.Bardsley@arm.com                        array[index % strips][index / strips] = \
31910259SAndrew.Bardsley@arm.com                            special_view_decoder(elemClass)(value)
32010259SAndrew.Bardsley@arm.com                    except:
32110259SAndrew.Bardsley@arm.com                        print "Element out of range strips: %d," \
32210259SAndrew.Bardsley@arm.com                            " stripelems %d, index: %d" % (strips,
32310259SAndrew.Bardsley@arm.com                            strip_elems, index)
32410259SAndrew.Bardsley@arm.com
32510259SAndrew.Bardsley@arm.com                # return class_(array)
32610259SAndrew.Bardsley@arm.com                return class_(array)
32710259SAndrew.Bardsley@arm.com        return decode
32810259SAndrew.Bardsley@arm.com
32910259SAndrew.Bardsley@arm.com    def elems(self):
33010259SAndrew.Bardsley@arm.com        """Get a flat list of all elements"""
33110259SAndrew.Bardsley@arm.com        ret = []
33210259SAndrew.Bardsley@arm.com        for blocks in self.blockss:
33310259SAndrew.Bardsley@arm.com            ret += blocks
33410259SAndrew.Bardsley@arm.com        return ret
33510259SAndrew.Bardsley@arm.com
33610259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
33710259SAndrew.Bardsley@arm.com        return parse.map2(lambda d: d.to_striped_block(select), self.blockss)
33810259SAndrew.Bardsley@arm.com
33910259SAndrew.Bardsley@arm.comclass FrameColours(ColourPattern):
34010259SAndrew.Bardsley@arm.com    """Decode to a 2D grid which has a single occupied row from the event
34110259SAndrew.Bardsley@arm.com    data and some blank rows forming a frame with the occupied row as a
34210259SAndrew.Bardsley@arm.com    'title' coloured stripe"""
34310259SAndrew.Bardsley@arm.com    def __init__(self, block, numBlankSlots):
34410259SAndrew.Bardsley@arm.com        self.numBlankSlots = numBlankSlots
34510259SAndrew.Bardsley@arm.com        self.block = block
34610259SAndrew.Bardsley@arm.com
34710259SAndrew.Bardsley@arm.com    @classmethod
34810259SAndrew.Bardsley@arm.com    def decoder(class_, elemClass, numBlankSlots, dataName):
34910259SAndrew.Bardsley@arm.com        """Factory for element type"""
35010259SAndrew.Bardsley@arm.com        def decode(pairs):
35110259SAndrew.Bardsley@arm.com            if dataName not in pairs:
35210259SAndrew.Bardsley@arm.com                print 'FrameColours: no event data called:', dataName, \
35310259SAndrew.Bardsley@arm.com                    'in:', pairs
35410259SAndrew.Bardsley@arm.com                return class_([Colour(colours.errorColour)])
35510259SAndrew.Bardsley@arm.com            else:
35610259SAndrew.Bardsley@arm.com                parsed = parse.list_parser(pairs[dataName])
35710259SAndrew.Bardsley@arm.com                return class_(special_view_decoder(elemClass)
35810259SAndrew.Bardsley@arm.com                    (parsed[0][0]), numBlankSlots)
35910259SAndrew.Bardsley@arm.com        return decode
36010259SAndrew.Bardsley@arm.com
36110259SAndrew.Bardsley@arm.com    def elems(self):
36210259SAndrew.Bardsley@arm.com        return [self.block]
36310259SAndrew.Bardsley@arm.com
36410259SAndrew.Bardsley@arm.com    def to_striped_block(self, select):
36510259SAndrew.Bardsley@arm.com        return ([[self.block.to_striped_block(select)]] +
36610259SAndrew.Bardsley@arm.com            (self.numBlankSlots * [[[colours.backgroundColour]]]))
36710259SAndrew.Bardsley@arm.com
36810259SAndrew.Bardsley@arm.comspecial_state_colours = {
36910259SAndrew.Bardsley@arm.com    'U': colours.unknownColour,
37010259SAndrew.Bardsley@arm.com    'B': colours.blockedColour,
37110259SAndrew.Bardsley@arm.com    '-': colours.bubbleColour,
37210259SAndrew.Bardsley@arm.com    '': colours.emptySlotColour,
37310259SAndrew.Bardsley@arm.com    'E': colours.emptySlotColour,
37410259SAndrew.Bardsley@arm.com    'R': colours.reservedSlotColour,
37510259SAndrew.Bardsley@arm.com    'X': colours.errorColour,
37610259SAndrew.Bardsley@arm.com    'F': colours.faultColour,
37710259SAndrew.Bardsley@arm.com    'r': colours.readColour,
37810259SAndrew.Bardsley@arm.com    'w': colours.writeColour
37910259SAndrew.Bardsley@arm.com    }
38010259SAndrew.Bardsley@arm.com
38110259SAndrew.Bardsley@arm.comspecial_state_names = {
38210259SAndrew.Bardsley@arm.com    'U': '(U)nknown',
38310259SAndrew.Bardsley@arm.com    'B': '(B)locked',
38410259SAndrew.Bardsley@arm.com    '-': '(-)Bubble',
38510259SAndrew.Bardsley@arm.com    '': '()Empty',
38610259SAndrew.Bardsley@arm.com    'E': '(E)mpty',
38710259SAndrew.Bardsley@arm.com    'R': '(R)eserved',
38810259SAndrew.Bardsley@arm.com    'X': '(X)Error',
38910259SAndrew.Bardsley@arm.com    'F': '(F)ault',
39010259SAndrew.Bardsley@arm.com    'r': '(r)ead',
39110259SAndrew.Bardsley@arm.com    'w': '(w)rite'
39210259SAndrew.Bardsley@arm.com    }
39310259SAndrew.Bardsley@arm.com
39410259SAndrew.Bardsley@arm.comspecial_state_chars = special_state_colours.keys()
39510259SAndrew.Bardsley@arm.com
39610259SAndrew.Bardsley@arm.com# The complete set of available block data types
39710259SAndrew.Bardsley@arm.comdecoder_element_classes = {
39810259SAndrew.Bardsley@arm.com    'insts': Id,
39910259SAndrew.Bardsley@arm.com    'lines': Id,
40010259SAndrew.Bardsley@arm.com    'branch': Branch,
40110259SAndrew.Bardsley@arm.com    'dcache': DcacheAccess,
40210259SAndrew.Bardsley@arm.com    'counts': Counts
40310259SAndrew.Bardsley@arm.com    }
40410259SAndrew.Bardsley@arm.com
40510259SAndrew.Bardsley@arm.comindexed_decoder_element_classes = {
40610259SAndrew.Bardsley@arm.com    'indexedCounts' : Counts
40710259SAndrew.Bardsley@arm.com    }
40810259SAndrew.Bardsley@arm.com
40910259SAndrew.Bardsley@arm.comdef find_colour_decoder(stripSpace, decoderName, dataName, picPairs):
41010259SAndrew.Bardsley@arm.com    """Make a colour decoder from some picture file blob attributes"""
41110259SAndrew.Bardsley@arm.com    if decoderName == 'frame':
41210259SAndrew.Bardsley@arm.com        return FrameColours.decoder(Counts, stripSpace, dataName)
41310259SAndrew.Bardsley@arm.com    elif decoderName in decoder_element_classes:
41410259SAndrew.Bardsley@arm.com        return TwoDColours.decoder(decoder_element_classes[decoderName],
41510259SAndrew.Bardsley@arm.com            dataName)
41610259SAndrew.Bardsley@arm.com    elif decoderName in indexed_decoder_element_classes:
41710259SAndrew.Bardsley@arm.com        return TwoDColours.indexed_decoder(
41810259SAndrew.Bardsley@arm.com            indexed_decoder_element_classes[decoderName], dataName, picPairs)
41910259SAndrew.Bardsley@arm.com    else:
42010259SAndrew.Bardsley@arm.com        return None
42110259SAndrew.Bardsley@arm.com
42210259SAndrew.Bardsley@arm.comclass IdedObj(object):
42310259SAndrew.Bardsley@arm.com    """An object identified by an Id carrying paired data.
42410259SAndrew.Bardsley@arm.com    The super class for Inst and Line"""
42510259SAndrew.Bardsley@arm.com
42610259SAndrew.Bardsley@arm.com    def __init__(self, id, pairs={}):
42710259SAndrew.Bardsley@arm.com        self.id = id
42810259SAndrew.Bardsley@arm.com        self.pairs = pairs
42910259SAndrew.Bardsley@arm.com
43010259SAndrew.Bardsley@arm.com    def __cmp__(self, right):
43110259SAndrew.Bardsley@arm.com        return cmp(self.id, right.id)
43210259SAndrew.Bardsley@arm.com
43310259SAndrew.Bardsley@arm.com    def table_line(self):
43410259SAndrew.Bardsley@arm.com        """Represent the object as a list of table row data"""
43510259SAndrew.Bardsley@arm.com        return []
43610259SAndrew.Bardsley@arm.com
43710259SAndrew.Bardsley@arm.com    # FIXME, add a table column titles?
43810259SAndrew.Bardsley@arm.com
43910259SAndrew.Bardsley@arm.com    def __repr__(self):
44010259SAndrew.Bardsley@arm.com        return ' '.join(self.table_line())
44110259SAndrew.Bardsley@arm.com
44210259SAndrew.Bardsley@arm.comclass Inst(IdedObj):
44310259SAndrew.Bardsley@arm.com    """A non-fault instruction"""
44410259SAndrew.Bardsley@arm.com    def __init__(self, id, disassembly, addr, pairs={}):
44510259SAndrew.Bardsley@arm.com        super(Inst,self).__init__(id, pairs)
44610259SAndrew.Bardsley@arm.com        if 'nextAddr' in pairs:
44710259SAndrew.Bardsley@arm.com            self.nextAddr = int(pairs['nextAddr'], 0)
44810259SAndrew.Bardsley@arm.com            del pairs['nextAddr']
44910259SAndrew.Bardsley@arm.com        else:
45010259SAndrew.Bardsley@arm.com            self.nextAddr = None
45110259SAndrew.Bardsley@arm.com        self.disassembly = disassembly
45210259SAndrew.Bardsley@arm.com        self.addr = addr
45310259SAndrew.Bardsley@arm.com
45410259SAndrew.Bardsley@arm.com    def table_line(self):
45510259SAndrew.Bardsley@arm.com        if self.nextAddr is not None:
45610259SAndrew.Bardsley@arm.com            addrStr = '0x%x->0x%x' % (self.addr, self.nextAddr)
45710259SAndrew.Bardsley@arm.com        else:
45810259SAndrew.Bardsley@arm.com            addrStr = '0x%x' % self.addr
45910259SAndrew.Bardsley@arm.com        ret = [addrStr, self.disassembly]
46010259SAndrew.Bardsley@arm.com        for name, value in self.pairs.iteritems():
46110259SAndrew.Bardsley@arm.com            ret.append("%s=%s" % (name, str(value)))
46210259SAndrew.Bardsley@arm.com        return ret
46310259SAndrew.Bardsley@arm.com
46410259SAndrew.Bardsley@arm.comclass InstFault(IdedObj):
46510259SAndrew.Bardsley@arm.com    """A fault instruction"""
46610259SAndrew.Bardsley@arm.com    def __init__(self, id, fault, addr, pairs={}):
46710259SAndrew.Bardsley@arm.com        super(InstFault,self).__init__(id, pairs)
46810259SAndrew.Bardsley@arm.com        self.fault = fault
46910259SAndrew.Bardsley@arm.com        self.addr = addr
47010259SAndrew.Bardsley@arm.com
47110259SAndrew.Bardsley@arm.com    def table_line(self):
47210259SAndrew.Bardsley@arm.com        ret = ["0x%x" % self.addr, self.fault]
47310259SAndrew.Bardsley@arm.com        for name, value in self.pairs:
47410259SAndrew.Bardsley@arm.com            ret.append("%s=%s", name, str(value))
47510259SAndrew.Bardsley@arm.com        return ret
47610259SAndrew.Bardsley@arm.com
47710259SAndrew.Bardsley@arm.comclass Line(IdedObj):
47810259SAndrew.Bardsley@arm.com    """A fetched line"""
47910259SAndrew.Bardsley@arm.com    def __init__(self, id, vaddr, paddr, size, pairs={}):
48010259SAndrew.Bardsley@arm.com        super(Line,self).__init__(id, pairs)
48110259SAndrew.Bardsley@arm.com        self.vaddr = vaddr
48210259SAndrew.Bardsley@arm.com        self.paddr = paddr
48310259SAndrew.Bardsley@arm.com        self.size = size
48410259SAndrew.Bardsley@arm.com
48510259SAndrew.Bardsley@arm.com    def table_line(self):
48610259SAndrew.Bardsley@arm.com        ret = ["0x%x/0x%x" % (self.vaddr, self.paddr), "%d" % self.size]
48710259SAndrew.Bardsley@arm.com        for name, value in self.pairs:
48810259SAndrew.Bardsley@arm.com            ret.append("%s=%s", name, str(value))
48910259SAndrew.Bardsley@arm.com        return ret
49010259SAndrew.Bardsley@arm.com
49110259SAndrew.Bardsley@arm.comclass LineFault(IdedObj):
49210259SAndrew.Bardsley@arm.com    """A faulting line"""
49310259SAndrew.Bardsley@arm.com    def __init__(self, id, fault, vaddr, pairs={}):
49410259SAndrew.Bardsley@arm.com        super(LineFault,self).__init__(id, pairs)
49510259SAndrew.Bardsley@arm.com        self.vaddr = vaddr
49610259SAndrew.Bardsley@arm.com        self.fault = fault
49710259SAndrew.Bardsley@arm.com
49810259SAndrew.Bardsley@arm.com    def table_line(self):
49910259SAndrew.Bardsley@arm.com        ret = ["0x%x" % self.vaddr, self.fault]
50010259SAndrew.Bardsley@arm.com        for name, value in self.pairs:
50110259SAndrew.Bardsley@arm.com            ret.append("%s=%s", name, str(value))
50210259SAndrew.Bardsley@arm.com        return ret
50310259SAndrew.Bardsley@arm.com
50410259SAndrew.Bardsley@arm.comclass BlobEvent(object):
50510259SAndrew.Bardsley@arm.com    """Time event for a single blob"""
50610259SAndrew.Bardsley@arm.com    def __init__(self, unit, time, pairs = {}):
50710259SAndrew.Bardsley@arm.com        # blob's unit name
50810259SAndrew.Bardsley@arm.com        self.unit = unit
50910259SAndrew.Bardsley@arm.com        self.time = time
51010259SAndrew.Bardsley@arm.com        # dict of picChar (blob name) to visual data
51110259SAndrew.Bardsley@arm.com        self.visuals = {}
51210259SAndrew.Bardsley@arm.com        # Miscellaneous unparsed MinorTrace line data
51310259SAndrew.Bardsley@arm.com        self.pairs = pairs
51410259SAndrew.Bardsley@arm.com        # Non-MinorTrace debug printout for this unit at this time
51510259SAndrew.Bardsley@arm.com        self.comments = []
51610259SAndrew.Bardsley@arm.com
51710259SAndrew.Bardsley@arm.com    def find_ided_objects(self, model, picChar, includeInstLines):
51810259SAndrew.Bardsley@arm.com        """Find instructions/lines mentioned in the blob's event
51910259SAndrew.Bardsley@arm.com        data"""
52010259SAndrew.Bardsley@arm.com        ret = []
52110259SAndrew.Bardsley@arm.com        if picChar in self.visuals:
52210259SAndrew.Bardsley@arm.com            blocks = self.visuals[picChar].elems()
52310259SAndrew.Bardsley@arm.com            def find_inst(data):
52410259SAndrew.Bardsley@arm.com                instId = data.get_inst()
52510259SAndrew.Bardsley@arm.com                lineId = data.get_line()
52610259SAndrew.Bardsley@arm.com                if instId is not None:
52710259SAndrew.Bardsley@arm.com                    inst = model.find_inst(instId)
52810259SAndrew.Bardsley@arm.com                    line = model.find_line(instId)
52910259SAndrew.Bardsley@arm.com                    if inst is not None:
53010259SAndrew.Bardsley@arm.com                        ret.append(inst)
53110259SAndrew.Bardsley@arm.com                    if includeInstLines and line is not None:
53210259SAndrew.Bardsley@arm.com                        ret.append(line)
53310259SAndrew.Bardsley@arm.com                elif lineId is not None:
53410259SAndrew.Bardsley@arm.com                    line = model.find_line(lineId)
53510259SAndrew.Bardsley@arm.com                    if line is not None:
53610259SAndrew.Bardsley@arm.com                        ret.append(line)
53710259SAndrew.Bardsley@arm.com            map(find_inst, blocks)
53810259SAndrew.Bardsley@arm.com        return sorted(ret)
53910259SAndrew.Bardsley@arm.com
54010259SAndrew.Bardsley@arm.comclass BlobModel(object):
54110259SAndrew.Bardsley@arm.com    """Model bringing together blob definitions and parsed events"""
54210259SAndrew.Bardsley@arm.com    def __init__(self, unitNamePrefix=''):
54310259SAndrew.Bardsley@arm.com        self.blobs = []
54410259SAndrew.Bardsley@arm.com        self.unitNameToBlobs = {}
54510259SAndrew.Bardsley@arm.com        self.unitEvents = {}
54610259SAndrew.Bardsley@arm.com        self.clear_events()
54710259SAndrew.Bardsley@arm.com        self.picSize = Point(20,10)
54810259SAndrew.Bardsley@arm.com        self.lastTime = 0
54910259SAndrew.Bardsley@arm.com        self.unitNamePrefix = unitNamePrefix
55010259SAndrew.Bardsley@arm.com
55110259SAndrew.Bardsley@arm.com    def clear_events(self):
55210259SAndrew.Bardsley@arm.com        """Drop all events and times"""
55310259SAndrew.Bardsley@arm.com        self.lastTime = 0
55410259SAndrew.Bardsley@arm.com        self.times = []
55510259SAndrew.Bardsley@arm.com        self.insts = {}
55610259SAndrew.Bardsley@arm.com        self.lines = {}
55710259SAndrew.Bardsley@arm.com        self.numEvents = 0
55810259SAndrew.Bardsley@arm.com
55910259SAndrew.Bardsley@arm.com        for unit, events in self.unitEvents.iteritems():
56010259SAndrew.Bardsley@arm.com            self.unitEvents[unit] = []
56110259SAndrew.Bardsley@arm.com
56210259SAndrew.Bardsley@arm.com    def add_blob(self, blob):
56310259SAndrew.Bardsley@arm.com        """Add a parsed blob to the model"""
56410259SAndrew.Bardsley@arm.com        self.blobs.append(blob)
56510259SAndrew.Bardsley@arm.com        if blob.unit not in self.unitNameToBlobs:
56610259SAndrew.Bardsley@arm.com            self.unitNameToBlobs[blob.unit] = []
56710259SAndrew.Bardsley@arm.com
56810259SAndrew.Bardsley@arm.com        self.unitNameToBlobs[blob.unit].append(blob)
56910259SAndrew.Bardsley@arm.com
57010259SAndrew.Bardsley@arm.com    def add_inst(self, inst):
57110259SAndrew.Bardsley@arm.com        """Add a MinorInst instruction definition to the model"""
57210259SAndrew.Bardsley@arm.com        # Is this a non micro-op instruction.  Microops (usually) get their
57310259SAndrew.Bardsley@arm.com        #   fetchSeqNum == 0 varient stored first
57410259SAndrew.Bardsley@arm.com        macroop_key = (inst.id.fetchSeqNum, 0)
57510259SAndrew.Bardsley@arm.com        full_key = (inst.id.fetchSeqNum, inst.id.execSeqNum)
57610259SAndrew.Bardsley@arm.com
57710259SAndrew.Bardsley@arm.com        if inst.id.execSeqNum != 0 and macroop_key not in self.insts:
57810259SAndrew.Bardsley@arm.com            self.insts[macroop_key] = inst
57910259SAndrew.Bardsley@arm.com
58010259SAndrew.Bardsley@arm.com        self.insts[full_key] = inst
58110259SAndrew.Bardsley@arm.com
58210259SAndrew.Bardsley@arm.com    def find_inst(self, id):
58310259SAndrew.Bardsley@arm.com        """Find an instruction either as a microop or macroop"""
58410259SAndrew.Bardsley@arm.com        macroop_key = (id.fetchSeqNum, 0)
58510259SAndrew.Bardsley@arm.com        full_key = (id.fetchSeqNum, id.execSeqNum)
58610259SAndrew.Bardsley@arm.com
58710259SAndrew.Bardsley@arm.com        if full_key in self.insts:
58810259SAndrew.Bardsley@arm.com            return self.insts[full_key]
58910259SAndrew.Bardsley@arm.com        elif macroop_key in self.insts:
59010259SAndrew.Bardsley@arm.com            return self.insts[macroop_key]
59110259SAndrew.Bardsley@arm.com        else:
59210259SAndrew.Bardsley@arm.com            return None
59310259SAndrew.Bardsley@arm.com
59410259SAndrew.Bardsley@arm.com    def add_line(self, line):
59510259SAndrew.Bardsley@arm.com        """Add a MinorLine line to the model"""
59610259SAndrew.Bardsley@arm.com        self.lines[line.id.lineSeqNum] = line
59710259SAndrew.Bardsley@arm.com
59810259SAndrew.Bardsley@arm.com    def add_unit_event(self, event):
59910259SAndrew.Bardsley@arm.com        """Add a single event to the model.  This must be an event at a
60010259SAndrew.Bardsley@arm.com        time >= the current maximum time"""
60110259SAndrew.Bardsley@arm.com        if event.unit in self.unitEvents:
60210259SAndrew.Bardsley@arm.com            events = self.unitEvents[event.unit]
60310259SAndrew.Bardsley@arm.com            if len(events) > 0 and events[len(events)-1].time > event.time:
60410259SAndrew.Bardsley@arm.com                print "Bad event ordering"
60510259SAndrew.Bardsley@arm.com            events.append(event)
60610259SAndrew.Bardsley@arm.com        self.numEvents += 1
60710259SAndrew.Bardsley@arm.com        self.lastTime = max(self.lastTime, event.time)
60810259SAndrew.Bardsley@arm.com
60910259SAndrew.Bardsley@arm.com    def extract_times(self):
61010259SAndrew.Bardsley@arm.com        """Extract a list of all the times from the seen events.  Call after
61110259SAndrew.Bardsley@arm.com        reading events to give a safe index list to use for time indices"""
61210259SAndrew.Bardsley@arm.com        times = {}
61310259SAndrew.Bardsley@arm.com        for unitEvents in self.unitEvents.itervalues():
61410259SAndrew.Bardsley@arm.com            for event in unitEvents:
61510259SAndrew.Bardsley@arm.com                times[event.time] = 1
61610259SAndrew.Bardsley@arm.com        self.times = times.keys()
61710259SAndrew.Bardsley@arm.com        self.times.sort()
61810259SAndrew.Bardsley@arm.com
61910259SAndrew.Bardsley@arm.com    def find_line(self, id):
62010259SAndrew.Bardsley@arm.com        """Find a line by id"""
62110259SAndrew.Bardsley@arm.com        key = id.lineSeqNum
62210259SAndrew.Bardsley@arm.com        return self.lines.get(key, None)
62310259SAndrew.Bardsley@arm.com
62410259SAndrew.Bardsley@arm.com    def find_event_bisection(self, unit, time, events,
62510259SAndrew.Bardsley@arm.com        lower_index, upper_index):
62610259SAndrew.Bardsley@arm.com        """Find an event by binary search on time indices"""
62710259SAndrew.Bardsley@arm.com        while lower_index <= upper_index:
62810259SAndrew.Bardsley@arm.com            pivot = (upper_index + lower_index) / 2
62910259SAndrew.Bardsley@arm.com            pivotEvent = events[pivot]
63010259SAndrew.Bardsley@arm.com            event_equal = (pivotEvent.time == time or
63110259SAndrew.Bardsley@arm.com                (pivotEvent.time < time and
63210259SAndrew.Bardsley@arm.com                    (pivot == len(events) - 1 or
63310259SAndrew.Bardsley@arm.com                        events[pivot + 1].time > time)))
63410259SAndrew.Bardsley@arm.com
63510259SAndrew.Bardsley@arm.com            if event_equal:
63610259SAndrew.Bardsley@arm.com                return pivotEvent
63710259SAndrew.Bardsley@arm.com            elif time > pivotEvent.time:
63810259SAndrew.Bardsley@arm.com                if pivot == upper_index:
63910259SAndrew.Bardsley@arm.com                    return None
64010259SAndrew.Bardsley@arm.com                else:
64110259SAndrew.Bardsley@arm.com                    lower_index = pivot + 1
64210259SAndrew.Bardsley@arm.com            elif time < pivotEvent.time:
64310259SAndrew.Bardsley@arm.com                if pivot == lower_index:
64410259SAndrew.Bardsley@arm.com                    return None
64510259SAndrew.Bardsley@arm.com                else:
64610259SAndrew.Bardsley@arm.com                    upper_index = pivot - 1
64710259SAndrew.Bardsley@arm.com            else:
64810259SAndrew.Bardsley@arm.com                return None
64910259SAndrew.Bardsley@arm.com        return None
65010259SAndrew.Bardsley@arm.com
65110259SAndrew.Bardsley@arm.com    def find_unit_event_by_time(self, unit, time):
65210259SAndrew.Bardsley@arm.com        """Find the last event for the given unit at time <= time"""
65310259SAndrew.Bardsley@arm.com        if unit in self.unitEvents:
65410259SAndrew.Bardsley@arm.com            events = self.unitEvents[unit]
65510259SAndrew.Bardsley@arm.com            ret = self.find_event_bisection(unit, time, events,
65610259SAndrew.Bardsley@arm.com                0, len(events)-1)
65710259SAndrew.Bardsley@arm.com
65810259SAndrew.Bardsley@arm.com            return ret
65910259SAndrew.Bardsley@arm.com        else:
66010259SAndrew.Bardsley@arm.com            return None
66110259SAndrew.Bardsley@arm.com
66210259SAndrew.Bardsley@arm.com    def find_time_index(self, time):
66310259SAndrew.Bardsley@arm.com        """Find a time index close to the given time (where
66410259SAndrew.Bardsley@arm.com        times[return] <= time and times[return+1] > time"""
66510259SAndrew.Bardsley@arm.com        ret = 0
66610259SAndrew.Bardsley@arm.com        lastIndex = len(self.times) - 1
66710259SAndrew.Bardsley@arm.com        while ret < lastIndex and self.times[ret + 1] <= time:
66810259SAndrew.Bardsley@arm.com            ret += 1
66910259SAndrew.Bardsley@arm.com        return ret
67010259SAndrew.Bardsley@arm.com
67110259SAndrew.Bardsley@arm.com    def add_minor_inst(self, rest):
67210259SAndrew.Bardsley@arm.com        """Parse and add a MinorInst line to the model"""
67310259SAndrew.Bardsley@arm.com        pairs = parse.parse_pairs(rest)
67410259SAndrew.Bardsley@arm.com        other_pairs = dict(pairs)
67510259SAndrew.Bardsley@arm.com
67610259SAndrew.Bardsley@arm.com        id = Id().from_string(pairs['id'])
67710259SAndrew.Bardsley@arm.com        del other_pairs['id']
67810259SAndrew.Bardsley@arm.com
67910259SAndrew.Bardsley@arm.com        addr = int(pairs['addr'], 0)
68010259SAndrew.Bardsley@arm.com        del other_pairs['addr']
68110259SAndrew.Bardsley@arm.com
68210259SAndrew.Bardsley@arm.com        if 'inst' in other_pairs:
68310259SAndrew.Bardsley@arm.com            del other_pairs['inst']
68410259SAndrew.Bardsley@arm.com
68510259SAndrew.Bardsley@arm.com            # Collapse unnecessary spaces in disassembly
68610259SAndrew.Bardsley@arm.com            disassembly = re.sub('  *', ' ',
68710259SAndrew.Bardsley@arm.com                re.sub('^ *', '', pairs['inst']))
68810259SAndrew.Bardsley@arm.com
68910259SAndrew.Bardsley@arm.com            inst = Inst(id, disassembly, addr, other_pairs)
69010259SAndrew.Bardsley@arm.com            self.add_inst(inst)
69110259SAndrew.Bardsley@arm.com        elif 'fault' in other_pairs:
69210259SAndrew.Bardsley@arm.com            del other_pairs['fault']
69310259SAndrew.Bardsley@arm.com
69410259SAndrew.Bardsley@arm.com            inst = InstFault(id, pairs['fault'], addr, other_pairs)
69510259SAndrew.Bardsley@arm.com
69610259SAndrew.Bardsley@arm.com            self.add_inst(inst)
69710259SAndrew.Bardsley@arm.com
69810259SAndrew.Bardsley@arm.com    def add_minor_line(self, rest):
69910259SAndrew.Bardsley@arm.com        """Parse and add a MinorLine line to the model"""
70010259SAndrew.Bardsley@arm.com        pairs = parse.parse_pairs(rest)
70110259SAndrew.Bardsley@arm.com        other_pairs = dict(pairs)
70210259SAndrew.Bardsley@arm.com
70310259SAndrew.Bardsley@arm.com        id = Id().from_string(pairs['id'])
70410259SAndrew.Bardsley@arm.com        del other_pairs['id']
70510259SAndrew.Bardsley@arm.com
70610259SAndrew.Bardsley@arm.com        vaddr = int(pairs['vaddr'], 0)
70710259SAndrew.Bardsley@arm.com        del other_pairs['vaddr']
70810259SAndrew.Bardsley@arm.com
70910259SAndrew.Bardsley@arm.com        if 'paddr' in other_pairs:
71010259SAndrew.Bardsley@arm.com            del other_pairs['paddr']
71110259SAndrew.Bardsley@arm.com            del other_pairs['size']
71210259SAndrew.Bardsley@arm.com            paddr = int(pairs['paddr'], 0)
71310259SAndrew.Bardsley@arm.com            size = int(pairs['size'], 0)
71410259SAndrew.Bardsley@arm.com
71510259SAndrew.Bardsley@arm.com            self.add_line(Line(id,
71610259SAndrew.Bardsley@arm.com                vaddr, paddr, size, other_pairs))
71710259SAndrew.Bardsley@arm.com        elif 'fault' in other_pairs:
71810259SAndrew.Bardsley@arm.com            del other_pairs['fault']
71910259SAndrew.Bardsley@arm.com
72010259SAndrew.Bardsley@arm.com            self.add_line(LineFault(id, pairs['fault'], vaddr, other_pairs))
72110259SAndrew.Bardsley@arm.com
72210259SAndrew.Bardsley@arm.com    def load_events(self, file, startTime=0, endTime=None):
72310259SAndrew.Bardsley@arm.com        """Load an event file and add everything to this model"""
72410259SAndrew.Bardsley@arm.com        def update_comments(comments, time):
72510259SAndrew.Bardsley@arm.com            # Add a list of comments to an existing event, if there is one at
72610259SAndrew.Bardsley@arm.com            #   the given time, or create a new, correctly-timed, event from
72710259SAndrew.Bardsley@arm.com            #   the last event and attach the comments to that
72810259SAndrew.Bardsley@arm.com            for commentUnit, commentRest in comments:
72910259SAndrew.Bardsley@arm.com                event = self.find_unit_event_by_time(commentUnit, time)
73010259SAndrew.Bardsley@arm.com                # Find an event to which this comment can be attached
73110259SAndrew.Bardsley@arm.com                if event is None:
73210259SAndrew.Bardsley@arm.com                    # No older event, make a new empty one
73310259SAndrew.Bardsley@arm.com                    event = BlobEvent(commentUnit, time, {})
73410259SAndrew.Bardsley@arm.com                    self.add_unit_event(event)
73510259SAndrew.Bardsley@arm.com                elif event.time != time:
73610259SAndrew.Bardsley@arm.com                    # Copy the old event and make a new one with the right
73710259SAndrew.Bardsley@arm.com                    #   time and comment
73810259SAndrew.Bardsley@arm.com                    newEvent = BlobEvent(commentUnit, time, event.pairs)
73910259SAndrew.Bardsley@arm.com                    newEvent.visuals = dict(event.visuals)
74010259SAndrew.Bardsley@arm.com                    event = newEvent
74110259SAndrew.Bardsley@arm.com                    self.add_unit_event(event)
74210259SAndrew.Bardsley@arm.com                event.comments.append(commentRest)
74310259SAndrew.Bardsley@arm.com
74410259SAndrew.Bardsley@arm.com        self.clear_events()
74510259SAndrew.Bardsley@arm.com
74610259SAndrew.Bardsley@arm.com        # A negative time will *always* be different from an event time
74710259SAndrew.Bardsley@arm.com        time = -1
74810259SAndrew.Bardsley@arm.com        time_events = {}
74910259SAndrew.Bardsley@arm.com        last_time_lines = {}
75010259SAndrew.Bardsley@arm.com        minor_trace_line_count = 0
75110259SAndrew.Bardsley@arm.com        comments = []
75210259SAndrew.Bardsley@arm.com
75310259SAndrew.Bardsley@arm.com        default_colour = [[colours.unknownColour]]
75410259SAndrew.Bardsley@arm.com        next_progress_print_event_count = 1000
75510259SAndrew.Bardsley@arm.com
75610259SAndrew.Bardsley@arm.com        if not os.access(file, os.R_OK):
75710259SAndrew.Bardsley@arm.com            print 'Can\'t open file', file
75810259SAndrew.Bardsley@arm.com            exit(1)
75910259SAndrew.Bardsley@arm.com        else:
76010259SAndrew.Bardsley@arm.com            print 'Opening file', file
76110259SAndrew.Bardsley@arm.com
76210259SAndrew.Bardsley@arm.com        f = open(file)
76310259SAndrew.Bardsley@arm.com
76410259SAndrew.Bardsley@arm.com        start_wall_time = wall_time()
76510259SAndrew.Bardsley@arm.com
76610259SAndrew.Bardsley@arm.com        # Skip leading events
76710259SAndrew.Bardsley@arm.com        still_skipping = True
76810259SAndrew.Bardsley@arm.com        l = f.readline()
76910259SAndrew.Bardsley@arm.com        while l and still_skipping:
77010259SAndrew.Bardsley@arm.com            match = re.match('^\s*(\d+):', l)
77110259SAndrew.Bardsley@arm.com            if match is not None:
77210259SAndrew.Bardsley@arm.com                event_time = match.groups()
77310259SAndrew.Bardsley@arm.com                if int(event_time[0]) >= startTime:
77410259SAndrew.Bardsley@arm.com                    still_skipping = False
77510259SAndrew.Bardsley@arm.com                else:
77610259SAndrew.Bardsley@arm.com                    l = f.readline()
77710259SAndrew.Bardsley@arm.com            else:
77810259SAndrew.Bardsley@arm.com                l = f.readline()
77910259SAndrew.Bardsley@arm.com
78010259SAndrew.Bardsley@arm.com        match_line_re = re.compile(
78110259SAndrew.Bardsley@arm.com            '^\s*(\d+):\s*([\w\.]+):\s*(Minor\w+:)?\s*(.*)$')
78210259SAndrew.Bardsley@arm.com
78310259SAndrew.Bardsley@arm.com        # Parse each line of the events file, accumulating comments to be
78410259SAndrew.Bardsley@arm.com        #   attached to MinorTrace events when the time changes
78510259SAndrew.Bardsley@arm.com        reached_end_time = False
78610259SAndrew.Bardsley@arm.com        while not reached_end_time and l:
78710259SAndrew.Bardsley@arm.com            match = match_line_re.match(l)
78810259SAndrew.Bardsley@arm.com            if match is not None:
78910259SAndrew.Bardsley@arm.com                event_time, unit, line_type, rest = match.groups()
79010259SAndrew.Bardsley@arm.com                event_time = int(event_time)
79110259SAndrew.Bardsley@arm.com
79210259SAndrew.Bardsley@arm.com                unit = re.sub('^' + self.unitNamePrefix + '\.?(.*)$',
79310259SAndrew.Bardsley@arm.com                    '\\1', unit)
79410259SAndrew.Bardsley@arm.com
79510259SAndrew.Bardsley@arm.com                # When the time changes, resolve comments
79610259SAndrew.Bardsley@arm.com                if event_time != time:
79710259SAndrew.Bardsley@arm.com                    if self.numEvents > next_progress_print_event_count:
79810259SAndrew.Bardsley@arm.com                        print ('Parsed to time: %d' % event_time)
79910259SAndrew.Bardsley@arm.com                        next_progress_print_event_count = (
80010259SAndrew.Bardsley@arm.com                            self.numEvents + 1000)
80110259SAndrew.Bardsley@arm.com                    update_comments(comments, time)
80210259SAndrew.Bardsley@arm.com                    comments = []
80310259SAndrew.Bardsley@arm.com                    time = event_time
80410259SAndrew.Bardsley@arm.com
80510259SAndrew.Bardsley@arm.com                if line_type is None:
80610259SAndrew.Bardsley@arm.com                    # Treat this line as just a 'comment'
80710259SAndrew.Bardsley@arm.com                    comments.append((unit, rest))
80810259SAndrew.Bardsley@arm.com                elif line_type == 'MinorTrace:':
80910259SAndrew.Bardsley@arm.com                    minor_trace_line_count += 1
81010259SAndrew.Bardsley@arm.com
81110259SAndrew.Bardsley@arm.com                    # Only insert this event if it's not the same as
81210259SAndrew.Bardsley@arm.com                    #   the last event we saw for this unit
81310259SAndrew.Bardsley@arm.com                    if last_time_lines.get(unit, None) != rest:
81410259SAndrew.Bardsley@arm.com                        event = BlobEvent(unit, event_time, {})
81510259SAndrew.Bardsley@arm.com                        pairs = parse.parse_pairs(rest)
81610259SAndrew.Bardsley@arm.com                        event.pairs = pairs
81710259SAndrew.Bardsley@arm.com
81810259SAndrew.Bardsley@arm.com                        # Try to decode the colour data for this event
81910259SAndrew.Bardsley@arm.com                        blobs = self.unitNameToBlobs.get(unit, [])
82010259SAndrew.Bardsley@arm.com                        for blob in blobs:
82110259SAndrew.Bardsley@arm.com                            if blob.visualDecoder is not None:
82210259SAndrew.Bardsley@arm.com                                event.visuals[blob.picChar] = (
82310259SAndrew.Bardsley@arm.com                                    blob.visualDecoder(pairs))
82410259SAndrew.Bardsley@arm.com
82510259SAndrew.Bardsley@arm.com                        self.add_unit_event(event)
82610259SAndrew.Bardsley@arm.com                        last_time_lines[unit] = rest
82710259SAndrew.Bardsley@arm.com                elif line_type == 'MinorInst:':
82810259SAndrew.Bardsley@arm.com                    self.add_minor_inst(rest)
82910259SAndrew.Bardsley@arm.com                elif line_type == 'MinorLine:':
83010259SAndrew.Bardsley@arm.com                    self.add_minor_line(rest)
83110259SAndrew.Bardsley@arm.com
83210259SAndrew.Bardsley@arm.com            if endTime is not None and time > endTime:
83310259SAndrew.Bardsley@arm.com                reached_end_time = True
83410259SAndrew.Bardsley@arm.com
83510259SAndrew.Bardsley@arm.com            l = f.readline()
83610259SAndrew.Bardsley@arm.com
83710259SAndrew.Bardsley@arm.com        update_comments(comments, time)
83810259SAndrew.Bardsley@arm.com        self.extract_times()
83910259SAndrew.Bardsley@arm.com        f.close()
84010259SAndrew.Bardsley@arm.com
84110259SAndrew.Bardsley@arm.com        end_wall_time = wall_time()
84210259SAndrew.Bardsley@arm.com
84310259SAndrew.Bardsley@arm.com        print 'Total events:', minor_trace_line_count, 'unique events:', \
84410259SAndrew.Bardsley@arm.com            self.numEvents
84510259SAndrew.Bardsley@arm.com        print 'Time to parse:', end_wall_time - start_wall_time
84610259SAndrew.Bardsley@arm.com
84710259SAndrew.Bardsley@arm.com    def add_blob_picture(self, offset, pic, nameDict):
84810259SAndrew.Bardsley@arm.com        """Add a parsed ASCII-art pipeline markup to the model"""
84910259SAndrew.Bardsley@arm.com        pic_width = 0
85010259SAndrew.Bardsley@arm.com        for line in pic:
85110259SAndrew.Bardsley@arm.com            pic_width = max(pic_width, len(line))
85210259SAndrew.Bardsley@arm.com        pic_height = len(pic)
85310259SAndrew.Bardsley@arm.com
85410259SAndrew.Bardsley@arm.com        # Number of horizontal characters per 'pixel'.  Should be 2
85510259SAndrew.Bardsley@arm.com        charsPerPixel = 2
85610259SAndrew.Bardsley@arm.com
85710259SAndrew.Bardsley@arm.com        # Clean up pic_width to a multiple of charsPerPixel
85810259SAndrew.Bardsley@arm.com        pic_width = (pic_width + charsPerPixel - 1) // 2
85910259SAndrew.Bardsley@arm.com
86010259SAndrew.Bardsley@arm.com        self.picSize = Point(pic_width, pic_height)
86110259SAndrew.Bardsley@arm.com
86210259SAndrew.Bardsley@arm.com        def pic_at(point):
86310259SAndrew.Bardsley@arm.com            """Return the char pair at the given point.
86410259SAndrew.Bardsley@arm.com            Returns None for characters off the picture"""
86510259SAndrew.Bardsley@arm.com            x, y = point.to_pair()
86610259SAndrew.Bardsley@arm.com            x *= 2
86710259SAndrew.Bardsley@arm.com            if y >= len(pic) or x >= len(pic[y]):
86810259SAndrew.Bardsley@arm.com                return None
86910259SAndrew.Bardsley@arm.com            else:
87010259SAndrew.Bardsley@arm.com                return pic[y][x:x + charsPerPixel]
87110259SAndrew.Bardsley@arm.com
87210259SAndrew.Bardsley@arm.com        def clear_pic_at(point):
87310259SAndrew.Bardsley@arm.com            """Clear the chars at point so we don't trip over them again"""
87410259SAndrew.Bardsley@arm.com            line = pic[point.y]
87510259SAndrew.Bardsley@arm.com            x = point.x * charsPerPixel
87610259SAndrew.Bardsley@arm.com            pic[point.y] = line[0:x] + (' ' * charsPerPixel) + \
87710259SAndrew.Bardsley@arm.com                line[x + charsPerPixel:]
87810259SAndrew.Bardsley@arm.com
87910259SAndrew.Bardsley@arm.com        def skip_same_char(start, increment):
88010259SAndrew.Bardsley@arm.com            """Skip characters which match pic_at(start)"""
88110259SAndrew.Bardsley@arm.com            char = pic_at(start)
88210259SAndrew.Bardsley@arm.com            hunt = start
88310259SAndrew.Bardsley@arm.com            while pic_at(hunt) == char:
88410259SAndrew.Bardsley@arm.com                hunt += increment
88510259SAndrew.Bardsley@arm.com            return hunt
88610259SAndrew.Bardsley@arm.com
88710259SAndrew.Bardsley@arm.com        def find_size(start):
88810259SAndrew.Bardsley@arm.com            """Find the size of a rectangle with top left hand corner at
88910259SAndrew.Bardsley@arm.com            start consisting of (at least) a -. shaped corner describing
89010259SAndrew.Bardsley@arm.com            the top right corner of a rectangle of the same char"""
89110259SAndrew.Bardsley@arm.com            char = pic_at(start)
89210259SAndrew.Bardsley@arm.com            hunt_x = skip_same_char(start, Point(1,0))
89310259SAndrew.Bardsley@arm.com            hunt_y = skip_same_char(start, Point(0,1))
89410259SAndrew.Bardsley@arm.com            off_bottom_right = (hunt_x * Point(1,0)) + (hunt_y * Point(0,1))
89510259SAndrew.Bardsley@arm.com            return off_bottom_right - start
89610259SAndrew.Bardsley@arm.com
89710259SAndrew.Bardsley@arm.com        def point_return(point):
89810259SAndrew.Bardsley@arm.com            """Carriage return, line feed"""
89910259SAndrew.Bardsley@arm.com            return Point(0, point.y + 1)
90010259SAndrew.Bardsley@arm.com
90110259SAndrew.Bardsley@arm.com        def find_arrow(start):
90210259SAndrew.Bardsley@arm.com            """Find a simple 1-char wide arrow"""
90310259SAndrew.Bardsley@arm.com
90410259SAndrew.Bardsley@arm.com            def body(endChar, contChar, direc):
90510259SAndrew.Bardsley@arm.com                arrow_point = start
90610259SAndrew.Bardsley@arm.com                arrow_point += Point(0, 1)
90710259SAndrew.Bardsley@arm.com                clear_pic_at(start)
90810259SAndrew.Bardsley@arm.com                while pic_at(arrow_point) == contChar:
90910259SAndrew.Bardsley@arm.com                    clear_pic_at(arrow_point)
91010259SAndrew.Bardsley@arm.com                    arrow_point += Point(0, 1)
91110259SAndrew.Bardsley@arm.com
91210259SAndrew.Bardsley@arm.com                if pic_at(arrow_point) == endChar:
91310259SAndrew.Bardsley@arm.com                    clear_pic_at(arrow_point)
91410259SAndrew.Bardsley@arm.com                    self.add_blob(blobs.Arrow('_', start + offset,
91510259SAndrew.Bardsley@arm.com                        direc = direc,
91610259SAndrew.Bardsley@arm.com                        size = (Point(1, 1) + arrow_point - start)))
91710259SAndrew.Bardsley@arm.com                else:
91810259SAndrew.Bardsley@arm.com                    print 'Bad arrow', start
91910259SAndrew.Bardsley@arm.com
92010259SAndrew.Bardsley@arm.com            char = pic_at(start)
92110259SAndrew.Bardsley@arm.com            if char == '-\\':
92210259SAndrew.Bardsley@arm.com                body('-/', ' :', 'right')
92310259SAndrew.Bardsley@arm.com            elif char == '/-':
92410259SAndrew.Bardsley@arm.com                body('\\-', ': ', 'left')
92510259SAndrew.Bardsley@arm.com
92610259SAndrew.Bardsley@arm.com        blank_chars = ['  ', ' :', ': ']
92710259SAndrew.Bardsley@arm.com
92810259SAndrew.Bardsley@arm.com        # Traverse the picture left to right, top to bottom to find blobs
92910259SAndrew.Bardsley@arm.com        seen_dict = {}
93010259SAndrew.Bardsley@arm.com        point = Point(0,0)
93110259SAndrew.Bardsley@arm.com        while pic_at(point) is not None:
93210259SAndrew.Bardsley@arm.com            while pic_at(point) is not None:
93310259SAndrew.Bardsley@arm.com                char = pic_at(point)
93410259SAndrew.Bardsley@arm.com                if char == '->':
93510259SAndrew.Bardsley@arm.com                    self.add_blob(blobs.Arrow('_', point + offset,
93610259SAndrew.Bardsley@arm.com                        direc = 'right'))
93710259SAndrew.Bardsley@arm.com                elif char == '<-':
93810259SAndrew.Bardsley@arm.com                    self.add_blob(blobs.Arrow('_', point + offset,
93910259SAndrew.Bardsley@arm.com                        direc = 'left'))
94010259SAndrew.Bardsley@arm.com                elif char == '-\\' or char == '/-':
94110259SAndrew.Bardsley@arm.com                    find_arrow(point)
94210259SAndrew.Bardsley@arm.com                elif char in blank_chars:
94310259SAndrew.Bardsley@arm.com                    pass
94410259SAndrew.Bardsley@arm.com                else:
94510259SAndrew.Bardsley@arm.com                    if char not in seen_dict:
94610259SAndrew.Bardsley@arm.com                        size = find_size(point)
94710259SAndrew.Bardsley@arm.com                        topLeft = point + offset
94810259SAndrew.Bardsley@arm.com                        if char not in nameDict:
94910259SAndrew.Bardsley@arm.com                            # Unnamed blobs
95010259SAndrew.Bardsley@arm.com                            self.add_blob(blobs.Block(char,
95110259SAndrew.Bardsley@arm.com                                nameDict.get(char, '_'),
95210259SAndrew.Bardsley@arm.com                                topLeft, size = size))
95310259SAndrew.Bardsley@arm.com                        else:
95410259SAndrew.Bardsley@arm.com                            # Named blobs, set visual info.
95510259SAndrew.Bardsley@arm.com                            blob = nameDict[char]
95610259SAndrew.Bardsley@arm.com                            blob.size = size
95710259SAndrew.Bardsley@arm.com                            blob.topLeft = topLeft
95810259SAndrew.Bardsley@arm.com                            self.add_blob(blob)
95910259SAndrew.Bardsley@arm.com                    seen_dict[char] = True
96010259SAndrew.Bardsley@arm.com                point = skip_same_char(point, Point(1,0))
96110259SAndrew.Bardsley@arm.com            point = point_return(point)
96210259SAndrew.Bardsley@arm.com
96310259SAndrew.Bardsley@arm.com    def load_picture(self, filename):
96410259SAndrew.Bardsley@arm.com        """Load a picture file into the model"""
96510259SAndrew.Bardsley@arm.com        def parse_blob_description(char, unit, macros, pairsList):
96610259SAndrew.Bardsley@arm.com            # Parse the name value pairs in a blob-describing line
96710259SAndrew.Bardsley@arm.com            def expand_macros(pairs, newPairs):
96810259SAndrew.Bardsley@arm.com                # Recursively expand macros
96910259SAndrew.Bardsley@arm.com                for name, value in newPairs:
97010259SAndrew.Bardsley@arm.com                    if name in macros:
97110259SAndrew.Bardsley@arm.com                        expand_macros(pairs, macros[name])
97210259SAndrew.Bardsley@arm.com                    else:
97310259SAndrew.Bardsley@arm.com                        pairs[name] = value
97410259SAndrew.Bardsley@arm.com                return pairs
97510259SAndrew.Bardsley@arm.com
97610259SAndrew.Bardsley@arm.com            pairs = expand_macros({}, pairsList)
97710259SAndrew.Bardsley@arm.com
97810259SAndrew.Bardsley@arm.com            ret = None
97910259SAndrew.Bardsley@arm.com
98010259SAndrew.Bardsley@arm.com            typ = pairs.get('type', 'block')
98110259SAndrew.Bardsley@arm.com            colour = colours.name_to_colour(pairs.get('colour', 'black'))
98210259SAndrew.Bardsley@arm.com
98310259SAndrew.Bardsley@arm.com            if typ == 'key':
98410259SAndrew.Bardsley@arm.com                ret = blobs.Key(char, unit, Point(0,0), colour)
98510259SAndrew.Bardsley@arm.com            elif typ == 'block':
98610259SAndrew.Bardsley@arm.com                ret = blobs.Block(char, unit, Point(0,0), colour)
98710259SAndrew.Bardsley@arm.com            else:
98810259SAndrew.Bardsley@arm.com                print "Bad picture blog type:", typ
98910259SAndrew.Bardsley@arm.com
99010259SAndrew.Bardsley@arm.com            if 'hideId' in pairs:
99110259SAndrew.Bardsley@arm.com                hide = pairs['hideId']
99210259SAndrew.Bardsley@arm.com                ret.dataSelect.ids -= set(hide)
99310259SAndrew.Bardsley@arm.com
99410259SAndrew.Bardsley@arm.com            if typ == 'block':
99510259SAndrew.Bardsley@arm.com                ret.displayName = pairs.get('name', unit)
99610259SAndrew.Bardsley@arm.com                ret.nameLoc = pairs.get('nameLoc', 'top')
99710259SAndrew.Bardsley@arm.com                ret.shape = pairs.get('shape', 'box')
99810259SAndrew.Bardsley@arm.com                ret.stripDir = pairs.get('stripDir', 'horiz')
99910259SAndrew.Bardsley@arm.com                ret.stripOrd = pairs.get('stripOrd', 'LR')
100010259SAndrew.Bardsley@arm.com                ret.blankStrips = int(pairs.get('blankStrips', '0'))
100110259SAndrew.Bardsley@arm.com                ret.shorten = int(pairs.get('shorten', '0'))
100210259SAndrew.Bardsley@arm.com
100310259SAndrew.Bardsley@arm.com                if 'decoder' in pairs:
100410259SAndrew.Bardsley@arm.com                    decoderName = pairs['decoder']
100510259SAndrew.Bardsley@arm.com                    dataElement = pairs.get('dataElement', decoderName)
100610259SAndrew.Bardsley@arm.com
100710259SAndrew.Bardsley@arm.com                    decoder = find_colour_decoder(ret.blankStrips,
100810259SAndrew.Bardsley@arm.com                        decoderName, dataElement, pairs)
100910259SAndrew.Bardsley@arm.com                    if decoder is not None:
101010259SAndrew.Bardsley@arm.com                        ret.visualDecoder = decoder
101110259SAndrew.Bardsley@arm.com                    else:
101210259SAndrew.Bardsley@arm.com                        print 'Bad visualDecoder requested:', decoderName
101310259SAndrew.Bardsley@arm.com
101410259SAndrew.Bardsley@arm.com                if 'border' in pairs:
101510259SAndrew.Bardsley@arm.com                    border = pairs['border']
101610259SAndrew.Bardsley@arm.com                    if border == 'thin':
101710259SAndrew.Bardsley@arm.com                        ret.border = 0.2
101810259SAndrew.Bardsley@arm.com                    elif border == 'mid':
101910259SAndrew.Bardsley@arm.com                        ret.border = 0.5
102010259SAndrew.Bardsley@arm.com                    else:
102110259SAndrew.Bardsley@arm.com                        ret.border = 1.0
102210259SAndrew.Bardsley@arm.com            elif typ == 'key':
102310259SAndrew.Bardsley@arm.com                ret.colours = pairs.get('colours', ret.colours)
102410259SAndrew.Bardsley@arm.com
102510259SAndrew.Bardsley@arm.com            return ret
102610259SAndrew.Bardsley@arm.com
102710259SAndrew.Bardsley@arm.com        def line_is_comment(line):
102810259SAndrew.Bardsley@arm.com            """Returns true if a line starts with #, returns False
102910259SAndrew.Bardsley@arm.com            for lines which are None"""
103010259SAndrew.Bardsley@arm.com            return line is not None \
103110259SAndrew.Bardsley@arm.com                and re.match('^\s*#', line) is not None
103210259SAndrew.Bardsley@arm.com
103310259SAndrew.Bardsley@arm.com        def get_line(f):
103410259SAndrew.Bardsley@arm.com            """Get a line from file f extending that line if it ends in
103510259SAndrew.Bardsley@arm.com            '\' and dropping lines that start with '#'s"""
103610259SAndrew.Bardsley@arm.com            ret = f.readline()
103710259SAndrew.Bardsley@arm.com
103810259SAndrew.Bardsley@arm.com            # Discard comment lines
103910259SAndrew.Bardsley@arm.com            while line_is_comment(ret):
104010259SAndrew.Bardsley@arm.com                ret = f.readline()
104110259SAndrew.Bardsley@arm.com
104210259SAndrew.Bardsley@arm.com            if ret is not None:
104310259SAndrew.Bardsley@arm.com                extend_match = re.match('^(.*)\\\\$', ret)
104410259SAndrew.Bardsley@arm.com
104510259SAndrew.Bardsley@arm.com                while extend_match is not None:
104610259SAndrew.Bardsley@arm.com                    new_line = f.readline()
104710259SAndrew.Bardsley@arm.com
104810259SAndrew.Bardsley@arm.com                    if new_line is not None and not line_is_comment(new_line):
104910259SAndrew.Bardsley@arm.com                        line_wo_backslash, = extend_match.groups()
105010259SAndrew.Bardsley@arm.com                        ret = line_wo_backslash + new_line
105110259SAndrew.Bardsley@arm.com                        extend_match = re.match('^(.*)\\\\$', ret)
105210259SAndrew.Bardsley@arm.com                    else:
105310259SAndrew.Bardsley@arm.com                        extend_match = None
105410259SAndrew.Bardsley@arm.com
105510259SAndrew.Bardsley@arm.com            return ret
105610259SAndrew.Bardsley@arm.com
105710259SAndrew.Bardsley@arm.com        # Macros are recursively expanded into name=value pairs
105810259SAndrew.Bardsley@arm.com        macros = {}
105910259SAndrew.Bardsley@arm.com
106010259SAndrew.Bardsley@arm.com        if not os.access(filename, os.R_OK):
106110259SAndrew.Bardsley@arm.com            print 'Can\'t open file', filename
106210259SAndrew.Bardsley@arm.com            exit(1)
106310259SAndrew.Bardsley@arm.com        else:
106410259SAndrew.Bardsley@arm.com            print 'Opening file', filename
106510259SAndrew.Bardsley@arm.com
106610259SAndrew.Bardsley@arm.com        f = open(filename)
106710259SAndrew.Bardsley@arm.com        l = get_line(f)
106810259SAndrew.Bardsley@arm.com        picture = []
106910259SAndrew.Bardsley@arm.com        blob_char_dict = {}
107010259SAndrew.Bardsley@arm.com
107110259SAndrew.Bardsley@arm.com        self.unitEvents = {}
107210259SAndrew.Bardsley@arm.com        self.clear_events()
107310259SAndrew.Bardsley@arm.com
107410259SAndrew.Bardsley@arm.com        # Actually parse the file
107510259SAndrew.Bardsley@arm.com        in_picture = False
107610259SAndrew.Bardsley@arm.com        while l:
107710259SAndrew.Bardsley@arm.com            l = parse.remove_trailing_ws(l)
107810259SAndrew.Bardsley@arm.com            l = re.sub('#.*', '', l)
107910259SAndrew.Bardsley@arm.com
108010259SAndrew.Bardsley@arm.com            if re.match("^\s*$", l) is not None:
108110259SAndrew.Bardsley@arm.com                pass
108210259SAndrew.Bardsley@arm.com            elif l == '<<<':
108310259SAndrew.Bardsley@arm.com                in_picture = True
108410259SAndrew.Bardsley@arm.com            elif l == '>>>':
108510259SAndrew.Bardsley@arm.com                in_picture = False
108610259SAndrew.Bardsley@arm.com            elif in_picture:
108710259SAndrew.Bardsley@arm.com                picture.append(re.sub('\s*$', '', l))
108810259SAndrew.Bardsley@arm.com            else:
108910259SAndrew.Bardsley@arm.com                line_match = re.match(
109010259SAndrew.Bardsley@arm.com                    '^([a-zA-Z0-9][a-zA-Z0-9]):\s+([\w.]+)\s*(.*)', l)
109110259SAndrew.Bardsley@arm.com                macro_match = re.match('macro\s+(\w+):(.*)', l)
109210259SAndrew.Bardsley@arm.com
109310259SAndrew.Bardsley@arm.com                if macro_match is not None:
109410259SAndrew.Bardsley@arm.com                    name, defn = macro_match.groups()
109510259SAndrew.Bardsley@arm.com                    macros[name] = parse.parse_pairs_list(defn)
109610259SAndrew.Bardsley@arm.com                elif line_match is not None:
109710259SAndrew.Bardsley@arm.com                    char, unit, pairs = line_match.groups()
109810259SAndrew.Bardsley@arm.com                    blob = parse_blob_description(char, unit, macros,
109910259SAndrew.Bardsley@arm.com                        parse.parse_pairs_list(pairs))
110010259SAndrew.Bardsley@arm.com                    blob_char_dict[char] = blob
110110259SAndrew.Bardsley@arm.com                    # Setup the events structure
110210259SAndrew.Bardsley@arm.com                    self.unitEvents[unit] = []
110310259SAndrew.Bardsley@arm.com                else:
110410259SAndrew.Bardsley@arm.com                    print 'Problem with Blob line:', l
110510259SAndrew.Bardsley@arm.com
110610259SAndrew.Bardsley@arm.com            l = get_line(f)
110710259SAndrew.Bardsley@arm.com
110810259SAndrew.Bardsley@arm.com        self.blobs = []
110910259SAndrew.Bardsley@arm.com        self.add_blob_picture(Point(0,1), picture, blob_char_dict)
1110