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