@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.
- package/README.md +11 -0
- package/dist/__tests__/parser/HeapParser.test.d.ts +11 -0
- package/dist/__tests__/parser/HeapParser.test.d.ts.map +1 -0
- package/dist/__tests__/parser/HeapParser.test.js +54 -0
- package/dist/__tests__/parser/NodeHeap.test.d.ts +11 -0
- package/dist/__tests__/parser/NodeHeap.test.d.ts.map +1 -0
- package/dist/__tests__/parser/NodeHeap.test.js +96 -0
- package/dist/__tests__/parser/StringNode.test.d.ts +11 -0
- package/dist/__tests__/parser/StringNode.test.d.ts.map +1 -0
- package/dist/__tests__/parser/StringNode.test.js +61 -0
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts +16 -0
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts.map +1 -0
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.js +140 -0
- package/dist/__tests__/utils/utils.test.d.ts +11 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +81 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/lib/BaseOption.d.ts +31 -0
- package/dist/lib/BaseOption.d.ts.map +1 -0
- package/dist/lib/BaseOption.js +109 -0
- package/dist/lib/BrowserInfo.d.ts +33 -0
- package/dist/lib/BrowserInfo.d.ts.map +1 -0
- package/dist/lib/BrowserInfo.js +117 -0
- package/dist/lib/Config.d.ts +203 -0
- package/dist/lib/Config.d.ts.map +1 -0
- package/dist/lib/Config.js +427 -0
- package/dist/lib/Console.d.ts +67 -0
- package/dist/lib/Console.d.ts.map +1 -0
- package/dist/lib/Console.js +344 -0
- package/dist/lib/Constant.d.ts +38 -0
- package/dist/lib/Constant.d.ts.map +1 -0
- package/dist/lib/Constant.js +58 -0
- package/dist/lib/FileManager.d.ts +69 -0
- package/dist/lib/FileManager.d.ts.map +1 -0
- package/dist/lib/FileManager.js +309 -0
- package/dist/lib/HeapAnalyzer.d.ts +51 -0
- package/dist/lib/HeapAnalyzer.d.ts.map +1 -0
- package/dist/lib/HeapAnalyzer.js +719 -0
- package/dist/lib/HeapParser.d.ts +19 -0
- package/dist/lib/HeapParser.d.ts.map +1 -0
- package/dist/lib/HeapParser.js +128 -0
- package/dist/lib/InternalValueSetter.d.ts +14 -0
- package/dist/lib/InternalValueSetter.d.ts.map +1 -0
- package/dist/lib/InternalValueSetter.js +43 -0
- package/dist/lib/NodeHeap.d.ts +16 -0
- package/dist/lib/NodeHeap.d.ts.map +1 -0
- package/dist/lib/NodeHeap.js +62 -0
- package/dist/lib/ProcessManager.d.ts +25 -0
- package/dist/lib/ProcessManager.d.ts.map +1 -0
- package/dist/lib/ProcessManager.js +67 -0
- package/dist/lib/Serializer.d.ts +49 -0
- package/dist/lib/Serializer.d.ts.map +1 -0
- package/dist/lib/Serializer.js +702 -0
- package/dist/lib/StringLoader.d.ts +26 -0
- package/dist/lib/StringLoader.d.ts.map +1 -0
- package/dist/lib/StringLoader.js +290 -0
- package/dist/lib/Types.d.ts +432 -0
- package/dist/lib/Types.d.ts.map +1 -0
- package/dist/lib/Types.js +11 -0
- package/dist/lib/Utils.d.ts +223 -0
- package/dist/lib/Utils.d.ts.map +1 -0
- package/dist/lib/Utils.js +1736 -0
- package/dist/lib/heap-data/HeapEdge.d.ts +27 -0
- package/dist/lib/heap-data/HeapEdge.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapEdge.js +75 -0
- package/dist/lib/heap-data/HeapLocation.d.ts +22 -0
- package/dist/lib/heap-data/HeapLocation.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapLocation.js +40 -0
- package/dist/lib/heap-data/HeapNode.d.ts +55 -0
- package/dist/lib/heap-data/HeapNode.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapNode.js +344 -0
- package/dist/lib/heap-data/HeapSnapshot.d.ts +85 -0
- package/dist/lib/heap-data/HeapSnapshot.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapSnapshot.js +462 -0
- package/dist/lib/heap-data/HeapStringNode.d.ts +18 -0
- package/dist/lib/heap-data/HeapStringNode.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapStringNode.js +43 -0
- package/dist/lib/heap-data/HeapUtils.d.ts +17 -0
- package/dist/lib/heap-data/HeapUtils.d.ts.map +1 -0
- package/dist/lib/heap-data/HeapUtils.js +25 -0
- package/dist/logger/LeakClusterLogger.d.ts +40 -0
- package/dist/logger/LeakClusterLogger.d.ts.map +1 -0
- package/dist/logger/LeakClusterLogger.js +228 -0
- package/dist/logger/LeakTraceDetailsLogger.d.ts +19 -0
- package/dist/logger/LeakTraceDetailsLogger.d.ts.map +1 -0
- package/dist/logger/LeakTraceDetailsLogger.js +50 -0
- package/dist/modes/BaseMode.d.ts +30 -0
- package/dist/modes/BaseMode.d.ts.map +1 -0
- package/dist/modes/BaseMode.js +95 -0
- package/dist/modes/InteractionTestMode.d.ts +23 -0
- package/dist/modes/InteractionTestMode.d.ts.map +1 -0
- package/dist/modes/InteractionTestMode.js +46 -0
- package/dist/modes/MeasureMode.d.ts +23 -0
- package/dist/modes/MeasureMode.d.ts.map +1 -0
- package/dist/modes/MeasureMode.js +58 -0
- package/dist/modes/RunningModes.d.ts +15 -0
- package/dist/modes/RunningModes.d.ts.map +1 -0
- package/dist/modes/RunningModes.js +40 -0
- package/dist/paths/TraceFinder.d.ts +31 -0
- package/dist/paths/TraceFinder.d.ts.map +1 -0
- package/dist/paths/TraceFinder.js +537 -0
- package/dist/trace-cluster/ClusterUtils.d.ts +14 -0
- package/dist/trace-cluster/ClusterUtils.d.ts.map +1 -0
- package/dist/trace-cluster/ClusterUtils.js +17 -0
- package/dist/trace-cluster/ClusterUtilsHelper.d.ts +38 -0
- package/dist/trace-cluster/ClusterUtilsHelper.d.ts.map +1 -0
- package/dist/trace-cluster/ClusterUtilsHelper.js +373 -0
- package/dist/trace-cluster/ClusteringHeuristics.d.ts +22 -0
- package/dist/trace-cluster/ClusteringHeuristics.d.ts.map +1 -0
- package/dist/trace-cluster/ClusteringHeuristics.js +23 -0
- package/dist/trace-cluster/EvalutationMetric.d.ts +22 -0
- package/dist/trace-cluster/EvalutationMetric.d.ts.map +1 -0
- package/dist/trace-cluster/EvalutationMetric.js +158 -0
- package/dist/trace-cluster/TraceBucket.d.ts +36 -0
- package/dist/trace-cluster/TraceBucket.d.ts.map +1 -0
- package/dist/trace-cluster/TraceBucket.js +238 -0
- package/dist/trace-cluster/TraceElement.d.ts +71 -0
- package/dist/trace-cluster/TraceElement.d.ts.map +1 -0
- package/dist/trace-cluster/TraceElement.js +182 -0
- package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts +15 -0
- package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts.map +1 -0
- package/dist/trace-cluster/strategies/TraceAsClusterStrategy.js +37 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts +15 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts.map +1 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +60 -0
- package/package.json +60 -0
- package/static/run-meta.json +10 -0
- package/static/visit-order.json +27 -0
|
@@ -0,0 +1,373 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.debugTraceElementSimilarityStats = exports.debugLog = void 0;
|
|
13
|
+
function NON_NULLABLE(v) {
|
|
14
|
+
if (v == null) {
|
|
15
|
+
throw new Error('value must not be null or undefined');
|
|
16
|
+
}
|
|
17
|
+
return v;
|
|
18
|
+
}
|
|
19
|
+
// Set this flag to true when debugging the leak trace clustering algorithm
|
|
20
|
+
// to get more runtime information in terminal.
|
|
21
|
+
const DEBUG = false;
|
|
22
|
+
function debugLog(...args) {
|
|
23
|
+
if (!DEBUG) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (typeof console === 'undefined' || typeof console.log !== 'function') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log(args.join(' |\t'));
|
|
30
|
+
}
|
|
31
|
+
exports.debugLog = debugLog;
|
|
32
|
+
const debugTraceElementSimilarityStats = ({ elementA, elementB, matchedSum, totalSum, }) => {
|
|
33
|
+
const similarityScoreString = `${matchedSum} / ${totalSum}`;
|
|
34
|
+
if (elementA.kind === 'edge') {
|
|
35
|
+
debugLog(elementA.kind, elementA.name_or_index, elementB.name_or_index, similarityScoreString);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
debugLog(elementA.kind, elementA.name, elementB.name, similarityScoreString);
|
|
39
|
+
};
|
|
40
|
+
exports.debugTraceElementSimilarityStats = debugTraceElementSimilarityStats;
|
|
41
|
+
const SIMILAR_TRACE_THRESHOLD = 0.8;
|
|
42
|
+
const REACT_FIBER_EDGE_PREFIX = '__reactFiber$';
|
|
43
|
+
const WINDOW_NODE_PREFIX = 'Window / ';
|
|
44
|
+
const initialize = (heuristics) => {
|
|
45
|
+
const { edgeNameStopWords, nodeNameStopWords, similarWordRegExps, decendentDecayFactors, startingModuleForTraceMatching, } = heuristics;
|
|
46
|
+
function _searchForEdge(t, name) {
|
|
47
|
+
for (let i = 0; i < t.length; ++i) {
|
|
48
|
+
const traceElement = t[i];
|
|
49
|
+
if (traceElement.kind === 'edge' && 'name_or_index' in traceElement) {
|
|
50
|
+
if (name instanceof RegExp) {
|
|
51
|
+
if (name.test(`${traceElement.name_or_index}`)) {
|
|
52
|
+
return i;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (traceElement.name_or_index === name) {
|
|
56
|
+
return i;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return -1;
|
|
61
|
+
}
|
|
62
|
+
function _searchForNode(t, name) {
|
|
63
|
+
for (let i = 0; i < t.length; ++i) {
|
|
64
|
+
const traceElement = t[i];
|
|
65
|
+
if (traceElement.kind === 'node' && 'name' in traceElement) {
|
|
66
|
+
if (name instanceof RegExp) {
|
|
67
|
+
if (name.test(NON_NULLABLE(traceElement.name))) {
|
|
68
|
+
return i;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (traceElement.name === name) {
|
|
72
|
+
return i;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return -1;
|
|
77
|
+
}
|
|
78
|
+
function _searchForMatch(t1, t2, name) {
|
|
79
|
+
let i1 = _searchForEdge(t1, name);
|
|
80
|
+
let i2 = _searchForEdge(t2, name);
|
|
81
|
+
if (i1 >= 0 && i2 >= 0) {
|
|
82
|
+
return [i1, i2];
|
|
83
|
+
}
|
|
84
|
+
i1 = _searchForNode(t1, name);
|
|
85
|
+
i2 = _searchForNode(t2, name);
|
|
86
|
+
if (i1 >= 0 && i2 >= 0) {
|
|
87
|
+
return [i1, i2];
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// the trace similarity calculation starts from modulesMap if
|
|
92
|
+
// both traces have the modulesMap heap object
|
|
93
|
+
function matchTraceStartItem(t1, t2) {
|
|
94
|
+
for (const name of startingModuleForTraceMatching) {
|
|
95
|
+
const ret = _searchForMatch(t1, t2, name);
|
|
96
|
+
if (ret) {
|
|
97
|
+
return ret;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return [0, 0];
|
|
101
|
+
}
|
|
102
|
+
function getNodeStopWordWeight(name) {
|
|
103
|
+
return _getStopWordWeight(nodeNameStopWords, name);
|
|
104
|
+
}
|
|
105
|
+
function getEdgeStopWordWeight(name) {
|
|
106
|
+
return _getStopWordWeight(edgeNameStopWords, name);
|
|
107
|
+
}
|
|
108
|
+
function _getStopWordWeight(stopWordsMap, name) {
|
|
109
|
+
if (stopWordsMap.has(name)) {
|
|
110
|
+
return NON_NULLABLE(stopWordsMap.get(name));
|
|
111
|
+
}
|
|
112
|
+
for (const stopWord of stopWordsMap.keys()) {
|
|
113
|
+
if (!(stopWord instanceof RegExp)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (stopWord.test(`${name}`)) {
|
|
117
|
+
return NON_NULLABLE(stopWordsMap.get(stopWord));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
function getSimilarWordsWeight(name1, name2) {
|
|
123
|
+
const n1 = `${name1}`;
|
|
124
|
+
const n2 = `${name2}`;
|
|
125
|
+
for (const regexp of similarWordRegExps.keys()) {
|
|
126
|
+
if (regexp.test(n1) && regexp.test(n2)) {
|
|
127
|
+
return NON_NULLABLE(similarWordRegExps.get(regexp));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
function getDecendentDecayFactor(elem,
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
134
|
+
_elem2) {
|
|
135
|
+
for (const factors of decendentDecayFactors) {
|
|
136
|
+
if (factors.kind === elem.kind) {
|
|
137
|
+
if ('name' in elem && factors.name === elem.name) {
|
|
138
|
+
return factors.decay;
|
|
139
|
+
}
|
|
140
|
+
if ('name_or_index' in elem && factors.name === elem.name_or_index) {
|
|
141
|
+
return factors.decay;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return 1;
|
|
146
|
+
}
|
|
147
|
+
function isNodeNameSame(name1, name2) {
|
|
148
|
+
const isWindowNode1 = typeof name1 === 'string' && name1.startsWith(WINDOW_NODE_PREFIX);
|
|
149
|
+
const isWindowNode2 = typeof name2 === 'string' && name2.startsWith(WINDOW_NODE_PREFIX);
|
|
150
|
+
if (isWindowNode1 && isWindowNode2) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return name1 === name2;
|
|
154
|
+
}
|
|
155
|
+
function isNameSimilar(name1, name2) {
|
|
156
|
+
const n1 = `${name1}`;
|
|
157
|
+
const n2 = `${name2}`;
|
|
158
|
+
for (const regexp of similarWordRegExps.keys()) {
|
|
159
|
+
if (regexp.test(n1) && regexp.test(n2)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
function isEdgeNameSame(name1, name2) {
|
|
166
|
+
const isFiberEdge1 = typeof name1 === 'string' && name1.startsWith(REACT_FIBER_EDGE_PREFIX);
|
|
167
|
+
const isFiberEdge2 = typeof name2 === 'string' && name2.startsWith(REACT_FIBER_EDGE_PREFIX);
|
|
168
|
+
if (isFiberEdge1 && isFiberEdge2) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
return name1 === name2;
|
|
172
|
+
}
|
|
173
|
+
function representsNumber(value) {
|
|
174
|
+
if (typeof value === 'number') {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (parseInt(value, 10) + '' === value + '') {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
if (parseFloat(value) + '' === value + '') {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
function areBothNodes(elem1, elem2) {
|
|
186
|
+
if (elem1 == null || elem2 == null) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (elem1.kind !== 'node' || elem2.kind !== 'node') {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (elem1.type !== elem2.type) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
function isSameNode(elem1, elem2) {
|
|
198
|
+
if (!areBothNodes(elem1, elem2)) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return isNodeNameSame(NON_NULLABLE(elem1.name), NON_NULLABLE(elem2.name));
|
|
202
|
+
}
|
|
203
|
+
function isSimilarNode(elem1, elem2) {
|
|
204
|
+
if (!areBothNodes(elem1, elem2)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return isNameSimilar(NON_NULLABLE(elem1.name), NON_NULLABLE(elem2.name));
|
|
208
|
+
}
|
|
209
|
+
function isSameEdge(trace1, i1, trace2, i2) {
|
|
210
|
+
const elem1 = trace1[i1];
|
|
211
|
+
const elem2 = trace2[i2];
|
|
212
|
+
if (elem1 == null || elem2 == null) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
if (elem1.kind !== 'edge' || elem2.kind !== 'edge') {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
if (elem1.type !== elem2.type) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
// index properties are considered the same if
|
|
222
|
+
// their referrer node are the same
|
|
223
|
+
if (representsNumber(elem1.name_or_index) &&
|
|
224
|
+
representsNumber(elem2.name_or_index)) {
|
|
225
|
+
const referrer1 = trace1[i1 - 1];
|
|
226
|
+
const referrer2 = trace2[i2 - 1];
|
|
227
|
+
return isSameNode(referrer1, referrer2);
|
|
228
|
+
}
|
|
229
|
+
return isEdgeNameSame(NON_NULLABLE(elem1.name_or_index), NON_NULLABLE(elem2.name_or_index));
|
|
230
|
+
}
|
|
231
|
+
function areBothEdges(elem1, elem2) {
|
|
232
|
+
if (elem1 == null || elem2 == null) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (elem1.kind !== 'edge' || elem2.kind !== 'edge') {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (elem1.type !== elem2.type) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
function isSimilarEdge(trace1, i1, trace2, i2) {
|
|
244
|
+
const elem1 = trace1[i1];
|
|
245
|
+
const elem2 = trace2[i2];
|
|
246
|
+
if (!areBothEdges(elem1, elem2)) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
// index properties are considered the same if
|
|
250
|
+
// their referrer node are the same
|
|
251
|
+
if (representsNumber(elem1.name_or_index) &&
|
|
252
|
+
representsNumber(elem2.name_or_index)) {
|
|
253
|
+
const referrer1 = trace1[i1 - 1];
|
|
254
|
+
const referrer2 = trace2[i2 - 1];
|
|
255
|
+
return (isSameNode(referrer1, referrer2) || isSimilarNode(referrer1, referrer2));
|
|
256
|
+
}
|
|
257
|
+
return isNameSimilar(NON_NULLABLE(elem1.name_or_index), NON_NULLABLE(elem2.name_or_index));
|
|
258
|
+
}
|
|
259
|
+
function isSameOrSimilarElement(trace1, i1, trace2, i2) {
|
|
260
|
+
const elem1 = trace1[i1];
|
|
261
|
+
const elem2 = trace2[i2];
|
|
262
|
+
if (!elem1 || !elem2) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
if (elem1.kind !== elem2.kind) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
if (elem1.kind === 'edge') {
|
|
269
|
+
if (isSameEdge(trace1, i1, trace2, i2)) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
return isSimilarEdge(trace1, i1, trace2, i2);
|
|
273
|
+
}
|
|
274
|
+
if (isSameNode(elem1, elem2)) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
return isSimilarNode(elem1, elem2);
|
|
278
|
+
}
|
|
279
|
+
function getDecayFactor(trace1, i1, trace2, i2) {
|
|
280
|
+
const elem1 = trace1[i1];
|
|
281
|
+
const elem2 = trace2[i2];
|
|
282
|
+
if (!elem1 || !elem2) {
|
|
283
|
+
return 1;
|
|
284
|
+
}
|
|
285
|
+
if (elem1.kind !== elem2.kind) {
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
return getDecendentDecayFactor(elem1, elem2);
|
|
289
|
+
}
|
|
290
|
+
function getElementPairWeight(trace1, i1, trace2, i2) {
|
|
291
|
+
const elem1 = trace1[i1];
|
|
292
|
+
const elem2 = trace2[i2];
|
|
293
|
+
if (!elem1 || !elem2) {
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
if (elem1.kind !== elem2.kind) {
|
|
297
|
+
return 1;
|
|
298
|
+
}
|
|
299
|
+
if (elem1.kind === 'edge') {
|
|
300
|
+
if (!isSameEdge(trace1, i1, trace2, i2)) {
|
|
301
|
+
return getSimilarWordsWeight(NON_NULLABLE(elem1.name_or_index), NON_NULLABLE(elem2.name_or_index));
|
|
302
|
+
}
|
|
303
|
+
const edgeName = NON_NULLABLE(elem1.name_or_index);
|
|
304
|
+
return getEdgeStopWordWeight(edgeName);
|
|
305
|
+
}
|
|
306
|
+
if (!isSameNode(elem1, elem2)) {
|
|
307
|
+
return getSimilarWordsWeight(NON_NULLABLE(elem1.name), NON_NULLABLE(elem2.name));
|
|
308
|
+
}
|
|
309
|
+
const nodeName = NON_NULLABLE(elem1.name);
|
|
310
|
+
return getNodeStopWordWeight(nodeName);
|
|
311
|
+
}
|
|
312
|
+
function getSimilarityByTraceElementList(t1, t2) {
|
|
313
|
+
// find first starting item that matches
|
|
314
|
+
let [i1, i2] = matchTraceStartItem(t1, t2);
|
|
315
|
+
const l1 = t1.length;
|
|
316
|
+
const l2 = t2.length;
|
|
317
|
+
const d1 = l1 - i1;
|
|
318
|
+
const d2 = l2 - i2;
|
|
319
|
+
let delta1 = 0.9 / d1;
|
|
320
|
+
let delta2 = 0.9 / d2;
|
|
321
|
+
let w1 = 1;
|
|
322
|
+
let w2 = 1;
|
|
323
|
+
const l = Math.max(l1, l2);
|
|
324
|
+
let matchedSum = 0;
|
|
325
|
+
let totalSum = 0;
|
|
326
|
+
while (i1 < l || i2 < l) {
|
|
327
|
+
const trace1 = t1;
|
|
328
|
+
const trace2 = t2;
|
|
329
|
+
const pw = getElementPairWeight(trace1, i1, trace2, i2);
|
|
330
|
+
if (isSameOrSimilarElement(trace1, i1, trace2, i2)) {
|
|
331
|
+
(0, exports.debugTraceElementSimilarityStats)({
|
|
332
|
+
elementA: trace1[i1],
|
|
333
|
+
elementB: trace2[i2],
|
|
334
|
+
matchedSum,
|
|
335
|
+
totalSum,
|
|
336
|
+
});
|
|
337
|
+
matchedSum += (w1 + w2) * pw;
|
|
338
|
+
}
|
|
339
|
+
totalSum += (w1 + w2) * pw;
|
|
340
|
+
const decayFactor = 0.99 * getDecayFactor(trace1, i1, trace2, i2);
|
|
341
|
+
w1 *= decayFactor;
|
|
342
|
+
w2 *= decayFactor;
|
|
343
|
+
delta1 *= decayFactor;
|
|
344
|
+
delta2 *= decayFactor;
|
|
345
|
+
if (w1 - delta1 >= 0) {
|
|
346
|
+
w1 -= delta1;
|
|
347
|
+
}
|
|
348
|
+
if (w2 - delta2 >= 0) {
|
|
349
|
+
w2 -= delta2;
|
|
350
|
+
}
|
|
351
|
+
++i1;
|
|
352
|
+
++i2;
|
|
353
|
+
if (i1 >= l1 || i2 >= l2) {
|
|
354
|
+
const sim = matchedSum / (totalSum + 0.0001);
|
|
355
|
+
if (sim > 0.9) {
|
|
356
|
+
return sim;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return matchedSum / (totalSum + 0.0001);
|
|
361
|
+
}
|
|
362
|
+
function isSimilarTrace(t1, t2) {
|
|
363
|
+
const similarity = getSimilarityByTraceElementList(t1, t2);
|
|
364
|
+
debugLog(similarity);
|
|
365
|
+
return similarity >= SIMILAR_TRACE_THRESHOLD;
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
isSimilarTrace,
|
|
369
|
+
};
|
|
370
|
+
};
|
|
371
|
+
exports.default = {
|
|
372
|
+
initialize,
|
|
373
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
edgeNameStopWords: Map<any, any>;
|
|
12
|
+
nodeNameStopWords: Map<any, any>;
|
|
13
|
+
similarWordRegExps: Map<any, any>;
|
|
14
|
+
decendentDecayFactors: {
|
|
15
|
+
kind: string;
|
|
16
|
+
name: string;
|
|
17
|
+
decay: number;
|
|
18
|
+
}[];
|
|
19
|
+
startingModuleForTraceMatching: (string | RegExp)[];
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|
|
22
|
+
//# sourceMappingURL=ClusteringHeuristics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClusteringHeuristics.d.ts","sourceRoot":"","sources":["../../src/trace-cluster/ClusteringHeuristics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;;;;;;cAUqC,MAAM;cAAQ,MAAM;eAAS,MAAM;;;;AAL3E,wBAUE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 InternalValueSetter_1 = require("../lib/InternalValueSetter");
|
|
16
|
+
const Constant_1 = __importDefault(require("../lib/Constant"));
|
|
17
|
+
exports.default = (0, InternalValueSetter_1.setInternalValue)({
|
|
18
|
+
edgeNameStopWords: new Map(),
|
|
19
|
+
nodeNameStopWords: new Map(),
|
|
20
|
+
similarWordRegExps: new Map(),
|
|
21
|
+
decendentDecayFactors: [],
|
|
22
|
+
startingModuleForTraceMatching: [],
|
|
23
|
+
}, __filename, Constant_1.default.internalDir);
|
|
@@ -0,0 +1,22 @@
|
|
|
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 type ClusteredLeakTraces = Record<string, string>;
|
|
11
|
+
declare const _default: {
|
|
12
|
+
evaluateAdjustRandScore: (trueTraceIdLabelMap: ClusteredLeakTraces, predictedTraceIdLabelMap: ClusteredLeakTraces) => number;
|
|
13
|
+
vMeasure: (trueLabels: number[], predictedLabels: number[], beta?: number) => number;
|
|
14
|
+
completeness: (trueLabels: number[], predictedLabels: number[]) => number;
|
|
15
|
+
homogeneity: (trueLabels?: number[], predictedLabels?: number[]) => number;
|
|
16
|
+
getRepresentativeTraceMetrics: (trueTraceIdLabelMap: ClusteredLeakTraces, predictedTraceIdLabelMap: ClusteredLeakTraces) => {
|
|
17
|
+
omitted: number;
|
|
18
|
+
duplicated: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|
|
22
|
+
//# sourceMappingURL=EvalutationMetric.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvalutationMetric.d.ts","sourceRoot":"","sources":["../../src/trace-cluster/EvalutationMetric.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqHH,aAAK,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;;;;;;;;;AA+ElD,wBAME"}
|
|
@@ -0,0 +1,158 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
////////////////////////
|
|
13
|
+
const zip = (arr1, arr2) => arr1.reduce((ret, el, idx) => {
|
|
14
|
+
ret.push([el, arr2[idx]]);
|
|
15
|
+
return ret;
|
|
16
|
+
}, []);
|
|
17
|
+
/*
|
|
18
|
+
countUnique returns array of count of unique items for given array of numbers.
|
|
19
|
+
For example, countUnique([1, 2, 3, 3, 2]) returns [['1', '2', '3'], [1, 2, 2]].
|
|
20
|
+
countUnique([[1, 2], [2, 3], [2, 3]]) returns [['1,2', '2,3'], [1, 2]].
|
|
21
|
+
*/
|
|
22
|
+
const countUnique = (arr = []) => {
|
|
23
|
+
const key = (el) => {
|
|
24
|
+
if (Array.isArray(el)) {
|
|
25
|
+
return el.join(',');
|
|
26
|
+
}
|
|
27
|
+
return `${el}`;
|
|
28
|
+
};
|
|
29
|
+
const map = new Map();
|
|
30
|
+
arr.forEach(el => {
|
|
31
|
+
const k = key(el);
|
|
32
|
+
if (map.has(key(el))) {
|
|
33
|
+
map.set(k, map.get(k) + 1);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
map.set(k, 1);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return [Array.from(map.keys()), Array.from(map.values())];
|
|
40
|
+
};
|
|
41
|
+
const entropy = (labels = []) => {
|
|
42
|
+
const nLabels = labels.length;
|
|
43
|
+
if (nLabels <= 1)
|
|
44
|
+
return 0;
|
|
45
|
+
const [, counts] = countUnique(labels);
|
|
46
|
+
const probabilities = counts.map(c => c / nLabels);
|
|
47
|
+
const nClassess = probabilities.filter(el => el !== 0).length;
|
|
48
|
+
if (nClassess <= 1)
|
|
49
|
+
return 0;
|
|
50
|
+
let ent = 0.0;
|
|
51
|
+
for (const prob of probabilities) {
|
|
52
|
+
ent -= prob * Math.log(prob);
|
|
53
|
+
}
|
|
54
|
+
return ent;
|
|
55
|
+
};
|
|
56
|
+
const homogeneity = (trueLabels = [], predictedLabels = []) => {
|
|
57
|
+
const trueLabelEntropy = entropy(trueLabels);
|
|
58
|
+
if (trueLabelEntropy == 0)
|
|
59
|
+
return 1;
|
|
60
|
+
// Combine two lists into a list of tuples which will be used for conditional Entropy.
|
|
61
|
+
const joined = zip(trueLabels, predictedLabels);
|
|
62
|
+
const joinedEntropy = entropy(joined);
|
|
63
|
+
const predictedLabelEntropy = entropy(predictedLabels);
|
|
64
|
+
// Conditional Entropy (Chain rule) - https://en.wikipedia.org/wiki/Conditional_entropy
|
|
65
|
+
// H(X|Y) = H(X,Y) - H(X)
|
|
66
|
+
const conditionalEntropy = joinedEntropy - predictedLabelEntropy;
|
|
67
|
+
const h = 1 - conditionalEntropy / trueLabelEntropy;
|
|
68
|
+
return h;
|
|
69
|
+
};
|
|
70
|
+
const completeness = (trueLabels, predictedLabels) => {
|
|
71
|
+
const predictedLabelEntropy = entropy(predictedLabels);
|
|
72
|
+
if (predictedLabelEntropy == 0)
|
|
73
|
+
return 1;
|
|
74
|
+
// # Combine two lists into a list of tuples.
|
|
75
|
+
// # For example, classes [1, 3] and clusters [20, 30] become [(1, 20), (3, 30)].
|
|
76
|
+
const joined = zip(trueLabels, predictedLabels);
|
|
77
|
+
const joinedEntropy = entropy(joined);
|
|
78
|
+
const trueLabelEntropy = entropy(trueLabels);
|
|
79
|
+
const conditionalEntropy = joinedEntropy - trueLabelEntropy;
|
|
80
|
+
const c = 1 - conditionalEntropy / predictedLabelEntropy;
|
|
81
|
+
return c;
|
|
82
|
+
};
|
|
83
|
+
/*
|
|
84
|
+
Ratio of weight attributed to homogeneity vs completeness.
|
|
85
|
+
If beta is greater than 1, completeness is weighted more strongly in the calculation.
|
|
86
|
+
If beta is less than 1, homogeneity is weighted more strongly.
|
|
87
|
+
*/
|
|
88
|
+
const vMeasure = (trueLabels, predictedLabels, beta = 1) => {
|
|
89
|
+
const h = homogeneity(trueLabels, predictedLabels);
|
|
90
|
+
const c = completeness(trueLabels, predictedLabels);
|
|
91
|
+
const v = ((1 + beta) * h * c) / (beta * h + c);
|
|
92
|
+
return v;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The dataset the function takes as argument is map of key:value pairs.
|
|
96
|
+
* Key is trace_id and value is the cluster_id, which is another trace_id
|
|
97
|
+
* that represents the cluster it, belongs to
|
|
98
|
+
* @param trueTraceIdLabelMap Baseline
|
|
99
|
+
* @param predictedTraceIdLabelMap Predicted outcome from some clustering
|
|
100
|
+
* @returns numeric evaluation ([-1:1] 1 being identical) of how close the traces are clustered using Adjust Rand Index formula.
|
|
101
|
+
*/
|
|
102
|
+
const evaluateAdjustRandScore = (trueTraceIdLabelMap, predictedTraceIdLabelMap) => {
|
|
103
|
+
let truePositive = 0;
|
|
104
|
+
let falsePositive = 0;
|
|
105
|
+
let falseNegative = 0;
|
|
106
|
+
let trueNegative = 0;
|
|
107
|
+
const predictedTraceIDs = Object.keys(predictedTraceIdLabelMap);
|
|
108
|
+
for (let i = 0; i < predictedTraceIDs.length - 1; i++) {
|
|
109
|
+
for (let j = i + 1; j < predictedTraceIDs.length - 1; j++) {
|
|
110
|
+
const [traceIdA, traceIdB] = [predictedTraceIDs[i], predictedTraceIDs[j]];
|
|
111
|
+
const isSameInPredicted = predictedTraceIdLabelMap[traceIdA] ===
|
|
112
|
+
predictedTraceIdLabelMap[traceIdB];
|
|
113
|
+
const isSameInTrue = trueTraceIdLabelMap[traceIdA] === trueTraceIdLabelMap[traceIdB];
|
|
114
|
+
if (isSameInPredicted && isSameInTrue) {
|
|
115
|
+
truePositive++;
|
|
116
|
+
}
|
|
117
|
+
else if (!isSameInPredicted && isSameInTrue) {
|
|
118
|
+
falseNegative++;
|
|
119
|
+
}
|
|
120
|
+
else if (isSameInPredicted && !isSameInTrue) {
|
|
121
|
+
falsePositive++;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
trueNegative++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const n = predictedTraceIDs.length;
|
|
129
|
+
// Total number of pairs
|
|
130
|
+
const totalNumberOfPairs = (n * (n - 1)) / 2;
|
|
131
|
+
// The formula is from https://arxiv.org/pdf/2002.03677.pdf
|
|
132
|
+
const ARI = (totalNumberOfPairs * (truePositive + trueNegative) -
|
|
133
|
+
((truePositive + falsePositive) * (truePositive + falseNegative) +
|
|
134
|
+
(falseNegative + trueNegative) * (falsePositive + trueNegative))) /
|
|
135
|
+
(Math.pow(totalNumberOfPairs, 2) -
|
|
136
|
+
((truePositive + falsePositive) * (truePositive + falseNegative) +
|
|
137
|
+
(falseNegative + trueNegative) * (falsePositive + trueNegative)));
|
|
138
|
+
return ARI;
|
|
139
|
+
};
|
|
140
|
+
const getRepresentativeTraceMetrics = (trueTraceIdLabelMap, predictedTraceIdLabelMap) => {
|
|
141
|
+
const [uniqTrueLabels] = countUnique(Object.values(trueTraceIdLabelMap));
|
|
142
|
+
const nTrueLabels = uniqTrueLabels.length;
|
|
143
|
+
const [uniqPredLabels] = countUnique(Object.values(predictedTraceIdLabelMap));
|
|
144
|
+
const predLabelToTrueLabel = uniqPredLabels.map(representativeTraceId => trueTraceIdLabelMap[representativeTraceId]);
|
|
145
|
+
const [, countsOfTrueLabelsForPredicted] = countUnique(predLabelToTrueLabel);
|
|
146
|
+
const nClustersThatShouldMerge = countsOfTrueLabelsForPredicted.filter(count => count > 1).length;
|
|
147
|
+
return {
|
|
148
|
+
omitted: (nTrueLabels - countsOfTrueLabelsForPredicted.length) / nTrueLabels,
|
|
149
|
+
duplicated: nClustersThatShouldMerge / nTrueLabels,
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
exports.default = {
|
|
153
|
+
evaluateAdjustRandScore,
|
|
154
|
+
vMeasure,
|
|
155
|
+
completeness,
|
|
156
|
+
homogeneity,
|
|
157
|
+
getRepresentativeTraceMetrics,
|
|
158
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
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 { IHeapNode, IHeapSnapshot, LeakTrace, LeakTracePathItem, Optional, TraceCluster, TraceClusterDiff, IClusterStrategy } from '../lib/Types';
|
|
11
|
+
import type { NormalizedTraceElement } from './TraceElement';
|
|
12
|
+
declare type AggregateNodeCb = (ids: Set<number>, snapshot: IHeapSnapshot, checkCb: (node: IHeapNode) => boolean, calculateCb: (node: IHeapNode) => number) => number;
|
|
13
|
+
export default class NormalizedTrace {
|
|
14
|
+
private trace;
|
|
15
|
+
private traceSummary;
|
|
16
|
+
constructor(p?: LeakTracePathItem | null, snapshot?: IHeapSnapshot | null);
|
|
17
|
+
static pathToTrace(p: LeakTracePathItem, options?: {
|
|
18
|
+
untilFirstDetachedDOMElem?: boolean;
|
|
19
|
+
}): NormalizedTraceElement[];
|
|
20
|
+
static traceToPath(trace: Optional<LeakTrace>): LeakTracePathItem;
|
|
21
|
+
getTraceSummary(): string;
|
|
22
|
+
static addLeakedNodeToCluster(cluster: TraceCluster, path: LeakTracePathItem): void;
|
|
23
|
+
static calculateClusterRetainedSize(cluster: TraceCluster, snapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb): number;
|
|
24
|
+
static samplePaths(paths: LeakTracePathItem[]): LeakTracePathItem[];
|
|
25
|
+
private static diffTraces;
|
|
26
|
+
static diffClusters(newClusters: TraceCluster[], existingClusters: TraceCluster[]): TraceClusterDiff;
|
|
27
|
+
static clusterLeakTraces(leakTraces: LeakTrace[]): Record<string, string>;
|
|
28
|
+
static clusterPaths(paths: LeakTracePathItem[], snapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb, option?: {
|
|
29
|
+
strategy?: IClusterStrategy;
|
|
30
|
+
}): TraceCluster[];
|
|
31
|
+
static generateUnClassifiedClusters(paths: LeakTracePathItem[], snapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb): TraceCluster[];
|
|
32
|
+
static loadCluster(): NormalizedTrace[];
|
|
33
|
+
static saveCluster(clusters: NormalizedTrace[]): void;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=TraceBucket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TraceBucket.d.ts","sourceRoot":"","sources":["../../src/trace-cluster/TraceBucket.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,iBAAiB,EAEjB,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gBAAgB,CAAC;AAW3D,aAAK,eAAe,GAAG,CACrB,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAChB,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,EACrC,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,KACrC,MAAM,CAAC;AAIZ,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,YAAY,CAAS;gBAE3B,CAAC,GAAE,iBAAiB,GAAG,IAAW,EAClC,QAAQ,GAAE,aAAa,GAAG,IAAW;IAgBvC,MAAM,CAAC,WAAW,CAChB,CAAC,EAAE,iBAAiB,EACpB,OAAO,GAAE;QACP,yBAAyB,CAAC,EAAE,OAAO,CAAC;KAChC,GACL,sBAAsB,EAAE;IA8B3B,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,iBAAiB;IAkBjE,eAAe,IAAI,MAAM;IAIzB,MAAM,CAAC,sBAAsB,CAC3B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,iBAAiB,GACtB,IAAI;IAUP,MAAM,CAAC,4BAA4B,CACjC,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,aAAa,EACvB,yBAAyB,EAAE,eAAe,GACzC,MAAM;IAYT,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,EAAE;IAmBnE,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,MAAM,CAAC,YAAY,CACjB,WAAW,EAAE,YAAY,EAAE,EAC3B,gBAAgB,EAAE,YAAY,EAAE,GAC/B,gBAAgB;IAwCnB,MAAM,CAAC,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAoBzE,MAAM,CAAC,YAAY,CACjB,KAAK,EAAE,iBAAiB,EAAE,EAC1B,QAAQ,EAAE,aAAa,EACvB,yBAAyB,EAAE,eAAe,EAC1C,MAAM,GAAE;QAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAA;KAAM,GACzC,YAAY,EAAE;IAgDjB,MAAM,CAAC,4BAA4B,CACjC,KAAK,EAAE,iBAAiB,EAAE,EAC1B,QAAQ,EAAE,aAAa,EACvB,yBAAyB,EAAE,eAAe,GACzC,YAAY,EAAE;IAMjB,MAAM,CAAC,WAAW,IAAI,eAAe,EAAE;IAevC,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI;CAKtD"}
|