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