Giacomo Travaglini has uploaded this change for review. (
https://gem5-review.googlesource.com/c/public/gem5/+/41314 )
Change subject: python: Add Range Writer utility
......................................................................
python: Add Range Writer utility
This is adding the range_writer.py module in src/python/m5/util/
The draw_ranges function can be used to draw a list of AddrRanges,
by stacking them (sorted by starting address) together with their
start/end address and their size
This is extremely useful for a user willing to visualize the
memory map of the platform
The module is drawing shapes with the Pillow package:
https://pypi.org/project/Pillow/
If the package is not available the call to draw_ranges will silently
fail and no image will be created
Signed-off-by: Giacomo Travaglini <[email protected]>
Change-Id: I1b6b595c652b4dabff15ba83221d22224c2bb2d6
---
M src/python/SConscript
A src/python/m5/util/range_writer.py
2 files changed, 240 insertions(+), 0 deletions(-)
diff --git a/src/python/SConscript b/src/python/SConscript
index 19f260a..bd08184 100644
--- a/src/python/SConscript
+++ b/src/python/SConscript
@@ -55,6 +55,7 @@
PySource('m5.util', 'm5/util/jobfile.py')
PySource('m5.util', 'm5/util/multidict.py')
PySource('m5.util', 'm5/util/pybind.py')
+PySource('m5.util', 'm5/util/range_writer.py')
PySource('m5.util', 'm5/util/terminal.py')
PySource('m5.util', 'm5/util/terminal_formatter.py')
diff --git a/src/python/m5/util/range_writer.py
b/src/python/m5/util/range_writer.py
new file mode 100644
index 0000000..6ee4c33
--- /dev/null
+++ b/src/python/m5/util/range_writer.py
@@ -0,0 +1,239 @@
+# Copyright (c) 2021 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from collections import OrderedDict
+
+__all__ = [ 'draw_ranges' ]
+
+class BoxSet(object):
+ """
+ Box Set: a global view of the set of instantiated boxes.
+ It is in charge of selecting the appropriate size
+ for each box, depending on the image size, number of boxes, and
+ address range size.
+ """
+
+ # Three box types: SMALL, MEDIUM, LARGE
+ SMALL_BOX = 1
+ MEDIUM_BOX = 2
+ LARGE_BOX = 3
+
+ def __init__(self, memory_ranges):
+ # We cannot store boxes in the box_data dictionary as we need to
keep
+ # them sorted among themselves
+ self.boxes = []
+ self.box_data = OrderedDict({
+ BoxSet.SMALL_BOX : {
+ "number" : 0, "height" : 0,
+ # Every range below 512k will use a small box
+ "threshold" : 512 * 1024,
+ "colour" : (230,230,230), "text_colour": (0,0,0), },
+ BoxSet.MEDIUM_BOX : {
+ "number" : 0, "height" : 0,
+ # Every range below 512MB will use a medium box
+ "threshold" : 512 * 1024 * 1024,
+ "colour" : (125,127,130), "text_colour": (255,255,255), },
+ BoxSet.LARGE_BOX : {
+ "number" : 0, "height" : 0,
+ # Every range below 512GB will use a large box
+ "threshold" : 512 * 1024 * 1024 * 1024,
+ "colour" : (0,149,175), "text_colour": (255,255,255), },
+ })
+
+ for mem_range in memory_ranges:
+ # Unpack parameters
+ range_name = mem_range[1]
+ range_address = mem_range[0]
+
+ for box_type, box_data in self.box_data.items():
+ if range_address.size() < box_data["threshold"]:
+ # Create a new Box for the selected range
+ # and append it to the list of boxes
+ self.append(Box(range_name, box_type, range_address))
+ break
+
+ def __enter__(self):
+ # Calculate height value for a small box
+ min_box_height = 1080 / self._size_in_boxes()
+
+ # Medium and Large boxes will be 2,3 times bigger than
+ # the small one
+ for key, val in self.box_data.items():
+ self.box_data[key]["height"] = min_box_height * key
+
+ return self
+
+ def __exit__(self, type, value, traceback):
+ pass
+
+ def append(self, box):
+ """
+ Add a single box to the BoxSet.
+ """
+ self.box_data[box.box_type]["number"] += 1
+ self.boxes.append(box)
+
+ def _size_in_boxes(self):
+ """
+ Return the global window size as number of small boxes.
+ Small boxes count as 1, Medium as 2 and Large as 3.
+ """
+ acc = 0
+ for key, val in self.box_data.items():
+ acc += val['number'] * key
+ return acc
+
+ def draw(self, draw):
+ """
+ Iterate over the box set and draw
+ """
+ cursor = 0
+ for box in self.boxes:
+ height = self.box_data[box.box_type]['height']
+ colour = self.box_data[box.box_type]['colour']
+ text_colour = self.box_data[box.box_type]['text_colour']
+
+ cursor += box.draw(draw, cursor, height, colour, text_colour)
+
+
+class Box(object):
+ """
+ Box Object: a box is the visual representation of single address range.
+ It is made of a rectangle whose size depends on the address range size
+ and some text describing its properties:
+ * range start address
+ * range end address
+ * size in bytes
+ """
+ x0 = 0
+ x1 = 1080
+
+ def __init__(self, name, box_type, addr_range):
+ self.name = name
+ self.box_type = box_type
+ self.addr_range = addr_range
+
+ def _size_string(self):
+ units = ["GiB", "MiB", "KiB", "B"]
+ size_bytes = self.addr_range.size() / (1024 * 1024 * 1024)
+
+ unit_idx = 0
+ while not size_bytes.is_integer():
+ size_bytes *= 1024
+ unit_idx += 1
+
+ return "size=" + str(int(size_bytes)) + units[unit_idx]
+
+ def draw(self, draw, cursor, height, colour, text_colour):
+ # Draw the box
+ draw.rectangle((self.x0, cursor, self.x1, cursor + height),
+ fill=colour,
+ outline=(255, 255, 255))
+
+ # Write the name of the range in the middle of the box
+ middle_x = (self.x1 - self.x0) // 2
+ middle_y = cursor + (height // 2)
+ middle_point = (middle_x, middle_y)
+
+ draw.text(middle_point, self.name, text_colour)
+
+ # Write the start and end address of the range
+ draw.text((self.x0, cursor),
+ str(hex(self.addr_range.start())), text_colour)
+ draw.text((self.x0, cursor + height),
+ str(hex(self.addr_range.end())), text_colour)
+
+ # Write the size of the range in bytes
+ draw.text((middle_x / 2, middle_y),
+ self._size_string(), text_colour)
+
+ return height
+
+def create_list(addr_ranges, names):
+ from _m5.range import AddrRange
+
+ combined_list = list(zip(addr_ranges, names))
+
+ # Sorting depending on the ranges starting address
+ combined_list.sort(key=lambda x: x[0].start())
+
+ # Introduce unnamed (reserved) ranges to fill the gaps
+ # between two consecutive ranges
+ full_list = []
+ prev_end = 0
+ for memory_range in combined_list:
+ if memory_range[0].start() > prev_end:
+ # add a new reserved range to fill the gap
+ full_list.append([
+ AddrRange(prev_end, memory_range[0].start()),
+ "reserved" ])
+
+ full_list.append(memory_range)
+
+ # Update the ending address of previous region
+ prev_end = memory_range[0].end()
+
+ return full_list
+
+def create_boxes(memory_ranges):
+ return BoxSet(memory_ranges)
+
+def draw_ranges(filename, addr_ranges, names):
+ """
+ Draw a list of memory ranges into an image file
+ This is used to print the memory map of a system
+
+ @param filename: name of the image file
+ @param addr_ranges: list of AddrRange
+ @param names: list of AddrRange names
+ """
+ try:
+ from PIL import Image, ImageDraw, ImageFont
+ except:
+ # python package not present: do nothing
+ return
+ else:
+ # Aggregate range names and slots in a single list
+ # Sort the ranges if necessary and introduce unnamed
+ # ranges to fill the gaps
+ combined_list = create_list(addr_ranges, names)
+
+ with create_boxes(combined_list) as bset:
+ im = Image.new('RGB', (1080, 1080), (128, 128, 128))
+ draw = ImageDraw.Draw(im)
+
+ bset.draw(draw)
+
+ im.save(filename, quality=95)
--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/41314
To unsubscribe, or for help writing mail filters, visit
https://gem5-review.googlesource.com/settings
Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I1b6b595c652b4dabff15ba83221d22224c2bb2d6
Gerrit-Change-Number: 41314
Gerrit-PatchSet: 1
Gerrit-Owner: Giacomo Travaglini <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s