Profiling is easy now :)

This commit is contained in:
Sleepy Monax 2024-01-17 10:40:06 +01:00
parent f134c5752b
commit dd4324bad4
4 changed files with 77 additions and 54 deletions

View file

@ -371,50 +371,17 @@ def runCmd(args: cli.Args):
os.environ["CK_BUILDDIR"] = product.target.builddir
os.environ["CK_COMPONENT"] = product.component.id
if debug:
if debugger == "lldb":
shell.exec(
"lldb",
*(("-o", "b main") if wait else ()),
*("-o", "run"),
str(product.path),
*args.extra,
)
elif debugger == "gdb":
shell.exec(
"gdb",
*(("-ex", "b main") if wait else ()),
*("-ex", "run"),
str(product.path),
*args.extra,
)
try:
command = [str(product.path), *args.extra]
if debug:
shell.debug(command, debugger=debugger, wait=wait)
elif profile:
shell.profile(command)
else:
raise RuntimeError(f"Unknown debugger {debugger}")
elif profile:
# Profile the executable using perf
shell.exec(
"perf",
"record",
"-g",
"-o",
"perf.data",
"--call-graph",
"dwarf",
str(object=product.path),
*args.extra,
)
# Run this command using subprocess
# perf script -i perf.data | speedscope -
import subprocess
proc = subprocess.Popen(
["perf", "script", "-i", "perf.data"], stdout=subprocess.PIPE
)
subprocess.run(["speedscope", "-"], stdin=proc.stdout)
proc.wait()
else:
shell.exec(str(product.path), *args.extra)
shell.exec(*command)
except Exception as e:
cli.error(e)
@cli.command("t", "builder/test", "Run all test targets")

View file

@ -24,6 +24,7 @@ GLOBAL_CK_DIR = os.path.join(os.path.expanduser("~"), ".cutekit")
BUILD_DIR = os.path.join(PROJECT_CK_DIR, "build")
CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache")
EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern")
TMP_DIR = os.path.join(PROJECT_CK_DIR, "tmp")
SRC_DIR = "src"
META_DIR = "meta"
TARGETS_DIR = os.path.join(META_DIR, "targets")

View file

@ -69,14 +69,15 @@ def combineMixins(*mixins: Mixin) -> Mixin:
mixins: dict[str, Mixin] = {
"cache": mixinCache,
"debug": mixinDebug,
"asan": makeMixinSan("address"),
"asan": combineMixins(makeMixinSan("address"), makeMixinSan("leak")),
"msan": makeMixinSan("memory"),
"tsan": makeMixinSan("thread"),
"ubsan": makeMixinSan("undefined"),
"sanitize": combineMixins(
"lsan": makeMixinSan("leak"),
"san": combineMixins(
makeMixinSan("address"),
makeMixinSan("memory"),
makeMixinSan("thread"),
makeMixinSan("undefined"),
makeMixinSan("leak"),
),
"tune": makeMixinTune("native"),
"release": combineMixins(

View file

@ -12,6 +12,7 @@ import logging
import tempfile
import dataclasses as dt
from pathlib import Path
from typing import Optional
from . import const
@ -134,6 +135,7 @@ def wget(url: str, path: Optional[str] = None) -> str:
def exec(*args: str, quiet: bool = False, cwd: Optional[str] = None) -> bool:
_logger.debug(f"Executing {args}")
cmdName = Path(args[0]).name
try:
proc = subprocess.run(
@ -156,34 +158,86 @@ def exec(*args: str, quiet: bool = False, cwd: Optional[str] = None) -> bool:
raise RuntimeError(f"{args[0]}: Command not found")
except KeyboardInterrupt:
raise RuntimeError(f"{args[0]}: Interrupted")
raise RuntimeError(f"{cmdName}: Interrupted")
if proc.returncode == -signal.SIGSEGV:
raise RuntimeError(f"{args[0]}: Segmentation fault")
raise RuntimeError(f"{cmdName}: Segmentation fault")
if proc.returncode != 0:
raise RuntimeError(f"{args[0]}: Process exited with code {proc.returncode}")
raise RuntimeError(f"{cmdName}: Process exited with code {proc.returncode}")
return True
def popen(*args: str) -> str:
_logger.debug(f"Executing {args}")
_logger.debug(f"Executing {args}...")
cmdName = Path(args[0]).name
try:
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=sys.stderr)
except FileNotFoundError:
raise RuntimeError(f"{args[0]}: Command not found")
raise RuntimeError(f"{cmdName}: Command not found")
if proc.returncode == -signal.SIGSEGV:
raise RuntimeError(f"{args[0]}: Segmentation fault")
raise RuntimeError(f"{cmdName}: Segmentation fault")
if proc.returncode != 0:
raise RuntimeError(f"{args[0]}: Process exited with code {proc.returncode}")
raise RuntimeError(f"{cmdName}: Process exited with code {proc.returncode}")
return proc.stdout.decode("utf-8").strip()
def debug(cmd: list[str], debugger: str = "lldb", wait: bool = False):
if debugger == "lldb":
exec(
"lldb",
*(("-o", "b main") if wait else ()),
*("-o", "run"),
*cmd,
)
elif debugger == "gdb":
exec(
"gdb",
*(("-ex", "b main") if wait else ()),
*("-ex", "run"),
*cmd,
)
else:
raise RuntimeError(f"Unknown debugger {debugger}")
def profile(cmd: list[str]):
mkdir(const.TMP_DIR)
perfFile = f"{const.TMP_DIR}/perf.data"
try:
exec(
"perf",
"record",
"-g",
"-o",
perfFile,
"--call-graph",
"dwarf",
*cmd,
)
except Exception as e:
if not os.path.exists(perfFile):
raise e
try:
proc = subprocess.Popen(
["perf", "script", "-i", perfFile], stdout=subprocess.PIPE
)
subprocess.run(["speedscope", "-"], stdin=proc.stdout)
proc.wait()
except Exception as e:
rmrf(perfFile)
raise e
rmrf(perfFile)
def readdir(path: str) -> list[str]:
_logger.debug(f"Reading directory {path}")