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_BUILDDIR"] = product.target.builddir
os.environ["CK_COMPONENT"] = product.component.id os.environ["CK_COMPONENT"] = product.component.id
try:
command = [str(product.path), *args.extra]
if debug: if debug:
if debugger == "lldb": shell.debug(command, debugger=debugger, wait=wait)
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,
)
else:
raise RuntimeError(f"Unknown debugger {debugger}")
elif profile: elif profile:
# Profile the executable using perf shell.profile(command)
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: 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") @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") BUILD_DIR = os.path.join(PROJECT_CK_DIR, "build")
CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache") CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache")
EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern") EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern")
TMP_DIR = os.path.join(PROJECT_CK_DIR, "tmp")
SRC_DIR = "src" SRC_DIR = "src"
META_DIR = "meta" META_DIR = "meta"
TARGETS_DIR = os.path.join(META_DIR, "targets") TARGETS_DIR = os.path.join(META_DIR, "targets")

View file

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

View file

@ -12,6 +12,7 @@ import logging
import tempfile import tempfile
import dataclasses as dt import dataclasses as dt
from pathlib import Path
from typing import Optional from typing import Optional
from . import const 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: def exec(*args: str, quiet: bool = False, cwd: Optional[str] = None) -> bool:
_logger.debug(f"Executing {args}") _logger.debug(f"Executing {args}")
cmdName = Path(args[0]).name
try: try:
proc = subprocess.run( 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") raise RuntimeError(f"{args[0]}: Command not found")
except KeyboardInterrupt: except KeyboardInterrupt:
raise RuntimeError(f"{args[0]}: Interrupted") raise RuntimeError(f"{cmdName}: Interrupted")
if proc.returncode == -signal.SIGSEGV: if proc.returncode == -signal.SIGSEGV:
raise RuntimeError(f"{args[0]}: Segmentation fault") raise RuntimeError(f"{cmdName}: Segmentation fault")
if proc.returncode != 0: 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 return True
def popen(*args: str) -> str: def popen(*args: str) -> str:
_logger.debug(f"Executing {args}") _logger.debug(f"Executing {args}...")
cmdName = Path(args[0]).name
try: try:
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=sys.stderr) proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=sys.stderr)
except FileNotFoundError: except FileNotFoundError:
raise RuntimeError(f"{args[0]}: Command not found") raise RuntimeError(f"{cmdName}: Command not found")
if proc.returncode == -signal.SIGSEGV: if proc.returncode == -signal.SIGSEGV:
raise RuntimeError(f"{args[0]}: Segmentation fault") raise RuntimeError(f"{cmdName}: Segmentation fault")
if proc.returncode != 0: 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() 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]: def readdir(path: str) -> list[str]:
_logger.debug(f"Reading directory {path}") _logger.debug(f"Reading directory {path}")