Skip to content

Commit 06528cf

Browse files
committed
Report time spent in updateGraph
Add a response property indicating how much of the elapsed time (from `onMessage` to `doOutput`) was spent in `updateGraph` calls. If there's no `updateGraph` call, the property is undefined, to save space (with the downside that it's harder to tell whether a given telemetry event could have had the property). Fixes microsoft#34774
1 parent 71a9176 commit 06528cf

File tree

6 files changed

+60
-5
lines changed

6 files changed

+60
-5
lines changed

Diff for: src/server/editorServices.ts

+23
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,15 @@ namespace ts.server {
150150

151151
export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
152152

153+
/*@internal*/
154+
export interface PerformanceEvent {
155+
kind: "UpdateGraph";
156+
durationMs: number;
157+
}
158+
159+
/*@internal*/
160+
export type PerformanceEventHandler = (event: PerformanceEvent) => void;
161+
153162
export interface SafeList {
154163
[name: string]: { match: RegExp, exclude?: (string | number)[][], types?: string[] };
155164
}
@@ -612,6 +621,8 @@ namespace ts.server {
612621
/*@internal*/
613622
readonly watchFactory: WatchFactory<WatchType, Project>;
614623

624+
private performanceEventHandler?: PerformanceEventHandler;
625+
615626
constructor(opts: ProjectServiceOptions) {
616627
this.host = opts.host;
617628
this.logger = opts.logger;
@@ -849,6 +860,13 @@ namespace ts.server {
849860
this.eventHandler(event);
850861
}
851862

863+
/* @internal */
864+
sendUpdateGraphPerformanceEvent(durationMs: number) {
865+
if (this.performanceEventHandler) {
866+
this.performanceEventHandler({ kind: "UpdateGraph", durationMs });
867+
}
868+
}
869+
852870
/* @internal */
853871
delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project: Project) {
854872
this.delayUpdateProjectGraph(project);
@@ -2551,6 +2569,11 @@ namespace ts.server {
25512569
return info.sourceFileLike;
25522570
}
25532571

2572+
/*@internal*/
2573+
setPerformanceEventHandler(performanceEventHandler: PerformanceEventHandler) {
2574+
this.performanceEventHandler = performanceEventHandler;
2575+
}
2576+
25542577
setHostConfiguration(args: protocol.ConfigureRequestArguments) {
25552578
if (args.file) {
25562579
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file));

Diff for: src/server/project.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,7 @@ namespace ts.server {
10871087
removed => this.detachScriptInfoFromProject(removed)
10881088
);
10891089
const elapsed = timestamp() - start;
1090+
this.projectService.sendUpdateGraphPerformanceEvent(elapsed);
10901091
this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasNewProgram} Elapsed: ${elapsed}ms`);
10911092
if (this.hasAddedorRemovedFiles) {
10921093
this.print();

Diff for: src/server/protocol.ts

+6
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ namespace ts.server.protocol {
232232
* Contains extra information that plugin can include to be passed on
233233
*/
234234
metadata?: unknown;
235+
236+
/**
237+
* Time spent updating the program graph, in milliseconds.
238+
*/
239+
/* @internal */
240+
updateGraphDurationMs?: number;
235241
}
236242

237243
/**

Diff for: src/server/session.ts

+17
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,8 @@ namespace ts.server {
591591
protected projectService: ProjectService;
592592
private changeSeq = 0;
593593

594+
private updateGraphDurationMs: number | undefined;
595+
594596
private currentRequestId!: number;
595597
private errorCheck: MultistepOperation;
596598

@@ -648,13 +650,23 @@ namespace ts.server {
648650
syntaxOnly: opts.syntaxOnly,
649651
};
650652
this.projectService = new ProjectService(settings);
653+
this.projectService.setPerformanceEventHandler(this.performanceEventHandler.bind(this));
651654
this.gcTimer = new GcTimer(this.host, /*delay*/ 7000, this.logger);
652655
}
653656

654657
private sendRequestCompletedEvent(requestId: number): void {
655658
this.event<protocol.RequestCompletedEventBody>({ request_seq: requestId }, "requestCompleted");
656659
}
657660

661+
private performanceEventHandler(event: PerformanceEvent) {
662+
switch (event.kind) {
663+
case "UpdateGraph": {
664+
this.updateGraphDurationMs = (this.updateGraphDurationMs || 0) + event.durationMs;
665+
break;
666+
}
667+
}
668+
}
669+
658670
private defaultEventHandler(event: ProjectServiceEvent) {
659671
switch (event.eventName) {
660672
case ProjectsUpdatedInBackgroundEvent:
@@ -795,7 +807,9 @@ namespace ts.server {
795807
command: cmdName,
796808
request_seq: reqSeq,
797809
success,
810+
updateGraphDurationMs: this.updateGraphDurationMs,
798811
};
812+
799813
if (success) {
800814
let metadata: unknown;
801815
if (isArray(info)) {
@@ -2549,6 +2563,9 @@ namespace ts.server {
25492563

25502564
public onMessage(message: string) {
25512565
this.gcTimer.scheduleCollect();
2566+
2567+
this.updateGraphDurationMs = undefined;
2568+
25522569
let start: number[] | undefined;
25532570
if (this.logger.hasLevel(LogLevel.requestTime)) {
25542571
start = this.hrtime();

Diff for: src/testRunner/unittests/tsserver/session.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ namespace ts.server {
100100
seq: 0,
101101
message: "Unrecognized JSON command: foobar",
102102
request_seq: 0,
103-
success: false
103+
success: false,
104+
updateGraphDurationMs: undefined,
104105
};
105106
expect(lastSent).to.deep.equal(expected);
106107
});
@@ -126,7 +127,8 @@ namespace ts.server {
126127
success: true,
127128
request_seq: 0,
128129
seq: 0,
129-
body: undefined
130+
body: undefined,
131+
updateGraphDurationMs: undefined,
130132
});
131133
});
132134
it("should handle literal types in request", () => {
@@ -323,7 +325,8 @@ namespace ts.server {
323325
success: true,
324326
request_seq: 0,
325327
seq: 0,
326-
body: undefined
328+
body: undefined,
329+
updateGraphDurationMs: undefined,
327330
});
328331
});
329332
});
@@ -413,7 +416,8 @@ namespace ts.server {
413416
type: "response",
414417
command,
415418
body,
416-
success: true
419+
success: true,
420+
updateGraphDurationMs: undefined,
417421
});
418422
});
419423
});
@@ -532,7 +536,8 @@ namespace ts.server {
532536
type: "response",
533537
command,
534538
body,
535-
success: true
539+
success: true,
540+
updateGraphDurationMs: undefined,
536541
});
537542
});
538543
it("can add and respond to new protocol handlers", () => {

Diff for: tests/baselines/reference/api/tsserverlibrary.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -9033,6 +9033,7 @@ declare namespace ts.server {
90339033
readonly syntaxOnly?: boolean;
90349034
/** Tracks projects that we have already sent telemetry for. */
90359035
private readonly seenProjects;
9036+
private performanceEventHandler?;
90369037
constructor(opts: ProjectServiceOptions);
90379038
toPath(fileName: string): Path;
90389039
private loadTypesMap;
@@ -9262,6 +9263,7 @@ declare namespace ts.server {
92629263
private readonly gcTimer;
92639264
protected projectService: ProjectService;
92649265
private changeSeq;
9266+
private updateGraphDurationMs;
92659267
private currentRequestId;
92669268
private errorCheck;
92679269
protected host: ServerHost;
@@ -9276,6 +9278,7 @@ declare namespace ts.server {
92769278
private readonly noGetErrOnBackgroundUpdate?;
92779279
constructor(opts: SessionOptions);
92789280
private sendRequestCompletedEvent;
9281+
private performanceEventHandler;
92799282
private defaultEventHandler;
92809283
private projectsUpdatedInBackgroundEvent;
92819284
logError(err: Error, cmd: string): void;

0 commit comments

Comments
 (0)