Repository: electron/electron
Pull request: fix: trace-startup crashing child process on macOS

Backport of #44257

See that PR for details.


Notes: fix `trace-startup` not working on macOS

Changed files:
filename: shell/app/electron_library_main.mm
patch: @@ -23,6 +23,13 @@ int ElectronMain(int argc, char* argv[]) {
   params.argc = argc;
   params.argv = const_cast<const char**>(argv);
   electron::ElectronCommandLine::Init(argc, argv);
+
+  // Ensure that Bundle Id is set before ContentMain.
+  // Refs https://chromium-review.googlesource.com/c/chromium/src/+/5581006
+  delegate.OverrideChildProcessPath();
+  delegate.OverrideFrameworkBundlePath();
+  delegate.SetUpBundleOverrides();
+
   return content::ContentMain(std::move(params));
 }
 

filename: shell/app/electron_main_delegate.cc
patch: @@ -271,12 +271,6 @@ std::optional<int> ElectronMainDelegate::BasicStartupComplete() {
       kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
 #endif
 
-#if BUILDFLAG(IS_MAC)
-  OverrideChildProcessPath();
-  OverrideFrameworkBundlePath();
-  SetUpBundleOverrides();
-#endif
-
 #if BUILDFLAG(IS_WIN)
   // Ignore invalid parameter errors.
   _set_invalid_parameter_handler(InvalidParameterHandler);

filename: shell/app/electron_main_delegate.h
patch: @@ -34,6 +34,12 @@ class ElectronMainDelegate : public content::ContentMainDelegate {
   ElectronMainDelegate(const ElectronMainDelegate&) = delete;
   ElectronMainDelegate& operator=(const ElectronMainDelegate&) = delete;
 
+#if BUILDFLAG(IS_MAC)
+  void OverrideChildProcessPath();
+  void OverrideFrameworkBundlePath();
+  void SetUpBundleOverrides();
+#endif
+
  protected:
   // content::ContentMainDelegate:
   std::string_view GetBrowserV8SnapshotFilename() override;
@@ -57,12 +63,6 @@ class ElectronMainDelegate : public content::ContentMainDelegate {
 #endif
 
  private:
-#if BUILDFLAG(IS_MAC)
-  void OverrideChildProcessPath();
-  void OverrideFrameworkBundlePath();
-  void SetUpBundleOverrides();
-#endif
-
   std::unique_ptr<content::ContentBrowserClient> browser_client_;
   std::unique_ptr<content::ContentClient> content_client_;
   std::unique_ptr<content::ContentGpuClient> gpu_client_;

filename: spec/chromium-spec.ts
patch: @@ -15,7 +15,7 @@ import * as path from 'node:path';
 import { setTimeout } from 'node:timers/promises';
 import * as url from 'node:url';
 
-import { ifit, ifdescribe, defer, itremote, listen } from './lib/spec-helpers';
+import { ifit, ifdescribe, defer, itremote, listen, startRemoteControlApp } from './lib/spec-helpers';
 import { closeAllWindows } from './lib/window-helpers';
 import { PipeTransport } from './pipe-transport';
 
@@ -556,6 +556,36 @@ describe('command line switches', () => {
       });
     });
   });
+
+  describe('--trace-startup switch', () => {
+    const outputFilePath = path.join(app.getPath('temp'), 'trace.json');
+    afterEach(() => {
+      if (fs.existsSync(outputFilePath)) {
+        fs.unlinkSync(outputFilePath);
+      }
+    });
+
+    it('creates startup trace', async () => {
+      const rc = await startRemoteControlApp(['--trace-startup=*', `--trace-startup-file=${outputFilePath}`, '--trace-startup-duration=1', '--enable-logging']);
+      const stderrComplete = new Promise<string>(resolve => {
+        let stderr = '';
+        rc.process.stderr!.on('data', (chunk) => {
+          stderr += chunk.toString('utf8');
+        });
+        rc.process.on('close', () => { resolve(stderr); });
+      });
+      rc.remotely(() => {
+        global.setTimeout(() => {
+          require('electron').app.quit();
+        }, 5000);
+      });
+      const stderr = await stderrComplete;
+      expect(stderr).to.match(/Completed startup tracing to/);
+      expect(fs.existsSync(outputFilePath)).to.be.true('output exists');
+      expect(fs.statSync(outputFilePath).size).to.be.above(0,
+        `the trace output file is empty, check "${outputFilePath}"`);
+    });
+  });
 });
 
 describe('chromium features', () => {