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