93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
HELPER_PROJECT = ROOT / "tools" / "DtpSnapshotExtractor" / "DtpSnapshotExtractor.csproj"
|
|
HELPER_DLL = ROOT / "tools" / "DtpSnapshotExtractor" / "bin" / "Debug" / "net10.0" / "DtpSnapshotExtractor.dll"
|
|
DEFAULT_DOTTRACE_DIR = Path.home() / "Applications" / "dotTrace.app" / "Contents" / "DotFiles"
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description="Parse a raw dotTrace .dtp snapshot family into JSON call-tree data."
|
|
)
|
|
parser.add_argument("snapshot", help="Path to the .dtp snapshot index file.")
|
|
parser.add_argument("--out", help="Write JSON to this file.")
|
|
parser.add_argument("--stdout", action="store_true", help="Write JSON to stdout.")
|
|
return parser.parse_args()
|
|
|
|
|
|
def find_dottrace_dir() -> Path:
|
|
value = os.environ.get("DOTTRACE_APP_DIR")
|
|
if value:
|
|
candidate = Path(value).expanduser()
|
|
if candidate.is_dir():
|
|
return candidate
|
|
if DEFAULT_DOTTRACE_DIR.is_dir():
|
|
return DEFAULT_DOTTRACE_DIR
|
|
raise FileNotFoundError(
|
|
f"dotTrace assemblies not found. Set DOTTRACE_APP_DIR or install dotTrace under {DEFAULT_DOTTRACE_DIR}."
|
|
)
|
|
|
|
|
|
def build_helper(dottrace_dir: Path) -> None:
|
|
command = [
|
|
"dotnet",
|
|
"build",
|
|
str(HELPER_PROJECT),
|
|
"-nologo",
|
|
"-clp:ErrorsOnly",
|
|
f"-p:DotTraceAppDir={dottrace_dir}",
|
|
]
|
|
result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, check=False)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(result.stderr.strip() or result.stdout.strip() or "dotnet build failed")
|
|
|
|
|
|
def run_helper(snapshot: Path, dottrace_dir: Path) -> dict:
|
|
command = ["dotnet", str(HELPER_DLL), str(snapshot)]
|
|
env = os.environ.copy()
|
|
env["DOTTRACE_APP_DIR"] = str(dottrace_dir)
|
|
result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, check=False, env=env)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(result.stderr.strip() or result.stdout.strip() or "extractor failed")
|
|
return json.loads(result.stdout)
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
snapshot = Path(args.snapshot).expanduser().resolve()
|
|
if not snapshot.is_file():
|
|
print(f"Snapshot not found: {snapshot}", file=sys.stderr)
|
|
return 2
|
|
|
|
if not args.stdout and not args.out:
|
|
args.stdout = True
|
|
|
|
try:
|
|
dottrace_dir = find_dottrace_dir()
|
|
build_helper(dottrace_dir)
|
|
payload = run_helper(snapshot, dottrace_dir)
|
|
except Exception as exc: # noqa: BLE001
|
|
print(str(exc), file=sys.stderr)
|
|
return 1
|
|
|
|
text = json.dumps(payload, indent=2)
|
|
if args.out:
|
|
out_path = Path(args.out).expanduser().resolve()
|
|
out_path.write_text(text + "\n", encoding="utf-8")
|
|
if args.stdout:
|
|
sys.stdout.write(text + "\n")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|