mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 07:20:43 +00:00
* perf(plugin-sdk): per-phase + per-jiti-call probes for bundled channel entries Extends the existing OPENCLAW_PLUGIN_LOAD_PROFILE infrastructure (see src/plugins/loader.ts `profilePluginLoaderSync` and src/plugins/source-loader.ts) with two new probe sites inside src/plugin-sdk/channel-entry-contract.ts: 1. `bundled-register:<phase>` — wraps each phase of `defineBundledChannelEntry`'s register() callback (`setChannelRuntime`, `loadChannelPlugin`, `registerChannel`, `registerCliMetadata`, `registerFull`). Lets us pinpoint which phase of plugin registration is responsible for cold-start cost on a per-plugin basis. 2. `bundled-entry-module-load` — instruments `loadBundledEntryModuleSync` and reports `getJitiMs` (jiti loader factory) vs `jitiCallMs` (actual graph walk + transpile + ESM linking) separately. Lets us distinguish alias-map / loader setup overhead from import-graph traversal cost on a per-module basis. Both probes are gated on OPENCLAW_PLUGIN_LOAD_PROFILE=1 and have zero overhead when the env flag is unset (early return before any `performance.now()` call). Log format matches the existing `[plugin-load-profile]` line shape so existing log scrapers continue to work. The helper is a file-local mirror of `profilePluginLoaderSync` rather than a new SDK export — keeps the SDK boundary narrow per src/plugin-sdk/AGENTS.md and avoids cross-importing host internals. Used to validate PR #69317 (slack startup perf) — measurements showed slack `setChannelRuntime` dropping from 13183ms to 67ms after barrel narrowing, which would have been undiagnosable without these per-phase probes. * perf(plugins): per-plugin register() probe in plugin loader Adds a `phase=${registrationMode}:register` probe wrapping each call to `runPluginRegisterSync(register, api)` in src/plugins/loader.ts. Emits the established `[plugin-load-profile]` line shape via `profilePluginLoaderSync`, gated on OPENCLAW_PLUGIN_LOAD_PROFILE=1. Two call sites are wrapped: - The main load path (registrationMode is dynamic: "snapshot", "validate", "full") at the post-snapshot register block. Emits e.g. `phase=full:register plugin=slack elapsedMs=14102.1 source=...` - The cli-metadata-only path (registrationMode hardcoded to "cli-metadata") for fast `--metadata` boot flows. Together with the existing `phase=full` (entire load) and `phase=source-loader` probes plus the `bundled-register:*` and `bundled-entry-module-load` probes added in the previous commit, this gives a full breakdown: - `phase=full plugin=slack` — total cost from import through register return - `phase=full:register plugin=slack` — just the register() callback (NEW) - `phase=bundled-register:setChannelRuntime plugin=slack` — sub-phase - `phase=bundled-register:loadChannelPlugin plugin=slack` — sub-phase - `phase=bundled-entry-module-load plugin=(bundled-entry)` — per-module load Lets you `sort -k4 -n -r` the log output to find the slowest plugin's register() call across all bundled+third-party plugins, then drill in via the sub-phase probes for bundled entries. * perf(plugins): consolidate plugin-load-profile primitives in shared module Extracts the previously duplicated `shouldProfilePluginLoader` / `profilePluginLoaderSync` helpers into a new `src/plugins/plugin-load-profile.ts` module. Removes 3 file-local copies of the same env-flag check and 2 near-duplicate `try { run() } finally { console.error(...) }` wrappers. Files updated: - NEW src/plugins/plugin-load-profile.ts — sole owner of: shouldProfilePluginLoader() profilePluginLoaderSync<T>({phase, pluginId?, source, run, extras?}) formatPluginLoadProfileLine({phase, pluginId?, source, elapsedMs, extras?}) - src/plugins/loader.ts — drop file-local copies, import shared helper (existing 4 + new 2 call sites unchanged in shape) - src/plugins/source-loader.ts — drop renamed local copy (`shouldProfilePluginSourceLoader`), use shared helper with `pluginId: "(direct)"` to preserve the existing `plugin=(direct)` field - src/plugin-sdk/channel-entry-contract.ts — drop file-local copies and inline `profileStep` closure; use shared `profilePluginLoaderSync` directly at all 5 `bundled-register:*` call sites; dual-timing `bundled-entry-module-load` probe uses `formatPluginLoadProfileLine` with ordered `extras` for `getJitiMs`/`jitiCallMs` Log line format is byte-for-byte identical to before (validated against 3 cases: standard, with pluginId, dual-timing). The `extras` API is intentionally an ordered tuple list (not a record) so that scrapers see deterministic field order between `elapsedMs=` and `source=`. Net: +155/-87 lines across 4 files, removing ~60 lines of duplication while exposing a stable, documented probe surface. Verified: - pnpm tsgo (core) — 0 errors - pnpm lint on all 4 files — 0 warnings, 0 errors - pnpm test src/plugins/loader.test.ts — 102/102 - pnpm test src/plugins/contracts/plugin-entry-guardrails.test.ts — 7/7 - pnpm test src/plugin-sdk/channel-entry-contract.test.ts — 4/4 - Standalone formatter smoke test — output matches existing format byte-for-byte * refactor(plugins): rename profilePluginLoaderSync to withProfile and bind scope at register sites * fix(plugin-sdk): zero jiti sub-step timings on Win32 nodeRequire fast-path
24 lines
895 B
TypeScript
24 lines
895 B
TypeScript
import type { PluginJitiLoaderCache } from "./jiti-loader-cache.js";
|
|
import { getCachedPluginJitiLoader } from "./jiti-loader-cache.js";
|
|
import { withProfile } from "./plugin-load-profile.js";
|
|
|
|
export type PluginSourceLoader = (modulePath: string) => unknown;
|
|
|
|
export function createPluginSourceLoader(): PluginSourceLoader {
|
|
const loaders: PluginJitiLoaderCache = new Map();
|
|
return (modulePath) => {
|
|
const jiti = getCachedPluginJitiLoader({
|
|
cache: loaders,
|
|
modulePath,
|
|
importerUrl: import.meta.url,
|
|
jitiFilename: import.meta.url,
|
|
});
|
|
// Direct source loads are not associated with a specific plugin id —
|
|
// preserve the existing `plugin=(direct)` field used by tooling that
|
|
// scrapes [plugin-load-profile] lines.
|
|
return withProfile({ pluginId: "(direct)", source: modulePath }, "source-loader", () =>
|
|
jiti(modulePath),
|
|
);
|
|
};
|
|
}
|