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
This commit is contained in:
Andrew Casey
2020-01-08 14:41:34 -08:00
committed by GitHub
parent ab1458ac55
commit 071819bb37
6 changed files with 60 additions and 5 deletions

View File

@@ -150,6 +150,15 @@ namespace ts.server {
export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
/*@internal*/
export interface PerformanceEvent {
kind: "UpdateGraph";
durationMs: number;
}
/*@internal*/
export type PerformanceEventHandler = (event: PerformanceEvent) => void;
export interface SafeList {
[name: string]: { match: RegExp, exclude?: (string | number)[][], types?: string[] };
}
@@ -612,6 +621,8 @@ namespace ts.server {
/*@internal*/
readonly watchFactory: WatchFactory<WatchType, Project>;
private performanceEventHandler?: PerformanceEventHandler;
constructor(opts: ProjectServiceOptions) {
this.host = opts.host;
this.logger = opts.logger;
@@ -849,6 +860,13 @@ namespace ts.server {
this.eventHandler(event);
}
/* @internal */
sendUpdateGraphPerformanceEvent(durationMs: number) {
if (this.performanceEventHandler) {
this.performanceEventHandler({ kind: "UpdateGraph", durationMs });
}
}
/* @internal */
delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project: Project) {
this.delayUpdateProjectGraph(project);
@@ -2551,6 +2569,11 @@ namespace ts.server {
return info.sourceFileLike;
}
/*@internal*/
setPerformanceEventHandler(performanceEventHandler: PerformanceEventHandler) {
this.performanceEventHandler = performanceEventHandler;
}
setHostConfiguration(args: protocol.ConfigureRequestArguments) {
if (args.file) {
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file));

View File

@@ -1087,6 +1087,7 @@ namespace ts.server {
removed => this.detachScriptInfoFromProject(removed)
);
const elapsed = timestamp() - start;
this.projectService.sendUpdateGraphPerformanceEvent(elapsed);
this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasNewProgram} Elapsed: ${elapsed}ms`);
if (this.hasAddedorRemovedFiles) {
this.print();

View File

@@ -236,6 +236,12 @@ namespace ts.server.protocol {
* Contains extra information that plugin can include to be passed on
*/
metadata?: unknown;
/**
* Time spent updating the program graph, in milliseconds.
*/
/* @internal */
updateGraphDurationMs?: number;
}
/**

View File

@@ -591,6 +591,8 @@ namespace ts.server {
protected projectService: ProjectService;
private changeSeq = 0;
private updateGraphDurationMs: number | undefined;
private currentRequestId!: number;
private errorCheck: MultistepOperation;
@@ -648,6 +650,7 @@ namespace ts.server {
syntaxOnly: opts.syntaxOnly,
};
this.projectService = new ProjectService(settings);
this.projectService.setPerformanceEventHandler(this.performanceEventHandler.bind(this));
this.gcTimer = new GcTimer(this.host, /*delay*/ 7000, this.logger);
}
@@ -655,6 +658,15 @@ namespace ts.server {
this.event<protocol.RequestCompletedEventBody>({ request_seq: requestId }, "requestCompleted");
}
private performanceEventHandler(event: PerformanceEvent) {
switch (event.kind) {
case "UpdateGraph": {
this.updateGraphDurationMs = (this.updateGraphDurationMs || 0) + event.durationMs;
break;
}
}
}
private defaultEventHandler(event: ProjectServiceEvent) {
switch (event.eventName) {
case ProjectsUpdatedInBackgroundEvent:
@@ -795,7 +807,9 @@ namespace ts.server {
command: cmdName,
request_seq: reqSeq,
success,
updateGraphDurationMs: this.updateGraphDurationMs,
};
if (success) {
let metadata: unknown;
if (isArray(info)) {
@@ -2623,6 +2637,9 @@ namespace ts.server {
public onMessage(message: string) {
this.gcTimer.scheduleCollect();
this.updateGraphDurationMs = undefined;
let start: number[] | undefined;
if (this.logger.hasLevel(LogLevel.requestTime)) {
start = this.hrtime();