From 073ac920adee8e517350a455908aa420b29f9422 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 17 Mar 2022 13:56:42 -0800 Subject: [PATCH] Add server tracepoints (#48282) * Trace project creation, loading, and updateGraph * Drop generic event tracing * Make argument names more consistent * Trace diagnostics to make steps easier to interpret * Fill an unexplained gap in updateGraph * Move updateGraph tracing into base type * Fill the gaps in updateGraph --- src/compiler/commandLineParser.ts | 5 ++++- src/server/editorServices.ts | 3 +++ src/server/project.ts | 13 ++++++++++++- src/server/session.ts | 7 ++++++- src/services/services.ts | 2 ++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 757ae3bba2501..ba798525e7d7f 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -2591,7 +2591,10 @@ namespace ts { * file to. e.g. outDir */ export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { - return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); + tracing?.push(tracing.Phase.Parse, "parseJsonSourceFileConfigFileContent", { path: sourceFile.fileName }); + const result = parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); + tracing?.pop(); + return result; } /*@internal*/ diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 149332bb6f646..5a81aff9cc461 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -2062,6 +2062,7 @@ namespace ts.server { /* @internal */ createConfiguredProject(configFileName: NormalizedPath) { + tracing?.instant(tracing.Phase.Session, "createConfiguredProject", { configFilePath: configFileName }); this.logger.info(`Creating configuration project ${configFileName}`); const canonicalConfigFilePath = asNormalizedPath(this.toCanonicalFileName(configFileName)); let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); @@ -2119,6 +2120,7 @@ namespace ts.server { */ /* @internal */ private loadConfiguredProject(project: ConfiguredProject, reason: string) { + tracing?.push(tracing.Phase.Session, "loadConfiguredProject", { configFilePath: project.canonicalConfigFilePath }); this.sendProjectLoadingStartEvent(project, reason); // Read updated contents from disk @@ -2160,6 +2162,7 @@ namespace ts.server { project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides); const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles()); this.updateRootAndOptionsOfNonInferredProject(project, filesToAdd, fileNamePropertyReader, compilerOptions, parsedCommandLine.typeAcquisition!, parsedCommandLine.compileOnSave, parsedCommandLine.watchOptions); + tracing?.pop(); } /*@internal*/ diff --git a/src/server/project.ts b/src/server/project.ts index 91bc1e639a355..3ecd20cf76fec 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1045,6 +1045,7 @@ namespace ts.server { * @returns: true if set of files in the project stays the same and false - otherwise. */ updateGraph(): boolean { + tracing?.push(tracing.Phase.Session, "updateGraph", { name: this.projectName, kind: ProjectKind[this.projectKind] }); perfLogger.logStartUpdateGraph(); this.resolutionCache.startRecordingFilesWithChangedResolutions(); @@ -1092,6 +1093,7 @@ namespace ts.server { this.getPackageJsonAutoImportProvider(); } perfLogger.logStopUpdateGraph(); + tracing?.pop(); return !hasNewProgram; } @@ -1128,7 +1130,9 @@ namespace ts.server { this.resolutionCache.startCachingPerDirectoryResolution(); this.program = this.languageService.getProgram(); // TODO: GH#18217 this.dirty = false; + tracing?.push(tracing.Phase.Session, "finishCachingPerDirectoryResolution"); this.resolutionCache.finishCachingPerDirectoryResolution(); + tracing?.pop(); Debug.assert(oldProgram === undefined || this.program !== undefined); @@ -1747,13 +1751,16 @@ namespace ts.server { const dependencySelection = this.includePackageJsonAutoImports(); if (dependencySelection) { + tracing?.push(tracing.Phase.Session, "getPackageJsonAutoImportProvider"); const start = timestamp(); this.autoImportProviderHost = AutoImportProviderProject.create(dependencySelection, this, this.getModuleResolutionHostForAutoImportProvider(), this.documentRegistry); if (this.autoImportProviderHost) { updateProjectIfDirty(this.autoImportProviderHost); this.sendPerformanceEvent("CreatePackageJsonAutoImportProvider", timestamp() - start); + tracing?.pop(); return this.autoImportProviderHost.getCurrentProgram(); } + tracing?.pop(); } } @@ -1776,9 +1783,13 @@ namespace ts.server { } function getUnresolvedImports(program: Program, cachedUnresolvedImportsPerFile: ESMap): SortedReadonlyArray { + const sourceFiles = program.getSourceFiles(); + tracing?.push(tracing.Phase.Session, "getUnresolvedImports", { count: sourceFiles.length }); const ambientModules = program.getTypeChecker().getAmbientModules().map(mod => stripQuotes(mod.getName())); - return sortAndDeduplicate(flatMap(program.getSourceFiles(), sourceFile => + const result = sortAndDeduplicate(flatMap(sourceFiles, sourceFile => extractUnresolvedImportsFromSourceFile(sourceFile, ambientModules, cachedUnresolvedImportsPerFile))); + tracing?.pop(); + return result; } function extractUnresolvedImportsFromSourceFile(file: SourceFile, ambientModules: readonly string[], cachedUnresolvedImportsPerFile: ESMap): readonly string[] { return getOrUpdate(cachedUnresolvedImportsPerFile, file.path, () => { diff --git a/src/server/session.ts b/src/server/session.ts index b1807555462b6..51585b02f169f 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -903,7 +903,6 @@ namespace ts.server { } public event(body: T, eventName: string): void { - tracing?.instant(tracing.Phase.Session, "event", { eventName }); this.send(toEvent(eventName, body)); } @@ -955,18 +954,24 @@ namespace ts.server { } private semanticCheck(file: NormalizedPath, project: Project) { + tracing?.push(tracing.Phase.Session, "semanticCheck", { file, configFilePath: (project as ConfiguredProject).canonicalConfigFilePath }); // undefined is fine if the cast fails const diags = isDeclarationFileInJSOnlyNonConfiguredProject(project, file) ? emptyArray : project.getLanguageService().getSemanticDiagnostics(file).filter(d => !!d.file); this.sendDiagnosticsEvent(file, project, diags, "semanticDiag"); + tracing?.pop(); } private syntacticCheck(file: NormalizedPath, project: Project) { + tracing?.push(tracing.Phase.Session, "syntacticCheck", { file, configFilePath: (project as ConfiguredProject).canonicalConfigFilePath }); // undefined is fine if the cast fails this.sendDiagnosticsEvent(file, project, project.getLanguageService().getSyntacticDiagnostics(file), "syntaxDiag"); + tracing?.pop(); } private suggestionCheck(file: NormalizedPath, project: Project) { + tracing?.push(tracing.Phase.Session, "suggestionCheck", { file, configFilePath: (project as ConfiguredProject).canonicalConfigFilePath }); // undefined is fine if the cast fails this.sendDiagnosticsEvent(file, project, project.getLanguageService().getSuggestionDiagnostics(file), "suggestionDiag"); + tracing?.pop(); } private sendDiagnosticsEvent(file: NormalizedPath, project: Project, diagnostics: readonly Diagnostic[], kind: protocol.DiagnosticEventKind): void { diff --git a/src/services/services.ts b/src/services/services.ts index 503482d63c7a8..efb31f257970e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1004,9 +1004,11 @@ namespace ts { // Initialize the list with the root file names const rootFileNames = host.getScriptFileNames(); + tracing?.push(tracing.Phase.Session, "initializeHostCache", { count: rootFileNames.length }); for (const fileName of rootFileNames) { this.createEntry(fileName, toPath(fileName, this.currentDirectory, getCanonicalFileName)); } + tracing?.pop(); } private createEntry(fileName: string, path: Path) {