feat: Add startup profiler to measure and record application initialization phases. (#13638)

This commit is contained in:
Kevin Ramdass
2025-12-01 10:06:13 -08:00
committed by GitHub
parent 613fb2fed6
commit db027dd95b
10 changed files with 591 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ import {
type Config,
StartSessionEvent,
logCliConfiguration,
startupProfiler,
} from '@google/gemini-cli-core';
import { type LoadedSettings } from '../config/settings.js';
import { performInitialAuth } from './auth.js';
@@ -35,10 +36,12 @@ export async function initializeApp(
config: Config,
settings: LoadedSettings,
): Promise<InitializationResult> {
const authHandle = startupProfiler.start('authenticate');
const authError = await performInitialAuth(
config,
settings.merged.security?.auth?.selectedType,
);
authHandle?.end();
const themeError = validateTheme(settings);
const shouldOpenAuthDialog =

View File

@@ -56,6 +56,7 @@ import {
enterAlternateScreen,
disableLineWrapping,
shouldEnterAlternateScreen,
startupProfiler,
ExitCodes,
} from '@google/gemini-cli-core';
import {
@@ -281,6 +282,7 @@ export async function startInteractiveUI(
}
export async function main() {
const cliStartupHandle = startupProfiler.start('cli_startup');
const cleanupStdio = patchStdio();
registerSyncCleanup(() => {
// This is needed to ensure we don't lose any buffered output.
@@ -289,7 +291,11 @@ export async function main() {
});
setupUnhandledRejectionHandler();
const loadSettingsHandle = startupProfiler.start('load_settings');
const settings = loadSettings();
loadSettingsHandle?.end();
const migrateHandle = startupProfiler.start('migrate_settings');
migrateDeprecatedSettings(
settings,
// Temporary extension manager only used during this non-interactive UI phase.
@@ -301,9 +307,12 @@ export async function main() {
requestSetting: null,
}),
);
migrateHandle?.end();
await cleanupCheckpoints();
const parseArgsHandle = startupProfiler.start('parse_arguments');
const argv = await parseArguments(settings.merged);
parseArgsHandle?.end();
// Check for invalid input combinations early to prevent crashes
if (argv.promptInteractive && !process.stdin.isTTY) {
@@ -446,7 +455,9 @@ export async function main() {
// to run Gemini CLI. It is now safe to perform expensive initialization that
// may have side effects.
{
const loadConfigHandle = startupProfiler.start('load_cli_config');
const config = await loadCliConfig(settings.merged, sessionId, argv);
loadConfigHandle?.end();
const policyEngine = config.getPolicyEngine();
const messageBus = config.getMessageBus();
@@ -514,7 +525,9 @@ export async function main() {
}
setMaxSizedBoxDebugging(isDebugMode);
const initAppHandle = startupProfiler.start('initialize_app');
const initializationResult = await initializeApp(config, settings);
initAppHandle?.end();
if (
settings.merged.security?.auth?.selectedType ===
@@ -556,6 +569,7 @@ export async function main() {
}
}
cliStartupHandle?.end();
// Render UI, passing necessary config values. Check that there is no command line question.
if (config.isInteractive()) {
await startInteractiveUI(
@@ -570,6 +584,7 @@ export async function main() {
}
await config.initialize();
startupProfiler.flush(config);
// If not a TTY, read from stdin
// This is for cases where the user pipes input directly into the command

View File

@@ -8,6 +8,7 @@ import { isDevelopment } from '../utils/installationInfo.js';
import type { ICommandLoader } from './types.js';
import type { SlashCommand } from '../ui/commands/types.js';
import type { Config } from '@google/gemini-cli-core';
import { startupProfiler } from '@google/gemini-cli-core';
import { aboutCommand } from '../ui/commands/aboutCommand.js';
import { authCommand } from '../ui/commands/authCommand.js';
import { bugCommand } from '../ui/commands/bugCommand.js';
@@ -56,6 +57,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
* @returns A promise that resolves to an array of `SlashCommand` objects.
*/
async loadCommands(_signal: AbortSignal): Promise<SlashCommand[]> {
const handle = startupProfiler.start('load_builtin_commands');
const allDefinitions: Array<SlashCommand | null> = [
aboutCommand,
authCommand,
@@ -92,7 +94,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
setupGithubCommand,
terminalSetupCommand,
];
handle?.end();
return allDefinitions.filter((cmd): cmd is SlashCommand => cmd !== null);
}
}

View File

@@ -58,6 +58,7 @@ import {
enableMouseEvents,
disableLineWrapping,
shouldEnterAlternateScreen,
startupProfiler,
} from '@google/gemini-cli-core';
import { validateAuthMethod } from '../config/auth.js';
import process from 'node:process';
@@ -282,6 +283,7 @@ export const AppContainer = (props: AppContainerProps) => {
// handled by the global catch.
await config.initialize();
setConfigInitialized(true);
startupProfiler.flush(config);
})();
registerCleanup(async () => {
// Turn off mouse scroll.

View File

@@ -30,6 +30,7 @@ import {
debugLogger,
ReadManyFilesTool,
getEffectiveModel,
startupProfiler,
} from '@google/gemini-cli-core';
import * as acp from './acp.js';
import { AcpFileSystemService } from './fileSystemService.js';
@@ -189,6 +190,7 @@ export class GeminiAgent {
const config = await loadCliConfig(settings, sessionId, this.argv, cwd);
await config.initialize();
startupProfiler.flush(config);
return config;
}