Skip to content

Commit 071819b

Browse files
authored
Report time spent in updateGraph (#35675)
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 #34774
1 parent ab1458a commit 071819b

File tree

6 files changed

+60
-5
lines changed

6 files changed

+60
-5
lines changed

src/server/editorServices.ts

Lines changed: 23 additions & 0 deletions
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));

src/server/project.ts

Lines changed: 1 addition & 0 deletions
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();

src/server/protocol.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ namespace ts.server.protocol {
236236
* Contains extra information that plugin can include to be passed on
237237
*/
238238
metadata?: unknown;
239+
240+
/**
241+
* Time spent updating the program graph, in milliseconds.
242+
*/
243+
/* @internal */
244+
updateGraphDurationMs?: number;
239245
}
240246

241247
/**

src/server/session.ts

Lines changed: 17 additions & 0 deletions
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)) {
@@ -2623,6 +2637,9 @@ namespace ts.server {
26232637

26242638
public onMessage(message: string) {
26252639
this.gcTimer.scheduleCollect();
2640+
2641+
this.updateGraphDurationMs = undefined;
2642+
26262643
let start: number[] | undefined;
26272644
if (this.logger.hasLevel(LogLevel.requestTime)) {
26282645
start = this.hrtime();

src/testRunner/unittests/tsserver/session.ts

Lines changed: 10 additions & 5 deletions
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", () => {
@@ -326,7 +328,8 @@ namespace ts.server {
326328
success: true,
327329
request_seq: 0,
328330
seq: 0,
329-
body: undefined
331+
body: undefined,
332+
updateGraphDurationMs: undefined,
330333
});
331334
});
332335
});
@@ -416,7 +419,8 @@ namespace ts.server {
416419
type: "response",
417420
command,
418421
body,
419-
success: true
422+
success: true,
423+
updateGraphDurationMs: undefined,
420424
});
421425
});
422426
});
@@ -535,7 +539,8 @@ namespace ts.server {
535539
type: "response",
536540
command,
537541
body,
538-
success: true
542+
success: true,
543+
updateGraphDurationMs: undefined,
539544
});
540545
});
541546
it("can add and respond to new protocol handlers", () => {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9154,6 +9154,7 @@ declare namespace ts.server {
91549154
readonly syntaxOnly?: boolean;
91559155
/** Tracks projects that we have already sent telemetry for. */
91569156
private readonly seenProjects;
9157+
private performanceEventHandler?;
91579158
constructor(opts: ProjectServiceOptions);
91589159
toPath(fileName: string): Path;
91599160
private loadTypesMap;
@@ -9383,6 +9384,7 @@ declare namespace ts.server {
93839384
private readonly gcTimer;
93849385
protected projectService: ProjectService;
93859386
private changeSeq;
9387+
private updateGraphDurationMs;
93869388
private currentRequestId;
93879389
private errorCheck;
93889390
protected host: ServerHost;
@@ -9397,6 +9399,7 @@ declare namespace ts.server {
93979399
private readonly noGetErrOnBackgroundUpdate?;
93989400
constructor(opts: SessionOptions);
93999401
private sendRequestCompletedEvent;
9402+
private performanceEventHandler;
94009403
private defaultEventHandler;
94019404
private projectsUpdatedInBackgroundEvent;
94029405
logError(err: Error, cmd: string): void;

0 commit comments

Comments
 (0)