1#!/usr/bin/python3
2# this script is called form stack_report_gen.sh
3from tabulate import tabulate
4from dataclasses import dataclass
5import os, shutil
6import sys
7import gdb
8
9output_file = "build_reports/stack_report.html"
10
11
12def print_summary(d, fkt_name):
13    headers = ["measured function", "Used stack"]
14    with open(output_file, "a") as f:
15        f.write(
16            "<br> The following stack consumption is achieved when uoscore-uedhoc is built with the following flags: "
17        )
18        f.write(arg_flags)
19        f.write("<br>")
20        f.write(tabulate(d, headers, tablefmt="unsafehtml"))
21        f.write("<br>")
22
23
24def measure_stack_usage_of_function(f):
25    gdb.execute("set pagination off")
26    bp = gdb.Breakpoint(f.fkt_name)
27
28    gdb.execute("run")
29    i = gdb.inferiors()[0]
30
31    measurements = []
32
33    while True:
34        # Loop through all break points and measure the stack for each function.
35        try:
36            # Here we stop at the beginning of the function that we want to profile.
37            breakpoint_location = gdb.execute("backtrace 2", False, True)
38            sp_start = int(gdb.parse_and_eval("$sp"))
39            print("Starting SP: 0x{:08x}".format(sp_start))
40
41            stack_bottom = sp_start - f.max_stack_size
42            print("stack_bottom: 0x{:08x}".format(stack_bottom))
43
44            i = gdb.inferiors()[0]
45            stack_dump_start = i.read_memory(stack_bottom, f.max_stack_size)
46
47            while True:
48                # Step one line until we stepped out of the function.
49                gdb.execute("n")
50                sp = int(gdb.parse_and_eval("$sp"))
51                if sp > sp_start:
52                    break
53
54            stack_dump_end = i.read_memory(stack_bottom, f.max_stack_size)
55
56            used_stack_memory = 0
57            for i in range(f.max_stack_size):
58                if (
59                    stack_dump_start.tobytes()[f.max_stack_size - i - 1]
60                    != stack_dump_end.tobytes()[f.max_stack_size - i - 1]
61                ):
62                    used_stack_memory = i
63
64            print("Stack usage: ", used_stack_memory)
65            print(breakpoint_location)
66            fkt = breakpoint_location.split(" ")[2]
67            print("fkt is: ", fkt)
68
69            caller = breakpoint_location.split("#1")[1].split(" ")[-1:]
70            print("caller is: ", caller)
71            measurements.append([fkt, caller, used_stack_memory])
72            gdb.execute("c")
73        except gdb.error:
74            # We are here when the program went to its end.
75            break
76
77    gdb.Breakpoint.delete(bp)
78    print_summary(measurements, f.fkt_name)
79
80
81def main():
82    @dataclass
83    class Function:
84        fkt_name: str
85        max_stack_size: int
86
87    gdb.execute("file " + "build/zephyr/zephyr.elf")
88
89    functions = [
90        Function("coap2oscore", 1500),
91        Function("oscore2coap", 1500),
92        Function("edhoc_responder_run", 7000),
93        Function("edhoc_initiator_run", 7000),
94    ]
95    for f in functions:
96        measure_stack_usage_of_function(f)
97
98    gdb.execute("q")
99
100
101if __name__ == "__main__":
102    main()
103