xref: /cloud-hypervisor/scripts/ch-trace-visualiser.py (revision 5e811a6823ecc4c4105f608b867417fb80c41d1f)
1*5e811a68SRob Bradford#!/bin/env python3
2*5e811a68SRob Bradford#
3*5e811a68SRob Bradford# Copyright © 2022 Intel Corporation
4*5e811a68SRob Bradford#
5*5e811a68SRob Bradford# SPDX-License-Identifier: Apache-2.0
6*5e811a68SRob Bradford#
7*5e811a68SRob Bradford
8*5e811a68SRob Bradfordfrom colorsys import hsv_to_rgb
9*5e811a68SRob Bradfordfrom random import random
10*5e811a68SRob Bradfordimport xml
11*5e811a68SRob Bradfordimport json
12*5e811a68SRob Bradfordfrom sys import argv, stderr
13*5e811a68SRob Bradfordimport xml.etree.ElementTree as ET
14*5e811a68SRob Bradford
15*5e811a68SRob Bradfordwidth = 1000
16*5e811a68SRob Bradfordheight = 200
17*5e811a68SRob Bradfordpadding = 10
18*5e811a68SRob Bradford
19*5e811a68SRob Bradfordif len(argv) < 3:
20*5e811a68SRob Bradford    stderr.write("./ch-trace-visualiser <trace file> <output file>\n")
21*5e811a68SRob Bradford    exit(1)
22*5e811a68SRob Bradford
23*5e811a68SRob Bradford
24*5e811a68SRob Bradforddef nano_time(duration):
25*5e811a68SRob Bradford    return (duration["secs"] * 10 ** 9) + duration["nanos"]
26*5e811a68SRob Bradford
27*5e811a68SRob Bradford
28*5e811a68SRob Bradforddef duration_to_px_x(start):
29*5e811a68SRob Bradford    return (nano_time(start) * (width - 2 * padding)) / total_time
30*5e811a68SRob Bradford
31*5e811a68SRob Bradford
32*5e811a68SRob Bradforddef duration_to_px_width(start, end):
33*5e811a68SRob Bradford    return ((nano_time(end) - nano_time(start)) * (width - 2 * padding)) / total_time
34*5e811a68SRob Bradford
35*5e811a68SRob Bradford
36*5e811a68SRob Bradforddef duration_ms(start, end):
37*5e811a68SRob Bradford    return (nano_time(end) - nano_time(start)) // 1000000
38*5e811a68SRob Bradford
39*5e811a68SRob Bradford
40*5e811a68SRob Bradfordf = open(argv[1])
41*5e811a68SRob Bradfordreport = json.load(f)
42*5e811a68SRob Bradfordtotal_time = nano_time(report["duration"])
43*5e811a68SRob Bradford
44*5e811a68SRob Bradfordsvg = ET.Element("svg", attrib={"width": str(width), "height": str(height),
45*5e811a68SRob Bradford                                "xmlns": "http://www.w3.org/2000/svg",
46*5e811a68SRob Bradford                                "xmlns:svg": "http://www.w3.org/2000/svg"
47*5e811a68SRob Bradford                                })
48*5e811a68SRob Bradford
49*5e811a68SRob Bradford
50*5e811a68SRob Bradforddef add_traced_block(thread_group, depth, traced_block):
51*5e811a68SRob Bradford    g = ET.SubElement(thread_group, "g",
52*5e811a68SRob Bradford                      attrib={"transform": "translate(%d,%d)" % (
53*5e811a68SRob Bradford                          duration_to_px_x(traced_block["timestamp"]),
54*5e811a68SRob Bradford                          (depth * 18))})
55*5e811a68SRob Bradford    width = str(duration_to_px_width(
56*5e811a68SRob Bradford        traced_block["timestamp"], traced_block["end_timestamp"]))
57*5e811a68SRob Bradford
58*5e811a68SRob Bradford    clip = ET.SubElement(g, "clipPath", attrib={
59*5e811a68SRob Bradford        "id": "clip_%s" % (traced_block["event"]),
60*5e811a68SRob Bradford    })
61*5e811a68SRob Bradford    ET.SubElement(clip, "rect", attrib={
62*5e811a68SRob Bradford        "width": width,
63*5e811a68SRob Bradford        "height": "1.5em",
64*5e811a68SRob Bradford        "x": "0",
65*5e811a68SRob Bradford        "y": "0"
66*5e811a68SRob Bradford    })
67*5e811a68SRob Bradford
68*5e811a68SRob Bradford    (red, green, blue) = hsv_to_rgb(random(), 0.3, 0.75)
69*5e811a68SRob Bradford    ET.SubElement(g, "rect", attrib={
70*5e811a68SRob Bradford        "width": width,
71*5e811a68SRob Bradford        "height": "1.5em",
72*5e811a68SRob Bradford        "fill": "#%x%x%x" % (int(red * 255), int(green * 255), int(blue * 255))
73*5e811a68SRob Bradford    })
74*5e811a68SRob Bradford    text = ET.SubElement(g, "text", attrib={
75*5e811a68SRob Bradford        "x": "0.2em", "y": "1em", "clip-path": "url(#clip_%s)" % (traced_block["event"])})
76*5e811a68SRob Bradford    text.text = "%s (%dms)" % (traced_block["event"], duration_ms(
77*5e811a68SRob Bradford        traced_block["timestamp"], traced_block["end_timestamp"]))
78*5e811a68SRob Bradford
79*5e811a68SRob Bradford
80*5e811a68SRob Bradfordthread_size = (height - (2 * padding)) / len(report["events"])
81*5e811a68SRob Bradfordthread_offset = padding
82*5e811a68SRob Bradford
83*5e811a68SRob Bradfordfor thread in report["events"]:
84*5e811a68SRob Bradford    thread_events = report["events"][thread]
85*5e811a68SRob Bradford    thread_events = sorted(
86*5e811a68SRob Bradford        thread_events, key=lambda traced_block: nano_time(traced_block["timestamp"]))
87*5e811a68SRob Bradford    thread_group = ET.SubElement(
88*5e811a68SRob Bradford        svg, "g", attrib={"transform": "translate(%d,%d)" % (padding, thread_offset)})
89*5e811a68SRob Bradford    thread_text = ET.SubElement(thread_group, "text", attrib={
90*5e811a68SRob Bradford                                "y": "1em"}).text = "Thread: %s" % (thread)
91*5e811a68SRob Bradford    thread_children = ET.SubElement(thread_group, "g", attrib={
92*5e811a68SRob Bradford        "transform": "translate(0, 18)"})
93*5e811a68SRob Bradford    for traced_block in thread_events:
94*5e811a68SRob Bradford        add_traced_block(thread_children, traced_block["depth"], traced_block)
95*5e811a68SRob Bradford    thread_offset += thread_size + padding
96*5e811a68SRob Bradford
97*5e811a68SRob Bradfordxml = ET.ElementTree(element=svg)
98*5e811a68SRob Bradfordxml.write(argv[2], xml_declaration=True)
99