@memlab/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +11 -0
  2. package/dist/__tests__/parser/HeapParser.test.d.ts +11 -0
  3. package/dist/__tests__/parser/HeapParser.test.d.ts.map +1 -0
  4. package/dist/__tests__/parser/HeapParser.test.js +54 -0
  5. package/dist/__tests__/parser/NodeHeap.test.d.ts +11 -0
  6. package/dist/__tests__/parser/NodeHeap.test.d.ts.map +1 -0
  7. package/dist/__tests__/parser/NodeHeap.test.js +96 -0
  8. package/dist/__tests__/parser/StringNode.test.d.ts +11 -0
  9. package/dist/__tests__/parser/StringNode.test.d.ts.map +1 -0
  10. package/dist/__tests__/parser/StringNode.test.js +61 -0
  11. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts +16 -0
  12. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts.map +1 -0
  13. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.js +140 -0
  14. package/dist/__tests__/utils/utils.test.d.ts +11 -0
  15. package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
  16. package/dist/__tests__/utils/utils.test.js +81 -0
  17. package/dist/index.d.ts +29 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +62 -0
  20. package/dist/lib/BaseOption.d.ts +31 -0
  21. package/dist/lib/BaseOption.d.ts.map +1 -0
  22. package/dist/lib/BaseOption.js +109 -0
  23. package/dist/lib/BrowserInfo.d.ts +33 -0
  24. package/dist/lib/BrowserInfo.d.ts.map +1 -0
  25. package/dist/lib/BrowserInfo.js +117 -0
  26. package/dist/lib/Config.d.ts +203 -0
  27. package/dist/lib/Config.d.ts.map +1 -0
  28. package/dist/lib/Config.js +427 -0
  29. package/dist/lib/Console.d.ts +67 -0
  30. package/dist/lib/Console.d.ts.map +1 -0
  31. package/dist/lib/Console.js +344 -0
  32. package/dist/lib/Constant.d.ts +38 -0
  33. package/dist/lib/Constant.d.ts.map +1 -0
  34. package/dist/lib/Constant.js +58 -0
  35. package/dist/lib/FileManager.d.ts +69 -0
  36. package/dist/lib/FileManager.d.ts.map +1 -0
  37. package/dist/lib/FileManager.js +309 -0
  38. package/dist/lib/HeapAnalyzer.d.ts +51 -0
  39. package/dist/lib/HeapAnalyzer.d.ts.map +1 -0
  40. package/dist/lib/HeapAnalyzer.js +719 -0
  41. package/dist/lib/HeapParser.d.ts +19 -0
  42. package/dist/lib/HeapParser.d.ts.map +1 -0
  43. package/dist/lib/HeapParser.js +128 -0
  44. package/dist/lib/InternalValueSetter.d.ts +14 -0
  45. package/dist/lib/InternalValueSetter.d.ts.map +1 -0
  46. package/dist/lib/InternalValueSetter.js +43 -0
  47. package/dist/lib/NodeHeap.d.ts +16 -0
  48. package/dist/lib/NodeHeap.d.ts.map +1 -0
  49. package/dist/lib/NodeHeap.js +62 -0
  50. package/dist/lib/ProcessManager.d.ts +25 -0
  51. package/dist/lib/ProcessManager.d.ts.map +1 -0
  52. package/dist/lib/ProcessManager.js +67 -0
  53. package/dist/lib/Serializer.d.ts +49 -0
  54. package/dist/lib/Serializer.d.ts.map +1 -0
  55. package/dist/lib/Serializer.js +702 -0
  56. package/dist/lib/StringLoader.d.ts +26 -0
  57. package/dist/lib/StringLoader.d.ts.map +1 -0
  58. package/dist/lib/StringLoader.js +290 -0
  59. package/dist/lib/Types.d.ts +432 -0
  60. package/dist/lib/Types.d.ts.map +1 -0
  61. package/dist/lib/Types.js +11 -0
  62. package/dist/lib/Utils.d.ts +223 -0
  63. package/dist/lib/Utils.d.ts.map +1 -0
  64. package/dist/lib/Utils.js +1736 -0
  65. package/dist/lib/heap-data/HeapEdge.d.ts +27 -0
  66. package/dist/lib/heap-data/HeapEdge.d.ts.map +1 -0
  67. package/dist/lib/heap-data/HeapEdge.js +75 -0
  68. package/dist/lib/heap-data/HeapLocation.d.ts +22 -0
  69. package/dist/lib/heap-data/HeapLocation.d.ts.map +1 -0
  70. package/dist/lib/heap-data/HeapLocation.js +40 -0
  71. package/dist/lib/heap-data/HeapNode.d.ts +55 -0
  72. package/dist/lib/heap-data/HeapNode.d.ts.map +1 -0
  73. package/dist/lib/heap-data/HeapNode.js +344 -0
  74. package/dist/lib/heap-data/HeapSnapshot.d.ts +85 -0
  75. package/dist/lib/heap-data/HeapSnapshot.d.ts.map +1 -0
  76. package/dist/lib/heap-data/HeapSnapshot.js +462 -0
  77. package/dist/lib/heap-data/HeapStringNode.d.ts +18 -0
  78. package/dist/lib/heap-data/HeapStringNode.d.ts.map +1 -0
  79. package/dist/lib/heap-data/HeapStringNode.js +43 -0
  80. package/dist/lib/heap-data/HeapUtils.d.ts +17 -0
  81. package/dist/lib/heap-data/HeapUtils.d.ts.map +1 -0
  82. package/dist/lib/heap-data/HeapUtils.js +25 -0
  83. package/dist/logger/LeakClusterLogger.d.ts +40 -0
  84. package/dist/logger/LeakClusterLogger.d.ts.map +1 -0
  85. package/dist/logger/LeakClusterLogger.js +228 -0
  86. package/dist/logger/LeakTraceDetailsLogger.d.ts +19 -0
  87. package/dist/logger/LeakTraceDetailsLogger.d.ts.map +1 -0
  88. package/dist/logger/LeakTraceDetailsLogger.js +50 -0
  89. package/dist/modes/BaseMode.d.ts +30 -0
  90. package/dist/modes/BaseMode.d.ts.map +1 -0
  91. package/dist/modes/BaseMode.js +95 -0
  92. package/dist/modes/InteractionTestMode.d.ts +23 -0
  93. package/dist/modes/InteractionTestMode.d.ts.map +1 -0
  94. package/dist/modes/InteractionTestMode.js +46 -0
  95. package/dist/modes/MeasureMode.d.ts +23 -0
  96. package/dist/modes/MeasureMode.d.ts.map +1 -0
  97. package/dist/modes/MeasureMode.js +58 -0
  98. package/dist/modes/RunningModes.d.ts +15 -0
  99. package/dist/modes/RunningModes.d.ts.map +1 -0
  100. package/dist/modes/RunningModes.js +40 -0
  101. package/dist/paths/TraceFinder.d.ts +31 -0
  102. package/dist/paths/TraceFinder.d.ts.map +1 -0
  103. package/dist/paths/TraceFinder.js +537 -0
  104. package/dist/trace-cluster/ClusterUtils.d.ts +14 -0
  105. package/dist/trace-cluster/ClusterUtils.d.ts.map +1 -0
  106. package/dist/trace-cluster/ClusterUtils.js +17 -0
  107. package/dist/trace-cluster/ClusterUtilsHelper.d.ts +38 -0
  108. package/dist/trace-cluster/ClusterUtilsHelper.d.ts.map +1 -0
  109. package/dist/trace-cluster/ClusterUtilsHelper.js +373 -0
  110. package/dist/trace-cluster/ClusteringHeuristics.d.ts +22 -0
  111. package/dist/trace-cluster/ClusteringHeuristics.d.ts.map +1 -0
  112. package/dist/trace-cluster/ClusteringHeuristics.js +23 -0
  113. package/dist/trace-cluster/EvalutationMetric.d.ts +22 -0
  114. package/dist/trace-cluster/EvalutationMetric.d.ts.map +1 -0
  115. package/dist/trace-cluster/EvalutationMetric.js +158 -0
  116. package/dist/trace-cluster/TraceBucket.d.ts +36 -0
  117. package/dist/trace-cluster/TraceBucket.d.ts.map +1 -0
  118. package/dist/trace-cluster/TraceBucket.js +238 -0
  119. package/dist/trace-cluster/TraceElement.d.ts +71 -0
  120. package/dist/trace-cluster/TraceElement.d.ts.map +1 -0
  121. package/dist/trace-cluster/TraceElement.js +182 -0
  122. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts +15 -0
  123. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts.map +1 -0
  124. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.js +37 -0
  125. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts +15 -0
  126. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts.map +1 -0
  127. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +60 -0
  128. package/package.json +60 -0
  129. package/static/run-meta.json +10 -0
  130. package/static/visit-order.json +27 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails oncall+ws_labs
