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

Reply via email to