@memlab/core 1.1.20 → 1.1.21
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/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/lib/Config.d.ts +2 -0
- package/dist/lib/Config.js +9 -0
- package/dist/lib/FileManager.d.ts +3 -0
- package/dist/lib/FileManager.js +10 -1
- package/dist/lib/HeapAnalyzer.d.ts +5 -1
- package/dist/lib/HeapAnalyzer.js +33 -21
- package/dist/lib/RunInfoUtils.d.ts +39 -0
- package/dist/lib/RunInfoUtils.js +86 -0
- package/dist/lib/Types.d.ts +7 -2
- package/dist/lib/Utils.d.ts +25 -19
- package/dist/lib/Utils.js +90 -148
- package/dist/lib/charts/MemoryBarChart.d.ts +1 -0
- package/dist/lib/charts/MemoryBarChart.js +18 -3
- package/dist/lib/trace-filters/BaseTraceFilter.rule.d.ts +29 -0
- package/dist/lib/trace-filters/BaseTraceFilter.rule.js +22 -0
- package/dist/lib/trace-filters/LeakTraceFilter.d.ts +20 -0
- package/dist/lib/trace-filters/LeakTraceFilter.js +37 -0
- package/dist/lib/trace-filters/TraceFilterRuleList.d.ts +13 -0
- package/dist/lib/trace-filters/TraceFilterRuleList.js +33 -0
- package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.js +55 -0
- package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.js +41 -0
- package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.js +29 -0
- package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.js +57 -0
- package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.js +62 -0
- package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.js +44 -0
- package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.js +49 -0
- package/dist/logger/LeakClusterLogger.js +1 -0
- package/dist/paths/TraceFinder.js +16 -2
- package/dist/trace-cluster/TraceBucket.d.ts +1 -1
- package/dist/trace-cluster/TraceBucket.js +7 -3
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +1 -0
- package/package.json +1 -1
package/dist/lib/Utils.js
CHANGED
|
@@ -53,7 +53,6 @@ const Config_1 = __importStar(require("./Config"));
|
|
|
53
53
|
const Console_1 = __importDefault(require("./Console"));
|
|
54
54
|
const Constant_1 = __importDefault(require("./Constant"));
|
|
55
55
|
const HeapParser_1 = __importDefault(require("./HeapParser"));
|
|
56
|
-
const BrowserInfo_1 = __importDefault(require("./BrowserInfo"));
|
|
57
56
|
const memCache = Object.create(null);
|
|
58
57
|
const FileManager_1 = __importDefault(require("./FileManager"));
|
|
59
58
|
const __1 = require("..");
|
|
@@ -133,6 +132,21 @@ function isFiberNode(node) {
|
|
|
133
132
|
function isDetachedFiberNode(node) {
|
|
134
133
|
return isFiberNode(node) && isDetached(node);
|
|
135
134
|
}
|
|
135
|
+
// return true if this node is InternalNode (native)
|
|
136
|
+
function isDOMInternalNode(node) {
|
|
137
|
+
if (node == null) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return (node.type === 'native' &&
|
|
141
|
+
(node.name === 'InternalNode' || node.name === 'Detached InternalNode'));
|
|
142
|
+
}
|
|
143
|
+
// return true the the nodee is a global handles node
|
|
144
|
+
function isGlobalHandlesNode(node) {
|
|
145
|
+
if (node == null) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return node.name === '(Global handles)' && node.type === 'synthetic';
|
|
149
|
+
}
|
|
136
150
|
// this function returns a more general sense of DOM nodes. Specifically,
|
|
137
151
|
// any detached DOM nodes (e.g., HTMLXXElement, IntersectionObserver etc.)
|
|
138
152
|
// that are not internal nodes.
|
|
@@ -211,18 +225,51 @@ function isPendingActivityNode(node) {
|
|
|
211
225
|
if (!node || !node.name) {
|
|
212
226
|
return false;
|
|
213
227
|
}
|
|
214
|
-
|
|
228
|
+
if (node.type !== 'synthetic' && node.type !== 'native') {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
return node.name === 'Pending activities';
|
|
215
232
|
}
|
|
233
|
+
const htmlElementRegex = /^HTML.*Element$/;
|
|
234
|
+
const svgElementRegex = /^SVG.*Element$/;
|
|
235
|
+
const htmlCollectionRegex = /^HTML.*Collection$/;
|
|
236
|
+
const cssElementRegex = /^CSS/;
|
|
237
|
+
const styleSheetRegex = /StyleSheet/;
|
|
238
|
+
// special DOM element names that are not
|
|
239
|
+
// included in the previous regex definitions
|
|
240
|
+
const domElementSpecialNames = new Set([
|
|
241
|
+
'DOMTokenList',
|
|
242
|
+
'HTMLDocument',
|
|
243
|
+
'InternalNode',
|
|
244
|
+
'Text',
|
|
245
|
+
'XMLDocument',
|
|
246
|
+
]);
|
|
216
247
|
// check the node against a curated list of known HTML Elements
|
|
217
248
|
// the list may be incomplete
|
|
218
249
|
function isDOMNodeIncomplete(node) {
|
|
250
|
+
if (node.type !== 'native') {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
219
253
|
let name = node.name;
|
|
220
|
-
const pattern = /^HTML.*Element$/;
|
|
221
254
|
const detachedPrefix = 'Detached ';
|
|
222
255
|
if (name.startsWith(detachedPrefix)) {
|
|
223
256
|
name = name.substring(detachedPrefix.length);
|
|
224
257
|
}
|
|
225
|
-
return
|
|
258
|
+
return (htmlElementRegex.test(name) ||
|
|
259
|
+
svgElementRegex.test(name) ||
|
|
260
|
+
cssElementRegex.test(name) ||
|
|
261
|
+
styleSheetRegex.test(name) ||
|
|
262
|
+
htmlCollectionRegex.test(name) ||
|
|
263
|
+
domElementSpecialNames.has(name));
|
|
264
|
+
}
|
|
265
|
+
function isXMLDocumentNode(node) {
|
|
266
|
+
return node.type === 'native' && node.name === 'XMLDocument';
|
|
267
|
+
}
|
|
268
|
+
function isHTMLDocumentNode(node) {
|
|
269
|
+
return node.type === 'native' && node.name === 'HTMLDocument';
|
|
270
|
+
}
|
|
271
|
+
function isDOMTextNode(node) {
|
|
272
|
+
return node.type === 'native' && node.name === 'Text';
|
|
226
273
|
}
|
|
227
274
|
function isRootNode(node, opt = {}) {
|
|
228
275
|
if (!node) {
|
|
@@ -471,7 +518,9 @@ function applyToNodes(idSet, snapshot, cb, options = {}) {
|
|
|
471
518
|
for (const id of ids) {
|
|
472
519
|
const node = snapshot.getNodeById(id);
|
|
473
520
|
if (!node) {
|
|
474
|
-
|
|
521
|
+
if (Config_1.default.verbose) {
|
|
522
|
+
Console_1.default.warning(`node @${id} is not found`);
|
|
523
|
+
}
|
|
475
524
|
return;
|
|
476
525
|
}
|
|
477
526
|
cb(node, snapshot);
|
|
@@ -831,27 +880,6 @@ function hasOnlyWeakReferrers(node) {
|
|
|
831
880
|
(edge) => edge.type !== 'weak' && edge.type !== 'shortcut');
|
|
832
881
|
return referrer == null;
|
|
833
882
|
}
|
|
834
|
-
function getRunMetaFilePath() {
|
|
835
|
-
return Config_1.default.useExternalSnapshot
|
|
836
|
-
? Config_1.default.externalRunMetaFile
|
|
837
|
-
: Config_1.default.runMetaFile;
|
|
838
|
-
}
|
|
839
|
-
function loadRunMetaInfo(metaFile = undefined) {
|
|
840
|
-
const file = metaFile || getRunMetaFilePath();
|
|
841
|
-
try {
|
|
842
|
-
const content = fs_1.default.readFileSync(file, 'UTF-8');
|
|
843
|
-
return JSON.parse(content);
|
|
844
|
-
}
|
|
845
|
-
catch (_) {
|
|
846
|
-
throw haltOrThrow('Run info missing. Please make sure `memlab run` is complete.');
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
function loadTargetInfoFromRunMeta() {
|
|
850
|
-
const meta = loadRunMetaInfo();
|
|
851
|
-
Config_1.default.targetApp = meta.app;
|
|
852
|
-
Config_1.default.targetTab = meta.interaction;
|
|
853
|
-
BrowserInfo_1.default.load(meta.browserInfo);
|
|
854
|
-
}
|
|
855
883
|
function getSnapshotSequenceFilePath() {
|
|
856
884
|
if (!Config_1.default.useExternalSnapshot) {
|
|
857
885
|
// load the snapshot sequence meta file from the default location
|
|
@@ -870,7 +898,9 @@ function getSnapshotSequenceFilePath() {
|
|
|
870
898
|
// this should be called only after exploration
|
|
871
899
|
function loadTabsOrder(metaFile = undefined) {
|
|
872
900
|
try {
|
|
873
|
-
const file = metaFile
|
|
901
|
+
const file = metaFile != null && fs_1.default.existsSync(metaFile)
|
|
902
|
+
? metaFile
|
|
903
|
+
: getSnapshotSequenceFilePath();
|
|
874
904
|
const content = fs_1.default.readFileSync(file, 'UTF-8');
|
|
875
905
|
return JSON.parse(content);
|
|
876
906
|
}
|
|
@@ -878,31 +908,6 @@ function loadTabsOrder(metaFile = undefined) {
|
|
|
878
908
|
throw haltOrThrow('snapshot meta data invalid or missing');
|
|
879
909
|
}
|
|
880
910
|
}
|
|
881
|
-
// if true the leak trace is will be reported
|
|
882
|
-
function isInterestingPath(p) {
|
|
883
|
-
// do not filter paths when analyzing Hermes snapshots
|
|
884
|
-
if (Config_1.default.jsEngine === 'hermes') {
|
|
885
|
-
return true;
|
|
886
|
-
}
|
|
887
|
-
// if the path has pattern: Window -> [InternalNode]+ -> DetachedElement
|
|
888
|
-
if (Config_1.default.hideBrowserLeak && internalNodeRetainsDetachedElement(p)) {
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
// if the path has pattern: ShadowRoot -> DetachedElement
|
|
892
|
-
if (Config_1.default.hideBrowserLeak && shadowRootRetainsDetachedElement(p)) {
|
|
893
|
-
return false;
|
|
894
|
-
}
|
|
895
|
-
// if the path has pattern: StyleEngine -> InternalNode -> DetachedElement
|
|
896
|
-
if (Config_1.default.hideBrowserLeak && styleEngineRetainsDetachedElement(p)) {
|
|
897
|
-
return false;
|
|
898
|
-
}
|
|
899
|
-
// if the path has pattern: Pending activitiies -> DetachedElement
|
|
900
|
-
if (Config_1.default.hideBrowserLeak &&
|
|
901
|
-
pendingActivitiesRetainsDetachedElementChain(p)) {
|
|
902
|
-
return false;
|
|
903
|
-
}
|
|
904
|
-
return true;
|
|
905
|
-
}
|
|
906
911
|
// return true if the heap node represents JS object or closure
|
|
907
912
|
function isObjectNode(node) {
|
|
908
913
|
if (isPlainJSObjectNode(node)) {
|
|
@@ -920,89 +925,6 @@ function isPlainJSObjectNode(node) {
|
|
|
920
925
|
}
|
|
921
926
|
return node.name === 'Object';
|
|
922
927
|
}
|
|
923
|
-
// check if the path has pattern:
|
|
924
|
-
// Window -> [InternalNode | Text]+ -> DetachedElement
|
|
925
|
-
function internalNodeRetainsDetachedElement(path) {
|
|
926
|
-
var _a, _b;
|
|
927
|
-
if (!path) {
|
|
928
|
-
return false;
|
|
929
|
-
}
|
|
930
|
-
let p = path;
|
|
931
|
-
// GC root is not Window
|
|
932
|
-
if (!p.node || !p.node.name.startsWith('Window')) {
|
|
933
|
-
return false;
|
|
934
|
-
}
|
|
935
|
-
p = p.next;
|
|
936
|
-
// Window is not poining to InternalNode
|
|
937
|
-
if (!p || !p.node || p.node.name !== 'InternalNode') {
|
|
938
|
-
return false;
|
|
939
|
-
}
|
|
940
|
-
// skip the rest InternalNode
|
|
941
|
-
while (((_a = p.node) === null || _a === void 0 ? void 0 : _a.name) === 'InternalNode' || ((_b = p.node) === null || _b === void 0 ? void 0 : _b.name) === 'Text') {
|
|
942
|
-
p = p.next;
|
|
943
|
-
if (!p) {
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
// check if the node is a detached element
|
|
948
|
-
return p && isDetachedDOMNode(p.node);
|
|
949
|
-
}
|
|
950
|
-
// check if the path has pattern: ShadowRoot -> DetachedElement
|
|
951
|
-
function shadowRootRetainsDetachedElement(path) {
|
|
952
|
-
let p = path;
|
|
953
|
-
// find the ShadowRoot
|
|
954
|
-
while (p && p.node && p.node.name !== 'ShadowRoot') {
|
|
955
|
-
p = p.next;
|
|
956
|
-
if (!p) {
|
|
957
|
-
return false;
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
p = p.next;
|
|
961
|
-
// check if the node is a detached element
|
|
962
|
-
return !!p && isDetachedDOMNode(p.node);
|
|
963
|
-
}
|
|
964
|
-
// check if the path has pattern: StyleEngine -> InternalNode -> DetachedElement
|
|
965
|
-
function styleEngineRetainsDetachedElement(path) {
|
|
966
|
-
let p = path;
|
|
967
|
-
// find the StyleEngine
|
|
968
|
-
while (p && p.node && p.node.name !== 'StyleEngine') {
|
|
969
|
-
p = p.next;
|
|
970
|
-
if (!p) {
|
|
971
|
-
return false;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
p = p.next;
|
|
975
|
-
// StyleEngine is not poining to InternalNode
|
|
976
|
-
if (!p || !p.node || p.node.name !== 'InternalNode') {
|
|
977
|
-
return false;
|
|
978
|
-
}
|
|
979
|
-
p = p.next;
|
|
980
|
-
// check if the InternalNode is pointing to a detached element
|
|
981
|
-
return !!p && isDetachedDOMNode(p.node);
|
|
982
|
-
}
|
|
983
|
-
function pendingActivitiesRetainsDetachedElementChain(path) {
|
|
984
|
-
let p = path;
|
|
985
|
-
// find the Pending activities
|
|
986
|
-
while (p && p.node && !isPendingActivityNode(p.node)) {
|
|
987
|
-
p = p.next;
|
|
988
|
-
if (!p) {
|
|
989
|
-
return false;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
p = p.next;
|
|
993
|
-
if (!p || !p.node) {
|
|
994
|
-
return false;
|
|
995
|
-
}
|
|
996
|
-
// all the following reference chain is detached DOM elements
|
|
997
|
-
// pointing to other detached DOM elements
|
|
998
|
-
while (p && p.node) {
|
|
999
|
-
if (!isDetachedDOMNode(p.node)) {
|
|
1000
|
-
return false;
|
|
1001
|
-
}
|
|
1002
|
-
p = p.next;
|
|
1003
|
-
}
|
|
1004
|
-
return true;
|
|
1005
|
-
}
|
|
1006
928
|
function pathHasDetachedHTMLNode(path) {
|
|
1007
929
|
if (!path) {
|
|
1008
930
|
return false;
|
|
@@ -1782,6 +1704,9 @@ function runShell(command, options = {}) {
|
|
|
1782
1704
|
Console_1.default.lowLevel((_c = ex.stack) !== null && _c !== void 0 ? _c : '');
|
|
1783
1705
|
}
|
|
1784
1706
|
}
|
|
1707
|
+
if (options.throwError) {
|
|
1708
|
+
throw ex;
|
|
1709
|
+
}
|
|
1785
1710
|
if (options.ignoreError === true) {
|
|
1786
1711
|
return '';
|
|
1787
1712
|
}
|
|
@@ -1824,14 +1749,28 @@ function getNumberAtPercentile(arr, percentile) {
|
|
|
1824
1749
|
}
|
|
1825
1750
|
return arr[indexInt];
|
|
1826
1751
|
}
|
|
1752
|
+
function mapToObject(map) {
|
|
1753
|
+
const ret = Object.create(null);
|
|
1754
|
+
map.forEach((v, k) => {
|
|
1755
|
+
ret[k] = v;
|
|
1756
|
+
});
|
|
1757
|
+
return ret;
|
|
1758
|
+
}
|
|
1759
|
+
function objectToMap(object) {
|
|
1760
|
+
const ret = new Map();
|
|
1761
|
+
for (const k of Object.keys(object)) {
|
|
1762
|
+
ret.set(k, object[k]);
|
|
1763
|
+
}
|
|
1764
|
+
return ret;
|
|
1765
|
+
}
|
|
1827
1766
|
exports.default = {
|
|
1828
1767
|
aggregateDominatorMetrics,
|
|
1829
1768
|
applyToNodes,
|
|
1830
1769
|
callAsync,
|
|
1831
1770
|
camelCaseToReadableString,
|
|
1771
|
+
checkIsChildOfParent,
|
|
1832
1772
|
checkSnapshots,
|
|
1833
1773
|
checkUninstalledLibrary,
|
|
1834
|
-
checkIsChildOfParent,
|
|
1835
1774
|
closePuppeteer,
|
|
1836
1775
|
dumpSnapshot,
|
|
1837
1776
|
equalOrMatch,
|
|
@@ -1840,13 +1779,14 @@ exports.default = {
|
|
|
1840
1779
|
extractHTMLElementNodeInfo,
|
|
1841
1780
|
filterNodesInPlace,
|
|
1842
1781
|
getAllDominators,
|
|
1782
|
+
getBooleanNodeValue,
|
|
1843
1783
|
getClosureSourceUrl,
|
|
1844
1784
|
getConditionalDominatorIds,
|
|
1845
|
-
getError,
|
|
1846
1785
|
getEdgeByNameAndType,
|
|
1786
|
+
getError,
|
|
1847
1787
|
getLastNodeId,
|
|
1848
|
-
getLeakedNode,
|
|
1849
1788
|
getLeakTracePathLength,
|
|
1789
|
+
getLeakedNode,
|
|
1850
1790
|
getNodesIdSet,
|
|
1851
1791
|
getNumberAtPercentile,
|
|
1852
1792
|
getNumberNodeValue,
|
|
@@ -1854,17 +1794,16 @@ exports.default = {
|
|
|
1854
1794
|
getReadablePercent,
|
|
1855
1795
|
getReadableTime,
|
|
1856
1796
|
getRetainedSize,
|
|
1857
|
-
getRunMetaFilePath,
|
|
1858
1797
|
getScenarioName,
|
|
1859
1798
|
getSingleSnapshotFileForAnalysis,
|
|
1860
1799
|
getSnapshotDirForAnalysis,
|
|
1861
|
-
|
|
1800
|
+
getSnapshotFilePath,
|
|
1801
|
+
getSnapshotFilePathWithTabType,
|
|
1862
1802
|
getSnapshotFilesFromTabsOrder,
|
|
1803
|
+
getSnapshotFilesInDir,
|
|
1863
1804
|
getSnapshotFromFile,
|
|
1864
1805
|
getSnapshotNodeIdsFromFile,
|
|
1865
1806
|
getSnapshotSequenceFilePath,
|
|
1866
|
-
getSnapshotFilePath,
|
|
1867
|
-
getSnapshotFilePathWithTabType,
|
|
1868
1807
|
getStringNodeValue,
|
|
1869
1808
|
getToNodeByEdge,
|
|
1870
1809
|
getUniqueID,
|
|
@@ -1875,18 +1814,21 @@ exports.default = {
|
|
|
1875
1814
|
hasReactEdges,
|
|
1876
1815
|
isAlternateNode,
|
|
1877
1816
|
isBlinkRootNode,
|
|
1817
|
+
isDOMInternalNode,
|
|
1818
|
+
isDOMNodeIncomplete,
|
|
1819
|
+
isDOMTextNode,
|
|
1878
1820
|
isDebuggableNode,
|
|
1879
|
-
isDetachedFiberNode,
|
|
1880
1821
|
isDetachedDOMNode,
|
|
1822
|
+
isDetachedFiberNode,
|
|
1881
1823
|
isDirectPropEdge,
|
|
1882
1824
|
isDocumentDOMTreesRoot,
|
|
1883
|
-
isDOMNodeIncomplete,
|
|
1884
1825
|
isEssentialEdge,
|
|
1885
1826
|
isFiberNode,
|
|
1886
1827
|
isFiberNodeDeletionsEdge,
|
|
1828
|
+
isGlobalHandlesNode,
|
|
1829
|
+
isHTMLDocumentNode,
|
|
1887
1830
|
isHermesInternalObject,
|
|
1888
1831
|
isHostRoot,
|
|
1889
|
-
isInterestingPath,
|
|
1890
1832
|
isMeaningfulEdge,
|
|
1891
1833
|
isMeaningfulNode,
|
|
1892
1834
|
isNodeDominatedByDeletionsArray,
|
|
@@ -1905,17 +1847,18 @@ exports.default = {
|
|
|
1905
1847
|
isWeakMapEdge,
|
|
1906
1848
|
isWeakMapEdgeToKey,
|
|
1907
1849
|
isWeakMapEdgeToValue,
|
|
1850
|
+
isXMLDocumentNode,
|
|
1908
1851
|
iterateChildFiberNodes,
|
|
1909
1852
|
iterateDescendantFiberNodes,
|
|
1910
|
-
loadRunMetaInfo,
|
|
1911
1853
|
loadLeakFilter,
|
|
1912
1854
|
loadScenario,
|
|
1913
1855
|
loadTabsOrder,
|
|
1914
|
-
|
|
1856
|
+
mapToObject,
|
|
1915
1857
|
markAllDetachedFiberNode,
|
|
1916
1858
|
markAlternateFiberNode,
|
|
1917
1859
|
memCache,
|
|
1918
1860
|
normalizeBaseUrl,
|
|
1861
|
+
objectToMap,
|
|
1919
1862
|
pathHasDetachedHTMLNode,
|
|
1920
1863
|
pathHasEdgeWithIndex,
|
|
1921
1864
|
repeat,
|
|
@@ -1928,5 +1871,4 @@ exports.default = {
|
|
|
1928
1871
|
shuffleArray,
|
|
1929
1872
|
throwError,
|
|
1930
1873
|
upperCaseFirstCharacter,
|
|
1931
|
-
getBooleanNodeValue,
|
|
1932
1874
|
};
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { PlotMemoryOptions } from '../Types';
|
|
11
11
|
declare class MemoryBarChart {
|
|
12
12
|
plotMemoryBarChart(options?: PlotMemoryOptions): void;
|
|
13
|
+
private isPlotDataValid;
|
|
13
14
|
private loadPlotDataFromTabsOrder;
|
|
14
15
|
private loadPlotDataFromWorkDir;
|
|
15
16
|
private loadPlotData;
|
|
@@ -30,7 +30,7 @@ class MemoryBarChart {
|
|
|
30
30
|
Console_1.default.warning(`plot data not load correctly: ${Utils_1.default.getError(ex).message}`);
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
if (plotData
|
|
33
|
+
if (!this.isPlotDataValid(plotData)) {
|
|
34
34
|
if (Config_1.default.verbose) {
|
|
35
35
|
Console_1.default.warning('no memory usage data to plot');
|
|
36
36
|
}
|
|
@@ -57,6 +57,21 @@ class MemoryBarChart {
|
|
|
57
57
|
}));
|
|
58
58
|
Console_1.default.topLevel('');
|
|
59
59
|
}
|
|
60
|
+
isPlotDataValid(plotData) {
|
|
61
|
+
if (plotData.length === 0) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
let isEntryValueAllZero = true;
|
|
65
|
+
for (const entry of plotData) {
|
|
66
|
+
if (entry.length !== 2) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (entry[1] !== 0) {
|
|
70
|
+
isEntryValueAllZero = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return !isEntryValueAllZero;
|
|
74
|
+
}
|
|
60
75
|
loadPlotDataFromTabsOrder(tabsOrder) {
|
|
61
76
|
for (const tab of tabsOrder) {
|
|
62
77
|
if (!(tab.JSHeapUsedSize > 0)) {
|
|
@@ -82,12 +97,12 @@ class MemoryBarChart {
|
|
|
82
97
|
}
|
|
83
98
|
loadPlotData(options = {}) {
|
|
84
99
|
// plot data for a single run
|
|
85
|
-
if (!options.
|
|
100
|
+
if (!options.controlWorkDirs && !options.treatmentWorkDir) {
|
|
86
101
|
return this.loadPlotDataFromWorkDir(options);
|
|
87
102
|
}
|
|
88
103
|
// plot data for control and test run
|
|
89
104
|
const controlPlotData = this.loadPlotDataFromWorkDir({
|
|
90
|
-
workDir: options.
|
|
105
|
+
workDir: options.controlWorkDirs && options.controlWorkDirs[0],
|
|
91
106
|
});
|
|
92
107
|
const testPlotData = this.loadPlotDataFromWorkDir({
|
|
93
108
|
workDir: options.treatmentWorkDir,
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
* @format
|
|
8
|
+
* @oncall web_perf_infra
|
|
9
|
+
*/
|
|
10
|
+
import type { MemLabConfig } from '../Config';
|
|
11
|
+
import type { HeapNodeIdSet, IHeapSnapshot, LeakTracePathItem } from '../Types';
|
|
12
|
+
/**
|
|
13
|
+
* Every leak trace filter rule needs to give a label
|
|
14
|
+
* to each leak trace passed to the filter
|
|
15
|
+
*/
|
|
16
|
+
export declare enum TraceDecision {
|
|
17
|
+
INSIGHTFUL = "insightful",
|
|
18
|
+
MAYBE_INSIGHTFUL = "maybe-insightful",
|
|
19
|
+
NOT_INSIGHTFUL = "not-insightful"
|
|
20
|
+
}
|
|
21
|
+
export declare type LeakTraceFilterOptions = {
|
|
22
|
+
config?: MemLabConfig;
|
|
23
|
+
snapshot?: IHeapSnapshot;
|
|
24
|
+
leakedNodeIds?: HeapNodeIdSet;
|
|
25
|
+
};
|
|
26
|
+
export interface ILeakTraceFilterRule {
|
|
27
|
+
filter(p: LeakTracePathItem, options: LeakTraceFilterOptions): TraceDecision;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=BaseTraceFilter.rule.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
* @format
|
|
9
|
+
* @oncall web_perf_infra
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TraceDecision = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Every leak trace filter rule needs to give a label
|
|
15
|
+
* to each leak trace passed to the filter
|
|
16
|
+
*/
|
|
17
|
+
var TraceDecision;
|
|
18
|
+
(function (TraceDecision) {
|
|
19
|
+
TraceDecision["INSIGHTFUL"] = "insightful";
|
|
20
|
+
TraceDecision["MAYBE_INSIGHTFUL"] = "maybe-insightful";
|
|
21
|
+
TraceDecision["NOT_INSIGHTFUL"] = "not-insightful";
|
|
22
|
+
})(TraceDecision = exports.TraceDecision || (exports.TraceDecision = {}));
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
* @format
|
|
8
|
+
* @oncall web_perf_infra
|
|
9
|
+
*/
|
|
10
|
+
import type { LeakTracePathItem } from '../Types';
|
|
11
|
+
import type { LeakTraceFilterOptions } from './BaseTraceFilter.rule';
|
|
12
|
+
/**
|
|
13
|
+
* apply the leak trace filter rules chain and decide
|
|
14
|
+
* if a leak trace is useful for memory debugging,
|
|
15
|
+
* by default all leak traces are considered useful
|
|
16
|
+
*/
|
|
17
|
+
export declare class LeakTraceFilter {
|
|
18
|
+
filter(p: LeakTracePathItem, options: LeakTraceFilterOptions): boolean;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=LeakTraceFilter.d.ts.map
|
|
@@ -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
|
+
* @format
|
|
9
|
+
* @oncall web_perf_infra
|
|
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.LeakTraceFilter = void 0;
|
|
16
|
+
const BaseTraceFilter_rule_1 = require("./BaseTraceFilter.rule");
|
|
17
|
+
const TraceFilterRuleList_1 = __importDefault(require("./TraceFilterRuleList"));
|
|
18
|
+
/**
|
|
19
|
+
* apply the leak trace filter rules chain and decide
|
|
20
|
+
* if a leak trace is useful for memory debugging,
|
|
21
|
+
* by default all leak traces are considered useful
|
|
22
|
+
*/
|
|
23
|
+
class LeakTraceFilter {
|
|
24
|
+
filter(p, options) {
|
|
25
|
+
for (const rule of TraceFilterRuleList_1.default) {
|
|
26
|
+
const decision = rule.filter(p, options);
|
|
27
|
+
if (decision === BaseTraceFilter_rule_1.TraceDecision.INSIGHTFUL) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (decision === BaseTraceFilter_rule_1.TraceDecision.NOT_INSIGHTFUL) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.LeakTraceFilter = LeakTraceFilter;
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
* @format
|
|
8
|
+
* @oncall web_perf_infra
|
|
9
|
+
*/
|
|
10
|
+
import { ILeakTraceFilterRule } from './BaseTraceFilter.rule';
|
|
11
|
+
declare const _default: ILeakTraceFilterRule[];
|
|
12
|
+
export default _default;
|
|
13
|
+
//# sourceMappingURL=TraceFilterRuleList.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
* @format
|
|
9
|
+
* @oncall web_perf_infra
|
|
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 Constant_1 = __importDefault(require("../Constant"));
|
|
16
|
+
const InternalValueSetter_1 = require("../InternalValueSetter");
|
|
17
|
+
const FilterAttachedDOMToDetachedDOMTrace_rule_1 = require("./rules/FilterAttachedDOMToDetachedDOMTrace.rule");
|
|
18
|
+
const FilterDOMNodeChainTrace_rule_1 = require("./rules/FilterDOMNodeChainTrace.rule");
|
|
19
|
+
const FilterHermesTrace_rule_1 = require("./rules/FilterHermesTrace.rule");
|
|
20
|
+
const FilterInternalNodeTrace_rule_1 = require("./rules/FilterInternalNodeTrace.rule");
|
|
21
|
+
const FilterPendingActivitiesTrace_rule_1 = require("./rules/FilterPendingActivitiesTrace.rule");
|
|
22
|
+
const FilterShadowRootTrace_rule_1 = require("./rules/FilterShadowRootTrace.rule");
|
|
23
|
+
const FilterStyleEngineTrace_rule_1 = require("./rules/FilterStyleEngineTrace.rule");
|
|
24
|
+
const list = [
|
|
25
|
+
new FilterHermesTrace_rule_1.FilterHermesTraceRule(),
|
|
26
|
+
new FilterInternalNodeTrace_rule_1.FilterInternalNodeTraceRule(),
|
|
27
|
+
new FilterShadowRootTrace_rule_1.FilterShadowRootTraceRule(),
|
|
28
|
+
new FilterStyleEngineTrace_rule_1.FilterStyleEngineTraceRule(),
|
|
29
|
+
new FilterPendingActivitiesTrace_rule_1.FilterPendingActivitiesTraceRule(),
|
|
30
|
+
new FilterDOMNodeChainTrace_rule_1.FilterDOMNodeChainTraceRule(),
|
|
31
|
+
new FilterAttachedDOMToDetachedDOMTrace_rule_1.FilterAttachedDOMToDetachedDOMTraceRule(),
|
|
32
|
+
];
|
|
33
|
+
exports.default = (0, InternalValueSetter_1.setInternalValue)(list, __filename, Constant_1.default.internalDir);
|
|
@@ -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
|
+
* @format
|
|
8
|
+
* @oncall web_perf_infra
|
|
9
|
+
*/
|
|
10
|
+
import type { LeakTracePathItem } from '../../Types';
|
|
11
|
+
import { ILeakTraceFilterRule, LeakTraceFilterOptions, TraceDecision } from '../BaseTraceFilter.rule';
|
|
12
|
+
export declare class FilterAttachedDOMToDetachedDOMTraceRule implements ILeakTraceFilterRule {
|
|
13
|
+
filter(p: LeakTracePathItem, options?: LeakTraceFilterOptions): TraceDecision;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=FilterAttachedDOMToDetachedDOMTrace.rule.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
* @format
|
|
9
|
+
* @oncall web_perf_infra
|
|
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.FilterAttachedDOMToDetachedDOMTraceRule = void 0;
|
|
16
|
+
const Config_1 = __importDefault(require("../../Config"));
|
|
17
|
+
const Utils_1 = __importDefault(require("../../Utils"));
|
|
18
|
+
const BaseTraceFilter_rule_1 = require("../BaseTraceFilter.rule");
|
|
19
|
+
class FilterAttachedDOMToDetachedDOMTraceRule {
|
|
20
|
+
filter(p, options = {}) {
|
|
21
|
+
var _a;
|
|
22
|
+
const curConfig = (_a = options.config) !== null && _a !== void 0 ? _a : Config_1.default;
|
|
23
|
+
// if the path consists of only DOM native nodes/elements
|
|
24
|
+
if (curConfig.hideBrowserLeak && isAttachedDOMToDetachedDOMChain(p)) {
|
|
25
|
+
return BaseTraceFilter_rule_1.TraceDecision.NOT_INSIGHTFUL;
|
|
26
|
+
}
|
|
27
|
+
return BaseTraceFilter_rule_1.TraceDecision.MAYBE_INSIGHTFUL;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.FilterAttachedDOMToDetachedDOMTraceRule = FilterAttachedDOMToDetachedDOMTraceRule;
|
|
31
|
+
// check if the path has pattern:
|
|
32
|
+
// [Attached Element] -> [InternalNode | Text]+ -> [Detached Element]
|
|
33
|
+
function isAttachedDOMToDetachedDOMChain(path) {
|
|
34
|
+
if (!path) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
let p = path;
|
|
38
|
+
let hasEncounteredAttachedNode = false;
|
|
39
|
+
// skip the rest InternalNode
|
|
40
|
+
while (p != null && p.node) {
|
|
41
|
+
if (Utils_1.default.isDetachedDOMNode(p.node)) {
|
|
42
|
+
return hasEncounteredAttachedNode;
|
|
43
|
+
}
|
|
44
|
+
// else if this is an attached node
|
|
45
|
+
if (Utils_1.default.isDOMNodeIncomplete(p.node)) {
|
|
46
|
+
hasEncounteredAttachedNode = true;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// else if this not a DOM element
|
|
50
|
+
hasEncounteredAttachedNode = false;
|
|
51
|
+
}
|
|
52
|
+
p = p.next;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
@@ -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
|
+
* @format
|
|
8
|
+
* @oncall web_perf_infra
|
|
9
|
+
*/
|
|
10
|
+
import type { LeakTracePathItem } from '../../Types';
|
|
11
|
+
import { ILeakTraceFilterRule, LeakTraceFilterOptions, TraceDecision } from '../BaseTraceFilter.rule';
|
|
12
|
+
export declare class FilterDOMNodeChainTraceRule implements ILeakTraceFilterRule {
|
|
13
|
+
filter(p: LeakTracePathItem, options?: LeakTraceFilterOptions): TraceDecision;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=FilterDOMNodeChainTrace.rule.d.ts.map
|