Browse Source

fix: increase stack size on windows x86 (#30241)

* fix: increace main thread stack size on windows x86

* chore: improve quit-on-crashed-event spec

* chore: add debug logs

* Revert "chore: add debug logs"

This reverts commit 0be81ae07c85095ac2c920436b97557c95c1c524.

* chore: use a reliable crash endpoint

Co-authored-by: Stephen Wang <[email protected]>
Co-authored-by: Deepak Mohan <[email protected]>
trop[bot] 3 years ago
parent
commit
0b8971dec2

+ 73 - 0
shell/app/electron_main.cc

@@ -103,7 +103,75 @@ namespace crash_reporter {
 extern const char kCrashpadProcess[];
 }
 
+// In 32-bit builds, the main thread starts with the default (small) stack size.
+// The ARCH_CPU_32_BITS blocks here and below are in support of moving the main
+// thread to a fiber with a larger stack size.
+#if defined(ARCH_CPU_32_BITS)
+// The information needed to transfer control to the large-stack fiber and later
+// pass the main routine's exit code back to the small-stack fiber prior to
+// termination.
+struct FiberState {
+  HINSTANCE instance;
+  LPVOID original_fiber;
+  int fiber_result;
+};
+
+// A PFIBER_START_ROUTINE function run on a large-stack fiber that calls the
+// main routine, stores its return value, and returns control to the small-stack
+// fiber. |params| must be a pointer to a FiberState struct.
+void WINAPI FiberBinder(void* params) {
+  auto* fiber_state = static_cast<FiberState*>(params);
+  // Call the wWinMain routine from the fiber. Reusing the entry point minimizes
+  // confusion when examining call stacks in crash reports - seeing wWinMain on
+  // the stack is a handy hint that this is the main thread of the process.
+  fiber_state->fiber_result =
+      wWinMain(fiber_state->instance, nullptr, nullptr, 0);
+  // Switch back to the main thread to exit.
+  ::SwitchToFiber(fiber_state->original_fiber);
+}
+#endif  // defined(ARCH_CPU_32_BITS)
+
 int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
+#if defined(ARCH_CPU_32_BITS)
+  enum class FiberStatus { kConvertFailed, kCreateFiberFailed, kSuccess };
+  FiberStatus fiber_status = FiberStatus::kSuccess;
+  // GetLastError result if fiber conversion failed.
+  DWORD fiber_error = ERROR_SUCCESS;
+  if (!::IsThreadAFiber()) {
+    // Make the main thread's stack size 4 MiB so that it has roughly the same
+    // effective size as the 64-bit build's 8 MiB stack.
+    constexpr size_t kStackSize = 4 * 1024 * 1024;  // 4 MiB
+    // Leak the fiber on exit.
+    LPVOID original_fiber =
+        ::ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
+    if (original_fiber) {
+      FiberState fiber_state = {instance, original_fiber};
+      // Create a fiber with a bigger stack and switch to it. Leak the fiber on
+      // exit.
+      LPVOID big_stack_fiber = ::CreateFiberEx(
+          0, kStackSize, FIBER_FLAG_FLOAT_SWITCH, FiberBinder, &fiber_state);
+      if (big_stack_fiber) {
+        ::SwitchToFiber(big_stack_fiber);
+        // The fibers must be cleaned up to avoid obscure TLS-related shutdown
+        // crashes.
+        ::DeleteFiber(big_stack_fiber);
+        ::ConvertFiberToThread();
+        // Control returns here after Chrome has finished running on FiberMain.
+        return fiber_state.fiber_result;
+      }
+      fiber_status = FiberStatus::kCreateFiberFailed;
+    } else {
+      fiber_status = FiberStatus::kConvertFailed;
+    }
+    // If we reach here then creating and switching to a fiber has failed. This
+    // probably means we are low on memory and will soon crash. Try to report
+    // this error once crash reporting is initialized.
+    fiber_error = ::GetLastError();
+    base::debug::Alias(&fiber_error);
+  }
+  // If we are already a fiber then continue normal execution.
+#endif  // defined(ARCH_CPU_32_BITS)
+
   struct Arguments {
     int argc = 0;
     wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
@@ -198,6 +266,11 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
     return crashpad_status;
   }
 
+#if defined(ARCH_CPU_32_BITS)
+  // Intentionally crash if converting to a fiber failed.
+  CHECK_EQ(fiber_status, FiberStatus::kSuccess);
+#endif  // defined(ARCH_CPU_32_BITS)
+
   if (!electron::CheckCommandLineArguments(arguments.argc, arguments.argv))
     return -1;
 

+ 8 - 5
spec-main/fixtures/crash-cases/quit-on-crashed-event/index.js

@@ -1,6 +1,6 @@
 const { app, BrowserWindow } = require('electron');
 
-app.once('ready', () => {
+app.once('ready', async () => {
   const w = new BrowserWindow({
     show: false,
     webPreferences: {
@@ -8,9 +8,12 @@ app.once('ready', () => {
       nodeIntegration: true
     }
   });
-  w.webContents.once('crashed', () => {
-    app.quit();
+  w.webContents.once('render-process-gone', (_, details) => {
+    if (details.reason === 'crashed') {
+      process.exit(0);
+    } else {
+      process.exit(details.exitCode);
+    }
   });
-  w.webContents.loadURL('about:blank');
-  w.webContents.executeJavaScript('process.crash()');
+  await w.webContents.loadURL('chrome://checkcrash');
 });