diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 365a3d36515..bea34807d86 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -174,14 +174,14 @@ namespace ts { const binder = createBinder(); export function bindSourceFile(file: SourceFile, options: CompilerOptions) { - tracing.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true); performance.mark("beforeBind"); perfLogger.logStartBindFile("" + file.fileName); binder(file, options); perfLogger.logStopBindFile(); performance.mark("afterBind"); performance.measure("Bind", "beforeBind", "afterBind"); - tracing.pop(); + tracing?.pop(); } function createBinder(): (file: SourceFile, options: CompilerOptions) => void { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2edf4ada5a1..bf7d17a951f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13338,7 +13338,7 @@ namespace ts { // caps union types at 1000 unique object types. const estimatedCount = (count / (len - i)) * len; if (estimatedCount > 1000000) { - tracing.instant(tracing.Phase.CheckTypes, "removeSubtypes_DepthLimit", { typeIds: types.map(t => t.id) }); + tracing?.instant(tracing.Phase.CheckTypes, "removeSubtypes_DepthLimit", { typeIds: types.map(t => t.id) }); error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); return false; } @@ -13817,7 +13817,7 @@ namespace ts { function checkCrossProductUnion(types: readonly Type[]) { const size = getCrossProductUnionSize(types); if (size >= 100000) { - tracing.instant(tracing.Phase.CheckTypes, "checkCrossProductUnion_DepthLimit", { typeIds: types.map(t => t.id), size }); + tracing?.instant(tracing.Phase.CheckTypes, "checkCrossProductUnion_DepthLimit", { typeIds: types.map(t => t.id), size }); error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); return false; } @@ -15751,7 +15751,7 @@ namespace ts { // We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing // with a combination of infinite generic types that perpetually generate new type identities. We stop // the recursion here by yielding the error type. - tracing.instant(tracing.Phase.CheckTypes, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount }); + tracing?.instant(tracing.Phase.CheckTypes, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount }); error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); return errorType; } @@ -16881,7 +16881,7 @@ namespace ts { reportIncompatibleStack(); } if (overflow) { - tracing.instant(tracing.Phase.CheckTypes, "checkTypeRelatedTo_DepthLimit", { sourceId: source.id, targetId: target.id, depth }); + tracing?.instant(tracing.Phase.CheckTypes, "checkTypeRelatedTo_DepthLimit", { sourceId: source.id, targetId: target.id, depth }); const diag = error(errorNode || currentNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target)); if (errorOutputContainer) { (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); @@ -17368,7 +17368,7 @@ namespace ts { } function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void { - if (!tracing.isTracing()) { + if (!tracing) { return; } @@ -17730,7 +17730,7 @@ namespace ts { } if (expandingFlags === ExpandingFlags.Both) { - tracing.instant(tracing.Phase.CheckTypes, "recursiveTypeRelatedTo_DepthLimit", { + tracing?.instant(tracing.Phase.CheckTypes, "recursiveTypeRelatedTo_DepthLimit", { sourceId: source.id, sourceIdStack: sourceStack.map(t => t.id), targetId: target.id, @@ -17767,9 +17767,9 @@ namespace ts { } function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { - tracing.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id }); + tracing?.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id }); const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState); - tracing.pop(); + tracing?.pop(); return result; } @@ -18294,7 +18294,7 @@ namespace ts { numCombinations *= countTypes(getTypeOfSymbol(sourceProperty)); if (numCombinations > 25) { // We've reached the complexity limit. - tracing.instant(tracing.Phase.CheckTypes, "typeRelatedToDiscriminatedType_DepthLimit", { sourceId: source.id, targetId: target.id, numCombinations }); + tracing?.instant(tracing.Phase.CheckTypes, "typeRelatedToDiscriminatedType_DepthLimit", { sourceId: source.id, targetId: target.id, numCombinations }); return Ternary.False; } } @@ -19073,7 +19073,7 @@ namespace ts { function getVariancesWorker(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] { let variances = cache.variances; if (!variances) { - tracing.push(tracing.Phase.CheckTypes, "getVariancesWorker", { arity: typeParameters.length, id: (cache as any).id ?? (cache as any).declaredType?.id ?? -1 }); + tracing?.push(tracing.Phase.CheckTypes, "getVariancesWorker", { arity: typeParameters.length, id: (cache as any).id ?? (cache as any).declaredType?.id ?? -1 }); // The emptyArray singleton is used to signal a recursive invocation. cache.variances = emptyArray; variances = []; @@ -19108,7 +19108,7 @@ namespace ts { variances.push(variance); } cache.variances = variances; - tracing.pop(); + tracing?.pop(); } return variances; } @@ -22199,7 +22199,7 @@ namespace ts { if (flowDepth === 2000) { // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error // and disable further control flow analysis in the containing function or module body. - tracing.instant(tracing.Phase.CheckTypes, "getTypeAtFlowNode_DepthLimit", { flowId: flow.id }); + tracing?.instant(tracing.Phase.CheckTypes, "getTypeAtFlowNode_DepthLimit", { flowId: flow.id }); flowAnalysisDisabled = true; reportFlowControlError(reference); return errorType; @@ -31673,7 +31673,7 @@ namespace ts { } function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { - tracing.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end }); + tracing?.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end }); const saveCurrentNode = currentNode; currentNode = node; instantiationCount = 0; @@ -31683,7 +31683,7 @@ namespace ts { checkConstEnumAccess(node, type); } currentNode = saveCurrentNode; - tracing.pop(); + tracing?.pop(); return type; } @@ -34489,10 +34489,10 @@ namespace ts { } function checkVariableDeclaration(node: VariableDeclaration) { - tracing.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end }); + tracing?.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end }); checkGrammarVariableDeclaration(node); checkVariableLikeDeclaration(node); - tracing.pop(); + tracing?.pop(); } function checkBindingElement(node: BindingElement) { @@ -37570,7 +37570,7 @@ namespace ts { } function checkDeferredNode(node: Node) { - tracing.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end }); + tracing?.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end }); const saveCurrentNode = currentNode; currentNode = node; instantiationCount = 0; @@ -37606,16 +37606,16 @@ namespace ts { break; } currentNode = saveCurrentNode; - tracing.pop(); + tracing?.pop(); } function checkSourceFile(node: SourceFile) { - tracing.push(tracing.Phase.Check, "checkSourceFile", { path: node.path }, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Check, "checkSourceFile", { path: node.path }, /*separateBeginAndEnd*/ true); performance.mark("beforeCheck"); checkSourceFileWorker(node); performance.mark("afterCheck"); performance.measure("Check", "beforeCheck", "afterCheck"); - tracing.pop(); + tracing?.pop(); } function unusedIsError(kind: UnusedKind, isAmbient: boolean): boolean { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 24174483fdc..a2bba125ca8 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -340,17 +340,17 @@ namespace ts { sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()))) }; } - tracing.push(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath }); + tracing?.push(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath }); emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo); - tracing.pop(); + tracing?.pop(); - tracing.push(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath }); + tracing?.push(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath }); emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo); - tracing.pop(); + tracing?.pop(); - tracing.push(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath }); + tracing?.push(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath }); emitBuildInfo(bundleBuildInfo, buildInfoPath); - tracing.pop(); + tracing?.pop(); if (!emitSkipped && emittedFilesList) { if (!emitOnlyDtsFiles) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5d1735afedf..e27215e7a87 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -607,7 +607,7 @@ namespace ts { } export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { - tracing.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); performance.mark("beforeParse"); let result: SourceFile; @@ -622,7 +622,7 @@ namespace ts { performance.mark("afterParse"); performance.measure("Parse", "beforeParse", "afterParse"); - tracing.pop(); + tracing?.pop(); return result; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index e6a53885000..8cf16a806ec 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -833,7 +833,7 @@ namespace ts { // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. const sourceFilesFoundSearchingNodeModules = new Map(); - tracing.push(tracing.Phase.Program, "createProgram", { configFilePath: options.configFilePath, rootDir: options.rootDir }, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Program, "createProgram", { configFilePath: options.configFilePath, rootDir: options.rootDir }, /*separateBeginAndEnd*/ true); performance.mark("beforeProgram"); const host = createProgramOptions.host || createCompilerHost(options); @@ -919,15 +919,15 @@ namespace ts { forEachResolvedProjectReference }); - tracing.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram }); + tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram }); const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); - tracing.pop(); + tracing?.pop(); // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`. let structureIsReused: StructureIsReused; - tracing.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {}); + tracing?.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {}); structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const - tracing.pop(); + tracing?.pop(); if (structureIsReused !== StructureIsReused.Completely) { processingDefaultLibFiles = []; processingOtherFiles = []; @@ -964,15 +964,15 @@ namespace ts { } } - tracing.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length }); + tracing?.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length }); forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index })); - tracing.pop(); + tracing?.pop(); // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray; if (typeReferences.length) { - tracing.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length }); + tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length }); // This containingFilename needs to match with the one used in managed-side const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(); const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile); @@ -980,7 +980,7 @@ namespace ts { for (let i = 0; i < typeReferences.length; i++) { processTypeReferenceDirective(typeReferences[i], resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId }); } - tracing.pop(); + tracing?.pop(); } // Do not process the default library if: @@ -1108,7 +1108,7 @@ namespace ts { verifyCompilerOptions(); performance.mark("afterProgram"); performance.measure("Program", "beforeProgram", "afterProgram"); - tracing.pop(); + tracing?.pop(); return program; @@ -1116,12 +1116,12 @@ namespace ts { if (!moduleNames.length) return emptyArray; const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); const redirectedReference = getRedirectReferenceForResolution(containingFile); - tracing.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName }); + tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName }); performance.mark("beforeResolveModule"); const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, reusedNames, redirectedReference); performance.mark("afterResolveModule"); performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule"); - tracing.pop(); + tracing?.pop(); return result; } @@ -1129,12 +1129,12 @@ namespace ts { if (!typeDirectiveNames.length) return []; const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile; const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined; - tracing.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName }); + tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName }); performance.mark("beforeResolveTypeReference"); const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference); performance.mark("afterResolveTypeReference"); performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference"); - tracing.pop(); + tracing?.pop(); return result; } @@ -1655,7 +1655,7 @@ namespace ts { function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult { Debug.assert(!outFile(options)); - tracing.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true); performance.mark("beforeEmit"); const emitResult = emitFiles( notImplementedResolver, @@ -1668,7 +1668,7 @@ namespace ts { performance.mark("afterEmit"); performance.measure("Emit", "beforeEmit", "afterEmit"); - tracing.pop(); + tracing?.pop(); return emitResult; } @@ -1729,9 +1729,9 @@ namespace ts { } function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult { - tracing.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true); const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit)); - tracing.pop(); + tracing?.pop(); return result; } @@ -2485,13 +2485,13 @@ namespace ts { // Get source file from normalized fileName function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { - tracing.push(tracing.Phase.Program, "findSourceFile", { + tracing?.push(tracing.Phase.Program, "findSourceFile", { fileName, isDefaultLib: isDefaultLib || undefined, fileIncludeKind: (FileIncludeKind as any)[reason.kind], }); const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId); - tracing.pop(); + tracing?.pop(); return result; } @@ -2792,9 +2792,9 @@ namespace ts { resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, reason: FileIncludeReason ): void { - tracing.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined }); + tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined }); processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason); - tracing.pop(); + tracing?.pop(); } function processTypeReferenceDirectiveWorker( diff --git a/src/compiler/tracing.ts b/src/compiler/tracing.ts index e6e63ebf79a..41d7c8508e3 100644 --- a/src/compiler/tracing.ts +++ b/src/compiler/tracing.ts @@ -1,16 +1,25 @@ +/* Tracing events for the compiler. */ + /*@internal*/ -/** Tracing events for the compiler. */ -namespace ts.tracing { +namespace ts { // eslint-disable-line one-namespace-per-file + // should be used as tracing?.___ + export let tracing: typeof tracingEnabled | undefined; + // enable the above using startTracing() +} + +// `tracingEnabled` should never be used directly, only through the above +/* @internal */ +namespace ts.tracingEnabled { // eslint-disable-line one-namespace-per-file export const enum Mode { Project, Build, Server, } - let fs: typeof import("fs") | false | undefined; + let fs: typeof import("fs"); let traceCount = 0; - let traceFd: number | undefined; + let traceFd = 0; let mode: Mode; @@ -22,23 +31,19 @@ namespace ts.tracing { [key: string]: string | number | boolean | null | undefined | Args | readonly (string | number | boolean | null | undefined | Args)[]; }; - /** Starts tracing for the given project (unless the `fs` module is unavailable). */ + /** Starts tracing for the given project. */ export function startTracing(tracingMode: Mode, traceDir: string, configFilePath?: string) { - Debug.assert(!traceFd, "Tracing already started"); + Debug.assert(!tracing, "Tracing already started"); if (fs === undefined) { try { fs = require("fs"); } - catch { - fs = false; + catch (e) { + throw new Error(`tracing requires having fs\n(original error: ${e.message || e})`); } } - if (!fs) { - return; - } - mode = tracingMode; if (legendPath === undefined) { @@ -51,9 +56,9 @@ namespace ts.tracing { } const countPart = - mode === Mode.Build ? `.${process.pid}-${++traceCount}` : - mode === Mode.Server ? `.${process.pid}` : - ``; + mode === Mode.Build ? `.${process.pid}-${++traceCount}` + : mode === Mode.Server ? `.${process.pid}` + : ``; const tracePath = combinePaths(traceDir, `trace${countPart}.json`); const typesPath = combinePaths(traceDir, `types${countPart}.json`); @@ -64,6 +69,7 @@ namespace ts.tracing { }); traceFd = fs.openSync(tracePath, "w"); + tracing = tracingEnabled; // only when traceFd is properly set // Start with a prefix that contains some metadata that the devtools profiler expects (also avoids a warning on import) const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp(), pid: 1, tid: 1 }; @@ -75,19 +81,14 @@ namespace ts.tracing { .map(v => JSON.stringify(v)).join(",\n")); } - /** Stops tracing for the in-progress project and dumps the type catalog (unless the `fs` module is unavailable). */ + /** Stops tracing for the in-progress project and dumps the type catalog. */ export function stopTracing(typeCatalog?: readonly Type[]) { - if (!traceFd) { - Debug.assert(!fs, "Tracing is not in progress"); - return; - } - - Debug.assert(fs); + Debug.assert(tracing, "Tracing is not in progress"); Debug.assert(!!typeCatalog === (mode !== Mode.Server)); // Have a type catalog iff not in server mode fs.writeSync(traceFd, `\n]\n`); fs.closeSync(traceFd); - traceFd = undefined; + tracing = undefined; if (typeCatalog) { dumpTypes(typeCatalog); @@ -99,10 +100,6 @@ namespace ts.tracing { } } - export function isTracing() { - return !!traceFd; - } - export const enum Phase { Parse = "parse", Program = "program", @@ -114,7 +111,6 @@ namespace ts.tracing { } export function instant(phase: Phase, name: string, args?: Args) { - if (!traceFd) return; writeEvent("I", phase, name, args, `"s":"g"`); } @@ -127,20 +123,17 @@ namespace ts.tracing { * these operations. */ export function push(phase: Phase, name: string, args?: Args, separateBeginAndEnd = false) { - if (!traceFd) return; if (separateBeginAndEnd) { writeEvent("B", phase, name, args); } eventStack.push({ phase, name, args, time: 1000 * timestamp(), separateBeginAndEnd }); } export function pop() { - if (!traceFd) return; Debug.assert(eventStack.length > 0); writeStackEvent(eventStack.length - 1, 1000 * timestamp()); eventStack.length--; } export function popAll() { - if (!traceFd) return; const endTime = 1000 * timestamp(); for (let i = eventStack.length - 1; i >= 0; i--) { writeStackEvent(i, endTime); @@ -159,8 +152,6 @@ namespace ts.tracing { function writeEvent(eventType: string, phase: Phase, name: string, args: Args | undefined, extras?: string, time: number = 1000 * timestamp()) { - Debug.assert(traceFd); - Debug.assert(fs); // In server mode, there's no easy way to dump type information, so we drop events that would require it. if (mode === Mode.Server && phase === Phase.CheckTypes) return; @@ -182,8 +173,6 @@ namespace ts.tracing { } function dumpTypes(types: readonly Type[]) { - Debug.assert(fs); - performance.mark("beginDumpTypes"); const typesPath = legend[legend.length - 1].typesPath!; @@ -293,7 +282,6 @@ namespace ts.tracing { if (!legendPath) { return; } - Debug.assert(fs); fs.writeFileSync(legendPath, JSON.stringify(legend)); } @@ -304,3 +292,9 @@ namespace ts.tracing { typesPath?: string; } } + +/*@internal*/ +namespace ts { // eslint-disable-line one-namespace-per-file + // define after tracingEnabled is initialized + export const startTracing = tracingEnabled.startTracing; +} diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index efca6f7f187..4de469afc57 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -223,9 +223,9 @@ namespace ts { // Transform each node. const transformed: T[] = []; for (const node of nodes) { - tracing.push(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end }); + tracing?.push(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end }); transformed.push((allowDtsFiles ? transformation : transformRoot)(node)); - tracing.pop(); + tracing?.pop(); } // prevent modification of the lexical environment. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8c3ff87cbbf..d91bbac3b74 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5643,7 +5643,7 @@ namespace ts { function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { this.flags = flags; - if (Debug.isDebugging || tracing.isTracing()) { + if (Debug.isDebugging || tracing) { this.checker = checker; } } diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 593653a7717..09f5230ab8a 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -501,7 +501,7 @@ namespace ts { updateSolutionBuilderHost(sys, cb, buildHost); const builder = createSolutionBuilder(buildHost, projects, buildOptions); const exitStatus = buildOptions.clean ? builder.clean() : builder.build(); - tracing.dumpLegend(); + tracing?.dumpLegend(); return sys.exit(exitStatus); } @@ -666,7 +666,8 @@ namespace ts { } if (canTrace(system, compilerOptions)) { - tracing.startTracing(isBuildMode ? tracing.Mode.Build : tracing.Mode.Project, compilerOptions.generateTrace!, compilerOptions.configFilePath); + startTracing(isBuildMode ? tracingEnabled.Mode.Build : tracingEnabled.Mode.Project, + compilerOptions.generateTrace!, compilerOptions.configFilePath); } } @@ -674,7 +675,7 @@ namespace ts { const compilerOptions = program.getCompilerOptions(); if (canTrace(sys, compilerOptions)) { - tracing.stopTracing(program.getTypeCatalog()); + tracing?.stopTracing(program.getTypeCatalog()); } let statistics: Statistic[]; diff --git a/src/server/session.ts b/src/server/session.ts index 2ee15bd1b8a..dc0d4f42f5c 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -208,25 +208,25 @@ namespace ts.server { try { if (this.operationHost.isCancellationRequested()) { stop = true; - tracing.instant(tracing.Phase.Session, "stepCanceled", { seq: this.requestId, early: true }); + tracing?.instant(tracing.Phase.Session, "stepCanceled", { seq: this.requestId, early: true }); } else { - tracing.push(tracing.Phase.Session, "stepAction", { seq: this.requestId }); + tracing?.push(tracing.Phase.Session, "stepAction", { seq: this.requestId }); action(this); - tracing.pop(); + tracing?.pop(); } } catch (e) { // Cancellation or an error may have left incomplete events on the tracing stack. - tracing.popAll(); + tracing?.popAll(); stop = true; // ignore cancellation request if (e instanceof OperationCanceledException) { - tracing.instant(tracing.Phase.Session, "stepCanceled", { seq: this.requestId }); + tracing?.instant(tracing.Phase.Session, "stepCanceled", { seq: this.requestId }); } else { - tracing.instant(tracing.Phase.Session, "stepError", { seq: this.requestId, message: (e).message }); + tracing?.instant(tracing.Phase.Session, "stepError", { seq: this.requestId, message: (e).message }); this.operationHost.logError(e, `delayed processing of request ${this.requestId}`); } } @@ -947,7 +947,7 @@ namespace ts.server { } public event(body: T, eventName: string): void { - tracing.instant(tracing.Phase.Session, "event", { eventName }); + tracing?.instant(tracing.Phase.Session, "event", { eventName }); this.send(toEvent(eventName, body)); } @@ -2962,12 +2962,12 @@ namespace ts.server { request = this.parseMessage(message); relevantFile = request.arguments && (request as protocol.FileRequest).arguments.file ? (request as protocol.FileRequest).arguments : undefined; - tracing.instant(tracing.Phase.Session, "request", { seq: request.seq, command: request.command }); + tracing?.instant(tracing.Phase.Session, "request", { seq: request.seq, command: request.command }); perfLogger.logStartCommand("" + request.command, this.toStringMessage(message).substring(0, 100)); - tracing.push(tracing.Phase.Session, "executeCommand", { seq: request.seq, command: request.command }, /*separateBeginAndEnd*/ true); + tracing?.push(tracing.Phase.Session, "executeCommand", { seq: request.seq, command: request.command }, /*separateBeginAndEnd*/ true); const { response, responseRequired } = this.executeCommand(request); - tracing.pop(); + tracing?.pop(); if (this.logger.hasLevel(LogLevel.requestTime)) { const elapsedTime = hrTimeToMilliseconds(this.hrtime(start)).toFixed(4); @@ -2981,7 +2981,7 @@ namespace ts.server { // Note: Log before writing the response, else the editor can complete its activity before the server does perfLogger.logStopCommand("" + request.command, "Success"); - tracing.instant(tracing.Phase.Session, "response", { seq: request.seq, command: request.command, success: !!response }); + tracing?.instant(tracing.Phase.Session, "response", { seq: request.seq, command: request.command, success: !!response }); if (response) { this.doOutput(response, request.command, request.seq, /*success*/ true); } @@ -2991,19 +2991,19 @@ namespace ts.server { } catch (err) { // Cancellation or an error may have left incomplete events on the tracing stack. - tracing.popAll(); + tracing?.popAll(); if (err instanceof OperationCanceledException) { // Handle cancellation exceptions perfLogger.logStopCommand("" + (request && request.command), "Canceled: " + err); - tracing.instant(tracing.Phase.Session, "commandCanceled", { seq: request?.seq, command: request?.command }); + tracing?.instant(tracing.Phase.Session, "commandCanceled", { seq: request?.seq, command: request?.command }); this.doOutput({ canceled: true }, request!.command, request!.seq, /*success*/ true); return; } this.logErrorWorker(err, this.toStringMessage(message), relevantFile); perfLogger.logStopCommand("" + (request && request.command), "Error: " + err); - tracing.instant(tracing.Phase.Session, "commandError", { seq: request?.seq, command: request?.command, message: (err).message }); + tracing?.instant(tracing.Phase.Session, "commandError", { seq: request?.seq, command: request?.command, message: (err).message }); this.doOutput( /*info*/ undefined, diff --git a/src/services/services.ts b/src/services/services.ts index e1b60bdef80..1463d81dfde 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1143,7 +1143,7 @@ namespace ts { public throwIfCancellationRequested(): void { if (this.isCancellationRequested()) { - tracing.instant(tracing.Phase.Session, "cancellationThrown", { kind: "CancellationTokenObject" }); + tracing?.instant(tracing.Phase.Session, "cancellationThrown", { kind: "CancellationTokenObject" }); throw new OperationCanceledException(); } } @@ -1174,7 +1174,7 @@ namespace ts { public throwIfCancellationRequested(): void { if (this.isCancellationRequested()) { - tracing.instant(tracing.Phase.Session, "cancellationThrown", { kind: "ThrottledCancellationToken" }); + tracing?.instant(tracing.Phase.Session, "cancellationThrown", { kind: "ThrottledCancellationToken" }); throw new OperationCanceledException(); } } diff --git a/src/tsserver/nodeServer.ts b/src/tsserver/nodeServer.ts index f4b7061881b..97aa0a198e8 100644 --- a/src/tsserver/nodeServer.ts +++ b/src/tsserver/nodeServer.ts @@ -833,9 +833,7 @@ namespace ts.server { exit() { this.logger.info("Exiting..."); this.projectService.closeLog(); - if (traceDir) { - tracing.stopTracing(ts.emptyArray); - } + tracing?.stopTracing(ts.emptyArray); process.exit(0); } @@ -863,7 +861,7 @@ namespace ts.server { ? stripQuotes(commandLineTraceDir) : process.env.TSS_TRACE; if (traceDir) { - tracing.startTracing(tracing.Mode.Server, traceDir); + startTracing(tracingEnabled.Mode.Server, traceDir); } const ioSession = new IOSession();