@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,238 @@
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 fs_1 = __importDefault(require("fs"));
16
+ const Config_1 = __importDefault(require("../lib/Config"));
17
+ const Console_1 = __importDefault(require("../lib/Console"));
18
+ const Serializer_1 = __importDefault(require("../lib/Serializer"));
19
+ const Utils_1 = __importDefault(require("../lib/Utils"));
20
+ const TraceElement_1 = require("./TraceElement");
21
+ const TraceSimilarityStrategy_1 = __importDefault(require("./strategies/TraceSimilarityStrategy"));
22
+ const TraceAsClusterStrategy_1 = __importDefault(require("./strategies/TraceAsClusterStrategy"));
23
+ // sync up with html/intern/js/webspeed/memlab/lib/LeakCluster.js
24
+ class NormalizedTrace {
25
+ constructor(p = null, snapshot = null) {
26
+ if (!p) {
27
+ this.trace = [];
28
+ this.traceSummary = '';
29
+ }
30
+ else {
31
+ this.trace = NormalizedTrace.pathToTrace(p, {
32
+ untilFirstDetachedDOMElem: true,
33
+ });
34
+ this.traceSummary = snapshot
35
+ ? Serializer_1.default.summarizePath(p, new Set(), snapshot)
36
+ : '';
37
+ }
38
+ }
39
+ // convert path to leak trace
40
+ static pathToTrace(p, options = {}) {
41
+ const skipRest = !!options.untilFirstDetachedDOMElem;
42
+ const shouldSkip = (node) => {
43
+ // only consider the trace from GC root to the first detached element
44
+ // NOTE: do not use utils.isDetachedDOMNode, which relies on
45
+ // the fact that p.node is a HeapNode
46
+ return (skipRest &&
47
+ node.name.startsWith('Detached ') &&
48
+ node.name !== 'Detached InternalNode');
49
+ };
50
+ const trace = [];
51
+ let curItem = p;
52
+ while (curItem) {
53
+ if (curItem.node) {
54
+ trace.push(new TraceElement_1.NodeRecord(curItem.node));
55
+ if (shouldSkip(curItem.node)) {
56
+ break;
57
+ }
58
+ }
59
+ if (curItem.edge) {
60
+ trace.push(new TraceElement_1.EdgeRecord(curItem.edge));
61
+ }
62
+ curItem = curItem.next;
63
+ }
64
+ return trace;
65
+ }
66
+ // convert leak trace to path
67
+ static traceToPath(trace) {
68
+ if (!trace) {
69
+ return {};
70
+ }
71
+ let p = {};
72
+ for (let i = trace.length - 1; i >= 0; --i) {
73
+ const item = trace[i];
74
+ if (item.kind === 'edge') {
75
+ p.edge = item;
76
+ }
77
+ if (item.kind === 'node') {
78
+ p.node = item;
79
+ p = { next: p };
80
+ }
81
+ }
82
+ return p.next || {};
83
+ }
84
+ getTraceSummary() {
85
+ return this.traceSummary;
86
+ }
87
+ static addLeakedNodeToCluster(cluster, path) {
88
+ const leakedNode = Utils_1.default.getLeakedNode(path);
89
+ if (!cluster.leakedNodeIds) {
90
+ cluster.leakedNodeIds = new Set();
91
+ }
92
+ if (leakedNode) {
93
+ cluster.leakedNodeIds.add(leakedNode.id);
94
+ }
95
+ }
96
+ static calculateClusterRetainedSize(cluster, snapshot, aggregateDominatorMetrics) {
97
+ if (!cluster.leakedNodeIds) {
98
+ return 0;
99
+ }
100
+ return (cluster.retainedSize = aggregateDominatorMetrics(cluster.leakedNodeIds, snapshot, () => true, (node) => node.retainedSize));
101
+ }
102
+ static samplePaths(paths) {
103
+ const maxCount = 5000;
104
+ const sampleRatio = Math.min(1, maxCount / paths.length);
105
+ if (sampleRatio < 1) {
106
+ Console_1.default.warning('Sampling trace due to a large number of traces:');
107
+ Console_1.default.lowLevel(` Number of Traces: ${paths.length}`);
108
+ Console_1.default.lowLevel(` Sampling Ratio: ${Utils_1.default.getReadablePercent(sampleRatio)}`);
109
+ }
110
+ const ret = [];
111
+ for (const p of paths) {
112
+ if (Math.random() < sampleRatio) {
113
+ ret.push(p);
114
+ }
115
+ }
116
+ return ret;
117
+ }
118
+ static diffTraces(newTraces, existingTraces, // existing representative traces
119
+ option = {}) {
120
+ var _a, _b;
121
+ const strategy = (_b = (_a = option.strategy) !== null && _a !== void 0 ? _a : Config_1.default.clusterStrategy) !== null && _b !== void 0 ? _b : new TraceSimilarityStrategy_1.default();
122
+ return strategy.diffTraces(newTraces, existingTraces);
123
+ }
124
+ static diffClusters(newClusters, existingClusters) {
125
+ Console_1.default.overwrite('Diffing clusters');
126
+ // build trace to cluster map
127
+ const traceToClusterMap = new Map();
128
+ const newTraces = [];
129
+ const convertOption = { untilFirstDetachedDOMElem: true };
130
+ for (const cluster of newClusters) {
131
+ const trace = NormalizedTrace.pathToTrace(cluster.path, convertOption);
132
+ newTraces.push(trace);
133
+ traceToClusterMap.set(trace, cluster);
134
+ }
135
+ const existingTraces = [];
136
+ for (const cluster of existingClusters) {
137
+ const trace = NormalizedTrace.pathToTrace(cluster.path, convertOption);
138
+ existingTraces.push(trace);
139
+ traceToClusterMap.set(trace, cluster);
140
+ }
141
+ // differing representative traces in existing clusters vs new traces
142
+ // and calculate which representative traces are stale
143
+ // and which new traces should form new clusters
144
+ const traceDiff = NormalizedTrace.diffTraces(newTraces, existingTraces);
145
+ const { staleClusters, clustersToAdd, allClusters } = traceDiff;
146
+ // map trace to cluster
147
+ const traceToCluster = (trace) => {
148
+ if (!traceToClusterMap.has(trace)) {
149
+ throw Utils_1.default.haltOrThrow('trace to cluster mapping failed');
150
+ }
151
+ return traceToClusterMap.get(trace);
152
+ };
153
+ return {
154
+ staleClusters: staleClusters.map(traceToCluster),
155
+ clustersToAdd: clustersToAdd.map(traceToCluster),
156
+ allClusters: allClusters.map(cluster => cluster.map(traceToCluster)),
157
+ };
158
+ }
159
+ static clusterLeakTraces(leakTraces) {
160
+ const { allClusters } = NormalizedTrace.diffTraces(leakTraces, []);
161
+ const lastNodeFromTrace = (trace) => trace[trace.length - 1];
162
+ const labaledLeakTraces = allClusters.reduce((acc, bucket) => {
163
+ const lastNodeFromFirstTrace = lastNodeFromTrace(bucket[0]);
164
+ bucket.map(lastNodeFromTrace).forEach(lastNodeInTrace => {
165
+ if (lastNodeInTrace.id == null || lastNodeFromFirstTrace.id == null) {
166
+ throw new Error('node id not found in last node of the leak trace');
167
+ }
168
+ acc[lastNodeInTrace.id] = String(lastNodeFromFirstTrace.id);
169
+ });
170
+ return acc;
171
+ }, {});
172
+ return labaledLeakTraces;
173
+ }
174
+ static clusterPaths(paths, snapshot, aggregateDominatorMetrics, option = {}) {
175
+ Console_1.default.overwrite('Clustering leak traces');
176
+ if (paths.length === 0) {
177
+ Console_1.default.midLevel('No leaks found');
178
+ return [];
179
+ }
180
+ // sample paths if there are too many
181
+ paths = this.samplePaths(paths);
182
+ // build trace to path map
183
+ const traceToPathMap = new Map();
184
+ const traces = [];
185
+ for (const p of paths) {
186
+ const trace = NormalizedTrace.pathToTrace(p, {
187
+ untilFirstDetachedDOMElem: true,
188
+ });
189
+ traceToPathMap.set(trace, p);
190
+ traces.push(trace);
191
+ }
192
+ // cluster traces
193
+ const { allClusters } = NormalizedTrace.diffTraces(traces, [], option);
194
+ // construct TraceCluster from clustering result
195
+ const clusters = allClusters.map((traces) => {
196
+ const cluster = {
197
+ path: traceToPathMap.get(traces[0]),
198
+ count: traces.length,
199
+ snapshot,
200
+ retainedSize: 0,
201
+ };
202
+ traces.forEach((trace) => {
203
+ NormalizedTrace.addLeakedNodeToCluster(cluster, traceToPathMap.get(trace));
204
+ });
205
+ this.calculateClusterRetainedSize(cluster, snapshot, aggregateDominatorMetrics);
206
+ return cluster;
207
+ });
208
+ clusters.sort((c1, c2) => { var _a, _b; return ((_a = c2.retainedSize) !== null && _a !== void 0 ? _a : 0) - ((_b = c1.retainedSize) !== null && _b !== void 0 ? _b : 0); });
209
+ Console_1.default.midLevel(`MemLab found ${clusters.length} leak(s)`);
210
+ return clusters;
211
+ }
212
+ static generateUnClassifiedClusters(paths, snapshot, aggregateDominatorMetrics) {
213
+ return this.clusterPaths(paths, snapshot, aggregateDominatorMetrics, {
214
+ strategy: new TraceAsClusterStrategy_1.default(),
215
+ });
216
+ }
217
+ static loadCluster() {
218
+ let ret = [];
219
+ const file = Config_1.default.traceClusterFile;
220
+ if (!fs_1.default.existsSync(file)) {
221
+ return ret;
222
+ }
223
+ try {
224
+ const content = fs_1.default.readFileSync(file, 'UTF-8');
225
+ ret = JSON.parse(content);
226
+ }
227
+ catch (ex) {
228
+ throw Utils_1.default.haltOrThrow(Utils_1.default.getError(ex));
229
+ }
230
+ return ret;
231
+ }
232
+ static saveCluster(clusters) {
233
+ const file = Config_1.default.traceClusterFile;
234
+ const content = JSON.stringify(clusters, null, 2);
235
+ fs_1.default.writeFileSync(file, content, 'UTF-8');
236
+ }
237
+ }
238
+ exports.default = NormalizedTrace;
@@ -0,0 +1,71 @@
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 { EdgeIterationCallback, IHeapEdge, IHeapLocation, IHeapNode, IHeapSnapshot, IHeapStringNode, Nullable } from '../lib/Types';
11
+ export declare class NodeRecord implements IHeapNode {
12
+ kind: string;
13
+ name: string;
14
+ type: string;
15
+ id: number;
16
+ is_detached: boolean;
17
+ detachState: number;
18
+ attributes: number;
19
+ self_size: number;
20
+ edge_count: number;
21
+ trace_node_id: number;
22
+ nodeIndex: number;
23
+ retainedSize: number;
24
+ highlight?: boolean;
25
+ markAsDetached(): void;
26
+ get isString(): boolean;
27
+ set isString(b: boolean);
28
+ set snapshot(s: IHeapSnapshot);
29
+ get snapshot(): IHeapSnapshot;
30
+ set references(r: IHeapEdge[]);
31
+ get references(): IHeapEdge[];
32
+ forEachReference(_callback: EdgeIterationCallback): void;
33
+ set referrers(r: IHeapEdge[]);
34
+ get referrers(): IHeapEdge[];
35
+ toStringNode(): IHeapStringNode;
36
+ forEachReferrer(_callback: EdgeIterationCallback): void;
37
+ findReference(): Nullable<IHeapEdge>;
38
+ findAnyReferrer(): Nullable<IHeapEdge>;
39
+ findReferrers(): IHeapEdge[];
40
+ set pathEdge(r: IHeapEdge);
41
+ get pathEdge(): IHeapEdge;
42
+ set dominatorNode(r: IHeapNode);
43
+ get dominatorNode(): IHeapNode;
44
+ set location(r: IHeapLocation);
45
+ get location(): IHeapLocation;
46
+ getReference(_edgeName: string | number, _edgeType?: string): Nullable<IHeapEdge>;
47
+ getReferenceNode(_edgeName: string | number, _edgeType?: string): Nullable<IHeapNode>;
48
+ getAnyReferrer(_edgeName: string | number, _edgeType?: string): Nullable<IHeapEdge>;
49
+ getAnyReferrerNode(_edgeName: string | number, _edgeType?: string): Nullable<IHeapNode>;
50
+ getReferrers(_edgeName: string | number, _edgeType?: string): IHeapEdge[];
51
+ getReferrerNodes(_edgeName: string | number, _edgeType?: string): IHeapNode[];
52
+ constructor(node: IHeapNode);
53
+ private extraceNodeName;
54
+ }
55
+ export declare class EdgeRecord implements IHeapEdge {
56
+ kind: string;
57
+ name_or_index: string | number;
58
+ type: string;
59
+ edgeIndex: number;
60
+ is_index: boolean;
61
+ to_node: number;
62
+ constructor(edge: IHeapEdge);
63
+ set snapshot(s: IHeapSnapshot);
64
+ get snapshot(): IHeapSnapshot;
65
+ set toNode(s: IHeapNode);
66
+ get toNode(): IHeapNode;
67
+ set fromNode(s: IHeapNode);
68
+ get fromNode(): IHeapNode;
69
+ }
70
+ export declare type NormalizedTraceElement = NodeRecord | EdgeRecord;
71
+ //# sourceMappingURL=TraceElement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceElement.d.ts","sourceRoot":"","sources":["../../src/trace-cluster/TraceElement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
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
+ exports.EdgeRecord = exports.NodeRecord = void 0;
16
+ const Utils_1 = __importDefault(require("../lib/Utils"));
17
+ class NodeRecord {
18
+ constructor(node) {
19
+ this.kind = 'node';
20
+ this.name = this.extraceNodeName(node);
21
+ this.type = node.type;
22
+ this.id = node.id;
23
+ this.is_detached = node.is_detached;
24
+ this.detachState = node.detachState;
25
+ this.attributes = node.attributes;
26
+ this.self_size = node.self_size;
27
+ this.edge_count = node.edge_count;
28
+ this.trace_node_id = node.trace_node_id;
29
+ this.nodeIndex = node.nodeIndex;
30
+ this.retainedSize = node.retainedSize;
31
+ this.highlight = node.highlight;
32
+ }
33
+ markAsDetached() {
34
+ throw new Error('NodeRecord.markAsDetached not callable.');
35
+ }
36
+ get isString() {
37
+ const type = this.type;
38
+ return (type === 'string' ||
39
+ type === 'sliced string' ||
40
+ type === 'concatenated string');
41
+ }
42
+ set isString(b) {
43
+ throw new Error('NodeRecord.string cannot be assigned');
44
+ }
45
+ set snapshot(s) {
46
+ throw new Error('NodeRecord.snapshot cannot be assigned.');
47
+ }
48
+ get snapshot() {
49
+ throw new Error('NodeRecord.snapshot cannot be read.');
50
+ }
51
+ set references(r) {
52
+ throw new Error('NodeRecord.references cannot be assigned');
53
+ }
54
+ get references() {
55
+ throw new Error('NodeRecord.references cannot be read');
56
+ }
57
+ forEachReference(
58
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
+ _callback) {
60
+ throw new Error('NodeRecord.forEachReference is not implemented');
61
+ }
62
+ set referrers(r) {
63
+ throw new Error('NodeRecord.referrers cannot be assigned');
64
+ }
65
+ get referrers() {
66
+ throw new Error('NodeRecord.referrers cannot be read');
67
+ }
68
+ toStringNode() {
69
+ throw new Error('NodeRecord.toStringNode is not implemented');
70
+ }
71
+ forEachReferrer(
72
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
73
+ _callback) {
74
+ throw new Error('NodeRecord.forEachReferrer is not implemented');
75
+ }
76
+ findReference() {
77
+ throw new Error('NodeRecord.findReference is not implemented');
78
+ }
79
+ findAnyReferrer() {
80
+ throw new Error('NodeRecord.findAnyReferrer is not implemented');
81
+ }
82
+ findReferrers() {
83
+ throw new Error('NodeRecord.findReferrers is not implemented');
84
+ }
85
+ set pathEdge(r) {
86
+ throw new Error('NodeRecord.pathEdge cannot be assigned');
87
+ }
88
+ get pathEdge() {
89
+ throw new Error('NodeRecord.pathEdge cannot be read');
90
+ }
91
+ set dominatorNode(r) {
92
+ throw new Error('NodeRecord.pathEdge cannot be assigned');
93
+ }
94
+ get dominatorNode() {
95
+ throw new Error('NodeRecord.pathEdge cannot be read');
96
+ }
97
+ set location(r) {
98
+ throw new Error('NodeRecord.location cannot be assigned');
99
+ }
100
+ get location() {
101
+ throw new Error('NodeRecord.location cannot be read');
102
+ }
103
+ getReference(
104
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
105
+ _edgeName,
106
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
107
+ _edgeType) {
108
+ throw new Error('NodeRecord.getReference is not implemented');
109
+ }
110
+ getReferenceNode(
111
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
112
+ _edgeName,
113
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
114
+ _edgeType) {
115
+ throw new Error('NodeRecord.getReferenceNode is not implemented');
116
+ }
117
+ getAnyReferrer(
118
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
+ _edgeName,
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ _edgeType) {
122
+ throw new Error('NodeRecord.getReferrer is not implemented');
123
+ }
124
+ getAnyReferrerNode(
125
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
126
+ _edgeName,
127
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
128
+ _edgeType) {
129
+ throw new Error('NodeRecord.getReferrerNode is not implemented');
130
+ }
131
+ getReferrers(
132
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
133
+ _edgeName,
134
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
135
+ _edgeType) {
136
+ throw new Error('NodeRecord.getReferrers is not implemented');
137
+ }
138
+ getReferrerNodes(
139
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
140
+ _edgeName,
141
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
142
+ _edgeType) {
143
+ throw new Error('NodeRecord.getReferrerNodes is not implemented');
144
+ }
145
+ extraceNodeName(node) {
146
+ // deserialized node may not have snapshot info
147
+ if (!node.snapshot || !Utils_1.default.isFiberNode(node)) {
148
+ return node.name;
149
+ }
150
+ return Utils_1.default.extractFiberNodeInfo(node);
151
+ }
152
+ }
153
+ exports.NodeRecord = NodeRecord;
154
+ class EdgeRecord {
155
+ constructor(edge) {
156
+ this.kind = 'edge';
157
+ this.name_or_index = edge.name_or_index;
158
+ this.type = edge.type;
159
+ this.edgeIndex = edge.edgeIndex;
160
+ this.is_index = edge.is_index;
161
+ this.to_node = edge.to_node;
162
+ }
163
+ set snapshot(s) {
164
+ throw new Error('EdgeRecord.snapshot cannot be assigned.');
165
+ }
166
+ get snapshot() {
167
+ throw new Error('EdgeRecord.snapshot cannot be read.');
168
+ }
169
+ set toNode(s) {
170
+ throw new Error('EdgeRecord.toNode cannot be assigned.');
171
+ }
172
+ get toNode() {
173
+ throw new Error('EdgeRecord.toNode cannot be read.');
174
+ }
175
+ set fromNode(s) {
176
+ throw new Error('EdgeRecord.fromNode cannot be assigned.');
177
+ }
178
+ get fromNode() {
179
+ throw new Error('EdgeRecord.fromNode cannot be read.');
180
+ }
181
+ }
182
+ exports.EdgeRecord = EdgeRecord;
@@ -0,0 +1,15 @@
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 { IClusterStrategy, LeakTrace, TraceDiff } from '../../lib/Types';
11
+ export default class TraceAsClusterStrategy implements IClusterStrategy {
12
+ diffTraces(newTraces: LeakTrace[], existingTraces: LeakTrace[]): TraceDiff;
13
+ static isSimilarTrace(t1: LeakTrace, t2: LeakTrace): boolean;
14
+ }
15
+ //# sourceMappingURL=TraceAsClusterStrategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceAsClusterStrategy.d.ts","sourceRoot":"","sources":["../../../src/trace-cluster/strategies/TraceAsClusterStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAI5E,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,gBAAgB;IAC9D,UAAU,CACf,SAAS,EAAE,SAAS,EAAE,EACtB,cAAc,EAAE,SAAS,EAAE,GAC1B,SAAS;IAkBZ,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO;CAG7D"}
@@ -0,0 +1,37 @@
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 ClusterUtils_1 = __importDefault(require("../ClusterUtils"));
16
+ // each trace is a cluster
17
+ class TraceAsClusterStrategy {
18
+ diffTraces(newTraces, existingTraces) {
19
+ // duplicated cluster to remove
20
+ const staleClusters = [];
21
+ // new cluster to save
22
+ const clustersToAdd = [];
23
+ // all clusters, with duplicated cluters in the same sub-array
24
+ const clusters = existingTraces.map(trace => [trace]);
25
+ // checking new clusters
26
+ for (let i = 0; i < newTraces.length; ++i) {
27
+ const traceToCheck = newTraces[i];
28
+ clustersToAdd.push(traceToCheck);
29
+ clusters.push([traceToCheck]);
30
+ }
31
+ return { staleClusters, clustersToAdd, allClusters: clusters };
32
+ }
33
+ static isSimilarTrace(t1, t2) {
34
+ return ClusterUtils_1.default.isSimilarTrace(t1, t2);
35
+ }
36
+ }
37
+ exports.default = TraceAsClusterStrategy;
@@ -0,0 +1,15 @@
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 { IClusterStrategy, LeakTrace, TraceDiff } from '../../lib/Types';
11
+ export default class TraceSimilarityStrategy implements IClusterStrategy {
12
+ diffTraces(newTraces: LeakTrace[], existingTraces: LeakTrace[]): TraceDiff;
13
+ static isSimilarTrace(t1: LeakTrace, t2: LeakTrace): boolean;
14
+ }
15
+ //# sourceMappingURL=TraceSimilarityStrategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceSimilarityStrategy.d.ts","sourceRoot":"","sources":["../../../src/trace-cluster/strategies/TraceSimilarityStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAI5E,MAAM,CAAC,OAAO,OAAO,uBAAwB,YAAW,gBAAgB;IAC/D,UAAU,CACf,SAAS,EAAE,SAAS,EAAE,EACtB,cAAc,EAAE,SAAS,EAAE,GAC1B,SAAS;IA0CZ,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO;CAG7D"}
@@ -0,0 +1,60 @@
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 ClusterUtils_1 = __importDefault(require("../ClusterUtils"));
16
+ // cluster by putting similar traces together
17
+ class TraceSimilarityStrategy {
18
+ diffTraces(newTraces, existingTraces) {
19
+ // duplicated cluster to remove
20
+ const staleClusters = [];
21
+ // new cluster to save
22
+ const clustersToAdd = [];
23
+ // all clusters, with duplicated cluters in the same sub-array
24
+ const clusters = [];
25
+ // consolidating existing clusters
26
+ if (existingTraces.length > 0) {
27
+ clusters.push([existingTraces[0]]);
28
+ outer: for (let i = 1; i < existingTraces.length; ++i) {
29
+ const traceToCheck = existingTraces[i];
30
+ for (let j = 0; j < clusters.length; ++j) {
31
+ const repTrace = clusters[j][0];
32
+ if (TraceSimilarityStrategy.isSimilarTrace(repTrace, traceToCheck)) {
33
+ staleClusters.push(traceToCheck);
34
+ clusters[j].push(traceToCheck);
35
+ continue outer;
36
+ }
37
+ }
38
+ clusters.push([traceToCheck]);
39
+ }
40
+ }
41
+ // checking new clusters
42
+ outer: for (let i = 0; i < newTraces.length; ++i) {
43
+ const traceToCheck = newTraces[i];
44
+ for (let j = 0; j < clusters.length; ++j) {
45
+ const repTrace = clusters[j][0];
46
+ if (TraceSimilarityStrategy.isSimilarTrace(repTrace, traceToCheck)) {
47
+ clusters[j].push(traceToCheck);
48
+ continue outer;
49
+ }
50
+ }
51
+ clustersToAdd.push(traceToCheck);
52
+ clusters.push([traceToCheck]);
53
+ }
54
+ return { staleClusters, clustersToAdd, allClusters: clusters };
55
+ }
56
+ static isSimilarTrace(t1, t2) {
57
+ return ClusterUtils_1.default.isSimilarTrace(t1, t2);
58
+ }
59
+ }
60
+ exports.default = TraceSimilarityStrategy;