diff options
author | Luca Casonato <hello@lcas.dev> | 2022-02-16 19:53:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-16 19:53:17 +0100 |
commit | 53088e16de9728696df12b04670a0f2d5203592b (patch) | |
tree | 74df68fc3f39db638db6431580d03c235a8f5ff4 /core/01_core.js | |
parent | b98afb59ae43b4fcfc2bf06e82942005d7f68c7b (diff) |
feat(test): improved op sanitizer errors + traces (#13676)
This commit improves the error messages for the `deno test` async op
sanitizer. It does this in two ways:
- it uses handwritten error messages for each op that could be leaking
- it includes traces showing where each op was started
This "async op tracing" functionality is a new feature in deno_core.
It likely has a significant performance impact, which is why it is only
enabled in tests.
Diffstat (limited to 'core/01_core.js')
-rw-r--r-- | core/01_core.js | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/core/01_core.js b/core/01_core.js index d9a110eea..3a05a0cff 100644 --- a/core/01_core.js +++ b/core/01_core.js @@ -22,6 +22,8 @@ MapPrototypeDelete, MapPrototypeSet, PromisePrototypeThen, + PromisePrototypeFinally, + StringPrototypeSlice, ObjectAssign, SymbolFor, } = window.__bootstrap.primordials; @@ -48,6 +50,13 @@ // to users. Currently missing bindings. const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); + let opCallTracingEnabled = false; + const opCallTraces = new Map(); + + function enableOpCallTracing() { + opCallTracingEnabled = true; + } + function setPromise(promiseId) { const idx = promiseId % RING_SIZE; // Move old promise from ring to map @@ -139,7 +148,17 @@ const maybeError = opcallAsync(opsCache[opName], promiseId, arg1, arg2); // Handle sync error (e.g: error parsing args) if (maybeError) return unwrapOpResult(maybeError); - const p = PromisePrototypeThen(setPromise(promiseId), unwrapOpResult); + let p = PromisePrototypeThen(setPromise(promiseId), unwrapOpResult); + if (opCallTracingEnabled) { + // Capture a stack trace by creating a new `Error` object. We remove the + // first 6 characters (the `Error\n` prefix) to get just the stack trace. + const stack = StringPrototypeSlice(new Error().stack, 6); + MapPrototypeSet(opCallTraces, promiseId, { opName, stack }); + p = PromisePrototypeFinally( + p, + () => MapPrototypeDelete(opCallTraces, promiseId), + ); + } // Save the id on the promise so it can later be ref'ed or unref'ed p[promiseIdSymbol] = promiseId; return p; @@ -226,6 +245,8 @@ BadResourcePrototype, Interrupted, InterruptedPrototype, + enableOpCallTracing, + opCallTraces, }); ObjectAssign(globalThis.__bootstrap, { core }); |