Skip to content

Commit f238920

Browse files
committed
Show a notification for Daemon timeouts on macOS for pre-release users
This is to help gather more information about the issue in #5793, that the timeout added for trying to track down a Windows-only issue seems to be record a lot of timeouts on macOS.
1 parent 56cd1d2 commit f238920

6 files changed

Lines changed: 44 additions & 15 deletions

File tree

‎src/extension/analysis/analyzer.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ export class LspAnalyzer extends Analyzer {
642642
void promptToReloadExtension(logger, {
643643
prompt: "Running the analysis server from source failed because of a version mismatch. Is your Dart SDK an older version than this server version requires?",
644644
offerLog: true,
645-
useError: true,
645+
severity: "ERROR",
646646
restartReason: ExtensionRestartReason.AnalysisServerFromSourceMismatch
647647
});
648648
}
@@ -770,7 +770,7 @@ class DartErrorHandler implements ls.ErrorHandler {
770770
void promptToReloadExtension(this.logger, {
771771
prompt,
772772
offerLog: true,
773-
useError: true,
773+
severity: "ERROR",
774774
restartReason: ExtensionRestartReason.AnalysisServerCrashedManyTimes
775775
});
776776
return {

‎src/extension/analytics.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,12 @@ export class Analytics implements IAmDisposable {
368368
this.event(AnalyticsEvent.AnalysisServer_Terminate, customData);
369369
}
370370

371-
public logErrorFlutterDaemonTimeout() { this.event(AnalyticsEvent.Error_FlutterDaemonTimeout); }
371+
public logErrorFlutterDaemonTimeout(requestMethod: string) {
372+
const customData: Partial<AnalyticsData> = {
373+
data: requestMethod,
374+
};
375+
this.event(AnalyticsEvent.Error_FlutterDaemonTimeout, customData);
376+
}
372377
public logSdkDetectionFailure() { this.event(AnalyticsEvent.SdkDetectionFailure); }
373378
public logDebuggerStart(debuggerType: string, debuggerRunType: string, sdkDap: boolean) {
374379
const customData: Partial<AnalyticsData> = {

‎src/extension/flutter/flutter_daemon.ts‎

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as path from "path";
33
import * as vs from "vscode";
44
import { ProgressLocation } from "vscode";
55
import { DaemonCapabilities, FlutterCapabilities } from "../../shared/capabilities/flutter";
6-
import { ExtensionRestartReason, flutterPath, isChromeOS, isDartCodeTestRun, tenMinutesInMs, twentySecondsInMs } from "../../shared/constants";
6+
import { ExtensionRestartReason, flutterPath, isChromeOS, isDartCodeTestRun, isMac, tenMinutesInMs, twentySecondsInMs } from "../../shared/constants";
77
import { FLUTTER_SUPPORTS_ATTACH } from "../../shared/constants.contexts";
88
import { LogCategory } from "../../shared/enums";
99
import * as f from "../../shared/flutter/daemon_interfaces";
@@ -12,6 +12,7 @@ import { CategoryLogger, logProcess } from "../../shared/logging";
1212
import { UnknownNotification, UnknownResponse } from "../../shared/services/interfaces";
1313
import { StdIOService } from "../../shared/services/stdio_service";
1414
import { PromiseCompleter, usingCustomScript, withTimeout } from "../../shared/utils";
15+
import { isDevExtension, isPreReleaseExtension } from "../../shared/vscode/extension_utils";
1516
import { isRunningLocally } from "../../shared/vscode/utils";
1617
import { Analytics } from "../analytics";
1718
import { config } from "../config";
@@ -20,7 +21,8 @@ import { getFlutterConfigValue } from "../utils/misc";
2021
import { getGlobalFlutterArgs, getToolEnv, runToolProcess, safeToolSpawn } from "../utils/processes";
2122

2223
export class FlutterDaemon extends StdIOService<UnknownNotification> implements IFlutterDaemon {
23-
private hasStarted = false;
24+
private startTime: Date | undefined;
25+
private get hasStarted() { return !!this.startTime; };
2426
private hasShownTerminatedError = false;
2527
private hasLoggedDaemonTimeout = false;
2628
private isShuttingDown = false;
@@ -178,7 +180,7 @@ export class FlutterDaemon extends StdIOService<UnknownNotification> implements
178180
if (message.startsWith("[{") && message.endsWith("}]")) {
179181
// When we get the first message to handle, complete the status notifications.
180182
if (!this.hasStarted) {
181-
this.hasStarted = true;
183+
this.startTime = new Date();
182184
this.daemonStartedCompleter.resolve();
183185
}
184186
return true;
@@ -291,7 +293,7 @@ export class FlutterDaemon extends StdIOService<UnknownNotification> implements
291293
}
292294

293295
public getEmulators(): Thenable<f.FlutterEmulator[]> {
294-
return this.withRecordedTimeout(this.sendRequest("emulator.getEmulators"));
296+
return this.withRecordedTimeout("emulator.getEmulators", this.sendRequest("emulator.getEmulators"));
295297
}
296298

297299
public launchEmulator(emulatorId: string, coldBoot: boolean): Thenable<void> {
@@ -303,7 +305,7 @@ export class FlutterDaemon extends StdIOService<UnknownNotification> implements
303305
}
304306

305307
public getSupportedPlatforms(projectRoot: string): Thenable<f.SupportedPlatformsResponse> {
306-
return this.withRecordedTimeout(this.sendRequest("daemon.getSupportedPlatforms", { projectRoot }));
308+
return this.withRecordedTimeout("daemon.getSupportedPlatforms", this.sendRequest("daemon.getSupportedPlatforms", { projectRoot }));
307309
}
308310

309311
public serveDevTools(): Thenable<f.ServeDevToolsResponse> {
@@ -315,7 +317,7 @@ export class FlutterDaemon extends StdIOService<UnknownNotification> implements
315317
return this.hasStarted && !this.hasShownTerminatedError ? this.sendRequest("daemon.shutdown") : new Promise<void>((resolve) => resolve());
316318
}
317319

318-
private async withRecordedTimeout<T>(promise: Thenable<T>): Promise<T> {
320+
private async withRecordedTimeout<T>(requestMethod: string, promise: Thenable<T>): Promise<T> {
319321
// Don't use timeout unless we haven't shown the message before and we know
320322
// we have fully started up (so we don't false trigger during slow startups
321323
// caused by SDK upgrades, etc.).
@@ -324,14 +326,31 @@ export class FlutterDaemon extends StdIOService<UnknownNotification> implements
324326
return promise; // Short-cut creating the timer.
325327

326328
return new Promise<T>((resolve, reject) => {
329+
const timeoutMs = twentySecondsInMs;
330+
327331
// Set a timer to record if the request doesn't respond fast enough.
328332
const timeoutTimer = setTimeout(() => {
333+
const uptimeSeconds = this.startTime ? (Date.now() - this.startTime.getTime()) / 1000 : -1;
334+
335+
// Always write to the log.
336+
this.logger.error(`Request "${requestMethod} to daemon was not responded to within ${timeoutMs}ms. Daemon has been up for ${uptimeSeconds}s when this timeout fired."`);
337+
329338
const recordTimeouts = this.hasStarted && !this.hasLoggedDaemonTimeout && !this.isShuttingDown && !this.processExited;
330339
if (recordTimeouts) {
331-
this.analytics.logErrorFlutterDaemonTimeout();
340+
this.analytics.logErrorFlutterDaemonTimeout(requestMethod);
332341
this.hasLoggedDaemonTimeout = true;
342+
343+
if ((isDevExtension || isPreReleaseExtension) && isMac) {
344+
void promptToReloadExtension(this.logger, {
345+
prompt: `The Flutter daemon did not respond to a request within ${(timeoutMs / 1000).toFixed(0)}s. Please post any "FlutterDaemon" errors from the log to [this GitHub issue](https://github.com/Dart-Code/Dart-Code/issues/5793#issuecomment-3527504661).`,
346+
offerLog: true,
347+
severity: "WARNING",
348+
specificLog: config.flutterDaemonLogFile,
349+
restartReason: ExtensionRestartReason.FlutterDaemonTimeout
350+
});
351+
}
333352
}
334-
}, twentySecondsInMs);
353+
}, timeoutMs);
335354

336355
promise.then(
337356
(result) => {

‎src/extension/utils.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ export function getLatestSdkVersion(): Promise<string> {
214214

215215
export async function promptToReloadExtension(
216216
logger: Logger,
217-
{ prompt, buttonText, offerLog, specificLog, useError, restartReason }:
218-
{ prompt?: string; buttonText?: string; offerLog?: boolean; specificLog?: string; useError?: boolean; restartReason: ExtensionRestartReason, },
217+
{ prompt, buttonText, offerLog, specificLog, severity, restartReason }:
218+
{ prompt?: string; buttonText?: string; offerLog?: boolean; specificLog?: string; severity?: "ERROR" | "WARNING"; restartReason: ExtensionRestartReason, },
219219
): Promise<void> {
220220
const restartAction = buttonText || "Restart Extension";
221221
const actions = offerLog ? [restartAction, showLogAction] : [restartAction];
@@ -227,7 +227,11 @@ export async function promptToReloadExtension(
227227
const tempLogPath = path.join(os.tmpdir(), `log-${getRandomInt(0x1000, 0x10000).toString(16)}.txt`);
228228
while (showPromptAgain) {
229229
showPromptAgain = false;
230-
const show = useError ? window.showErrorMessage : window.showInformationMessage;
230+
const show = severity === "ERROR"
231+
? window.showErrorMessage
232+
: severity === "WARNING"
233+
? window.showWarningMessage
234+
: window.showInformationMessage;
231235
const chosenAction = prompt && await show(prompt, ...actions);
232236
if (chosenAction === showLogAction) {
233237
showPromptAgain = true;

‎src/extension/utils/misc.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function reportAnalyzerTerminatedWithError(logger: Logger, duringStartup
1717
void promptToReloadExtension(logger, {
1818
prompt,
1919
offerLog: true,
20-
useError: true,
20+
severity: "ERROR",
2121
specificLog: config.analyzerLogFile,
2222
restartReason: ExtensionRestartReason.AnalyzerTerminated,
2323
}).then(() => isShowingAnalyzerError = false);

‎src/shared/constants.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export enum ExtensionRestartReason {
8686
FlutterDaemonTerminatedExit = "flutterDaemonTerminatedExit",
8787
FlutterDaemonTerminatedPing = "flutterDaemonTerminatedPing",
8888
FlutterDaemonTerminatedSend = "flutterDaemonTerminatedSend",
89+
FlutterDaemonTimeout = "flutterDaemonTimeout",
8990
FlutterProjectAdded = "flutterProjectAdded",
9091
FlutterSdkActivationFailure = "flutterSdkActivationFailure",
9192
LocatedSdk = "locatedSdk",

0 commit comments

Comments
 (0)