mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-31 18:40:56 +00:00
build(core): emulator valgrind support
[no changelog]
This commit is contained in:
parent
c44f901a97
commit
9dee211c27
38
core/emu.py
38
core/emu.py
@ -67,27 +67,31 @@ def watch_emulator(emulator: CoreEmulator) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def run_debugger(emulator: CoreEmulator, gdb_script_file: str | Path | None) -> None:
|
||||
def run_debugger(emulator: CoreEmulator, gdb_script_file: str | Path | None, valgrind: bool = False, run_command: list[str] = []) -> None:
|
||||
os.chdir(emulator.workdir)
|
||||
env = emulator.make_env()
|
||||
if platform.system() == "Darwin":
|
||||
if valgrind:
|
||||
dbg_command = ["valgrind", "-v", "--tool=callgrind", "--read-inline-info=yes", str(emulator.executable)] + emulator.make_args()
|
||||
elif platform.system() == "Darwin":
|
||||
env["PATH"] = "/usr/bin"
|
||||
os.execvpe(
|
||||
"lldb",
|
||||
["lldb", "-f", str(emulator.executable), "--"] + emulator.make_args(),
|
||||
env,
|
||||
)
|
||||
dbg_command = ["lldb", "-f", str(emulator.executable), "--"] + emulator.make_args()
|
||||
else:
|
||||
# Optionally run a gdb script from a file
|
||||
if gdb_script_file is None:
|
||||
gdb = ["gdb"]
|
||||
dbg_command = ["gdb"]
|
||||
else:
|
||||
gdb = ["gdb", "-x", str(HERE / gdb_script_file)]
|
||||
os.execvpe(
|
||||
"gdb",
|
||||
gdb + ["--args", str(emulator.executable)] + emulator.make_args(),
|
||||
env,
|
||||
)
|
||||
dbg_command = ["gdb", "-x", str(HERE / gdb_script_file)]
|
||||
dbg_command += ["--args", str(emulator.executable)]
|
||||
dbg_command += emulator.make_args()
|
||||
|
||||
if not run_command:
|
||||
os.execvpe(dbg_command[0], dbg_command, env)
|
||||
else:
|
||||
dbg_process = subprocess.Popen(dbg_command, env=env)
|
||||
run_process = subprocess.Popen(run_command, env=env, shell=True)
|
||||
rc = run_process.wait()
|
||||
dbg_process.send_signal(signal.SIGINT)
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
def _from_env(name: str) -> bool:
|
||||
@ -118,6 +122,7 @@ def _from_env(name: str) -> bool:
|
||||
@click.option("-r", "--record-dir", help="Directory where to record screen changes")
|
||||
@click.option("-s", "--slip0014", is_flag=True, help="Initialize device with SLIP-14 seed (all all all...)")
|
||||
@click.option("-S", "--script-gdb-file", type=click.Path(exists=True, dir_okay=False), help="Run gdb with an init file")
|
||||
@click.option("-V", "--valgrind", is_flag=True, help="Use valgrind instead of debugger (-D)")
|
||||
@click.option("-t", "--temporary-profile", is_flag=True, help="Create an empty temporary profile")
|
||||
@click.option("-w", "--watch", is_flag=True, help="Restart emulator if sources change")
|
||||
@click.option("-X", "--extra-arg", "extra_args", multiple=True, help="Extra argument to pass to micropython")
|
||||
@ -144,6 +149,7 @@ def cli(
|
||||
record_dir: Optional[str],
|
||||
slip0014: bool,
|
||||
script_gdb_file: str | Path | None,
|
||||
valgrind: bool,
|
||||
temporary_profile: bool,
|
||||
watch: bool,
|
||||
extra_args: list[str],
|
||||
@ -261,8 +267,8 @@ def cli(
|
||||
if alloc_profiling:
|
||||
os.environ["TREZOR_MEMPERF"] = "1"
|
||||
|
||||
if debugger:
|
||||
run_debugger(emulator, script_gdb_file)
|
||||
if debugger or valgrind:
|
||||
run_debugger(emulator, script_gdb_file, valgrind, command)
|
||||
raise RuntimeError("run_debugger should not return")
|
||||
|
||||
emulator.start()
|
||||
|
@ -6,6 +6,7 @@
|
||||
- [Embedded](core/build/embedded.md)
|
||||
- [Emulator](core/build/emulator.md)
|
||||
- [Emulator](core/emulator/index.md)
|
||||
- [Valgrind profiling](core/emulator/valgrind.md)
|
||||
- [Event Loop](core/src/event-loop.md)
|
||||
- [Apps](core/src/apps.md)
|
||||
- [Tests](core/tests/index.md)
|
||||
|
43
docs/core/emulator/valgrind.md
Normal file
43
docs/core/emulator/valgrind.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Profiling emulator with Valgrind
|
||||
|
||||
Sometimes, it can be helpful to know which parts of your code take most of the CPU time.
|
||||
[Callgrind](https://valgrind.org/docs/manual/cl-manual.html) tool from the [Valgrind](https://valgrind.org/)
|
||||
instrumentation framework can generate profiling data for a run of Trezor emulator. These can then be visualized
|
||||
with [KCachegrind](https://kcachegrind.github.io/).
|
||||
|
||||
Bear in mind that profiling the emulator is of very limited usefulness due to:
|
||||
* different CPU architecture,
|
||||
* different/mocked drivers,
|
||||
* & other differences from actual hardware.
|
||||
Still, it might be a way to get *some* insight without a [hardware debugger](../systemview/index.md)
|
||||
and a development board.
|
||||
|
||||
Valgrind also currently doesn't understand MicroPython call stack so it won't help you when your code is spending
|
||||
a lot of time in pure python functions that don't call out to C. It might be possible to instrument trezor-core
|
||||
so that Valgrind is aware of MicroPython stack frames.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
make build_unix_frozen TREZOR_EMULATOR_DEBUGGABLE=1 ADDRESS_SANITIZER=0
|
||||
```
|
||||
|
||||
With `PYOPT=0`, most of the execution time is spent formatting and writing logs, so it is recommended to use
|
||||
`PYOPT=1` (and lose DebugLink) or get rid of logging manually.
|
||||
|
||||
## Run
|
||||
|
||||
If you're using Nix, you can use Valgrind and KCachegrind packages from our `shell.nix`:
|
||||
```
|
||||
nix-shell --args devTools true --run "poetry shell"
|
||||
```
|
||||
|
||||
Record profiling data on some device tests:
|
||||
```
|
||||
./emu.py -a --debugger --valgrind -c 'sleep 10; pytest ../../tests/device_tests/ -v --other-pytest-args...'
|
||||
```
|
||||
|
||||
Open profiling data in KCachegrind (file suffix is different for each emulator process):
|
||||
```
|
||||
kcachegrind src/callgrind.out.$PID
|
||||
```
|
Loading…
Reference in New Issue
Block a user