8
+ * @format
9
+ */
10
+ import type { AnyOptions, HeapNodeIdSet, IHeapEdge, IHeapNode, IHeapSnapshot, LeakTracePathItem, Nullable, Optional, Predicator } from '../lib/Types';
11
+ declare class TraceFinder {
12
+ getRootNodeList(snapshot: IHeapSnapshot, opt?: {
13
+ prioritize?: boolean;
14
+ }): [IHeapNode[], IHeapNode[]];
15
+ visitReachableNodesbyDFS(snapshot: IHeapSnapshot, nodeVisitor?: Optional<Predicator<IHeapNode>>, edgeVisitor?: Optional<Predicator<IHeapEdge>>): void;
16
+ flagReachableNodesFromWindow(snapshot: IHeapSnapshot, flags: Uint32Array, flag: number): void;
17
+ private buildPostOrderIndex;
18
+ private calculateDominatorNodesFromPostOrder;
19
+ private calculateRetainedSizesFromDominatorNodes;
20
+ shouldIgnoreEdgeInTraceFinding(edge: IHeapEdge): boolean;
21
+ shouldTraverseEdge(edge: IHeapEdge, options?: AnyOptions): boolean;
22
+ isBlockListedEdge(edge: IHeapEdge): boolean;
23
+ isLessPreferableEdge(edge: IHeapEdge): boolean;
24
+ isLessPreferableNode(node: IHeapNode): boolean;
25
+ getEdgeKey(edge: IHeapEdge): string;
26
+ calculateAllNodesRetainedSizes(snapshot: IHeapSnapshot): void;
27
+ annotateShortestPaths(snapshot: IHeapSnapshot, excludeKeySet?: HeapNodeIdSet): void;
28
+ getPathToGCRoots(_snapshot: IHeapSnapshot, node: Nullable<IHeapNode>): Optional<LeakTracePathItem>;
29
+ }
30
+ export default TraceFinder;
31
+ //# sourceMappingURL=TraceFinder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceFinder.d.ts","sourceRoot":"","sources":["../../src/paths/TraceFinder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,SAAS,EAET,SAAS,EAET,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,cAAc,CAAC;AActB,cAAM,WAAW;IACf,eAAe,CACb,QAAQ,EAAE,aAAa,EACvB,GAAG,GAAE;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAM,GAC/B,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,CAAC;IA0B7B,wBAAwB,CACtB,QAAQ,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAC7C,WAAW,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GAC5C,IAAI;IA6CP,4BAA4B,CAC1B,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GACX,IAAI;IA2CP,OAAO,CAAC,mBAAmB;IAoJ3B,OAAO,CAAC,oCAAoC;IAyJ5C,OAAO,CAAC,wCAAwC;IA4BhD,8BAA8B,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO;IAYxD,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO;IAQtE,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO;IAmB3C,oBAAoB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO;IAO9C,oBAAoB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO;IAQ9C,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM;IAMnC,8BAA8B,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IA2B7D,qBAAqB,CACnB,QAAQ,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,aAAa,GAC5B,IAAI;IA2EP,gBAAgB,CACd,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,GACxB,QAAQ,CAAC,iBAAiB,CAAC;CAY/B;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,537 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @emails oncall+ws_labs
9
+ * @format
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const Config_1 = __importDefault(require("../lib/Config"));
16
+ const Console_1 = __importDefault(require("../lib/Console"));
17
+ const Utils_1 = __importDefault(require("../lib/Utils"));
18
+ const ROOT_NODE_INDEX = 0;
19
+ const PAGE_OBJECT_FLAG = 1;
20
+ class TraceFinder {
21
+ getRootNodeList(snapshot, opt = {}) {
22
+ const highPri = [];
23
+ const lowPri = [];
24
+ if (opt.prioritize) {
25
+ snapshot.nodes.forEach(node => {
26
+ if (Utils_1.default.isRootNode(node, {
27
+ excludeBlinkRoot: true,
28
+ excludePendingActivity: true,
29
+ })) {
30
+ highPri.push(node);
31
+ }
32
+ else if (Utils_1.default.isRootNode(node)) {
33
+ lowPri.push(node);
34
+ }
35
+ });
36
+ }
37
+ else {
38
+ snapshot.nodes.forEach(node => {
39
+ if (Utils_1.default.isRootNode(node)) {
40
+ highPri.push(node);
41
+ }
42
+ });
43
+ }
44
+ return [highPri, lowPri];
45
+ }
46
+ visitReachableNodesbyDFS(snapshot, nodeVisitor, edgeVisitor) {
47
+ const [queue] = this.getRootNodeList(snapshot);
48
+ const queuedIDs = new Set(queue.map(n => n.id));
49
+ const visitedIDs = new Set();
50
+ const traverseOption = {
51
+ visited: visitedIDs,
52
+ queued: queuedIDs,
53
+ excludeWeakMapEdge: false,
54
+ isForward: true,
55
+ };
56
+ while (queue.length > 0) {
57
+ const node = queue.pop();
58
+ if (!node || visitedIDs.has(node.id)) {
59
+ continue;
60
+ }
61
+ if (nodeVisitor && nodeVisitor(node) === false) {
62
+ continue;
63
+ }
64
+ visitedIDs.add(node.id);
65
+ for (const edge of node.references) {
66
+ if (!this.shouldTraverseEdge(edge, traverseOption)) {
67
+ continue;
68
+ }
69
+ const nextNode = edge.toNode;
70
+ // deal with weak map specifically
71
+ if (Utils_1.default.isWeakMapEdgeToKey(edge)) {
72
+ const weakMapKeyObjectId = Utils_1.default.getWeakMapEdgeKeyId(edge);
73
+ // in weak map keys are weakly referenced
74
+ if (weakMapKeyObjectId === nextNode.id) {
75
+ continue;
76
+ }
77
+ }
78
+ if (edgeVisitor && edgeVisitor(edge) === false) {
79
+ continue;
80
+ }
81
+ queue.push(nextNode);
82
+ queuedIDs.add(nextNode.id);
83
+ }
84
+ }
85
+ }
86
+ flagReachableNodesFromWindow(snapshot, flags, flag) {
87
+ const nodesCount = snapshot.nodes.length;
88
+ const nodesToVisit = new Uint32Array(nodesCount);
89
+ let nodesToVisitLength = 0;
90
+ const node = snapshot.nodes.get(ROOT_NODE_INDEX);
91
+ for (const edge of node.references) {
92
+ if (edge.type === 'element') {
93
+ if (Utils_1.default.isDocumentDOMTreesRoot(edge.toNode)) {
94
+ continue;
95
+ }
96
+ }
97
+ else if (edge.type === 'shortcut') {
98
+ continue;
99
+ }
100
+ const childNodeIndex = edge.toNode.nodeIndex;
101
+ nodesToVisit[nodesToVisitLength++] = childNodeIndex;
102
+ flags[childNodeIndex] |= flag;
103
+ }
104
+ // flag all heap objects reachable from the root
105
+ while (nodesToVisitLength > 0) {
106
+ const nodeIndex = nodesToVisit[--nodesToVisitLength];
107
+ const node = snapshot.nodes.get(nodeIndex);
108
+ for (const edge of node.references) {
109
+ const childNode = edge.toNode;
110
+ const childNodeIndex = childNode.nodeIndex;
111
+ if (flags[childNodeIndex] & flag) {
112
+ continue;
113
+ }
114
+ if (edge.type === 'weak') {
115
+ continue;
116
+ }
117
+ nodesToVisit[nodesToVisitLength++] = childNodeIndex;
118
+ flags[childNodeIndex] |= flag;
119
+ }
120
+ }
121
+ }
122
+ // build post order based on:
123
+ // Keith D. Cooper and Timothy J. Harvey and Ken Kennedy
124
+ // "A Simple, Fast Dominance Algorithm"
125
+ buildPostOrderIndex(snapshot, flags) {
126
+ const nodeCount = snapshot.nodes.length;
127
+ const rootNodeIndex = ROOT_NODE_INDEX;
128
+ const forwardEdges = snapshot.edges;
129
+ const firstEdgeIndexes = new Uint32Array(nodeCount + 1);
130
+ firstEdgeIndexes[nodeCount] = forwardEdges.length;
131
+ for (let nodeIndex = 0, edgeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
132
+ firstEdgeIndexes[nodeIndex] = edgeIndex;
133
+ edgeIndex += snapshot.nodes.get(nodeIndex).edge_count;
134
+ }
135
+ const flag = PAGE_OBJECT_FLAG;
136
+ const nodeStack = new Uint32Array(nodeCount);
137
+ const edgeStack = new Uint32Array(nodeCount);
138
+ const postOrderIndex2NodeIndex = new Uint32Array(nodeCount);
139
+ const nodeIndex2PostOrderIndex = new Uint32Array(nodeCount);
140
+ const visited = new Uint8Array(nodeCount);
141
+ let postOrderIndex = 0;
142
+ // build a DFS stack and put the root node
143
+ // at the bottom of the stack
144
+ let stackTopIndex = 0;
145
+ nodeStack[0] = rootNodeIndex;
146
+ edgeStack[0] = firstEdgeIndexes[rootNodeIndex];
147
+ visited[rootNodeIndex] = 1;
148
+ let iteratedOnce = false;
149
+ // eslint-disable-next-line no-constant-condition
150
+ while (true) {
151
+ // use DFS to traverse all nodes via a stack
152
+ while (stackTopIndex >= 0) {
153
+ const nodeIndex = nodeStack[stackTopIndex];
154
+ const edgeIndex = edgeStack[stackTopIndex];
155
+ const edgesEnd = firstEdgeIndexes[nodeIndex + 1];
156
+ if (edgeIndex < edgesEnd) {
157
+ edgeStack[stackTopIndex]++;
158
+ const edgeType = forwardEdges.get(edgeIndex).type;
159
+ if (!Utils_1.default.isEssentialEdge(nodeIndex, edgeType, rootNodeIndex)) {
160
+ continue;
161
+ }
162
+ const childNodeIndex = forwardEdges.get(edgeIndex).toNode.nodeIndex;
163
+ if (visited[childNodeIndex]) {
164
+ continue;
165
+ }
166
+ const nodeFlag = flags[nodeIndex] & flag;
167
+ const childNodeFlag = flags[childNodeIndex] & flag;
168
+ // According to Chrome devtools, need to skip the edges from
169
+ // non-page-owned nodes to page-owned nodes (since debugger may
170
+ // also have references to heap objects)
171
+ if (nodeIndex !== rootNodeIndex && childNodeFlag && !nodeFlag) {
172
+ continue;
173
+ }
174
+ ++stackTopIndex;
175
+ nodeStack[stackTopIndex] = childNodeIndex;
176
+ edgeStack[stackTopIndex] = firstEdgeIndexes[childNodeIndex];
177
+ visited[childNodeIndex] = 1;
178
+ }
179
+ else {
180
+ // DFS is done, now build the post order based on the stack
181
+ nodeIndex2PostOrderIndex[nodeIndex] = postOrderIndex;
182
+ postOrderIndex2NodeIndex[postOrderIndex++] = nodeIndex;
183
+ --stackTopIndex;
184
+ }
185
+ }
186
+ // If we have tried by build the stack once previously
187
+ // or we have already built the post order for all nodes
188
+ if (iteratedOnce || postOrderIndex === nodeCount) {
189
+ break;
190
+ }
191
+ // Otherwise there are some nodes unreachable from
192
+ // the root node
193
+ if (Config_1.default.verbose) {
194
+ Console_1.default.overwrite(`${nodeCount - postOrderIndex} nodes are unreachable from the root`);
195
+ }
196
+ // Now the root node has the last post order index and
197
+ // the DFS stack is empty; we need to put the root node
198
+ // back to the bottom of the DFS stack, traverse all the
199
+ // orphan nodes with weak referrers (nodes unreachable
200
+ // from the root), and make sure the root node has the
201
+ // last post order index
202
+ --postOrderIndex;
203
+ stackTopIndex = 0;
204
+ nodeStack[0] = rootNodeIndex;
205
+ // skip iterating the edges of the root node
206
+ edgeStack[0] = firstEdgeIndexes[rootNodeIndex + 1];
207
+ for (let nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
208
+ if (visited[nodeIndex] ||
209
+ !Utils_1.default.hasOnlyWeakReferrers(snapshot.nodes.get(nodeIndex))) {
210
+ continue;
211
+ }
212
+ // Add all nodes that have only weak referrers
213
+ // to traverse their subgraphs
214
+ ++stackTopIndex;
215
+ nodeStack[stackTopIndex] = nodeIndex;
216
+ edgeStack[stackTopIndex] = firstEdgeIndexes[nodeIndex];
217
+ visited[nodeIndex] = nodeIndex;
218
+ }
219
+ iteratedOnce = true;
220
+ }
221
+ // If we already processed all orphan nodes (nodes unreachable from root)
222
+ // that have only weak referrers and still have some orphans
223
+ if (postOrderIndex !== nodeCount) {
224
+ if (Config_1.default.verbose) {
225
+ Console_1.default.lowLevel(nodeCount - postOrderIndex + ' unreachable nodes in heap snapshot');
226
+ }
227
+ // Now the root node has the last post order index and
228
+ // the DFS stack is empty; we need to put the root node
229
+ // back to the bottom of the DFS stack, traverse all the
230
+ // remaining orphan nodes (nodes unreachable from the root),
231
+ // and make sure the root node has the last post order index
232
+ --postOrderIndex;
233
+ for (let nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
234
+ if (visited[nodeIndex]) {
235
+ continue;
236
+ }
237
+ // give the orphan node a postorder index anyway
238
+ nodeIndex2PostOrderIndex[nodeIndex] = postOrderIndex;
239
+ postOrderIndex2NodeIndex[postOrderIndex++] = nodeIndex;
240
+ }
241
+ nodeIndex2PostOrderIndex[rootNodeIndex] = postOrderIndex;
242
+ postOrderIndex2NodeIndex[postOrderIndex++] = rootNodeIndex;
243
+ }
244
+ return {
245
+ postOrderIndex2NodeIndex,
246
+ nodeIndex2PostOrderIndex,
247
+ };
248
+ }
249
+ // The dominance algorithm is from:
250
+ // Keith D. Cooper and Timothy J. Harvey and Ken Kennedy
251
+ // "A Simple, Fast Dominance Algorithm"
252
+ calculateDominatorNodesFromPostOrder(nodes, edges, postOrderInfo, flags) {
253
+ const { postOrderIndex2NodeIndex, nodeIndex2PostOrderIndex } = postOrderInfo;
254
+ const nodeCount = nodes.length;
255
+ const forwardEdges = edges;
256
+ const firstEdgeIndexes = new Uint32Array(nodeCount + 1);
257
+ firstEdgeIndexes[nodeCount] = forwardEdges.length;
258
+ for (let nodeIndex = 0, edgeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
259
+ firstEdgeIndexes[nodeIndex] = edgeIndex;
260
+ edgeIndex += nodes.get(nodeIndex).edge_count;
261
+ }
262
+ const flag = PAGE_OBJECT_FLAG;
263
+ const rootPostOrderedIndex = nodeCount - 1;
264
+ const emptySlot = nodeCount;
265
+ const dominators = new Uint32Array(nodeCount);
266
+ for (let i = 0; i < rootPostOrderedIndex; ++i) {
267
+ dominators[i] = emptySlot;
268
+ }
269
+ dominators[rootPostOrderedIndex] = rootPostOrderedIndex;
270
+ // flag heap objects whose referrers changed and therefore
271
+ // the dominators of those heap objects needs to be recomputed
272
+ const nodesWithOutdatedDominatorInfo = new Uint8Array(nodeCount);
273
+ // start from the direct children of the root node
274
+ let nodeIndex = ROOT_NODE_INDEX;
275
+ const endEdgeIndex = firstEdgeIndexes[nodeIndex + 1];
276
+ for (let edgeIndex = firstEdgeIndexes[nodeIndex]; edgeIndex < endEdgeIndex; edgeIndex++) {
277
+ const edgeType = forwardEdges.get(edgeIndex).type;
278
+ if (!Utils_1.default.isEssentialEdge(ROOT_NODE_INDEX, edgeType, ROOT_NODE_INDEX)) {
279
+ continue;
280
+ }
281
+ const childNodeIndex = forwardEdges.get(edgeIndex).toNode.nodeIndex;
282
+ nodesWithOutdatedDominatorInfo[nodeIndex2PostOrderIndex[childNodeIndex]] = 1;
283
+ }
284
+ // now iterate through all nodes in the heap
285
+ let dominatorInfoChanged = true;
286
+ // iterate until no dominator info changed
287
+ while (dominatorInfoChanged) {
288
+ dominatorInfoChanged = false;
289
+ for (let postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) {
290
+ if (nodesWithOutdatedDominatorInfo[postOrderIndex] === 0) {
291
+ continue;
292
+ }
293
+ nodesWithOutdatedDominatorInfo[postOrderIndex] = 0;
294
+ // If dominator of the heap object has already been set to root node,
295
+ // then the heap object's dominator can't be changed anymore
296
+ if (dominators[postOrderIndex] === rootPostOrderedIndex) {
297
+ continue;
298
+ }
299
+ nodeIndex = postOrderIndex2NodeIndex[postOrderIndex];
300
+ const nodeFlag = flags[nodeIndex] & flag;
301
+ let newDominatorIndex = emptySlot;
302
+ let isOrphanNode = true;
303
+ const node = nodes.get(nodeIndex);
304
+ for (const edge of node.referrers) {
305
+ const referrerEdgeType = edge.type;
306
+ const referrerNodeIndex = edge.fromNode.nodeIndex;
307
+ if (!Utils_1.default.isEssentialEdge(referrerNodeIndex, referrerEdgeType, ROOT_NODE_INDEX)) {
308
+ continue;
309
+ }
310
+ isOrphanNode = false;
311
+ const referrerNodeFlag = flags[referrerNodeIndex] & flag;
312
+ // According to Chrome devtools, need to skip the edges from
313
+ // non-page-owned nodes to page-owned nodes (since debugger may
314
+ // also have references to heap objects)
315
+ if (referrerNodeIndex !== ROOT_NODE_INDEX &&
316
+ nodeFlag &&
317
+ !referrerNodeFlag) {
318
+ continue;
319
+ }
320
+ if (!this.shouldTraverseEdge(edge)) {
321
+ continue;
322
+ }
323
+ let referrerPostOrderIndex = nodeIndex2PostOrderIndex[referrerNodeIndex];
324
+ if (dominators[referrerPostOrderIndex] !== emptySlot) {
325
+ if (newDominatorIndex === emptySlot) {
326
+ newDominatorIndex = referrerPostOrderIndex;
327
+ }
328
+ else {
329
+ while (referrerPostOrderIndex !== newDominatorIndex) {
330
+ while (referrerPostOrderIndex < newDominatorIndex) {
331
+ referrerPostOrderIndex = dominators[referrerPostOrderIndex];
332
+ }
333
+ while (newDominatorIndex < referrerPostOrderIndex) {
334
+ newDominatorIndex = dominators[newDominatorIndex];
335
+ }
336
+ }
337
+ }
338
+ // no need to check any further if reaching the root node
339
+ if (newDominatorIndex === rootPostOrderedIndex) {
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ // set root node as the dominator of orphan nodes
345
+ if (isOrphanNode) {
346
+ newDominatorIndex = rootPostOrderedIndex;
347
+ }
348
+ if (newDominatorIndex !== emptySlot &&
349
+ dominators[postOrderIndex] !== newDominatorIndex) {
350
+ dominators[postOrderIndex] = newDominatorIndex;
351
+ dominatorInfoChanged = true;
352
+ nodeIndex = postOrderIndex2NodeIndex[postOrderIndex];
353
+ const node = nodes.get(nodeIndex);
354
+ for (const edge of node.references) {
355
+ nodesWithOutdatedDominatorInfo[nodeIndex2PostOrderIndex[edge.toNode.nodeIndex]] = 1;
356
+ }
357
+ }
358
+ }
359
+ }
360
+ const dominatorInfo = new Uint32Array(nodeCount);
361
+ for (let postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) {
362
+ nodeIndex = postOrderIndex2NodeIndex[postOrderIndex];
363
+ dominatorInfo[nodeIndex] =
364
+ postOrderIndex2NodeIndex[dominators[postOrderIndex]];
365
+ }
366
+ return dominatorInfo;
367
+ }
368
+ calculateRetainedSizesFromDominatorNodes(nodes, dominatorInfo, postOrderInfo) {
369
+ const { postOrderIndex2NodeIndex } = postOrderInfo;
370
+ const nodeCount = nodes.length;
371
+ const retainedSizes = new Float64Array(nodeCount);
372
+ for (let nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
373
+ retainedSizes[nodeIndex] = nodes.get(nodeIndex).self_size;
374
+ }
375
+ // add each heap object size to its dominator
376
+ // based on the post order
377
+ for (let postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) {
378
+ const nodeIndex = postOrderIndex2NodeIndex[postOrderIndex];
379
+ const dominatorIndex = dominatorInfo[nodeIndex];
380
+ retainedSizes[dominatorIndex] += retainedSizes[nodeIndex];
381
+ }
382
+ return retainedSizes;
383
+ }
384
+ shouldIgnoreEdgeInTraceFinding(edge) {
385
+ if (!edge || !edge.toNode || !edge.fromNode) {
386
+ return true;
387
+ }
388
+ return (Config_1.default.hideBrowserLeak &&
389
+ (Utils_1.default.isBlinkRootNode(edge.fromNode) ||
390
+ Utils_1.default.isPendingActivityNode(edge.fromNode)) &&
391
+ Utils_1.default.isDetachedDOMNode(edge.toNode));
392
+ }
393
+ shouldTraverseEdge(edge, options = {}) {
394
+ if (this.isBlockListedEdge(edge)) {
395
+ return false;
396
+ }
397
+ return Utils_1.default.isMeaningfulEdge(edge, Object.assign({ includeString: true }, options));
398
+ }
399
+ // remove edges that are already part of reported leaked paths
400
+ isBlockListedEdge(edge) {
401
+ if (!edge) {
402
+ return false;
403
+ }
404
+ const isStrName = typeof edge.name_or_index === 'string';
405
+ if (!Config_1.default.traverseDevToolsConsole &&
406
+ edge.type === 'internal' &&
407
+ isStrName &&
408
+ String(edge.name_or_index).indexOf('DevTools console') >= 0) {
409
+ return true;
410
+ }
411
+ if (Config_1.default.edgeNameBlockList.has(String(edge.name_or_index))) {
412
+ return true;
413
+ }
414
+ return false;
415
+ }
416
+ isLessPreferableEdge(edge) {
417
+ if (!edge) {
418
+ return false;
419
+ }
420
+ return Config_1.default.edgeNameGreyList.has(String(edge.name_or_index));
421
+ }
422
+ isLessPreferableNode(node) {
423
+ if (!node) {
424
+ return false;
425
+ }
426
+ return Config_1.default.nodeNameGreyList.has(node.name);
427
+ }
428
+ // each edge is indexed by fromNode's ID, toNode's ID, edge name, and edge type
429
+ getEdgeKey(edge) {
430
+ const fromNode = edge.fromNode;
431
+ const toNode = edge.toNode;
432
+ return `${fromNode.id}|${edge.name_or_index}|${edge.type}|${toNode.id}`;
433
+ }
434
+ calculateAllNodesRetainedSizes(snapshot) {
435
+ Console_1.default.overwrite('calculating dominators and retained sizes...');
436
+ // step 1: build post order index
437
+ const flags = new Uint32Array(snapshot.nodes.length);
438
+ this.flagReachableNodesFromWindow(snapshot, flags, PAGE_OBJECT_FLAG);
439
+ const postOrderInfo = this.buildPostOrderIndex(snapshot, flags);
440
+ // step 2: build dominator relations
441
+ const dominatorInfo = this.calculateDominatorNodesFromPostOrder(snapshot.nodes, snapshot.edges, postOrderInfo, flags);
442
+ // step 3: calculate retained sizes
443
+ const retainedSizes = this.calculateRetainedSizesFromDominatorNodes(snapshot.nodes, dominatorInfo, postOrderInfo);
444
+ // step 4: assign retained sizes and dominators to nodes
445
+ for (let i = 0; i < retainedSizes.length; i++) {
446
+ const node = snapshot.nodes.get(i);
447
+ node.retainedSize = retainedSizes[i];
448
+ node.dominatorNode = snapshot.nodes.get(dominatorInfo[i]);
449
+ }
450
+ }
451
+ annotateShortestPaths(snapshot, excludeKeySet) {
452
+ snapshot.clearShortestPathInfo();
453
+ Console_1.default.overwrite('annotating shortest path for all nodes');
454
+ const [nodeRootLists, lowPriRootLists] = this.getRootNodeList(snapshot, {
455
+ prioritize: true,
456
+ });
457
+ const nodeCount = snapshot.nodes.length;
458
+ const visited = new Uint8Array(nodeCount);
459
+ const queued = new Uint8Array(nodeCount);
460
+ const traverseOption = {
461
+ visited,
462
+ queued,
463
+ excludeWeakMapEdge: true,
464
+ isForward: true,
465
+ };
466
+ let curQueue = nodeRootLists;
467
+ const postponeQueue = [];
468
+ while (curQueue.length > 0) {
469
+ const nextQueue = [];
470
+ while (curQueue.length > 0) {
471
+ const node = curQueue.pop();
472
+ visited[node.nodeIndex] = 1;
473
+ for (const edge of node.references) {
474
+ // skip nodes that already have a parent
475
+ if (edge.toNode.pathEdge) {
476
+ continue;
477
+ }
478
+ if (!this.shouldTraverseEdge(edge, traverseOption)) {
479
+ continue;
480
+ }
481
+ if (this.shouldIgnoreEdgeInTraceFinding(edge)) {
482
+ continue;
483
+ }
484
+ if (Utils_1.default.isWeakMapEdge(edge) && excludeKeySet) {
485
+ const weakMapKeyObjectId = Utils_1.default.getWeakMapEdgeKeyId(edge);
486
+ if (excludeKeySet.has(weakMapKeyObjectId)) {
487
+ continue;
488
+ }
489
+ }
490
+ // postpone traversing edges and nodes that are less preferable
491
+ if (this.isLessPreferableEdge(edge) ||
492
+ this.isLessPreferableNode(edge.toNode)) {
493
+ postponeQueue.push(edge);
494
+ }
495
+ else {
496
+ edge.toNode.pathEdge = edge;
497
+ nextQueue.push(edge.toNode);
498
+ }
499
+ queued[edge.toNode.nodeIndex] = 1;
500
+ }
501
+ }
502
+ // if no other preferable traces available
503
+ // traverse the postpone queue
504
+ while (nextQueue.length === 0 && postponeQueue.length > 0) {
505
+ const edge = postponeQueue.pop();
506
+ if (edge.toNode.pathEdge) {
507
+ continue;
508
+ }
509
+ edge.toNode.pathEdge = edge;
510
+ nextQueue.push(edge.toNode);
511
+ }
512
+ // if no other preferable traces available
513
+ // consider the low priority root nodes
514
+ while (nextQueue.length === 0 && lowPriRootLists.length > 0) {
515
+ const root = lowPriRootLists.pop();
516
+ if (root.pathEdge) {
517
+ continue;
518
+ }
519
+ nextQueue.push(root);
520
+ }
521
+ curQueue = nextQueue;
522
+ }
523
+ }
524
+ getPathToGCRoots(_snapshot, node) {
525
+ if (!node || !node.pathEdge) {
526
+ return null;
527
+ }
528
+ let path = { node };
529
+ while (node && node.pathEdge) {
530
+ const edge = node.pathEdge;
531
+ path = { node: edge.fromNode, edge, next: path };
532
+ node = edge.fromNode;
533
+ }
534
+ return path;
535
+ }
536
+ }
537
+ exports.default = TraceFinder;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails oncall+ws_labs
8
+ * @format
9
+ */
10
+ declare const _default: {
11
+ isSimilarTrace: (t1: import("..").LeakTrace, t2: import("..").LeakTrace) => boolean;
12
+ };
13
+ export default _default;
14
+ //# sourceMappingURL=ClusterUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClusterUtils.d.ts","sourceRoot":"","sources":["../../src/trace-cluster/ClusterUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;;;;AAKH,wBAA6D"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @emails oncall+ws_labs
9
+ * @format
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const ClusterUtilsHelper_1 = __importDefault(require("./ClusterUtilsHelper"));
16
+ const ClusteringHeuristics_1 = __importDefault(require("./ClusteringHeuristics"));
17
+ exports.default = ClusterUtilsHelper_1.default.initialize(ClusteringHeuristics_1.default);
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails oncall+ws_labs
8
+ * @format
9
+ */
10
+ import type { AnyValue, LeakTraceElement, LeakTrace } from '../lib/Types';
11
+ declare type NameWeightMapType = Map<string | RegExp | number, number>;
12
+ interface ClusteringHeuristic {
13
+ edgeNameStopWords: NameWeightMapType;
14
+ nodeNameStopWords: NameWeightMapType;
15
+ similarWordRegExps: Map<RegExp, number>;
16
+ decendentDecayFactors: {
17
+ kind: string;
18
+ name: string;
19
+ decay: number;
20
+ }[];
21
+ startingModuleForTraceMatching: (string | RegExp)[];
22
+ }
23
+ export declare function debugLog(...args: AnyValue[]): void;
24
+ interface DebugElementSimilarityStatsParams {
25
+ elementA: LeakTraceElement;
26
+ elementB: LeakTraceElement;
27
+ matchedSum: number;
28
+ totalSum: number;
29
+ }
30
+ export declare const debugTraceElementSimilarityStats: ({ elementA, elementB, matchedSum, totalSum, }: DebugElementSimilarityStatsParams) => void;
31
+ declare type ClusteringUtilReturnType = {
32
+ isSimilarTrace: (t1: LeakTrace, t2: LeakTrace) => boolean;
33
+ };
34
+ declare const _default: {
35
+ initialize: (heuristics: ClusteringHeuristic) => ClusteringUtilReturnType;
36
+ };
37
+ export default _default;
38
+ //# sourceMappingURL=ClusterUtilsHelper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClusterUtilsHelper.d.ts","sourceRoot":"","sources":["../../src/trace-cluster/ClusterUtilsHelper.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,OAAO,KAAK,EAAC,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AASxE,aAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;AAK/D,UAAU,mBAAmB;IAC3B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,qBAAqB,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IACrE,8BAA8B,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CACrD;AAED,wBAAgB,QAAQ,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAQlD;AAED,UAAU,iCAAiC;IACzC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,gCAAgC,kDAK1C,iCAAiC,KAAG,IAatC,CAAC;AAMF,aAAK,wBAAwB,GAAG;IAC9B,cAAc,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC;CAC3D,CAAC;;;;AAqcF,wBAEE"}