From 83657b6bce8aa79c428fc25338be07c587c3ce85 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 12 Feb 2021 11:17:38 +0100 Subject: [PATCH] Check if parentspan is provided in profiler (#22061) Co-authored-by: Dale Bustad --- .vscode/launch.json | 11 ++++ packages/next/build/tracer.ts | 54 ++++++++++++++----- .../build/webpack/plugins/profiling-plugin.ts | 10 +++- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3a6858c2fd0d..4fb1ca11305e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,6 +25,17 @@ "port": 9229, "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] }, + { + "name": "Launch app build trace", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "trace-debug", "build", "test/integration/basic"], + "skipFiles": ["/**"], + "port": 9229, + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] + }, { "name": "Launch app production", "type": "node", diff --git a/packages/next/build/tracer.ts b/packages/next/build/tracer.ts index 2c915822366d..e1da864eb6f0 100644 --- a/packages/next/build/tracer.ts +++ b/packages/next/build/tracer.ts @@ -3,6 +3,7 @@ import api, { Span } from '@opentelemetry/api' export const tracer = api.trace.getTracer('next', process.env.__NEXT_VERSION) const compilerStacks = new WeakMap() +const compilerStoppedSpans = new WeakMap() export function stackPush(compiler: any, spanName: string, attrs?: any): any { let stack = compilerStacks.get(compiler) @@ -13,18 +14,20 @@ export function stackPush(compiler: any, spanName: string, attrs?: any): any { span = tracer.startSpan(spanName, attrs ? attrs() : undefined) } else { const parent = stack[stack.length - 1] - tracer.withSpan(parent, () => { + if (parent) { + tracer.withSpan(parent, () => { + span = tracer.startSpan(spanName, attrs ? attrs() : undefined) + }) + } else { span = tracer.startSpan(spanName, attrs ? attrs() : undefined) - }) + } } stack.push(span) return span } -export function stackPop(compiler: any, span: any) { - span.end() - +export function stackPop(compiler: any, span: any, associatedName?: string) { let stack = compilerStacks.get(compiler) if (!stack) { console.warn( @@ -32,15 +35,40 @@ export function stackPop(compiler: any, span: any) { ) return } - const poppedSpan = stack.pop() - if (poppedSpan !== span) { - stack.push(poppedSpan) - const spanIdx = stack.indexOf(span) - console.warn('Attempted to pop span that was not at top of stack.') - if (spanIdx !== -1) { - console.info( - `Span was found at index ${spanIdx} with stack size ${stack.length}` + + let stoppedSpans: Set = compilerStoppedSpans.get(compiler) + if (!stoppedSpans) { + stoppedSpans = new Set() + compilerStoppedSpans.set(compiler, stoppedSpans) + } + if (stoppedSpans.has(span)) { + console.warn( + `Attempted to terminate tracing span that was already stopped for ${associatedName}` + ) + return + } + + while (true) { + let poppedSpan = stack.pop() + + if (poppedSpan === span) { + stoppedSpans.add(poppedSpan) + span.end() + stoppedSpans.add(span) + break + } else if (poppedSpan === undefined || stack.indexOf(span) === -1) { + // We've either reached the top of the stack or the stack doesn't contain + // the span for another reason. + console.warn(`Tracing span was not found in stack for: ${associatedName}`) + stoppedSpans.add(span) + span.end() + break + } else if (stack.indexOf(span) !== -1) { + console.warn( + `Attempted to pop span that was not at top of stack for: ${associatedName}` ) + stoppedSpans.add(poppedSpan) + poppedSpan.end() } } } diff --git a/packages/next/build/webpack/plugins/profiling-plugin.ts b/packages/next/build/webpack/plugins/profiling-plugin.ts index 36642628990a..146ca81d09be 100644 --- a/packages/next/build/webpack/plugins/profiling-plugin.ts +++ b/packages/next/build/webpack/plugins/profiling-plugin.ts @@ -54,7 +54,13 @@ export class ProfilingPlugin { onSetSpan?.(span) }) stopHook.tap(pluginName, () => { - stackPop(this.compiler, span) + // `stopHook` may be triggered when `startHook` has not in cases + // where `stopHook` is used as the terminating event for more + // than one pair of hooks. + if (!span) { + return + } + stackPop(this.compiler, span, spanName) }) } @@ -66,7 +72,7 @@ export class ProfilingPlugin { } }) stopHook.tap(pluginName, () => { - stackPop(this.compiler, span) + stackPop(this.compiler, span, spanName) }) }