From 36489ff5c676c3b9a85b53fc8cd0d30a79ea18ee Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Fri, 28 Aug 2020 15:51:52 -0700 Subject: [PATCH] Add instantaneous events when depth limits are hit --- src/compiler/checker.ts | 11 ++++++++++- src/compiler/tracing.ts | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff7e456cb6c..ee5b7e91196 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10804,6 +10804,7 @@ namespace ts { // very high likelihood we're dealing with an infinite generic type that perpetually generates // new type identities as we descend into it. We stop the recursion here and mark this type // and the outer types as having circular constraints. + tracing.instant(tracing.Phase.Check, "getImmediateBaseConstraint_DepthLimit", { typeId: t.id, originalTypeId: type.id, depth: constraintDepth }); error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); nonTerminating = true; return t.immediateBaseConstraint = noConstraintType; @@ -12878,6 +12879,7 @@ namespace ts { // caps union types at 5000 unique literal types and 1000 unique object types. const estimatedCount = (count / (len - i)) * len; if (estimatedCount > (primitivesOnly ? 25000000 : 1000000)) { + tracing.instant(tracing.Phase.Check, "removeSubtypes_DepthLimit", { typeIds: types.map(t => t.id) }); error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); return false; } @@ -14940,6 +14942,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.Check, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount }); error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); return errorType; } @@ -16058,6 +16061,7 @@ namespace ts { reportIncompatibleStack(); } if (overflow) { + tracing.instant(tracing.Phase.Check, "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); @@ -17358,6 +17362,7 @@ namespace ts { numCombinations *= countTypes(getTypeOfSymbol(sourceProperty)); if (numCombinations > 25) { // We've reached the complexity limit. + tracing.instant(tracing.Phase.Check, "typeRelatedToDiscriminatedType_DepthLimit", { sourceId: source.id, targetId: target.id, numCombinations }); return Ternary.False; } } @@ -18281,7 +18286,10 @@ namespace ts { for (let i = 0; i < depth; i++) { if (getRecursionIdentity(stack[i]) === identity) { count++; - if (count >= 5) return true; + if (count >= 5) { + tracing.instant(tracing.Phase.Check, "isDeeplyNestedType_DepthLimit", { typeId: type.id, typeIdStack: stack.map(t => t.id), depth, count }); + return true; + } } } } @@ -21089,6 +21097,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.Check, "getTypeAtFlowNode_DepthLimit", { flowId: flow.id }); flowAnalysisDisabled = true; reportFlowControlError(reference); return errorType; diff --git a/src/compiler/tracing.ts b/src/compiler/tracing.ts index cabbb6c810c..30b90a2b0d1 100644 --- a/src/compiler/tracing.ts +++ b/src/compiler/tracing.ts @@ -112,6 +112,18 @@ namespace ts.tracing { performance.measure("Tracing", "beginTracing", "endTracing"); } + export function instant(phase: Phase, name: string, args: object) { + if (!traceFd) { + return; + } + Debug.assert(fs); + + performance.mark("beginTracing"); + fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"i","cat":"${phase}","ts":${1000 * timestamp()},"name":"${name}","s":"g","args":{ "ts": ${JSON.stringify(args)} }},\n`); + performance.mark("endTracing"); + performance.measure("Tracing", "beginTracing", "endTracing"); + } + function indexFromOne(lc: LineAndCharacter): LineAndCharacter { return { line: lc.line + 1,