Anas Nashif | 5ab117f | 2020-08-20 16:19:08 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (c) 2020 Intel Corporation. |
| 4 | # |
| 5 | # SPDX-License-Identifier: Apache-2.0 |
| 6 | """ |
| 7 | Script to parse CTF data and print to the screen in a custom and colorful |
| 8 | format. |
| 9 | |
| 10 | Generate trace using samples/subsys/tracing for example: |
| 11 | |
| 12 | west build -b qemu_x86 samples/subsys/tracing -t run \ |
| 13 | -- -DCONF_FILE=prj_uart_ctf.conf |
| 14 | |
| 15 | mkdir ctf |
| 16 | cp build/channel0_0 ctf/ |
| 17 | cp subsys/tracing/ctf/tsdl/metadata ctf/ |
| 18 | ./scripts/tracing/parse_ctf.py -t ctf |
| 19 | """ |
| 20 | |
| 21 | import sys |
| 22 | import datetime |
| 23 | from colorama import Fore |
| 24 | import argparse |
| 25 | try: |
| 26 | import bt2 |
| 27 | except ImportError: |
| 28 | sys.exit("Missing dependency: You need to install python bindings of babletrace.") |
| 29 | |
| 30 | def parse_args(): |
| 31 | parser = argparse.ArgumentParser( |
| 32 | description=__doc__, |
| 33 | formatter_class=argparse.RawDescriptionHelpFormatter) |
| 34 | parser.add_argument("-t", "--trace", |
| 35 | required=True, |
| 36 | help="tracing data (directory with metadata and trace file)") |
| 37 | args = parser.parse_args() |
| 38 | return args |
| 39 | |
| 40 | def main(): |
| 41 | args = parse_args() |
| 42 | |
| 43 | msg_it = bt2.TraceCollectionMessageIterator(args.trace) |
| 44 | last_event_ns_from_origin = None |
| 45 | timeline = [] |
| 46 | |
| 47 | def get_thread(name): |
| 48 | for t in timeline: |
| 49 | if t.get('name', None) == name and t.get('in', 0 ) != 0 and not t.get('out', None): |
| 50 | return t |
| 51 | return {} |
| 52 | |
| 53 | for msg in msg_it: |
| 54 | |
| 55 | if not isinstance(msg, bt2._EventMessageConst): |
| 56 | continue |
| 57 | |
| 58 | ns_from_origin = msg.default_clock_snapshot.ns_from_origin |
| 59 | event = msg.event |
| 60 | # Compute the time difference since the last event message. |
| 61 | diff_s = 0 |
| 62 | |
| 63 | if last_event_ns_from_origin is not None: |
| 64 | diff_s = (ns_from_origin - last_event_ns_from_origin) / 1e9 |
| 65 | |
| 66 | dt = datetime.datetime.fromtimestamp(ns_from_origin / 1e9) |
| 67 | |
| 68 | if event.name in [ |
| 69 | 'thread_switched_out', |
| 70 | 'thread_switched_in', |
| 71 | 'thread_pending', |
| 72 | 'thread_ready', |
| 73 | 'thread_resume', |
| 74 | 'thread_suspend', |
| 75 | 'thread_create', |
| 76 | 'thread_abort' |
| 77 | ]: |
| 78 | |
| 79 | cpu = event.payload_field.get("cpu", None) |
| 80 | thread_id = event.payload_field.get("thread_id", None) |
| 81 | thread_name = event.payload_field.get("name", None) |
| 82 | |
| 83 | th = {} |
| 84 | if event.name in ['thread_switched_out', 'thread_switched_in'] and cpu is not None: |
| 85 | cpu_string = f"(cpu: {cpu})" |
| 86 | else: |
| 87 | cpu_string = "" |
| 88 | |
| 89 | if thread_name: |
| 90 | print(f"{dt} (+{diff_s:.6f} s): {event.name}: {thread_name} {cpu_string}") |
| 91 | elif thread_id: |
| 92 | print(f"{dt} (+{diff_s:.6f} s): {event.name}: {thread_id} {cpu_string}") |
| 93 | else: |
| 94 | print(f"{dt} (+{diff_s:.6f} s): {event.name}") |
| 95 | |
| 96 | if event.name in ['thread_switched_out', 'thread_switched_in']: |
| 97 | if thread_name: |
| 98 | th = get_thread(thread_name) |
| 99 | if not th: |
| 100 | th['name'] = thread_name |
| 101 | else: |
| 102 | th = get_thread(thread_id) |
| 103 | if not th: |
| 104 | th['name'] = thread_id |
| 105 | |
| 106 | if event.name in ['thread_switched_out']: |
| 107 | th['out'] = ns_from_origin |
| 108 | tin = th.get('in', None) |
| 109 | tout = th.get('out', None) |
| 110 | if tout is not None and tin is not None: |
| 111 | diff = (tout - tin) |
| 112 | th['runtime'] = diff |
| 113 | elif event.name in ['thread_switched_in']: |
| 114 | th['in'] = ns_from_origin |
| 115 | |
| 116 | timeline.append(th) |
| 117 | |
| 118 | elif event.name in ['thread_info']: |
| 119 | stack_size = event.payload_field['stack_size'] |
| 120 | print(f"{dt} (+{diff_s:.6f} s): {event.name} (Stack size: {stack_size})") |
| 121 | elif event.name in ['start_call', 'end_call']: |
| 122 | if event.payload_field['id'] == 39: |
| 123 | c = Fore.GREEN |
| 124 | elif event.payload_field['id'] in [37, 38]: |
| 125 | c = Fore.CYAN |
| 126 | else: |
| 127 | c = Fore.YELLOW |
| 128 | print(c + f"{dt} (+{diff_s:.6f} s): {event.name} {event.payload_field['id']}" + Fore.RESET) |
| 129 | elif event.name in ['semaphore_init', 'semaphore_take', 'semaphore_give']: |
| 130 | c = Fore.CYAN |
| 131 | print(c + f"{dt} (+{diff_s:.6f} s): {event.name} ({event.payload_field['id']})" + Fore.RESET) |
| 132 | elif event.name in ['mutex_init', 'mutex_take', 'mutex_give']: |
| 133 | c = Fore.MAGENTA |
| 134 | print(c + f"{dt} (+{diff_s:.6f} s): {event.name} ({event.payload_field['id']})" + Fore.RESET) |
| 135 | |
| 136 | else: |
| 137 | print(f"{dt} (+{diff_s:.6f} s): {event.name}") |
| 138 | |
| 139 | last_event_ns_from_origin = ns_from_origin |
| 140 | |
| 141 | if __name__=="__main__": |
| 142 | main() |