mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
perf(gateway): add startup CPU profile option
This commit is contained in:
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Gateway/performance: lazy-load the heavy cron runtime after the rest of Gateway startup, defer restart-sentinel refresh after readiness, and let the Gateway startup benchmark write per-run V8 CPU profiles with `--cpu-prof-dir`.
|
||||
- Gateway/performance: keep raw channel-config schema parsing from discovering bundled plugin runtime metadata, and add `pnpm gateway:watch --benchmark-no-force` for profiling startup without the default port cleanup.
|
||||
- Plugins/onboarding: let Manual setup install optional official plugins, including ClawHub-backed diagnostics with npm fallback, and expose the external Codex plugin as a selectable provider setup choice. Thanks @vincentkoc.
|
||||
- Plugins/CLI: include package dependency install state in `openclaw plugins list --json` so scripts can spot missing plugin dependencies without runtime-loading plugins.
|
||||
|
||||
@@ -60,6 +60,7 @@ type CaseResult = {
|
||||
|
||||
type CliOptions = {
|
||||
cases: GatewayBenchCase[];
|
||||
cpuProfDir?: string;
|
||||
entry: string;
|
||||
json: boolean;
|
||||
output?: string;
|
||||
@@ -201,6 +202,7 @@ function resolveCases(caseIds: string[]): GatewayBenchCase[] {
|
||||
function parseOptions(): CliOptions {
|
||||
return {
|
||||
cases: resolveCases(parseRepeatableFlag("--case")),
|
||||
cpuProfDir: parseFlagValue("--cpu-prof-dir"),
|
||||
entry: parseFlagValue("--entry") ?? DEFAULT_ENTRY,
|
||||
json: hasFlag("--json"),
|
||||
output: parseFlagValue("--output"),
|
||||
@@ -223,6 +225,7 @@ Options:
|
||||
--runs <n> Measured runs per case (default: ${DEFAULT_RUNS})
|
||||
--warmup <n> Warmup runs per case (default: ${DEFAULT_WARMUP})
|
||||
--timeout-ms <ms> Per-run timeout (default: ${DEFAULT_TIMEOUT_MS})
|
||||
--cpu-prof-dir <dir> Write one V8 CPU profile per run
|
||||
--output <path> Write machine-readable JSON to a file
|
||||
--json Emit machine-readable JSON
|
||||
--help, -h Show this text
|
||||
@@ -658,7 +661,9 @@ function readProcessTreeCpuMs(rootPid: number | undefined): number | null {
|
||||
|
||||
async function runGatewaySample(options: {
|
||||
benchCase: GatewayBenchCase;
|
||||
cpuProfDir?: string;
|
||||
entry: string;
|
||||
sampleIndex: number;
|
||||
timeoutMs: number;
|
||||
}): Promise<GatewaySample> {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "openclaw-gateway-bench-"));
|
||||
@@ -674,24 +679,34 @@ async function runGatewaySample(options: {
|
||||
let readyLogMs: number | null = null;
|
||||
let childExited = false;
|
||||
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
[
|
||||
options.entry,
|
||||
"gateway",
|
||||
"run",
|
||||
"--port",
|
||||
String(port),
|
||||
"--bind",
|
||||
"loopback",
|
||||
"--auth",
|
||||
"none",
|
||||
"--tailscale",
|
||||
"off",
|
||||
"--allow-unconfigured",
|
||||
],
|
||||
{ cwd: process.cwd(), detached: process.platform !== "win32", env },
|
||||
);
|
||||
const childArgs = [
|
||||
...(options.cpuProfDir
|
||||
? [
|
||||
"--cpu-prof",
|
||||
"--cpu-prof-dir",
|
||||
options.cpuProfDir,
|
||||
"--cpu-prof-name",
|
||||
`openclaw-gateway-${options.benchCase.id}-${options.sampleIndex}-${Date.now()}.cpuprofile`,
|
||||
]
|
||||
: []),
|
||||
options.entry,
|
||||
"gateway",
|
||||
"run",
|
||||
"--port",
|
||||
String(port),
|
||||
"--bind",
|
||||
"loopback",
|
||||
"--auth",
|
||||
"none",
|
||||
"--tailscale",
|
||||
"off",
|
||||
"--allow-unconfigured",
|
||||
];
|
||||
const child = spawn(process.execPath, childArgs, {
|
||||
cwd: process.cwd(),
|
||||
detached: process.platform !== "win32",
|
||||
env,
|
||||
});
|
||||
const cpuStartMs = readProcessTreeCpuMs(child.pid);
|
||||
const sampleRss = () => {
|
||||
const rssMb = readProcessRssMb(child.pid);
|
||||
@@ -773,6 +788,7 @@ async function runGatewaySample(options: {
|
||||
|
||||
async function runCase(options: {
|
||||
benchCase: GatewayBenchCase;
|
||||
cpuProfDir?: string;
|
||||
entry: string;
|
||||
runs: number;
|
||||
timeoutMs: number;
|
||||
@@ -783,7 +799,9 @@ async function runCase(options: {
|
||||
for (let index = 0; index < total; index += 1) {
|
||||
const sample = await runGatewaySample({
|
||||
benchCase: options.benchCase,
|
||||
cpuProfDir: options.cpuProfDir,
|
||||
entry: options.entry,
|
||||
sampleIndex: index + 1,
|
||||
timeoutMs: options.timeoutMs,
|
||||
});
|
||||
if (index >= options.warmup) {
|
||||
@@ -828,11 +846,15 @@ async function main() {
|
||||
}
|
||||
|
||||
const options = parseOptions();
|
||||
if (options.cpuProfDir) {
|
||||
mkdirSync(options.cpuProfDir, { recursive: true });
|
||||
}
|
||||
const results: CaseResult[] = [];
|
||||
for (const benchCase of options.cases) {
|
||||
results.push(
|
||||
await runCase({
|
||||
benchCase,
|
||||
cpuProfDir: options.cpuProfDir,
|
||||
entry: options.entry,
|
||||
runs: options.runs,
|
||||
timeoutMs: options.timeoutMs,
|
||||
|
||||
@@ -16,6 +16,7 @@ describe("gateway startup benchmark script", () => {
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toContain("OpenClaw Gateway startup benchmark");
|
||||
expect(result.stdout).toContain("--case <id>");
|
||||
expect(result.stdout).toContain("--cpu-prof-dir <dir>");
|
||||
expect(result.stdout).toContain("default (gateway default)");
|
||||
expect(result.stdout).not.toContain("[gateway-startup-bench]");
|
||||
expect(result.stderr).toBe("");
|
||||
|
||||
Reference in New Issue
Block a user