Skip to content

Commit c336cd7

Browse files
committed
Tracing work
* Fix: `E` events need to have the same information that is on the corresponding `B` events. (Superseded below.) * Use `I` (not `i`) for instant events, so they show in devtools too. (Though they don't go through the flame chart as they do in `about://tracing`, so they're not nearly as useful.) * Abstract the code that writes the records in a single `writeEvent` local function. * Make `args` optional, and default to `undefined` (which will not add them) at all. * Drop the `{ "ts": ... }` wrapper around the `args`, after verifying that having arguments with names like `begin`, `end`, `pos`, `id` doesn't interfere with either UIs. * Add `tracing.push`/`tracing.pop` for complete events, change a few `.begin`/`.end` to use these. (The caveat is that until there's an exit handler to dump unterminated events, these won't show in the dump. When that's done the push/pop variant can be used everywhere.) * Add meta lines to name the process and the thread, and a line that avoids the warning when opening in devtools.
1 parent 5284555 commit c336cd7

File tree

7 files changed

+59
-53
lines changed

7 files changed

+59
-53
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ namespace ts {
181181
perfLogger.logStopBindFile();
182182
performance.mark("afterBind");
183183
performance.measure("Bind", "beforeBind", "afterBind");
184-
tracing.end();
184+
tracing.end(tracing.Phase.Bind, "bindSourceFile", { path: file.path });
185185
}
186186

187187
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {

src/compiler/checker.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17104,9 +17104,9 @@ namespace ts {
1710417104
}
1710517105

1710617106
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
17107-
tracing.begin(tracing.Phase.Check, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id });
17107+
tracing.push(tracing.Phase.Check, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id });
1710817108
const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState);
17109-
tracing.end();
17109+
tracing.pop();
1711017110
return result;
1711117111
}
1711217112

@@ -18320,7 +18320,7 @@ namespace ts {
1832018320
function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] {
1832118321
let variances = cache.variances;
1832218322
if (!variances) {
18323-
tracing.begin(tracing.Phase.Check, "getVariancesWorker", { arity: typeParameters.length, id: (cache as any).id ?? (cache as any).declaredType?.id ?? -1 });
18323+
tracing.push(tracing.Phase.Check, "getVariancesWorker", { arity: typeParameters.length, id: (cache as any).id ?? (cache as any).declaredType?.id ?? -1 });
1832418324
// The emptyArray singleton is used to signal a recursive invocation.
1832518325
cache.variances = emptyArray;
1832618326
variances = [];
@@ -18355,7 +18355,7 @@ namespace ts {
1835518355
variances.push(variance);
1835618356
}
1835718357
cache.variances = variances;
18358-
tracing.end();
18358+
tracing.pop();
1835918359
}
1836018360
return variances;
1836118361
}
@@ -30599,7 +30599,7 @@ namespace ts {
3059930599
}
3060030600

3060130601
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
30602-
tracing.begin(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end });
30602+
tracing.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end });
3060330603
const saveCurrentNode = currentNode;
3060430604
currentNode = node;
3060530605
instantiationCount = 0;
@@ -30609,7 +30609,7 @@ namespace ts {
3060930609
checkConstEnumAccess(node, type);
3061030610
}
3061130611
currentNode = saveCurrentNode;
30612-
tracing.end();
30612+
tracing.pop();
3061330613
return type;
3061430614
}
3061530615

@@ -33403,10 +33403,10 @@ namespace ts {
3340333403
}
3340433404

3340533405
function checkVariableDeclaration(node: VariableDeclaration) {
33406-
tracing.begin(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end });
33406+
tracing.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end });
3340733407
checkGrammarVariableDeclaration(node);
3340833408
checkVariableLikeDeclaration(node);
33409-
tracing.end();
33409+
tracing.pop();
3341033410
}
3341133411

