Any way to detect an async stack trace (like Chrome devtools does)?

kai zhu kaizhu256 at gmail.com
Fri Jul 10 20:21:40 UTC 2020


>  (I want to detect traces in third-party code installed locally).

1. here's 15-line javascript-hack to trace async-fetch-calls from
3rd-party-cdn-library

```html
<!doctype html>
<html lang="en">
<body>
<h1>test.html</h1>
<script>
(function () {
/*
 * 15-line-javascript-hack to trace async-fetch-calls
 * enable hack by adding search-query "?modeDebugFetch=1" to web-url
 */
    "use strict";
    if ((/\bmodeDebugFetch=1\b/).test(location.search)) {
        let fetch0;
        fetch0 = globalThis.fetch;
        globalThis.fetch = function (...argList) {
            let errStack = new Error().stack;
            console.error("\n\nfetch-call-arguments:");
            console.error(JSON.stringify(argList, undefined, 4));
            console.error("\n\nfetch-call-trace:");
            console.error(errStack);
            return fetch0(...argList);
        };
    }
}());
</script>
<!-- 3rd-party-cdn-library -->
<script src="https://unpkg.com/http-client/umd/http-client.js"></script>
<script>
(async function foo() {
    "use strict";
    let thirdParty = window.HTTPClient;
    let myFetch = thirdParty.createFetch(
        thirdParty.base("https://api.stripe.com/v1"),
        thirdParty.accept("application/json")
    );
    let response = await myFetch("/customers/5");
    console.log(response.jsonData);
/*
dev-console-output - http://localhost:8081/test.html?modeDebugFetch=1
fetch-call-arguments:
[
    "https://api.stripe.com/v1/customers/5",
    {
        "headers": {
            "Accept": "application/json"
        },
        "responseHandlers": [
            null
        ]
    }
]
fetch-call-trace:
Error
    at globalThis.fetch (test.html?modeDebugFetch=1:16)
    at http-client.js:194
    at http-client.js:127
    at http-client.js:217
    at http-client.js:126
    at http-client.js:154
    at http-client.js:95
    at foo (test.html?modeDebugFetch=1:35)
    at test.html?modeDebugFetch=1:64
*/
}());
</script>
</body>
```


2. here's real-world-hack added to npm-cli.js to debug its http-requests

```diff
C:\Program Files\nodejs\node_modules\npm>git diff
diff --git a/bin/npm-cli.js b/bin/npm-cli.js
index 561dec0..98cafb8 100644
--- a/bin/npm-cli.js
+++ b/bin/npm-cli.js
@@ -2,17 +2,6 @@
 ;(function () { // wrapper in case we're in module_context mode
+// hack - debug http-request
+let httpRequest;
+httpRequest = require("https").request.bind(require("https"));
+require("https").request = function (...argList) {
+    if (process.env.NPM_DEBUG) {
+        console.error(
+            "npm - httpRequest - " + JSON.stringify(argList.slice(0, 2),
undefined, 4)
+        );
+    }
+    return httpRequest(...argList);
+};
   // windows: running "npm blah" in this folder will invoke WSH, not node.

C:\Program Files\nodejs\node_modules\npm>
```

```mingw-bash
$ NPM_DEBUG=1 npm publish foo
# console-ouput:
# npm - httpRequest - [
#     {
#         "protocol": "https:",
#         "href": "https://registry.npmjs.org/foo",
#         "method": "GET",
#         "headers": {
#             "connection": [
#                 "keep-alive"
#             ],
#             "user-agent": [
#                 "npm/6.13.4 node/v12.16.1 win32 x64"
#             ],
#             ...
```





On Fri, Jul 10, 2020 at 3:34 AM #!/JoePea <joe at trusktr.io> wrote:

> Thanks for the idea! That's similar to what I thought would be necessary.
> I was hoping to monkey patch `fetch()` to have it get the trace if any
> `fetch` call, but they would not be async stack traces. For async traces it
> would require instrumentation by injecting code similar to yours (I want to
> detect traces in third-party code installed locally).
>
> #!/JoePea
>
> On Wed, Jul 8, 2020, 2:13 AM kai zhu <kaizhu256 at gmail.com> wrote:
>
>> here's a simple throwaway-function you can wrap around promises like fetch
>> to get the caller's async-stack-trace `promiseWithErrorStack`:
>>
>> ```html
>> <!doctype html>
>> <html lang="en">
>> <body>
>> <h1>test.html</h1>
>> <script>
>> (async function foo() {
>>     function promiseWithErrorStack(promise) {
>>     /*
>>      * this function will append current-stack to any err caught from
>> <promise>
>>      */
>>         let errStack;
>>         errStack = new Error().stack;
>>         return new Promise(function (resolve, reject) {
>>             promise.then(resolve).catch(function (err) {
>>                 // append current errStack to err.stack
>>                 if (err && typeof err.stack === "string") {
>>                     err.stack += "\n" + errStack;
>>                 }
>>                 reject(err);
>>             });
>>         });
>>     }
>>     await promiseWithErrorStack(fetch("https://example.com")); // at foo
>> (test.html:23)
>>
>>     /*
>>     console-output:
>>
>>     Uncaught (in promise) TypeError: Failed to fetch
>>     Error
>>         at promiseWithErrorStack (test.html:12)
>>         at foo (test.html:23) // async-stack-trace
>>         at test.html:32
>>     */
>> }());
>> </script>
>> </body>
>> ```
>>
>> On Tue, Jul 7, 2020 at 11:54 PM #!/JoePea <joe at trusktr.io> wrote:
>>
>>> Is there some way (perhaps by patching methods on objects?) so we can
>>> track async call stacks?
>>>
>>> When we pause code in devtools, we are able to see the async stack
>>> trace of the code.
>>>
>>> What I'd like to do is to effectively detect the same thing as
>>> devtools does at some point in any code. As a specific example, I'd
>>> like to detect the async stack trace of any call to `fetch`.
>>>
>>> Is such a thing possible with runtime code?
>>>
>>> Or would it require instrumentation of the source code?
>>>
>>> #!/JoePea
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20200710/54aed8bf/attachment.html>


More information about the es-discuss mailing list