@@ -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