cashapp/zipline

Production builds' stacktraces are terrible

Closed this issue · 2 comments

Here’s a stacktrace from a debug build:

app.cash.zipline.ZiplineException: Exception: boom!
	at captureStack (js-ir/runtime/coreRuntime.kt:48)
	at Exception_init_$Create$_0 (kotlin-kotlin-stdlib.js)
	at <anonymous> (src/commonMain/kotlin/com/squareup/cash/treehouse/playground/samples/ErrorRecovery.kt:49)
	at <anonymous> (js-ir/src/kotlin/coroutines_13/CoroutineImpl.kt:55)
	at <anonymous> (kotlin-kotlin-stdlib.js)
	at resume (mnt/agent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt:62)
	at dispatch (mnt/agent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt:168)
	at dispatchResume (mnt/agent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt:474)
	at resumeImpl (mnt/agent/work/88b0986a7186d029/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt:508)
	at resumeImpl$default (mnt/agent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt:1234)
	at <anonymous> (mnt/agent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt:595)
	at <anonymous> (src/kotlin/util/Standard.kt:48)
	at apply (native)
	at <anonymous> (Users/runner/work/zipline/zipline/zipline/src/jsMain/kotlin/app/cash/zipline/GlobalBridge.kt:72)
	at <anonymous> (Users/runner/work/zipline/zipline/zipline/src/commonMain/kotlin/app/cash/zipline/internal/GuestService.kt)
	at <anonymous> (zipline-root-zipline.js)
	at <anonymous> (Users/runner/work/zipline/zipline/zipline/src/commonMain/kotlin/app/cash/zipline/internal/bridge/InboundService.kt:1390)
	at <anonymous> (Users/runner/work/zipline/zipline/zipline/src/commonMain/kotlin/app/cash/zipline/internal/bridge/Endpoint.kt:99)
	at <anonymous> (zipline-root-zipline.js)
	at <anonymous> (Users/runner/work/zipline/zipline/zipline/src/jsMain/kotlin/app/cash/zipline/GlobalBridge.kt)
	at <anonymous> (zipline-root-zipline.js)
	at app.cash.zipline.internal.GuestService$Companion$Adapter$GeneratedOutboundService.runJob(GuestService.kt:23)
	at app.cash.zipline.internal.CoroutineEventLoop$DelayedJob$run$1.invokeSuspend(CoroutineEventLoop.kt:61)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
	at java.lang.Thread.run(Thread.java:1012)

And the same from a production build:

app.cash.zipline.ZiplineException: $o: boom!
	at Eu (treehouse.js)
	at vo (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at dl (treehouse.js)
	at <anonymous> (treehouse.js)
	at _r (treehouse.js)
	at vr (treehouse.js)
	at wr (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at apply (native)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at <anonymous> (treehouse.js)
	at app.cash.zipline.internal.GuestService$Companion$Adapter$GeneratedOutboundService.runJob(GuestService.kt:23)
	at app.cash.zipline.internal.CoroutineEventLoop$DelayedJob$run$1.invokeSuspend(CoroutineEventLoop.kt:61)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
	at java.lang.Thread.run(Thread.java:1012)

I really don’t like losing both function names and line numbers. When fixing we should confirm the resulting binary size isn’t adversely impacted.

The root problem is the JS file we’re generating our .zipline from is aggressively minified, and that deprives QuickJS of line number information.

I was able to fix by creating webpack.config.d/treehouseWebpackConfig.js in the module directory with these contents:

config.optimization = config.optimization || {};
const TerserPlugin = require("terser-webpack-plugin");
config.optimization.minimizer = [
  new TerserPlugin({
    terserOptions: {
      compress: {
        sequences: false,
      },
      mangle: false,
      format: {
        beautify: true,
        braces: true,
      }
    },
  }),
];

The costs of good debug data are too damn high!

                                    .zipline size        gzipped size
A NO DEBUG DATA                    2,856,719             941,474
B MANGLE CLASS + FUNCTION NAMES    3,400,044 (+19%)    1,193,241 (+27%)
C OMIT INSTRUCTION LINE NUMBERS    3,690,174 (+29%)    1,218,497 (+29%)
D TRUNCATE FILE PATH               3,642,791 (+28%)    1,338,461 (+42%)
E (B + C)                          3,218,986 (+13%)    1,064,318 (+13%)
F FULL DEBUG DATA                  3,871,128 (+35%)    1,346,230 (+42%)

Before closing this we should zipline { } Gradle plugin options for these:

  • Stripping the pc2line table
  • Mangling class + function names