1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2023, Linaro Limited
5#
6# Converts a ftrace binary file to text. The input file has the following
7# format:
8#
9#  <ASCII text> <zero or more nul bytes> FTRACE\x00\x01 <binary data>...
10#
11# <binary data> is an array of 64-bit integers.
12# - When the topmost byte is 0, the entry indicates a function return and the
13# remaining bytes are a duration in nanoseconds.
14# - A non-zero value is a stack depth, indicating a function entry, and the
15# remaining bytes are the function's address.
16
17import sys
18
19
20line = ""
21curr_depth = 0
22
23
24def usage():
25    print(f"Usage: {sys.argv[0]} ftrace.out")
26    print("Converts a ftrace file to text. Output is written to stdout.")
27    sys.exit(0)
28
29
30def format_time(ns):
31    if ns < 1000000:
32        us = ns / 1000
33        return f"{us:7.3f} us"
34    elif ns < 1000000000:
35        ms = ns / 1000000
36        return f"{ms:7.3f} ms"
37    else:
38        s = ns / 1000000000
39        return f"{s:7.3f} s "
40
41
42def display(depth, val):
43    global line, curr_depth
44    if depth != 0:
45        curr_depth = depth
46        if line != "":
47            line = line.replace("TIME", " " * 10) + " {"
48            print(line)
49            line = ""
50        line = f" TIME | {depth:3} | " + " " * depth + f"0x{val:016x}()"
51    else:
52        if line != "":
53            line = line.replace("TIME", format_time(val))
54            print(line)
55            line = ""
56        else:
57            if curr_depth != 0:
58                curr_depth = curr_depth - 1
59                print(" " + format_time(val) + f" | {curr_depth:3} | " +
60                      " " * curr_depth + "}")
61
62
63def main():
64    if len(sys.argv) < 2:
65        usage()
66    with open(sys.argv[1], 'rb') as f:
67        s = f.read()
68    magic = s.find(b'FTRACE\x00\x01')
69    if magic == -1:
70        print("Magic not found", file=sys.stderr)
71        sys.exit(1)
72    print(s[:magic].rstrip(b'\x00').decode())
73    s = s[magic + 8:]
74    for i in range(0, len(s), 8):
75        elem = int.from_bytes(s[i:i + 8], byteorder="little", signed=False)
76        depth = elem >> 56
77        val = elem & 0xFFFFFFFFFFFFFF
78        display(depth, val)
79
80
81if __name__ == "__main__":
82    main()
83