3341233412
function checkBindingElement(node: BindingElement) {
@@ -36476,7 +36476,7 @@ namespace ts {
3647636476
checkSourceFileWorker(node);
3647736477
performance.mark("afterCheck");
3647836478
performance.measure("Check", "beforeCheck", "afterCheck");
36479-
tracing.end();
36479+
tracing.end(tracing.Phase.Check, "checkSourceFile", { path: node.path });
3648036480
}
3648136481

3648236482
function unusedIsError(kind: UnusedKind, isAmbient: boolean): boolean {

src/compiler/emitter.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,17 @@ namespace ts {
300300
sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory())))
301301
};
302302
}
303-
tracing.begin(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath });
303+
tracing.push(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath });
304304
emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo);
305-
tracing.end();
305+
tracing.pop();
306306

307-
tracing.begin(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath });
307+
tracing.push(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath });
308308
emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo);
309-
tracing.end();
309+
tracing.pop();
310310

311-
tracing.begin(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath });
311+
tracing.push(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath });
312312
emitBuildInfo(bundleBuildInfo, buildInfoPath);
313-
tracing.end();
313+
tracing.pop();
314314

315315
if (!emitSkipped && emittedFilesList) {
316316
if (!emitOnlyDtsFiles) {

src/compiler/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ namespace ts {
629629

630630
performance.mark("afterParse");
631631
performance.measure("Parse", "beforeParse", "afterParse");
632-
tracing.end();
632+
tracing.end(tracing.Phase.Parse, "createSourceFile", { path: fileName });
633633
return result;
634634
}
635635

src/compiler/program.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ namespace ts {
734734
// Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
735735
const sourceFilesFoundSearchingNodeModules = new Map<string, boolean>();
736736

737-
tracing.begin(tracing.Phase.Program, "createProgram", {});
737+
tracing.begin(tracing.Phase.Program, "createProgram");
738738
performance.mark("beforeProgram");
739739

740740
const host = createProgramOptions.host || createCompilerHost(options);
@@ -984,7 +984,7 @@ namespace ts {
984984
verifyCompilerOptions();
985985
performance.mark("afterProgram");
986986
performance.measure("Program", "beforeProgram", "afterProgram");
987-
tracing.end();
987+
tracing.end(tracing.Phase.Program, "createProgram");
988988

989989
return program;
990990

@@ -1508,7 +1508,7 @@ namespace ts {
15081508

15091509
function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
15101510
Debug.assert(!outFile(options));
1511-
tracing.begin(tracing.Phase.Emit, "emitBuildInfo", {});
1511+
tracing.begin(tracing.Phase.Emit, "emitBuildInfo");
15121512
performance.mark("beforeEmit");
15131513
const emitResult = emitFiles(
15141514
notImplementedResolver,
@@ -1521,7 +1521,7 @@ namespace ts {
15211521

15221522
performance.mark("afterEmit");
15231523
performance.measure("Emit", "beforeEmit", "afterEmit");
1524-
tracing.end();
1524+
tracing.end(tracing.Phase.Emit, "emitBuildInfo");
15251525
return emitResult;
15261526
}
15271527

@@ -1584,7 +1584,7 @@ namespace ts {
15841584
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
15851585
tracing.begin(tracing.Phase.Emit, "emit", { path: sourceFile?.path });
15861586
const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit));
1587-
tracing.end();
1587+
tracing.end(tracing.Phase.Emit, "emit", { path: sourceFile?.path });
15881588
return result;
15891589
}
15901590

src/compiler/tracing.ts

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,16 @@ namespace ts.tracing {
4646
});
4747

4848
traceFd = fs.openSync(tracePath, "w");
49-
fs.writeSync(traceFd, `[\n`);
49+
50+
// Start with a prefix that contains some metadata that the devtools profiler expects (also avoids a warning on import)
51+
const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp(), pid: 1, tid: 1 };
52+
fs.writeSync(traceFd,
53+
"[\n"
54+
+ [{ name: "process_name", args: { name: "tsc" }, ...meta },
55+
{ name: "thread_name", args: { name: "Main" }, ...meta },
56+
{ name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }]
57+
.map(v => JSON.stringify(v)).join("\n,")
58+
+ "\n");
5059
}
5160

5261
/** Stops tracing for the in-progress project and dumps the type catalog (unless the `fs` module is unavailable). */
@@ -58,11 +67,7 @@ namespace ts.tracing {
5867

5968
Debug.assert(fs);
6069

61-
// This both indicates that the trace is untruncated and conveniently
62-
// ensures that the last array element won't have a trailing comma.
63-
fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"i","ts":${1000 * timestamp()},"name":"done","s":"g"}\n`);
6470
fs.writeSync(traceFd, `]\n`);
65-
6671
fs.closeSync(traceFd);
6772
traceFd = undefined;
6873

@@ -88,38 +93,39 @@ namespace ts.tracing {
8893
Emit = "emit",
8994
}
9095

91-
export function begin(phase: Phase, name: string, args: object) {
92-
if (!traceFd) {
93-
return;
94-
}
95-
Debug.assert(fs);
96+
export function begin(phase: Phase, name: string, args?: object) {
97+
writeEvent("B", phase, name, args);
98+
}
9699

97-
performance.mark("beginTracing");
98-
fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"B","cat":"${phase}","ts":${1000 * timestamp()},"name":"${name}","args":{ "ts": ${JSON.stringify(args)} }},\n`);
99-
performance.mark("endTracing");
100-
performance.measure("Tracing", "beginTracing", "endTracing");
100+
export function end(phase: Phase, name: string, args?: object) {
101+
writeEvent("E", phase, name, args);
101102
}
102103

