Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ blob-report/
coverage/
.pnpm-store/
.vite/
.profiles/
*.log
*.tsbuildinfo

Expand Down
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"!!.hermit",
"!!dist",
"!!dist-author",
"!!.profiles",
"!!src-tauri/target",
"!!src-tauri/gen",
"!!target",
Expand Down
15 changes: 15 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ need their own validation.
- `just web [args...]`: install locked dependencies and forward arguments to Vite,
e.g. `just web --port 1431 --host 127.0.0.1`. Vite uses the requested port
(default: 1430) or the next available port, allowing parallel browser development.
Use `just web profile` for opt-in Chromium and broker CPU profiles. Profiling
binds only `127.0.0.1`; wildcard, hostname, and IPv6 `--host` values are rejected
so the captured page and development broker have one unambiguous owner. Use
`just web profile --network` to additionally record sanitized browser network
metadata in `network.json`; payloads, cookies, authorization headers, query strings,
fragments, and WebSocket frame data are omitted. Press Ctrl+C to finalize the
capture; the command prints the `.profiles/...-web` output directory. Load
`.cpuprofile` files in Chromium DevTools (**Performance** > **Load profile**).
- `just desktop [args...]`: install locked dependencies and forward arguments to
Tauri, e.g. `just desktop --port 1431 --no-watch`. Before launching, the adapter
builds the pinned agent runtime when missing/outdated, or verifies and reuses it.
Expand All @@ -61,6 +69,13 @@ need their own validation.
implicit runner arguments. Explicit `--config` arguments merge afterward and can
override it; keep their development URL and frontend command consistent. Use `--`
before runner/application arguments if they contain their own `--port` flag.
On macOS, `just desktop profile` uses Instruments' Time Profiler to launch and
record only the Buzz native parent process, not every process on the desktop.
WebKit subprocesses and the Vite broker are outside this native trace; use web
profiling when renderer/broker CPU coverage is required. Native file watching is
disabled during capture. Press Ctrl+C to finalize and validate the trace; the
path, which opens in Instruments. Use `just profile-clean` to remove all generated
web and desktop captures.
- `just design [args...]`: install locked dependencies, start the standalone
design-system viewer, and open it in your browser. Arguments pass through to
Vite, e.g. `just design --port 1444`. The default port is 1442; an occupied port
Expand Down
8 changes: 6 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ install:
# Run the shared frontend in a browser; forward Vite arguments (e.g. --port 1431).
[positional-arguments]
web *args: install
pnpm dev "$@"
if [ "${1:-}" = "profile" ]; then shift; set -m; node scripts/profile-dev.mjs web "$@"; else exec pnpm dev "$@"; fi

# Run Tauri; --port selects Vite's port, other arguments pass through to Tauri.
[positional-arguments]
desktop *args: install
node scripts/desktop-dev.mjs "$@"
if [ "${1:-}" = "profile" ]; then shift; set -m; node scripts/profile-dev.mjs desktop "$@"; else exec node scripts/desktop-dev.mjs "$@"; fi

# Remove generated CPU, network, and Instruments profiles.
profile-clean:
rm -rf -- .profiles

# Open the design system in a browser; forward Vite arguments (e.g. --port 1444).
[positional-arguments]
Expand Down
77 changes: 77 additions & 0 deletions scripts/profile-desktop-runner.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import process from "node:process";

const trace = process.env.BUZZ_PROFILE_TRACE;
const separator = process.argv.indexOf("--", 2);
const runnerArgs = process.argv.slice(2, separator < 0 ? undefined : separator);
const appArgs = separator < 0 ? [] : process.argv.slice(separator + 1);
if (!trace || runnerArgs[0] !== "run") {
console.error(
"Desktop profiler runner requires a Cargo run command and BUZZ_PROFILE_TRACE.",
);
process.exit(1);
}

const build = spawnSync(
"cargo",
["build", "--message-format=json-render-diagnostics", ...runnerArgs.slice(1)],
{ encoding: "utf8" },
);
if (build.stderr) process.stderr.write(build.stderr);
if (build.error) console.error(build.error.message);
if (build.signal) process.kill(process.pid, build.signal);
if (build.status !== 0) process.exit(build.status ?? 1);

const metadata = spawnSync(
"cargo",
["metadata", "--no-deps", "--format-version", "1"],
{ encoding: "utf8" },
);
if (metadata.error) console.error(metadata.error.message);
if (metadata.signal) process.kill(process.pid, metadata.signal);
if (metadata.status !== 0) process.exit(metadata.status ?? 1);
const rootPackage = JSON.parse(metadata.stdout).packages.find(
({ manifest_path: manifestPath }) =>
manifestPath === `${process.cwd()}/Cargo.toml`,
);
if (!rootPackage) {
console.error("Could not resolve the desktop Cargo package.");
process.exit(1);
}

let executable;
for (const line of build.stdout.split("\n")) {
if (!line.startsWith("{")) continue;
const message = JSON.parse(line);
if (
message.reason === "compiler-artifact" &&
message.executable &&
message.target?.kind?.includes("bin") &&
rootPackage.targets.some(({ name }) => name === message.target.name)
)
executable = message.executable;
}
if (!executable) {
console.error("Cargo did not report a desktop executable.");
process.exit(1);
}
const result = spawnSync(
"xcrun",
[
"xctrace",
"record",
"--template",
"Time Profiler",
"--output",
trace,
"--launch",
"--",
executable,
...appArgs,
],
{ stdio: "inherit" },
);
if (result.error) console.error(result.error.message);
if (result.signal) process.kill(process.pid, result.signal);
process.exit(result.status ?? 1);
Loading
Loading