base: support dynamic loading of Linux ELF objects in SE mode
[gem5.git] / util / minorview / blobs.py
1 # Copyright (c) 2013 ARM Limited
2 # All rights reserved
3 #
4 # The license below extends only to copyright in the software and shall
5 # not be construed as granting a license to any other intellectual
6 # property including but not limited to intellectual property relating
7 # to a hardware implementation of the functionality of the software
8 # licensed hereunder. You may use the software subject to the license
9 # terms below provided that you ensure that this notice is replicated
10 # unmodified and in its entirety in all distributions of the software,
11 # modified or unmodified, in source code or in binary form.
12 #
13 # Redistribution and use in source and binary forms, with or without
14 # modification, are permitted provided that the following conditions are
15 # met: redistributions of source code must retain the above copyright
16 # notice, this list of conditions and the following disclaimer;
17 # redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution;
20 # neither the name of the copyright holders nor the names of its
21 # contributors may be used to endorse or promote products derived from
22 # this software without specific prior written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #
36 # Authors: Andrew Bardsley
37 #
38 # blobs.py: Blobs are the visual blocks, arrows and other coloured
39 # objects on the visualiser. This file contains Blob definition and
40 # their rendering instructions in pygtk/cairo.
41 #
42
43 import pygtk
44 pygtk.require('2.0')
45 import gtk
46 import gobject
47 import cairo
48 import re
49 import math
50
51 from point import Point
52 import parse
53 import colours
54 from colours import backgroundColour, black
55 import model
56
57 def centre_size_to_sides(centre, size):
58 """Returns a 4-tuple of the relevant ordinates of the left,
59 right, top and bottom sides of the described rectangle"""
60 (x, y) = centre.to_pair()
61 (half_width, half_height) = (size.scale(0.5)).to_pair()
62 left = x - half_width
63 right = x + half_width
64 top = y - half_height
65 bottom = y + half_height
66 return (left, right, top, bottom)
67
68 def box(cr, centre, size):
69 """Draw a simple box"""
70 (left, right, top, bottom) = centre_size_to_sides(centre, size)
71 cr.move_to(left, top)
72 cr.line_to(right, top)
73 cr.line_to(right, bottom)
74 cr.line_to(left, bottom)
75 cr.close_path()
76
77 def stroke_and_fill(cr, colour):
78 """Stroke with the current colour then fill the same path with the
79 given colour"""
80 join = cr.get_line_join()
81 cr.set_line_join(gtk.gdk.JOIN_ROUND)
82 cr.close_path()
83 cr.set_source_color(backgroundColour)
84 cr.stroke_preserve()
85 cr.set_source_color(colour)
86 cr.fill()
87 cr.set_line_join(join)
88
89 def striped_box(cr, centre, size, colours):
90 """Fill a rectangle (without outline) striped with the colours given"""
91 num_colours = len(colours)
92 if num_colours == 0:
93 box(cr, centre, size)
94 cr.set_source_color(backgroundColour)
95 cr.fill()
96 elif num_colours == 1:
97 box(cr, centre, size)
98 stroke_and_fill(cr, colours[0])
99 else:
100 (left, right, top, bottom) = centre_size_to_sides(centre, size)
101 (width, height) = size.to_pair()
102 x_stripe_width = width / num_colours
103 half_x_stripe_width = x_stripe_width / 2.0
104 # Left triangle
105 cr.move_to(left, bottom)
106 cr.line_to(left + half_x_stripe_width, bottom)
107 cr.line_to(left + x_stripe_width + half_x_stripe_width, top)
108 cr.line_to(left, top)
109 stroke_and_fill(cr, colours[0])
110 # Stripes
111 for i in xrange(1, num_colours - 1):
112 xOffset = x_stripe_width * i
113 cr.move_to(left + xOffset - half_x_stripe_width, bottom)
114 cr.line_to(left + xOffset + half_x_stripe_width, bottom)
115 cr.line_to(left + xOffset + x_stripe_width +
116 half_x_stripe_width, top)
117 cr.line_to(left + xOffset + x_stripe_width -
118 half_x_stripe_width, top)
119 stroke_and_fill(cr, colours[i])
120 # Right triangle
121 cr.move_to((right - x_stripe_width) - half_x_stripe_width, bottom)
122 cr.line_to(right, bottom)
123 cr.line_to(right, top)
124 cr.line_to((right - x_stripe_width) + half_x_stripe_width, top)
125 stroke_and_fill(cr, colours[num_colours - 1])
126
127 def speech_bubble(cr, top_left, size, unit):
128 """Draw a speech bubble with 'size'-sized internal space with its
129 top left corner at Point(2.0 * unit, 2.0 * unit)"""
130 def local_arc(centre, angleFrom, angleTo):
131 cr.arc(centre.x, centre.y, unit, angleFrom * math.pi,
132 angleTo * math.pi)
133
134 cr.move_to(*top_left.to_pair())
135 cr.rel_line_to(unit * 2.0, unit)
136 cr.rel_line_to(size.x, 0.0)
137 local_arc(top_left + Point(size.x + unit * 2.0, unit * 2.0), -0.5, 0.0)
138 cr.rel_line_to(0.0, size.y)
139 local_arc(top_left + Point(size.x + unit * 2.0, size.y + unit * 2.0),
140 0, 0.5)
141 cr.rel_line_to(-size.x, 0.0)
142 local_arc(top_left + Point(unit * 2.0, size.y + unit * 2.0), 0.5, 1.0)
143 cr.rel_line_to(0, -size.y)
144 cr.close_path()
145
146 def open_bottom(cr, centre, size):
147 """Draw a box with left, top and right sides"""
148 (left, right, top, bottom) = centre_size_to_sides(centre, size)
149 cr.move_to(left, bottom)
150 cr.line_to(left, top)
151 cr.line_to(right, top)
152 cr.line_to(right, bottom)
153
154 def fifo(cr, centre, size):
155 """Draw just the vertical sides of a box"""
156 (left, right, top, bottom) = centre_size_to_sides(centre, size)
157 cr.move_to(left, bottom)
158 cr.line_to(left, top)
159 cr.move_to(right, bottom)
160 cr.line_to(right, top)
161
162 def cross(cr, centre, size):
163 """Draw a cross parallel with the axes"""
164 (left, right, top, bottom) = centre_size_to_sides(centre, size)
165 (x, y) = centre.to_pair()
166 cr.move_to(left, y)
167 cr.line_to(right, y)
168 cr.move_to(x, top)
169 cr.line_to(x, bottom)
170
171 class Blob(object):
172 """Blob super class"""
173 def __init__(self, picChar, unit, topLeft, colour, size = Point(1,1)):
174 self.picChar = picChar
175 self.unit = unit
176 self.displayName = unit
177 self.nameLoc = 'top'
178 self.topLeft = topLeft
179 self.colour = colour
180 self.size = size
181 self.border = 1.0
182 self.dataSelect = model.BlobDataSelect()
183 self.shorten = 0
184
185 def render(self, cr, view, event, select, time):
186 """Render this blob with the given event's data. Returns either
187 None or a pair of (centre, size) in device coordinates for the drawn
188 blob. The return value can be used to detect if mouse clicks on
189 the canvas are within the blob"""
190 return None
191
192 class Block(Blob):
193 """Blocks are rectangular blogs colourable with a 2D grid of striped
194 blocks. visualDecoder specifies how event data becomes this coloured
195 grid"""
196 def __init__(self, picChar, unit, topLeft=Point(0,0),
197 colour=colours.black,
198 size=Point(1,1)):
199 super(Block,self).__init__(picChar, unit, topLeft, colour,
200 size = size)
201 # {horiz, vert}
202 self.stripDir = 'horiz'
203 # {LR, RL}: LR means the first strip will be on the left/top,
204 # RL means the first strip will be on the right/bottom
205 self.stripOrd = 'LR'
206 # Number of blank strips if this is a frame
207 self.blankStrips = 0
208 # {box, fifo, openBottom}
209 self.shape = 'box'
210 self.visualDecoder = None
211
212 def render(self, cr, view, event, select, time):
213 # Find the right event, visuals and sizes for things
214 if event is None or self.displayName.startswith('_'):
215 event = model.BlobEvent(self.unit, time)
216
217 if self.picChar in event.visuals:
218 strips = event.visuals[self.picChar].to_striped_block(
219 select & self.dataSelect)
220 else:
221 strips = [[[colours.unknownColour]]]
222
223 if self.stripOrd == 'RL':
224 strips.reverse()
225
226 if len(strips) == 0:
227 strips = [[colours.errorColour]]
228 print 'Problem with the colour of event:', event
229
230 num_strips = len(strips)
231 strip_proportion = 1.0 / num_strips
232 first_strip_offset = (num_strips / 2.0) - 0.5
233
234 # Adjust blocks with 'shorten' attribute to the length of the data
235 size = Point(*self.size.to_pair())
236 if self.shorten != 0 and self.size.x > (num_strips * self.shorten):
237 size.x = num_strips * self.shorten
238
239 box_size = size - view.blobIndentFactor.scale(2)
240
241 # Now do cr sensitive things
242 cr.save()
243 cr.scale(*view.pitch.to_pair())
244 cr.translate(*self.topLeft.to_pair())
245 cr.translate(*(size - Point(1,1)).scale(0.5).to_pair())
246
247 translated_centre = Point(*cr.user_to_device(0.0, 0.0))
248 translated_size = \
249 Point(*cr.user_to_device_distance(*size.to_pair()))
250
251 # The 2D grid is a grid of strips of blocks. Data [[1,2],[3]]
252 # is 2 strips of 2 and 1 blocks respectively.
253 # if stripDir == 'horiz', strips are stacked vertically
254 # from top to bottom if stripOrd == 'LR' or bottom to top if
255 # stripOrd == 'RL'.
256 # if stripDir == 'vert', strips are stacked horizontally
257 # from left to right if stripOf == 'LR' or right to left if
258 # stripOrd == 'RL'.
259
260 strip_is_horiz = self.stripDir == 'horiz'
261
262 if strip_is_horiz:
263 strip_step_base = Point(1.0,0.0)
264 block_step_base = Point(0.0,1.0)
265 else:
266 strip_step_base = Point(0.0,1.0)
267 block_step_base = Point(1.0,0.0)
268
269 strip_size = (box_size * (strip_step_base.scale(strip_proportion) +
270 block_step_base))
271 strip_step = strip_size * strip_step_base
272 strip_centre = Point(0,0) - (strip_size *
273 strip_step_base.scale(first_strip_offset))
274
275 cr.set_line_width(view.midLineWidth / view.pitch.x)
276
277 # Draw the strips and their blocks
278 for strip_index in xrange(0, num_strips):
279 num_blocks = len(strips[strip_index])
280 block_proportion = 1.0 / num_blocks
281 firstBlockOffset = (num_blocks / 2.0) - 0.5
282
283 block_size = (strip_size *
284 (block_step_base.scale(block_proportion) +
285 strip_step_base))
286 block_step = block_size * block_step_base
287 block_centre = (strip_centre + strip_step.scale(strip_index) -
288 (block_size * block_step_base.scale(firstBlockOffset)))
289
290 for block_index in xrange(0, num_blocks):
291 striped_box(cr, block_centre +
292 block_step.scale(block_index), block_size,
293 strips[strip_index][block_index])
294
295 cr.set_font_size(0.7)
296 if self.border > 0.5:
297 weight = cairo.FONT_WEIGHT_BOLD
298 else:
299 weight = cairo.FONT_WEIGHT_NORMAL
300 cr.select_font_face('Helvetica', cairo.FONT_SLANT_NORMAL,
301 weight)
302
303 xb, yb, width, height, dx, dy = cr.text_extents(self.displayName)
304
305 text_comfort_space = 0.15
306
307 if self.nameLoc == 'left':
308 # Position text vertically along left side, top aligned
309 cr.save()
310 cr.rotate(- (math.pi / 2.0))
311 text_point = Point(size.y, size.x).scale(0.5) * Point(-1, -1)
312 text_point += Point(max(0, size.y - width), 0)
313 text_point += Point(-text_comfort_space, -text_comfort_space)
314 else: # Including top
315 # Position text above the top left hand corner
316 text_point = size.scale(0.5) * Point(-1,-1)
317 text_point += Point(0.00, -text_comfort_space)
318
319 if (self.displayName != '' and
320 not self.displayName.startswith('_')):
321 cr.set_source_color(self.colour)
322 cr.move_to(*text_point.to_pair())
323 cr.show_text(self.displayName)
324
325 if self.nameLoc == 'left':
326 cr.restore()
327
328 # Draw the outline shape
329 cr.save()
330 if strip_is_horiz:
331 cr.rotate(- (math.pi / 2.0))
332 box_size = Point(box_size.y, box_size.x)
333
334 if self.stripOrd == "RL":
335 cr.rotate(math.pi)
336
337 if self.shape == 'box':
338 box(cr, Point(0,0), box_size)
339 elif self.shape == 'openBottom':
340 open_bottom(cr, Point(0,0), box_size)
341 elif self.shape == 'fifo':
342 fifo(cr, Point(0,0), box_size)
343 cr.restore()
344
345 # Restore scale and stroke the outline
346 cr.restore()
347 cr.set_source_color(self.colour)
348 cr.set_line_width(view.thickLineWidth * self.border)
349 cr.stroke()
350
351 # Return blob size/position
352 if self.unit == '_':
353 return None
354 else:
355 return (translated_centre, translated_size)
356
357 class Key(Blob):
358 """Draw a key to the special (and numeric colours) with swatches of the
359 colours half as wide as the key"""
360 def __init__(self, picChar, unit, topLeft, colour=colours.black,
361 size=Point(1,1)):
362 super(Key,self).__init__(picChar, unit, topLeft, colour, size = size)
363 self.colours = 'BBBB'
364 self.displayName = unit
365
366 def render(self, cr, view, event, select, time):
367 cr.save()
368 cr.scale(*view.pitch.to_pair())
369 cr.translate(*self.topLeft.to_pair())
370 # cr.translate(*(self.size - Point(1,1)).scale(0.5).to_pair())
371 half_width = self.size.x / 2.0
372 cr.translate(*(self.size - Point(1.0 + half_width,1.0)).scale(0.5).
373 to_pair())
374
375 num_colours = len(self.colours)
376 cr.set_line_width(view.midLineWidth / view.pitch.x)
377
378 blob_size = (Point(half_width,0.0) +
379 (self.size * Point(0.0,1.0 / num_colours)))
380 blob_step = Point(0.0,1.0) * blob_size
381 first_blob_centre = (Point(0.0,0.0) -
382 blob_step.scale((num_colours / 2.0) - 0.5))
383
384 cr.set_source_color(self.colour)
385 cr.set_line_width(view.thinLineWidth / view.pitch.x)
386
387 blob_proportion = 0.8
388
389 real_blob_size = blob_size.scale(blob_proportion)
390
391 cr.set_font_size(0.8 * blob_size.y * blob_proportion)
392 cr.select_font_face('Helvetica', cairo.FONT_SLANT_NORMAL,
393 cairo.FONT_WEIGHT_BOLD)
394
395 for i in xrange(0, num_colours):
396 centre = first_blob_centre + blob_step.scale(i)
397 box(cr, centre, real_blob_size)
398
399 colour_char = self.colours[i]
400 if colour_char.isdigit():
401 cr.set_source_color(colours.number_to_colour(
402 int(colour_char)))
403 label = '...' + colour_char
404 else:
405 cr.set_source_color(model.special_state_colours[colour_char])
406 label = model.special_state_names[colour_char]
407
408 cr.fill_preserve()
409 cr.set_source_color(self.colour)
410 cr.stroke()
411
412 xb, yb, width, height, dx, dy = cr.text_extents(label)
413
414 text_left = (centre + (Point(0.5,0.0) * blob_size) +
415 Point(0.0, height / 2.0))
416
417 cr.move_to(*text_left.to_pair())
418 cr.show_text(label)
419
420 class Arrow(Blob):
421 """Draw a left or right facing arrow"""
422 def __init__(self, unit, topLeft, colour=colours.black,
423 size=Point(1.0,1.0), direc='right'):
424 super(Arrow,self).__init__(unit, unit, topLeft, colour, size = size)
425 self.direc = direc
426
427 def render(self, cr, view, event, select, time):
428 cr.save()
429 cr.scale(*view.pitch.to_pair())
430 cr.translate(*self.topLeft.to_pair())
431 cr.translate(*(self.size - Point(1,1)).scale(0.5).to_pair())
432 cr.scale(*self.size.to_pair())
433 (blob_indent_x, blob_indent_y) = \
434 (view.blobIndentFactor / self.size).to_pair()
435 left = -0.5 - blob_indent_x
436 right = 0.5 + blob_indent_x
437
438 thickness = 0.2
439 flare = 0.2
440
441 if self.direc == 'left':
442 cr.rotate(math.pi)
443
444 cr.move_to(left, -thickness)
445 cr.line_to(0.0, -thickness)
446 cr.line_to(0.0, -(thickness + flare))
447 cr.line_to(right, 0)
448 # Break arrow to prevent the point ruining the appearance of boxes
449 cr.move_to(right, 0)
450 cr.line_to(0.0, (thickness + flare))
451 cr.line_to(0.0, +thickness)
452 cr.line_to(left, +thickness)
453
454 cr.restore()
455
456 # Draw arrow a bit more lightly than the standard line width
457 cr.set_line_width(cr.get_line_width() * 0.75)
458 cr.set_source_color(self.colour)
459 cr.stroke()
460
461 return None