mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-05 21:10:57 +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
|
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)
|
os.chdir(emulator.workdir)
|
||||||
env = emulator.make_env()
|
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"
|
env["PATH"] = "/usr/bin"
|
||||||
os.execvpe(
|
dbg_command = ["lldb", "-f", str(emulator.executable), "--"] + emulator.make_args()
|
||||||
"lldb",
|
|
||||||
["lldb", "-f", str(emulator.executable), "--"] + emulator.make_args(),
|
|
||||||
env,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# Optionally run a gdb script from a file
|
# Optionally run a gdb script from a file
|
||||||
if gdb_script_file is None:
|
if gdb_script_file is None:
|
||||||
gdb = ["gdb"]
|
dbg_command = ["gdb"]
|
||||||
else:
|
else:
|
||||||
gdb = ["gdb", "-x", str(HERE / gdb_script_file)]
|
dbg_command = ["gdb", "-x", str(HERE / gdb_script_file)]
|
||||||
os.execvpe(
|
dbg_command += ["--args", str(emulator.executable)]
|
||||||
"gdb",
|
dbg_command += emulator.make_args()
|
||||||
gdb + ["--args", str(emulator.executable)] + emulator.make_args(),
|
|
||||||
env,
|
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:
|
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("-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", "--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("-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("-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("-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")
|
@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],
|
record_dir: Optional[str],
|
||||||
slip0014: bool,
|
slip0014: bool,
|
||||||
script_gdb_file: str | Path | None,
|
script_gdb_file: str | Path | None,
|
||||||
|
valgrind: bool,
|
||||||
temporary_profile: bool,
|
temporary_profile: bool,
|
||||||
watch: bool,
|
watch: bool,
|
||||||
extra_args: list[str],
|
extra_args: list[str],
|
||||||
@ -261,8 +267,8 @@ def cli(
|
|||||||
if alloc_profiling:
|
if alloc_profiling:
|
||||||
os.environ["TREZOR_MEMPERF"] = "1"
|
os.environ["TREZOR_MEMPERF"] = "1"
|
||||||
|
|
||||||
if debugger:
|
if debugger or valgrind:
|
||||||
run_debugger(emulator, script_gdb_file)
|
run_debugger(emulator, script_gdb_file, valgrind, command)
|
||||||
raise RuntimeError("run_debugger should not return")
|
raise RuntimeError("run_debugger should not return")
|
||||||
|
|
||||||
emulator.start()
|
emulator.start()
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
- [Embedded](core/build/embedded.md)
|
- [Embedded](core/build/embedded.md)
|
||||||
- [Emulator](core/build/emulator.md)
|
- [Emulator](core/build/emulator.md)
|
||||||
- [Emulator](core/emulator/index.md)
|
- [Emulator](core/emulator/index.md)
|
||||||
|
- [Valgrind profiling](core/emulator/valgrind.md)
|
||||||
- [Event Loop](core/src/event-loop.md)
|
- [Event Loop](core/src/event-loop.md)
|
||||||
- [Apps](core/src/apps.md)
|
- [Apps](core/src/apps.md)
|
||||||
- [Tests](core/tests/index.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
|
||||||
|
```
|
@ -164,6 +164,7 @@ stdenvNoCC.mkDerivation ({
|
|||||||
] ++ lib.optionals devTools [
|
] ++ lib.optionals devTools [
|
||||||
shellcheck
|
shellcheck
|
||||||
openocd-stm
|
openocd-stm
|
||||||
|
kcachegrind
|
||||||
] ++ lib.optionals (devTools && !stdenv.isDarwin) [
|
] ++ lib.optionals (devTools && !stdenv.isDarwin) [
|
||||||
gdb
|
gdb
|
||||||
] ++ lib.optionals (devTools && acceptJlink) [
|
] ++ lib.optionals (devTools && acceptJlink) [
|
||||||
|
Loading…
Reference in New Issue
Block a user