113992Stiago.muck@arm.com# Copyright (c) 2019 ARM Limited
213992Stiago.muck@arm.com# All rights reserved.
313992Stiago.muck@arm.com#
413992Stiago.muck@arm.com# The license below extends only to copyright in the software and shall
513992Stiago.muck@arm.com# not be construed as granting a license to any other intellectual
613992Stiago.muck@arm.com# property including but not limited to intellectual property relating
713992Stiago.muck@arm.com# to a hardware implementation of the functionality of the software
813992Stiago.muck@arm.com# licensed hereunder.  You may use the software subject to the license
913992Stiago.muck@arm.com# terms below provided that you ensure that this notice is replicated
1013992Stiago.muck@arm.com# unmodified and in its entirety in all distributions of the software,
1113992Stiago.muck@arm.com# modified or unmodified, in source code or in binary form.
1213992Stiago.muck@arm.com#
1313992Stiago.muck@arm.com# Redistribution and use in source and binary forms, with or without
1413992Stiago.muck@arm.com# modification, are permitted provided that the following conditions are
1513992Stiago.muck@arm.com# met: redistributions of source code must retain the above copyright
1613992Stiago.muck@arm.com# notice, this list of conditions and the following disclaimer;
1713992Stiago.muck@arm.com# redistributions in binary form must reproduce the above copyright
1813992Stiago.muck@arm.com# notice, this list of conditions and the following disclaimer in the
1913992Stiago.muck@arm.com# documentation and/or other materials provided with the distribution;
2013992Stiago.muck@arm.com# neither the name of the copyright holders nor the names of its
2113992Stiago.muck@arm.com# contributors may be used to endorse or promote products derived from
2213992Stiago.muck@arm.com# this software without specific prior written permission.
2313992Stiago.muck@arm.com#
2413992Stiago.muck@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2513992Stiago.muck@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2613992Stiago.muck@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2713992Stiago.muck@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2813992Stiago.muck@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2913992Stiago.muck@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3013992Stiago.muck@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3113992Stiago.muck@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3213992Stiago.muck@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3313992Stiago.muck@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3413992Stiago.muck@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3513992Stiago.muck@arm.com#
3613992Stiago.muck@arm.com# Authors: Tiago Muck
3713992Stiago.muck@arm.com
3813992Stiago.muck@arm.com# Creates a visual representation of a Ruby network topology
3913992Stiago.muck@arm.com
4013992Stiago.muck@arm.comimport os
4113992Stiago.muck@arm.comimport m5
4213992Stiago.muck@arm.comfrom m5.util import warn
4313992Stiago.muck@arm.comtry:
4413992Stiago.muck@arm.com    import pydot
4513992Stiago.muck@arm.comexcept:
4613992Stiago.muck@arm.com    pydot = False
4713992Stiago.muck@arm.com
4813992Stiago.muck@arm.com
4913992Stiago.muck@arm.comdef _dot_rgb_to_html(r, g, b):
5013992Stiago.muck@arm.com    return "#%.2x%.2x%.2x" % (r, g, b)
5113992Stiago.muck@arm.com
5213992Stiago.muck@arm.comdef _dot_create_router_node(full_path, label):
5313992Stiago.muck@arm.com    return pydot.Node( \
5413992Stiago.muck@arm.com                         full_path, \
5513992Stiago.muck@arm.com                         shape = "Mrecord", \
5613992Stiago.muck@arm.com                         label = label, \
5713992Stiago.muck@arm.com                         style = "\"rounded, filled\"", \
5813992Stiago.muck@arm.com                         color = "#000000", \
5913992Stiago.muck@arm.com                         fillcolor = _dot_rgb_to_html(204, 230, 252), \
6013992Stiago.muck@arm.com                         fontname = "Arial", \
6113992Stiago.muck@arm.com                         fontsize = "14", \
6213992Stiago.muck@arm.com                         fontcolor = "#000000" \
6313992Stiago.muck@arm.com                         )
6413992Stiago.muck@arm.com
6513992Stiago.muck@arm.comdef _dot_create_ctrl_node(full_path, label):
6613992Stiago.muck@arm.com    return pydot.Node( \
6713992Stiago.muck@arm.com                         full_path, \
6813992Stiago.muck@arm.com                         shape = "Mrecord", \
6913992Stiago.muck@arm.com                         label = label, \
7013992Stiago.muck@arm.com                         style = "\"rounded, filled\"", \
7113992Stiago.muck@arm.com                         color = "#000000", \
7213992Stiago.muck@arm.com                         fillcolor = _dot_rgb_to_html(229, 188, 208), \
7313992Stiago.muck@arm.com                         fontname = "Arial", \
7413992Stiago.muck@arm.com                         fontsize = "14", \
7513992Stiago.muck@arm.com                         fontcolor = "#000000" \
7613992Stiago.muck@arm.com                         )
7713992Stiago.muck@arm.com
7813992Stiago.muck@arm.com
7913992Stiago.muck@arm.comdef _dot_create(network, callgraph):
8013992Stiago.muck@arm.com    for r in network.routers:
8113992Stiago.muck@arm.com        callgraph.add_node(_dot_create_router_node(r.path(),
8213992Stiago.muck@arm.com            'R %d' % r.router_id))
8313992Stiago.muck@arm.com
8413992Stiago.muck@arm.com    # One link for each direction but draw one edge only
8513992Stiago.muck@arm.com    connected = dict()
8613992Stiago.muck@arm.com    for link in network.int_links:
8713992Stiago.muck@arm.com        if (link.src_node.path() in connected) and \
8813992Stiago.muck@arm.com           (connected[link.src_node.path()] == link.dst_node.path()):
8913992Stiago.muck@arm.com           continue
9013992Stiago.muck@arm.com        callgraph.add_edge(
9113992Stiago.muck@arm.com            pydot.Edge(link.src_node.path(), link.dst_node.path())
9213992Stiago.muck@arm.com        )
9313992Stiago.muck@arm.com        connected[link.dst_node.path()] = link.src_node.path()
9413992Stiago.muck@arm.com
9513992Stiago.muck@arm.com    for link in network.ext_links:
9613992Stiago.muck@arm.com        ctrl = link.ext_node
9713992Stiago.muck@arm.com        label = ctrl._name
9813992Stiago.muck@arm.com        if hasattr(ctrl, '_node_type'):
9913992Stiago.muck@arm.com            label += ' (' + ctrl._node_type + ')'
10013992Stiago.muck@arm.com        callgraph.add_node(
10113992Stiago.muck@arm.com            _dot_create_ctrl_node(ctrl.path(), label)
10213992Stiago.muck@arm.com        )
10313992Stiago.muck@arm.com
10413992Stiago.muck@arm.com        callgraph.add_edge(
10513992Stiago.muck@arm.com            pydot.Edge(link.ext_node.path(), link.int_node.path())
10613992Stiago.muck@arm.com        )
10713992Stiago.muck@arm.com
10813992Stiago.muck@arm.comdef _do_dot(network, outdir, dotFilename):
10913992Stiago.muck@arm.com    callgraph = pydot.Dot(graph_type='graph', rankdir='LR')
11013992Stiago.muck@arm.com    _dot_create(network, callgraph)
11113992Stiago.muck@arm.com    dot_filename = os.path.join(outdir, dotFilename)
11213992Stiago.muck@arm.com    callgraph.write(dot_filename)
11313992Stiago.muck@arm.com    try:
11413992Stiago.muck@arm.com        # dot crashes if the figure is extremely wide.
11513992Stiago.muck@arm.com        # So avoid terminating simulation unnecessarily
11613992Stiago.muck@arm.com        callgraph.write_svg(dot_filename + ".svg", prog='neato')
11713992Stiago.muck@arm.com        callgraph.write_pdf(dot_filename + ".pdf", prog='neato')
11813992Stiago.muck@arm.com    except:
11913992Stiago.muck@arm.com        warn("failed to generate dot output from %s", dot_filename)
12013992Stiago.muck@arm.com
12113992Stiago.muck@arm.com
12213992Stiago.muck@arm.comdef do_ruby_dot(root, outdir, dotFilename):
12313992Stiago.muck@arm.com    if not pydot:
12413992Stiago.muck@arm.com        return
12513992Stiago.muck@arm.com
12613992Stiago.muck@arm.com    # Generate a graph for all ruby systems
12713992Stiago.muck@arm.com    networks = []
12813992Stiago.muck@arm.com    for obj in root.descendants():
12913992Stiago.muck@arm.com        if isinstance(obj, m5.objects.RubyNetwork):
13013992Stiago.muck@arm.com            networks.append(obj)
13113992Stiago.muck@arm.com
13213992Stiago.muck@arm.com    for network in networks:
13313992Stiago.muck@arm.com        # We assume each ruby system has a single network
13413992Stiago.muck@arm.com        rubydotFilename = dotFilename.replace(".dot",
13513992Stiago.muck@arm.com                                "." + network.get_parent().path() + ".dot")
13613992Stiago.muck@arm.com        _do_dot(network, outdir, rubydotFilename)
137