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