@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.
Files changed (41) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +4 -1
  3. package/dist/lib/Config.d.ts +2 -0
  4. package/dist/lib/Config.js +9 -0
  5. package/dist/lib/FileManager.d.ts +3 -0
  6. package/dist/lib/FileManager.js +10 -1
  7. package/dist/lib/HeapAnalyzer.d.ts +5 -1
  8. package/dist/lib/HeapAnalyzer.js +33 -21
  9. package/dist/lib/RunInfoUtils.d.ts +39 -0
  10. package/dist/lib/RunInfoUtils.js +86 -0
  11. package/dist/lib/Types.d.ts +7 -2
  12. package/dist/lib/Utils.d.ts +25 -19
  13. package/dist/lib/Utils.js +90 -148
  14. package/dist/lib/charts/MemoryBarChart.d.ts +1 -0
  15. package/dist/lib/charts/MemoryBarChart.js +18 -3
  16. package/dist/lib/trace-filters/BaseTraceFilter.rule.d.ts +29 -0
  17. package/dist/lib/trace-filters/BaseTraceFilter.rule.js +22 -0
  18. package/dist/lib/trace-filters/LeakTraceFilter.d.ts +20 -0
  19. package/dist/lib/trace-filters/LeakTraceFilter.js +37 -0
  20. package/dist/lib/trace-filters/TraceFilterRuleList.d.ts +13 -0
  21. package/dist/lib/trace-filters/TraceFilterRuleList.js +33 -0
  22. package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.d.ts +15 -0
  23. package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.js +55 -0
  24. package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.d.ts +15 -0
  25. package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.js +41 -0
  26. package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.d.ts +15 -0
  27. package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.js +29 -0
  28. package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.d.ts +15 -0
  29. package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.js +57 -0
  30. package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.d.ts +15 -0
  31. package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.js +62 -0
  32. package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.d.ts +15 -0
  33. package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.js +44 -0
  34. package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.d.ts +15 -0
  35. package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.js +49 -0
  36. package/dist/logger/LeakClusterLogger.js +1 -0
  37. package/dist/paths/TraceFinder.js +16 -2
  38. package/dist/trace-cluster/TraceBucket.d.ts +1 -1
  39. package/dist/trace-cluster/TraceBucket.js +7 -3
  40. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +1 -0
  41. 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
- return node.type === 'synthetic' && node.name === 'Pending activities';
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 pattern.test(name);
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
- Console_1.default.warning(`node @${id} is not found`);
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 || getSnapshotSequenceFilePath();
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
- getSnapshotFilesInDir,
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
- loadTargetInfoFromRunMeta,
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.length === 0) {
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.controlWorkDir && !options.treatmentWorkDir) {
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.controlWorkDir,
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