103-
export function end() {
104-
if (!traceFd) {
105-
return;
106-
}
107-
Debug.assert(fs);
104+
export function instant(phase: Phase, name: string, args?: object) {
105+
writeEvent("I", phase, name, args, `"s":"g"`);
106+
}
108107

109-
performance.mark("beginTracing");
110-
fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"E","ts":${1000 * timestamp()}},\n`);
111-
performance.mark("endTracing");
112-
performance.measure("Tracing", "beginTracing", "endTracing");
108+
// Used for "Complete" (ph:"X") events
109+
const completeEvents: { phase: Phase, name: string, args?: object, time: number }[] = [];
110+
export function push(phase: Phase, name: string, args?: object) {
111+
completeEvents.push({ phase, name, args, time: 1000 * timestamp() });
112+
}
113+
export function pop() {
114+
Debug.assert(completeEvents.length > 0);
115+
const { phase, name, args, time } = completeEvents.pop()!;
116+
const dur = 1000 * timestamp() - time;
117+
writeEvent("X", phase, name, args, `"dur":${dur}`, time);
113118
}
114119

115-
export function instant(phase: Phase, name: string, args: object) {
116-
if (!traceFd) {
117-
return;
118-
}
120+
function writeEvent(eventType: string, phase: Phase, name: string, args: object | undefined, extras?: string,
121+
time: number = 1000 * timestamp()) {
122+
if (!traceFd) return;
119123
Debug.assert(fs);
120-
121124
performance.mark("beginTracing");
122-
fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"i","cat":"${phase}","ts":${1000 * timestamp()},"name":"${name}","s":"g","args":{ "ts": ${JSON.stringify(args)} }},\n`);
125+
fs.writeSync(traceFd, `,{"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
126+
if (extras) fs.writeSync(traceFd, `,${extras}`);
127+
if (args) fs.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
128+
fs.writeSync(traceFd, `}\n`);
123129
performance.mark("endTracing");
124130
performance.measure("Tracing", "beginTracing", "endTracing");
125131
}

src/compiler/transformer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,9 @@ namespace ts {
223223
// Transform each node.
224224
const transformed: T[] = [];
225225
for (const node of nodes) {
226-
tracing.begin(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end });
226+
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 });
227227
transformed.push((allowDtsFiles ? transformation : transformRoot)(node));
228-
tracing.end();
228+
tracing.pop();
229229
}
230230

231231
// prevent modification of the lexical environment.

0 commit comments

Comments
 (0)