@@ -35,6 +35,18 @@ const String kWindowsExecutable = r'Google\Chrome\Application\chrome.exe';
3535/// The expected Edge executable name on Windows.
3636const String kWindowsEdgeExecutable = r'Microsoft\Edge\Application\msedge.exe' ;
3737
38+ /// Used by [ChromiumLauncher] to detect a glibc bug and retry launching the
39+ /// browser.
40+ ///
41+ /// Once every few thousands of launches we hit this glibc bug:
42+ ///
43+ /// https://sourceware.org/bugzilla/show_bug.cgi?id=19329.
44+ ///
45+ /// When this happens Chrome spits out something like the following then exits with code 127:
46+ ///
47+ /// Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed!
48+ const String _kGlibcError = 'Inconsistency detected by ld.so' ;
49+
3850typedef BrowserFinder = String Function (Platform , FileSystem );
3951
4052/// Find the chrome executable on the current platform.
@@ -168,6 +180,12 @@ class ChromiumLauncher {
168180 }
169181
170182 final String chromeExecutable = _browserFinder (_platform, _fileSystem);
183+
184+ if (_logger.isVerbose) {
185+ final ProcessResult versionResult = await _processManager.run (< String > [chromeExecutable, '--version' ]);
186+ _logger.printTrace ('Using ${versionResult .stdout }' );
187+ }
188+
171189 final Directory userDataDir = _fileSystem.systemTempDirectory
172190 .createTempSync ('flutter_tools_chrome_device.' );
173191
@@ -204,27 +222,7 @@ class ChromiumLauncher {
204222 url,
205223 ];
206224
207- final Process process = await _processManager.start (args);
208-
209- process.stdout
210- .transform (utf8.decoder)
211- .transform (const LineSplitter ())
212- .listen ((String line) {
213- _logger.printTrace ('[CHROME]: $line ' );
214- });
215-
216- // Wait until the DevTools are listening before trying to connect. This is
217- // only required for flutter_test --platform=chrome and not flutter run.
218- await process.stderr
219- .transform (utf8.decoder)
220- .transform (const LineSplitter ())
221- .map ((String line) {
222- _logger.printTrace ('[CHROME]:$line ' );
223- return line;
224- })
225- .firstWhere ((String line) => line.startsWith ('DevTools listening' ), orElse: () {
226- return 'Failed to spawn stderr' ;
227- });
225+ final Process process = await _spawnChromiumProcess (args);
228226
229227 // When the process exits, copy the user settings back to the provided data-dir.
230228 if (cacheDir != null ) {
@@ -241,6 +239,63 @@ class ChromiumLauncher {
241239 ), skipCheck);
242240 }
243241
242+ Future <Process > _spawnChromiumProcess (List <String > args) async {
243+ // Keep attempting to launch the browser until one of:
244+ // - Chrome launched successfully, in which case we just return from the loop.
245+ // - The tool detected an unretriable Chrome error, in which case we throw ToolExit.
246+ while (true ) {
247+ final Process process = await _processManager.start (args);
248+
249+ process.stdout
250+ .transform (utf8.decoder)
251+ .transform (const LineSplitter ())
252+ .listen ((String line) {
253+ _logger.printTrace ('[CHROME]: $line ' );
254+ });
255+
256+ // Wait until the DevTools are listening before trying to connect. This is
257+ // only required for flutter_test --platform=chrome and not flutter run.
258+ bool hitGlibcBug = false ;
259+ await process.stderr
260+ .transform (utf8.decoder)
261+ .transform (const LineSplitter ())
262+ .map ((String line) {
263+ _logger.printTrace ('[CHROME]:$line ' );
264+ if (line.contains (_kGlibcError)) {
265+ hitGlibcBug = true ;
266+ }
267+ return line;
268+ })
269+ .firstWhere ((String line) => line.startsWith ('DevTools listening' ), orElse: () {
270+ if (hitGlibcBug) {
271+ _logger.printTrace (
272+ 'Encountered glibc bug https://sourceware.org/bugzilla/show_bug.cgi?id=19329. '
273+ 'Will try launching browser again.' ,
274+ );
275+ return null ;
276+ }
277+ _logger.printTrace ('Failed to launch browser. Command used to launch it: ${args .join (' ' )}' );
278+ throw ToolExit (
279+ 'Failed to launch browser. Make sure you are using an up-to-date '
280+ 'Chrome or Edge. Otherwise, consider using -d web-server instead '
281+ 'and filing an issue at https://github.com/flutter/flutter/issues.' ,
282+ );
283+ });
284+
285+ if (! hitGlibcBug) {
286+ return process;
287+ }
288+
289+ // A precaution that avoids accumulating browser processes, in case the
290+ // glibc bug doesn't cause the browser to quit and we keep looping and
291+ // launching more processes.
292+ unawaited (process.exitCode.timeout (const Duration (seconds: 1 ), onTimeout: () {
293+ process.kill ();
294+ return null ;
295+ }));
296+ }
297+ }
298+
244299 // This is a JSON file which contains configuration from the browser session,
245300 // such as window position. It is located under the Chrome data-dir folder.
246301 String get _preferencesPath => _fileSystem.path.join ('Default' , 'preferences' );
0 commit comments