1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-27 09:58:27 +00:00
trezor-firmware/core/prof/prof.py
grdddj 07797158db feat(core): adjust the coverage file output for multicore tests
When the tests are run using multiple cores, there will

be one .coverage file for each core. So that the one file

is not being overwritten many times, using unique

filename for each core.

[no changelog]
2023-03-09 16:50:32 +01:00

135 lines
3.8 KiB
Python

import sys
from uio import open
from uos import getenv
import micropython
# We need to insert "" to sys.path so that the frozen build can import main from the
# frozen modules, and regular build can import it from current directory.
sys.path.insert(0, "")
PATH_PREFIX = (getenv("TREZOR_SRC") or ".") + "/"
class Coverage:
def __init__(self):
self.__files = {}
def line_tick(self, filename, lineno):
if not filename in self.__files:
self.__files[filename] = set()
self.__files[filename].add(lineno)
def lines_execution(self):
lines_execution = {"lines": {}}
lines = lines_execution["lines"]
this_file = globals()["__file__"]
for filename in self.__files:
if not filename == this_file:
lines[PATH_PREFIX + filename] = list(self.__files[filename])
return lines_execution
class _Prof:
trace_count = 0
display_flags = 0
__coverage = Coverage()
def trace_tick(self, frame, event, arg):
self.trace_count += 1
# if frame.f_code.co_filename.endswith('/loop.py'):
# print(event, frame.f_code.co_filename, frame.f_lineno)
if event == "line":
self.__coverage.line_tick(frame.f_code.co_filename, frame.f_lineno)
def coverage_data(self):
return self.__coverage.lines_execution()
def write_data(self):
print("Total traces executed: ", __prof__.trace_count)
# In case of multithreaded tests, we might be called multiple times.
# Making sure the threads do not overwrite each other's data.
worker_id = getenv("PYTEST_XDIST_WORKER")
if worker_id:
file_name = f".coverage.{worker_id}"
else:
file_name = ".coverage"
with open(file_name, "w") as f:
# wtf so private much beautiful wow
f.write("!coverage.py: This is a private format, don't read it directly!")
# poormans json
f.write(str(__prof__.coverage_data()).replace("'", '"'))
class AllocCounter:
def __init__(self):
self.last_alloc_count = 0
self.data = {}
self.last_line = None
def count_last_line(self, allocs):
if self.last_line is None:
return
entry = self.data.setdefault(
self.last_line,
{
"total_allocs": 0,
"calls": 0,
},
)
entry["total_allocs"] += allocs
entry["calls"] += 1
def trace_tick(self, frame, event, arg):
allocs_now = micropython.alloc_count()
if event != "line":
return
allocs_per_last_line = allocs_now - self.last_alloc_count
self.count_last_line(allocs_per_last_line)
self.last_line = f"{frame.f_code.co_filename}:{frame.f_lineno}"
self.last_alloc_count = micropython.alloc_count()
def dump_data(self, filename):
allocs_now = micropython.alloc_count()
allocs_per_last_line = allocs_now - self.last_alloc_count
self.count_last_line(allocs_per_last_line)
with open(filename, "w") as f:
for key, val in self.data.items():
f.write("{} {total_allocs} {calls}\n".format(key, **val))
def write_data(self):
self.dump_data("alloc_data.txt")
def trace_handler(frame, event, arg):
__prof__.trace_tick(frame, event, arg)
return trace_handler
def atexit():
print("\n------------------ script exited ------------------")
__prof__.write_data()
sys.atexit(atexit)
global __prof__
if not "__prof__" in globals():
if getenv("TREZOR_MEMPERF") == "1":
__prof__ = AllocCounter()
else:
__prof__ = _Prof()
sys.settrace(trace_handler)
if isinstance(__prof__, AllocCounter):
__prof__.last_alloc_count = micropython.alloc_count()
import main