UNPKG

14.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.clearLeakDetector = exports.onAsyncHook = exports.stopAsyncTracing = exports.printHooks = exports.detectAsyncLeaks = exports.printAsyncStack = exports.trace = exports.startAsyncTracing = void 0;
4const asyncHooks = require("async_hooks");
5const util_1 = require("util");
6let hook;
7const asyncObjects = new Map();
8const objectMapping = new Map();
9function startAsyncTracing(stackTraces = false) {
10 hook = onAsyncHook(stackTraces);
11}
12exports.startAsyncTracing = startAsyncTracing;
13function trace(obj) {
14 let res = objectMapping.get(obj);
15 if (!res) {
16 // console.log(`trace: object not found: ${util.inspect(obj)}`);
17 return;
18 }
19 let asyncTrace = `== Tracing leaked object ${res.asyncId} ==`;
20 while (res) {
21 const { stack, ...rest } = res;
22 asyncTrace += `${(0, util_1.inspect)(rest)}\n${stack}`;
23 res = asyncObjects.get(res.triggerId);
24 }
25 console.log(asyncTrace);
26 return { obj, trace: asyncTrace };
27}
28exports.trace = trace;
29function printAsyncStack() {
30 console.log(`Async stack:`);
31 let res = asyncObjects.get(asyncHooks.executionAsyncId());
32 while (res) {
33 const { stack, ...rest } = res;
34 console.log(`%O\n${stack}`, rest);
35 res = asyncObjects.get(res.triggerId);
36 }
37}
38exports.printAsyncStack = printAsyncStack;
39function detectAsyncLeaks() {
40 const leaks = [];
41 process._getActiveHandles().forEach((h) => {
42 if (h !== process.stdout && h !== process.stderr) {
43 const leak = trace(h);
44 leak && leaks.push(leak);
45 }
46 });
47 process._getActiveRequests().forEach((h) => {
48 const leak = trace(h);
49 leak && leaks.push(leak);
50 });
51 return leaks;
52}
53exports.detectAsyncLeaks = detectAsyncLeaks;
54function printHooks() {
55 for (const obj of asyncObjects) {
56 console.log(`%O`, obj);
57 }
58}
59exports.printHooks = printHooks;
60function stopAsyncTracing() {
61 hook?.();
62}
63exports.stopAsyncTracing = stopAsyncTracing;
64function onAsyncHook(stackTraces) {
65 const hooks = {
66 init,
67 before,
68 after,
69 destroy,
70 promiseResolve
71 };
72 const asyncHook = asyncHooks.createHook(hooks);
73 asyncHook.enable();
74 return () => {
75 asyncHook.disable();
76 };
77 function init(asyncId, type, triggerId, resource) {
78 const obj = {
79 asyncId,
80 type,
81 triggerId,
82 resource,
83 state: "init",
84 startedCount: 0,
85 finishedCount: 0,
86 stack: stackTraces
87 ? new Error("stack:").stack.replace(/Error:/, "")
88 : undefined
89 };
90 asyncObjects.set(asyncId, obj);
91 objectMapping.set(resource, obj);
92 }
93 function destroy(asyncId) {
94 const obj = asyncObjects.get(asyncId);
95 if (obj) {
96 obj.state = "destroyed";
97 }
98 else {
99 // console.log(`destroyed: No obj ${asyncId}`);
100 }
101 }
102 function before(asyncId) {
103 const obj = asyncObjects.get(asyncId);
104 if (obj) {
105 obj.state = "before";
106 obj.startedCount++;
107 }
108 else {
109 // console.log(`before: No obj ${asyncId}`);
110 }
111 }
112 function after(asyncId) {
113 const obj = asyncObjects.get(asyncId);
114 if (obj) {
115 obj.state = "after";
116 obj.finishedCount++;
117 }
118 else {
119 // console.log(`after: No obj ${asyncId}`);
120 }
121 }
122 function promiseResolve(asyncId) {
123 const obj = asyncObjects.get(asyncId);
124 if (obj) {
125 obj.state = "resolved";
126 }
127 else {
128 // console.log(`resolved: No obj ${asyncId}`);
129 }
130 }
131}
132exports.onAsyncHook = onAsyncHook;
133function clearLeakDetector() {
134 asyncObjects.clear();
135 objectMapping.clear();
136}
137exports.clearLeakDetector = clearLeakDetector;
138//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHJhY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMENBQTBDO0FBQzFDLCtCQUErQjtBQUUvQixJQUFJLElBQTRCLENBQUM7QUFlakMsTUFBTSxZQUFZLEdBQTZCLElBQUksR0FBRyxFQUFFLENBQUM7QUFDekQsTUFBTSxhQUFhLEdBQTZCLElBQUksR0FBRyxFQUFFLENBQUM7QUFFMUQsU0FBZ0IsaUJBQWlCLENBQUMsY0FBdUIsS0FBSztJQUMxRCxJQUFJLEdBQUcsV0FBVyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBQ3BDLENBQUM7QUFGRCw4Q0FFQztBQU9ELFNBQWdCLEtBQUssQ0FBQyxHQUFXO0lBQzdCLElBQUksR0FBRyxHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDakMsSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUNOLGdFQUFnRTtRQUNoRSxPQUFPO0tBQ1Y7SUFDRCxJQUFJLFVBQVUsR0FBRyw0QkFBNEIsR0FBRyxDQUFDLE9BQU8sS0FBSyxDQUFDO0lBQzlELE9BQU8sR0FBRyxFQUFFO1FBQ1IsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLElBQUksRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUMvQixVQUFVLElBQUksR0FBRyxJQUFBLGNBQU8sRUFBQyxJQUFJLENBQUMsS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUMzQyxHQUFHLEdBQUcsWUFBWSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7S0FDekM7SUFDRCxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3hCLE9BQU8sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxDQUFDO0FBQ3RDLENBQUM7QUFkRCxzQkFjQztBQUVELFNBQWdCLGVBQWU7SUFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FB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UUFDdEMsSUFBSSxHQUFHLEVBQUU7WUFDTCxHQUFHLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQztTQUMzQjthQUFNO1lBQ0gsK0NBQStDO1NBQ2xEO0lBQ0wsQ0FBQztJQUNELFNBQVMsTUFBTSxDQUFDLE9BQWU7UUFDM0IsTUFBTSxHQUFHLEdBQUcsWUFBWSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN0QyxJQUFJLEdBQUcsRUFBRTtZQUNMLEdBQUcsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDO1lBQ3JCLEdBQUcsQ0FBQyxZQUFZLEVBQUUsQ0FBQztTQUN0QjthQUFNO1lBQ0gsNENBQTRDO1NBQy9DO0lBQ0wsQ0FBQztJQUNELFNBQVMsS0FBSyxDQUFDLE9BQWU7UUFDMUIsTUFBTSxHQUFHLEdBQUcsWUFBWSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN0QyxJQUFJLEdBQUcsRUFBRTtZQUNMLEdBQUcsQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDO1lBQ3BCLEdBQUcsQ0FBQyxhQUFhLEVBQUUsQ0FBQztTQUN2QjthQUFNO1lBQ0gsMkNBQTJDO1NBQzlDO0lBQ0wsQ0FBQztJQUNELFNBQVMsY0FBYyxDQUFDLE9BQWU7UUFDbkMsTUFBTSxHQUFHLEdBQUcsWUFBWSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN0QyxJQUFJLEdBQUcsRUFBRTtZQUNMLEdBQUcsQ0FBQyxLQUFLLEdBQUcsVUFBVSxDQUFDO1NBQzFCO2FBQU07WUFDSCw4Q0FBOEM7U0FDakQ7SUFDTCxDQUFDO0FBQ0wsQ0FBQztBQWxFRCxrQ0FrRUM7QUFFRCxTQUFnQixpQkFBaUI7SUFDN0IsWUFBWSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3JCLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztBQUMxQixDQUFDO0FBSEQsOENBR0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBhc3luY0hvb2tzIGZyb20gXCJhc3luY19ob29rc1wiO1xuaW1wb3J0IHsgaW5zcGVjdCB9IGZyb20gXCJ1dGlsXCI7XG5cbmxldCBob29rOiAoKSA9PiB2b2lkIHwgdW5kZWZpbmVkO1xuXG50eXBlIEFzeW5jU3RhdGUgPSBcImluaXRcIiB8IFwiYmVmb3JlXCIgfCBcImFmdGVyXCIgfCBcImRlc3Ryb3llZFwiIHwgXCJyZXNvbHZlZFwiO1xuXG5pbnRlcmZhY2UgQXN5bmNPYmplY3Qge1xuICAgIGFzeW5jSWQ6IG51bWJlcjtcbiAgICB0eXBlOiBzdHJpbmc7XG4gICAgdHJpZ2dlcklkOiBudW1iZXI7XG4gICAgcmVzb3VyY2U6IGFueTtcbiAgICBzdGF0ZTogQXN5bmNTdGF0ZTtcbiAgICBzdGFydGVkQ291bnQ6IG51bWJlcjtcbiAgICBmaW5pc2hlZENvdW50OiBudW1iZXI7XG4gICAgc3RhY2s/OiBzdHJpbmc7XG59XG5cbmNvbnN0IGFzeW5jT2JqZWN0czogTWFwPG51bWJlciwgQXN5bmNPYmplY3Q+ID0gbmV3IE1hcCgpO1xuY29uc3Qgb2JqZWN0TWFwcGluZzogTWFwPG9iamVjdCwgQXN5bmNPYmplY3Q+ID0gbmV3IE1hcCgpO1xuXG5leHBvcnQgZnVuY3Rpb24gc3RhcnRBc3luY1RyYWNpbmcoc3RhY2tUcmFjZXM6IGJvb2xlYW4gPSBmYWxzZSkge1xuICAgIGhvb2sgPSBvbkFzeW5jSG9vayhzdGFja1RyYWNlcyk7XG59XG5cbmludGVyZmFjZSBUcmFjZSB7XG4gICAgb2JqOiBvYmplY3Q7XG4gICAgdHJhY2U6IHN0cmluZztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRyYWNlKG9iajogb2JqZWN0KTogVHJhY2UgfCB2b2lkIHtcbiAgICBsZXQgcmVzID0gb2JqZWN0TWFwcGluZy5nZXQob2JqKTtcbiAgICBpZiAoIXJlcykge1xuICAgICAgICAvLyBjb25zb2xlLmxvZyhgdHJhY2U6IG9iamVjdCBub3QgZm91bmQ6ICR7dXRpbC5pbnNwZWN0KG9iail9YCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IGFzeW5jVHJhY2UgPSBgPT0gVHJhY2luZyBsZWFrZWQgb2JqZWN0ICR7cmVzLmFzeW5jSWR9ID09YDtcbiAgICB3aGlsZSAocmVzKSB7XG4gICAgICAgIGNvbnN0IHsgc3RhY2ssIC4uLnJlc3QgfSA9IHJlcztcbiAgICAgICAgYXN5bmNUcmFjZSArPSBgJHtpbnNwZWN0KHJlc3QpfVxcbiR7c3RhY2t9YDtcbiAgICAgICAgcmVzID0gYXN5bmNPYmplY3RzLmdldChyZXMudHJpZ2dlcklkKTtcbiAgICB9XG4gICAgY29uc29sZS5sb2coYXN5bmNUcmFjZSk7XG4gICAgcmV0dXJuIHsgb2JqLCB0cmFjZTogYXN5bmNUcmFjZSB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRBc3luY1N0YWNrKCkge1xuICAgIGNvbnNvbGUubG9nKGBBc3luYyBzdGFjazpgKTtcbiAgICBsZXQgcmVzID0gYXN5bmNPYmplY3RzLmdldChhc3luY0hvb2tzLmV4ZWN1dGlvbkFzeW5jSWQoKSk7XG4gICAgd2hpbGUgKHJlcykge1xuICAgICAgICBjb25zdCB7IHN0YWNrLCAuLi5yZXN0IH0gPSByZXM7XG4gICAgICAgIGNvbnNvbGUubG9nKGAlT1xcbiR7c3RhY2t9YCwgcmVzdCk7XG4gICAgICAgIHJlcyA9IGFzeW5jT2JqZWN0cy5nZXQocmVzLnRyaWdnZXJJZCk7XG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZGV0ZWN0QXN5bmNMZWFrcygpOiBvYmplY3RbXSB7XG4gICAgY29uc3QgbGVha3M6IFRyYWNlW10gPSBbXTtcbiAgICAocHJvY2VzcyBhcyBhbnkpLl9nZXRBY3RpdmVIYW5kbGVzKCkuZm9yRWFjaCgoaDogb2JqZWN0KSA9PiB7XG4gICAgICAgIGlmIChoICE9PSBwcm9jZXNzLnN0ZG91dCAmJiBoICE9PSBwcm9jZXNzLnN0ZGVycikge1xuICAgICAgICAgICAgY29uc3QgbGVhayA9IHRyYWNlKGgpO1xuICAgICAgICAgICAgbGVhayAmJiBsZWFrcy5wdXNoKGxlYWspO1xuICAgICAgICB9XG4gICAgfSk7XG4gICAgKHByb2Nlc3MgYXMgYW55KS5fZ2V0QWN0aXZlUmVxdWVzdHMoKS5mb3JFYWNoKChoOiBvYmplY3QpID0+IHtcbiAgICAgICAgY29uc3QgbGVhayA9IHRyYWNlKGgpO1xuICAgICAgICBsZWFrICYmIGxlYWtzLnB1c2gobGVhayk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGxlYWtzO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRIb29rcygpIHtcbiAgICBmb3IgKGNvbnN0IG9iaiBvZiBhc3luY09iamVjdHMpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCVPYCwgb2JqKTtcbiAgICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzdG9wQXN5bmNUcmFjaW5nKCkge1xuICAgIGhvb2s/LigpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gb25Bc3luY0hvb2soc3RhY2tUcmFjZXM6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBob29rczogYXN5bmNIb29rcy5Ib29rQ2FsbGJhY2tzID0ge1xuICAgICAgICBpbml0LFxuICAgICAgICBiZWZvcmUsXG4gICAgICAgIGFmdGVyLFxuICAgICAgICBkZXN0cm95LFxuICAgICAgICBwcm9taXNlUmVzb2x2ZVxuICAgIH07XG5cbiAgICBjb25zdCBhc3luY0hvb2sgPSBhc3luY0hvb2tzLmNyZWF0ZUhvb2soaG9va3MpO1xuICAgIGFzeW5jSG9vay5lbmFibGUoKTtcblxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICAgIGFzeW5jSG9vay5kaXNhYmxlKCk7XG4gICAgfTtcblxuICAgIGZ1bmN0aW9uIGluaXQoYXN5bmNJZDogbnVtYmVyLCB0eXBlOiBzdHJpbmcsIHRyaWdnZXJJZDogbnVtYmVyLCByZXNvdXJjZTogb2JqZWN0KSB7XG4gICAgICAgIGNvbnN0IG9iajogQXN5bmNPYmplY3QgPSB7XG4gICAgICAgICAgICBhc3luY0lkLFxuICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgIHRyaWdnZXJJZCxcbiAgICAgICAgICAgIHJlc291cmNlLFxuICAgICAgICAgICAgc3RhdGU6IFwiaW5pdFwiLFxuICAgICAgICAgICAgc3RhcnRlZENvdW50OiAwLFxuICAgICAgICAgICAgZmluaXNoZWRDb3VudDogMCxcbiAgICAgICAgICAgIHN0YWNrOiBzdGFja1RyYWNlc1xuICAgICAgICAgICAgICAgID8gbmV3IEVycm9yKFwic3RhY2s6XCIpLnN0YWNrIS5yZXBsYWNlKC9FcnJvcjovLCBcIlwiKVxuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgIH07XG4gICAgICAgIGFzeW5jT2JqZWN0cy5zZXQoYXN5bmNJZCwgb2JqKTtcbiAgICAgICAgb2JqZWN0TWFwcGluZy5zZXQocmVzb3VyY2UsIG9iaik7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGRlc3Ryb3koYXN5bmNJZDogbnVtYmVyKSB7XG4gICAgICAgIGNvbnN0IG9iaiA9IGFzeW5jT2JqZWN0cy5nZXQoYXN5bmNJZCk7XG4gICAgICAgIGlmIChvYmopIHtcbiAgICAgICAgICAgIG9iai5zdGF0ZSA9IFwiZGVzdHJveWVkXCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBjb25zb2xlLmxvZyhgZGVzdHJveWVkOiBObyBvYmogJHthc3luY0lkfWApO1xuICAgICAgICB9XG4gICAgfVxuICAgIGZ1bmN0aW9uIGJlZm9yZShhc3luY0lkOiBudW1iZXIpIHtcbiAgICAgICAgY29uc3Qgb2JqID0gYXN5bmNPYmplY3RzLmdldChhc3luY0lkKTtcbiAgICAgICAgaWYgKG9iaikge1xuICAgICAgICAgICAgb2JqLnN0YXRlID0gXCJiZWZvcmVcIjtcbiAgICAgICAgICAgIG9iai5zdGFydGVkQ291bnQrKztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGBiZWZvcmU6IE5vIG9iaiAke2FzeW5jSWR9YCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZnVuY3Rpb24gYWZ0ZXIoYXN5bmNJZDogbnVtYmVyKSB7XG4gICAgICAgIGNvbnN0IG9iaiA9IGFzeW5jT2JqZWN0cy5nZXQoYXN5bmNJZCk7XG4gICAgICAgIGlmIChvYmopIHtcbiAgICAgICAgICAgIG9iai5zdGF0ZSA9IFwiYWZ0ZXJcIjtcbiAgICAgICAgICAgIG9iai5maW5pc2hlZENvdW50Kys7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBjb25zb2xlLmxvZyhgYWZ0ZXI6IE5vIG9iaiAke2FzeW5jSWR9YCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZnVuY3Rpb24gcHJvbWlzZVJlc29sdmUoYXN5bmNJZDogbnVtYmVyKSB7XG4gICAgICAgIGNvbnN0IG9iaiA9IGFzeW5jT2JqZWN0cy5nZXQoYXN5bmNJZCk7XG4gICAgICAgIGlmIChvYmopIHtcbiAgICAgICAgICAgIG9iai5zdGF0ZSA9IFwicmVzb2x2ZWRcIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGByZXNvbHZlZDogTm8gb2JqICR7YXN5bmNJZH1gKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyTGVha0RldGVjdG9yKCkge1xuICAgIGFzeW5jT2JqZWN0cy5jbGVhcigpO1xuICAgIG9iamVjdE1hcHBpbmcuY2xlYXIoKTtcbn1cbiJdfQ==
\No newline at end of file