chrome-devtools-frontend 1.0.1652307 → 1.0.1656291
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/.agents/skills/fixing-skipped-tests/SKILL.md +32 -0
- package/.agents/skills/migrate-chromium-test/SKILL.md +144 -0
- package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +116 -0
- package/.agents/skills/ui-eng-vision-logic-consolidator/SKILL.md +82 -0
- package/.agents/skills/ui-eng-vision-orchestrator/SKILL.md +123 -0
- package/.agents/skills/ui-eng-vision-test-scaffolder/SKILL.md +125 -0
- package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +75 -0
- package/AUTHORS +1 -0
- package/config/owner/COMMON_OWNERS +0 -4
- package/docs/get_the_code.md +1 -2
- package/docs/playbook.md +1 -1
- package/docs/ui_engineering.md +3 -1
- package/eslint.config.mjs +0 -5
- package/front_end/Tests.js +1 -0
- package/front_end/core/common/Settings.ts +47 -30
- package/front_end/core/sdk/CPUThrottlingManager.ts +23 -167
- package/front_end/core/sdk/CSSMetadata.ts +23 -0
- package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +8 -4
- package/front_end/core/sdk/ConsoleModel.ts +13 -11
- package/front_end/core/sdk/DOMDebuggerModel.ts +11 -5
- package/front_end/core/sdk/DOMModel.ts +4 -0
- package/front_end/core/sdk/EmulationModel.ts +4 -0
- package/front_end/core/sdk/EventBreakpointsModel.ts +9 -5
- package/front_end/core/sdk/IsolateManager.ts +6 -6
- package/front_end/core/sdk/ResourceTreeModel.ts +8 -5
- package/front_end/core/sdk/TargetManager.ts +17 -0
- package/front_end/entrypoints/devtools_app/devtools_app.ts +0 -1
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +459 -74
- package/front_end/entrypoints/main/MainImpl.ts +12 -42
- package/front_end/foundation/Universe.ts +116 -3
- package/front_end/generated/InspectorBackendCommands.ts +11 -6
- package/front_end/generated/SupportedCSSProperties.js +124 -34
- package/front_end/generated/protocol-mapping.d.ts +8 -0
- package/front_end/generated/protocol-proxy-api.d.ts +15 -0
- package/front_end/generated/protocol.ts +94 -1
- package/front_end/models/ai_assistance/AiConversation.ts +2 -7
- package/front_end/models/ai_assistance/README.md +4 -3
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +13 -320
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -24
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -358
- package/front_end/models/ai_assistance/ai_assistance.ts +6 -8
- package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +295 -0
- package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +0 -1
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +0 -2
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +10 -10
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +12 -11
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +6 -6
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -14
- package/front_end/models/ai_assistance/skills/accessibility.md +4 -2
- package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +142 -0
- package/front_end/models/ai_assistance/tools/GetStyles.ts +1 -1
- package/front_end/models/ai_assistance/tools/README.md +21 -1
- package/front_end/models/ai_assistance/tools/{ResolveLighthousePath.ts → ResolveDevtoolsNodePath.ts} +18 -17
- package/front_end/models/ai_assistance/tools/Tool.ts +6 -1
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -2
- package/front_end/models/autofill_manager/AutofillManager.ts +6 -15
- package/front_end/models/bindings/NetworkProject.ts +4 -8
- package/front_end/models/breakpoints/BreakpointManager.ts +9 -9
- package/front_end/models/emulation/DeviceModeModel.ts +24 -0
- package/front_end/models/emulation/EmulatedDevices.ts +108 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +36 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +8 -0
- package/front_end/models/issues_manager/IssuesManager.ts +10 -6
- package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +4 -5
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/live-metrics/LiveMetrics.ts +6 -6
- package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +5 -5
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +3 -3
- package/front_end/models/logs/LogManager.ts +25 -10
- package/front_end/models/logs/NetworkLog.ts +24 -19
- package/front_end/models/persistence/AutomaticFileSystemManager.ts +14 -13
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +26 -14
- package/front_end/models/persistence/IsolatedFileSystem.ts +9 -11
- package/front_end/models/persistence/IsolatedFileSystemManager.ts +22 -11
- package/front_end/models/persistence/NetworkPersistenceManager.ts +73 -44
- package/front_end/models/persistence/PersistenceImpl.ts +4 -5
- package/front_end/models/project_settings/ProjectSettingsModel.ts +12 -11
- package/front_end/models/trace/EntityMapper.ts +1 -1
- package/front_end/models/trace/handlers/ScreenshotsHandler.ts +14 -59
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -38
- package/front_end/panels/ai_assistance/ExportConversation.ts +4 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/application/DeviceBoundSessionsView.ts +15 -3
- package/front_end/panels/application/FrameDetailsView.ts +2 -1
- package/front_end/panels/application/IndexedDBViews.ts +38 -36
- package/front_end/panels/application/ServiceWorkersView.ts +23 -16
- package/front_end/panels/application/components/AdsView.ts +4 -0
- package/front_end/panels/autofill/AutofillView.ts +1 -1
- package/front_end/panels/autofill/autofill-meta.ts +3 -2
- package/front_end/panels/common/CPUThrottlingOption.ts +142 -0
- package/front_end/panels/common/ThrottlingUtils.ts +9 -4
- package/front_end/panels/common/common.ts +1 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +45 -0
- package/front_end/panels/elements/AccessibilityTreeUtils.ts +26 -15
- package/front_end/panels/elements/AccessibilityTreeView.ts +12 -9
- package/front_end/panels/elements/ElementsPanel.ts +0 -74
- package/front_end/panels/elements/ElementsTreeOutline.ts +5 -4
- package/front_end/panels/emulation/DeviceModeToolbar.ts +46 -45
- package/front_end/panels/layer_viewer/LayerTreeOutline.ts +118 -68
- package/front_end/panels/layer_viewer/Layers3DView.ts +1 -1
- package/front_end/panels/layer_viewer/layerTreeOutline.css +20 -13
- package/front_end/panels/layers/LayersPanel.ts +1 -1
- package/front_end/panels/mobile_throttling/CalibrationController.ts +6 -5
- package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingManager.ts +40 -13
- package/front_end/panels/mobile_throttling/ThrottlingPresets.ts +13 -17
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +79 -30
- package/front_end/panels/mobile_throttling/throttlingSettingsTab.css +5 -0
- package/front_end/panels/network/LinkPreloadGenerator.ts +101 -0
- package/front_end/panels/network/NetworkLogView.ts +28 -9
- package/front_end/panels/network/NetworkPanel.ts +1 -63
- package/front_end/panels/network/RequestInitiatorView.ts +29 -4
- package/front_end/panels/network/components/HeaderSectionRow.css +9 -7
- package/front_end/panels/network/components/HeaderSectionRow.ts +20 -1
- package/front_end/panels/network/components/RequestHeaderSection.css +5 -3
- package/front_end/panels/network/components/RequestHeaderSection.ts +1 -2
- package/front_end/panels/network/components/RequestHeadersView.css +1 -1
- package/front_end/panels/network/network.ts +2 -0
- package/front_end/panels/profiler/HeapSnapshotView.ts +121 -33
- package/front_end/panels/profiler/ModuleUIStrings.ts +4 -0
- package/front_end/panels/security/SecurityPanel.ts +36 -23
- package/front_end/panels/settings/SettingsScreen.ts +18 -103
- package/front_end/panels/settings/settings-meta.ts +0 -24
- package/front_end/panels/timeline/TimelineController.ts +3 -2
- package/front_end/panels/timeline/TimelineHistoryManager.ts +38 -37
- package/front_end/panels/timeline/TimelinePanel.ts +52 -11
- package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +20 -18
- package/front_end/panels/timeline/components/LiveMetricsView.ts +38 -38
- package/front_end/panels/timeline/components/MetricCard.ts +18 -18
- package/front_end/panels/timeline/components/liveMetricsView.css +6 -6
- package/front_end/panels/timeline/components/metricCard.css +4 -4
- package/front_end/panels/timeline/timelineFlamechartPopover.css +2 -0
- package/front_end/panels/utils/utils.ts +25 -24
- package/front_end/panels/whats_new/ReleaseNoteText.ts +3 -3
- package/front_end/panels/whats_new/resources/WNDT.md +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +34 -55
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +96 -83
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +10 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/CDPSession.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/CDPSession.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.d.ts +3 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Target.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Target.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js +2 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Realm.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +5 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.js +23 -13
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.js +4 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Input.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/LifecycleWatcher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/LifecycleWatcher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts +3 -41
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js +4 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/LaunchOptions.d.ts +5 -0
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/LaunchOptions.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.js +5 -5
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +34 -55
- package/front_end/third_party/puppeteer/package/package.json +4 -4
- package/front_end/third_party/puppeteer/package/src/api/Browser.ts +13 -1
- package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +2 -3
- package/front_end/third_party/puppeteer/package/src/api/CDPSession.ts +3 -1
- package/front_end/third_party/puppeteer/package/src/api/HTTPResponse.ts +3 -2
- package/front_end/third_party/puppeteer/package/src/api/Realm.ts +1 -2
- package/front_end/third_party/puppeteer/package/src/api/Target.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/api/locators/locators.ts +1 -2
- package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +3 -1
- package/front_end/third_party/puppeteer/package/src/bidi/core/Realm.ts +1 -3
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +13 -3
- package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/cdp/FrameManager.ts +24 -13
- package/front_end/third_party/puppeteer/package/src/cdp/HTTPResponse.ts +4 -1
- package/front_end/third_party/puppeteer/package/src/cdp/Input.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/cdp/LifecycleWatcher.ts +1 -4
- package/front_end/third_party/puppeteer/package/src/cdp/Target.ts +2 -4
- package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +7 -76
- package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +1 -4
- package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +4 -1
- package/front_end/third_party/puppeteer/package/src/node/LaunchOptions.ts +5 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/util/httpUtils.ts +5 -5
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/third_party/puppeteer/third_party/parsel/README.chromium +1 -1
- package/front_end/ui/components/dialogs/Dialog.ts +3 -0
- package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +0 -7
- package/front_end/ui/helpers/OpenInNewTab.ts +15 -7
- package/front_end/ui/legacy/InspectorDrawerView.ts +7 -5
- package/front_end/ui/legacy/ListWidget.ts +12 -0
- package/front_end/ui/legacy/SplitWidget.ts +12 -11
- package/front_end/ui/legacy/TabbedPane.ts +1 -108
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +9 -0
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +192 -139
- package/front_end/ui/legacy/components/data_grid/DataGridExporter.ts +89 -0
- package/front_end/ui/legacy/components/data_grid/dataGrid.css +10 -0
- package/front_end/ui/legacy/components/data_grid/data_grid.ts +3 -1
- package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +8 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +110 -17
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +89 -40
- package/front_end/ui/legacy/treeoutline.css +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- package/package.json +4 -3
- package/front_end/Images/navigationControls.png +0 -0
- package/front_end/Images/navigationControls_2x.png +0 -0
- package/front_end/Images/popoverArrows.png +0 -0
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -746
- package/front_end/entrypoints/greendev_floaty/floaty.css +0 -301
- package/front_end/entrypoints/greendev_floaty/floaty.html +0 -38
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -415
- package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +0 -834
- package/front_end/models/ai_assistance/agents/GreenDevAgentAntigravityCliSocketClient.ts +0 -53
- package/front_end/models/ai_assistance/agents/GreenDevAgentGeminiCliSocketClient.ts +0 -117
- package/front_end/models/annotations/AnnotationRepository.ts +0 -238
- package/front_end/models/annotations/AnnotationType.ts +0 -10
- package/front_end/models/annotations/README.md +0 -7
- package/front_end/models/annotations/annotations.ts +0 -6
- package/front_end/models/greendev/Prototypes.ts +0 -68
- package/front_end/models/greendev/README.md +0 -5
- package/front_end/models/greendev/greendev.ts +0 -5
- package/front_end/panels/common/Annotation.ts +0 -184
- package/front_end/panels/common/AnnotationManager.ts +0 -208
- package/front_end/panels/common/annotation.css +0 -40
- package/front_end/panels/greendev/GreenDevPanel.css +0 -282
- package/front_end/panels/greendev/GreenDevPanel.ts +0 -364
- package/front_end/panels/greendev/GreenDevShared.ts +0 -13
- package/front_end/panels/greendev/greendev-meta.ts +0 -52
- package/front_end/panels/greendev/greendev.ts +0 -9
|
@@ -518,11 +518,11 @@ export class HeapSnapshotNode implements HeapSnapshotItem {
|
|
|
518
518
|
}
|
|
519
519
|
}
|
|
520
520
|
|
|
521
|
-
detachedness(): DOMLinkState {
|
|
521
|
+
detachedness(): HeapSnapshotModel.HeapSnapshotModel.DOMLinkState {
|
|
522
522
|
return this.#detachednessAndClassIndex() & BITMASK_FOR_DOM_LINK_STATE;
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
setDetachedness(detachedness: DOMLinkState): void {
|
|
525
|
+
setDetachedness(detachedness: HeapSnapshotModel.HeapSnapshotModel.DOMLinkState): void {
|
|
526
526
|
let value = this.#detachednessAndClassIndex();
|
|
527
527
|
value &= ~BITMASK_FOR_DOM_LINK_STATE; // Clear the old bits.
|
|
528
528
|
value |= detachedness; // Set the new bits.
|
|
@@ -567,6 +567,31 @@ export class HeapSnapshotNode implements HeapSnapshotItem {
|
|
|
567
567
|
return undefined;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
+
nodeValueAsInt(): number|undefined {
|
|
571
|
+
if (this.rawType() !== this.snapshot.nodeNumberType) {
|
|
572
|
+
return undefined;
|
|
573
|
+
}
|
|
574
|
+
if (this.rawName() !== 'int') {
|
|
575
|
+
return undefined;
|
|
576
|
+
}
|
|
577
|
+
const valNode = this.findInternalEdgeTarget('value');
|
|
578
|
+
if (!valNode) {
|
|
579
|
+
return undefined;
|
|
580
|
+
}
|
|
581
|
+
const value = parseInt(valNode.rawName(), 10);
|
|
582
|
+
return isNaN(value) ? undefined : value;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
nodeStringLength(): number|undefined {
|
|
586
|
+
const lengthNode = this.findInternalEdgeTarget('length');
|
|
587
|
+
return lengthNode ? lengthNode.nodeValueAsInt() : undefined;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
nodeStringHash(): number|undefined {
|
|
591
|
+
const hashNode = this.findInternalEdgeTarget('hash');
|
|
592
|
+
return hashNode ? hashNode.nodeValueAsInt() : undefined;
|
|
593
|
+
}
|
|
594
|
+
|
|
570
595
|
nodeIsTruncatedString(): boolean {
|
|
571
596
|
const truncNode = this.findInternalEdgeTarget('truncated');
|
|
572
597
|
if (!truncNode) {
|
|
@@ -879,14 +904,7 @@ export class SecondaryInitManager {
|
|
|
879
904
|
}
|
|
880
905
|
}
|
|
881
906
|
|
|
882
|
-
|
|
883
|
-
* DOM node link state.
|
|
884
|
-
*/
|
|
885
|
-
const enum DOMLinkState {
|
|
886
|
-
UNKNOWN = 0,
|
|
887
|
-
ATTACHED = 1,
|
|
888
|
-
DETACHED = 2,
|
|
889
|
-
}
|
|
907
|
+
// Bitmask for accessing DOMLinkState in the detachedness field.
|
|
890
908
|
const BITMASK_FOR_DOM_LINK_STATE = 3;
|
|
891
909
|
|
|
892
910
|
// The class index is stored in the upper 30 bits of the detachedness field.
|
|
@@ -911,6 +929,13 @@ const MIN_OBJECT_COUNT_PER_INTERFACE = 2;
|
|
|
911
929
|
// long tail of unpopular interfaces that don't help analysis.
|
|
912
930
|
const MIN_OBJECT_PROPORTION_PER_INTERFACE = 1000;
|
|
913
931
|
|
|
932
|
+
// Values in the nodeNativeContextAttribution array:
|
|
933
|
+
// >= 0: The ordinal of the specific native context that owns the object.
|
|
934
|
+
// -1 (NO_NATIVE_CONTEXT): The object is not reachable from any native context.
|
|
935
|
+
// -2 (SHARED_NATIVE_CONTEXT): The object is reachable from multiple native contexts.
|
|
936
|
+
const NO_NATIVE_CONTEXT = -1;
|
|
937
|
+
const SHARED_NATIVE_CONTEXT = -2;
|
|
938
|
+
|
|
914
939
|
export abstract class HeapSnapshot {
|
|
915
940
|
nodes: Platform.TypedArrayUtilities.BigUint32Array;
|
|
916
941
|
containmentEdges: Platform.TypedArrayUtilities.BigUint32Array;
|
|
@@ -986,6 +1011,9 @@ export abstract class HeapSnapshot {
|
|
|
986
1011
|
#nodeDistancesForRetainersView: Int32Array|undefined;
|
|
987
1012
|
#edgeNamesThatAreNotWeakMaps: Platform.TypedArrayUtilities.BitVector;
|
|
988
1013
|
detachednessAndClassIndexArray?: Uint32Array;
|
|
1014
|
+
nodeNativeContextAttribution!: Int32Array;
|
|
1015
|
+
#nativeContextSizes!: HeapSnapshotModel.HeapSnapshotModel.NativeContextSizes;
|
|
1016
|
+
#nativeContextOrdinals!: number[];
|
|
989
1017
|
#interfaceNames = new Map<string, number>();
|
|
990
1018
|
#interfaceDefinitions?: InterfaceDefinition[];
|
|
991
1019
|
|
|
@@ -1083,8 +1111,11 @@ export abstract class HeapSnapshot {
|
|
|
1083
1111
|
this.buildSamples();
|
|
1084
1112
|
this.#progress.updateStatus('Building locations…');
|
|
1085
1113
|
this.buildLocationMap();
|
|
1114
|
+
this.#progress.updateStatus('Calculating native context attribution…');
|
|
1115
|
+
this.calculateNativeContextAttribution();
|
|
1086
1116
|
this.#progress.updateStatus('Calculating retained sizes…');
|
|
1087
1117
|
await this.installResultsFromSecondThread(resultsFromSecondWorker);
|
|
1118
|
+
this.calculateNativeContextSizes();
|
|
1088
1119
|
this.#progress.updateStatus('Calculating statistics…');
|
|
1089
1120
|
this.calculateStatistics();
|
|
1090
1121
|
|
|
@@ -1127,6 +1158,27 @@ export abstract class HeapSnapshot {
|
|
|
1127
1158
|
return undefined;
|
|
1128
1159
|
}
|
|
1129
1160
|
|
|
1161
|
+
getObjectInfo(nodeIndex: number): HeapSnapshotModel.HeapSnapshotModel.ObjectInfo {
|
|
1162
|
+
const nodesLength = this.nodes.length;
|
|
1163
|
+
const nodeFieldCount = this.nodeFieldCount;
|
|
1164
|
+
if (!Number.isInteger(nodeIndex) || nodeIndex < 0 || nodeIndex >= nodesLength || nodeIndex % nodeFieldCount !== 0) {
|
|
1165
|
+
throw new Error('Invalid nodeIndex ' + nodeIndex);
|
|
1166
|
+
}
|
|
1167
|
+
const node = this.createNode(nodeIndex);
|
|
1168
|
+
return {
|
|
1169
|
+
id: node.id(),
|
|
1170
|
+
name: node.name(),
|
|
1171
|
+
type: node.type(),
|
|
1172
|
+
nodeIndex,
|
|
1173
|
+
detachedness: node.detachedness(),
|
|
1174
|
+
selfSize: node.selfSize(),
|
|
1175
|
+
retainedSize: node.retainedSize(),
|
|
1176
|
+
distance: node.distance(),
|
|
1177
|
+
edgeCount: node.edgesCount(),
|
|
1178
|
+
retainerCount: node.retainersCount(),
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1130
1182
|
private startInitStep1InSecondThread(secondWorker: PlatformApi.HostRuntime.WorkerMessagePort):
|
|
1131
1183
|
Promise<ResultsFromSecondWorker> {
|
|
1132
1184
|
const resultsFromSecondWorker = new Promise<ResultsFromSecondWorker>((resolve, reject) => {
|
|
@@ -1366,7 +1418,9 @@ export abstract class HeapSnapshot {
|
|
|
1366
1418
|
|
|
1367
1419
|
getDuplicateStrings(): HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup[] {
|
|
1368
1420
|
const filter = this.createNamedFilter('duplicatedStrings');
|
|
1369
|
-
const
|
|
1421
|
+
const untruncatedGroups = new Map<string, HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup>();
|
|
1422
|
+
|
|
1423
|
+
const truncatedGroups = new Map<string, HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup[]>();
|
|
1370
1424
|
const node = this.createNode(0);
|
|
1371
1425
|
|
|
1372
1426
|
for (let i = 0; i < this.nodeCount; ++i) {
|
|
@@ -1375,39 +1429,69 @@ export abstract class HeapSnapshot {
|
|
|
1375
1429
|
const name = node.name();
|
|
1376
1430
|
const truncated = node.nodeIsTruncatedString();
|
|
1377
1431
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1432
|
+
if (truncated) {
|
|
1433
|
+
const length = node.nodeStringLength();
|
|
1434
|
+
const hash = node.nodeStringHash();
|
|
1435
|
+
let groups = truncatedGroups.get(name);
|
|
1436
|
+
if (!groups) {
|
|
1437
|
+
groups = [];
|
|
1438
|
+
truncatedGroups.set(name, groups);
|
|
1439
|
+
}
|
|
1440
|
+
let group = groups.find(g => g.length === length && g.hash === hash);
|
|
1441
|
+
if (!group) {
|
|
1442
|
+
group = {
|
|
1443
|
+
value: name,
|
|
1444
|
+
count: 0,
|
|
1445
|
+
totalSelfSize: 0,
|
|
1446
|
+
totalRetainedSize: 0,
|
|
1447
|
+
nodes: [],
|
|
1448
|
+
truncated: true,
|
|
1449
|
+
length,
|
|
1450
|
+
hash,
|
|
1451
|
+
};
|
|
1452
|
+
groups.push(group);
|
|
1453
|
+
}
|
|
1454
|
+
group.count++;
|
|
1455
|
+
group.totalSelfSize += node.selfSize();
|
|
1456
|
+
group.totalRetainedSize += node.retainedSize();
|
|
1457
|
+
group.nodes.push({
|
|
1458
|
+
id: node.id(),
|
|
1459
|
+
selfSize: node.selfSize(),
|
|
1460
|
+
retainedSize: node.retainedSize(),
|
|
1461
|
+
distance: node.distance(),
|
|
1462
|
+
});
|
|
1463
|
+
} else {
|
|
1464
|
+
let group = untruncatedGroups.get(name);
|
|
1465
|
+
if (!group) {
|
|
1466
|
+
group = {
|
|
1467
|
+
value: name,
|
|
1468
|
+
count: 0,
|
|
1469
|
+
totalSelfSize: 0,
|
|
1470
|
+
totalRetainedSize: 0,
|
|
1471
|
+
nodes: [],
|
|
1472
|
+
truncated: false,
|
|
1473
|
+
};
|
|
1474
|
+
untruncatedGroups.set(name, group);
|
|
1475
|
+
}
|
|
1476
|
+
group.count++;
|
|
1477
|
+
group.totalSelfSize += node.selfSize();
|
|
1478
|
+
group.totalRetainedSize += node.retainedSize();
|
|
1479
|
+
group.nodes.push({
|
|
1480
|
+
id: node.id(),
|
|
1481
|
+
selfSize: node.selfSize(),
|
|
1482
|
+
retainedSize: node.retainedSize(),
|
|
1483
|
+
distance: node.distance(),
|
|
1484
|
+
});
|
|
1397
1485
|
}
|
|
1398
|
-
group.count++;
|
|
1399
|
-
group.totalSelfSize += node.selfSize();
|
|
1400
|
-
group.totalRetainedSize += node.retainedSize();
|
|
1401
|
-
group.nodes.push({
|
|
1402
|
-
id: node.id(),
|
|
1403
|
-
selfSize: node.selfSize(),
|
|
1404
|
-
retainedSize: node.retainedSize(),
|
|
1405
|
-
distance: node.distance(),
|
|
1406
|
-
});
|
|
1407
1486
|
}
|
|
1408
1487
|
}
|
|
1409
1488
|
|
|
1410
|
-
|
|
1489
|
+
const allGroups: HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup[] = [
|
|
1490
|
+
...untruncatedGroups.values(),
|
|
1491
|
+
...Array.from(truncatedGroups.values()).flat(),
|
|
1492
|
+
];
|
|
1493
|
+
|
|
1494
|
+
return allGroups.sort((a, b) => b.totalRetainedSize - a.totalRetainedSize);
|
|
1411
1495
|
}
|
|
1412
1496
|
|
|
1413
1497
|
private createNodeIdFilter(minNodeId: number, maxNodeId: number): (arg0: HeapSnapshotNode) => boolean {
|
|
@@ -1485,7 +1569,7 @@ export abstract class HeapSnapshot {
|
|
|
1485
1569
|
case 'objectsRetainedByDetachedDomNodes':
|
|
1486
1570
|
// Traverse the graph, avoiding detached nodes.
|
|
1487
1571
|
traverse((_node: HeapSnapshotNode, edge: HeapSnapshotEdge) => {
|
|
1488
|
-
return edge.node().detachedness() !== DOMLinkState.DETACHED;
|
|
1572
|
+
return edge.node().detachedness() !== HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED;
|
|
1489
1573
|
});
|
|
1490
1574
|
markUnreachableNodes();
|
|
1491
1575
|
return (node: HeapSnapshotNode) => !getBit(node);
|
|
@@ -1497,33 +1581,8 @@ export abstract class HeapSnapshot {
|
|
|
1497
1581
|
});
|
|
1498
1582
|
markUnreachableNodes();
|
|
1499
1583
|
return (node: HeapSnapshotNode) => !getBit(node);
|
|
1500
|
-
case 'duplicatedStrings':
|
|
1501
|
-
|
|
1502
|
-
const node = this.createNode(0);
|
|
1503
|
-
for (let i = 0; i < this.nodeCount; ++i) {
|
|
1504
|
-
node.nodeIndex = i * this.nodeFieldCount;
|
|
1505
|
-
const rawType = node.rawType();
|
|
1506
|
-
if (rawType === this.nodeStringType || rawType === this.nodeConsStringType) {
|
|
1507
|
-
// Check whether the cons string is already "flattened", meaning
|
|
1508
|
-
// that one of its two parts is the empty string. If so, we should
|
|
1509
|
-
// skip it. We don't help anyone by reporting a flattened cons
|
|
1510
|
-
// string as a duplicate with its own content, since V8 controls
|
|
1511
|
-
// that behavior internally.
|
|
1512
|
-
if (node.isFlatConsString()) {
|
|
1513
|
-
continue;
|
|
1514
|
-
}
|
|
1515
|
-
const name = node.name();
|
|
1516
|
-
const alreadyVisitedNodeIndex = stringToNodeIndexMap.get(name);
|
|
1517
|
-
if (alreadyVisitedNodeIndex === undefined) {
|
|
1518
|
-
stringToNodeIndexMap.set(name, node.nodeIndex);
|
|
1519
|
-
} else {
|
|
1520
|
-
bitmap.setBit(alreadyVisitedNodeIndex / this.nodeFieldCount);
|
|
1521
|
-
bitmap.setBit(node.nodeIndex / this.nodeFieldCount);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
return getBit;
|
|
1526
|
-
}
|
|
1584
|
+
case 'duplicatedStrings':
|
|
1585
|
+
return this.createDuplicatedStringsFilter(bitmap);
|
|
1527
1586
|
case 'objectsRetainedByEventHandlers': {
|
|
1528
1587
|
// This filter is based on the assumption that event handler functions are contained
|
|
1529
1588
|
// (directly or indirectly) by V8EventListener nodes. In particular, the callback_object_
|
|
@@ -1589,10 +1648,87 @@ export abstract class HeapSnapshot {
|
|
|
1589
1648
|
|
|
1590
1649
|
return (node: HeapSnapshotNode) => !getBit(node);
|
|
1591
1650
|
}
|
|
1651
|
+
case 'sharedNativeContext':
|
|
1652
|
+
return (node: HeapSnapshotNode) => {
|
|
1653
|
+
const ordinal = node.nodeIndex / this.nodeFieldCount;
|
|
1654
|
+
return this.nodeNativeContextAttribution[ordinal] === SHARED_NATIVE_CONTEXT;
|
|
1655
|
+
};
|
|
1656
|
+
case 'noNativeContext':
|
|
1657
|
+
return (node: HeapSnapshotNode) => {
|
|
1658
|
+
const ordinal = node.nodeIndex / this.nodeFieldCount;
|
|
1659
|
+
return this.nodeNativeContextAttribution[ordinal] === NO_NATIVE_CONTEXT;
|
|
1660
|
+
};
|
|
1661
|
+
default:
|
|
1662
|
+
if (filterName.startsWith('nativeContext_')) {
|
|
1663
|
+
const targetNodeIndex = Number(filterName.substring('nativeContext_'.length));
|
|
1664
|
+
const targetOrdinal = targetNodeIndex / this.nodeFieldCount;
|
|
1665
|
+
return (node: HeapSnapshotNode) => {
|
|
1666
|
+
const ordinal = node.nodeIndex / this.nodeFieldCount;
|
|
1667
|
+
return this.nodeNativeContextAttribution[ordinal] === targetOrdinal;
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1592
1670
|
}
|
|
1593
1671
|
throw new Error('Invalid filter name');
|
|
1594
1672
|
}
|
|
1595
1673
|
|
|
1674
|
+
private createDuplicatedStringsFilter(bitmap: Platform.TypedArrayUtilities.BitVector):
|
|
1675
|
+
(node: HeapSnapshotNode) => boolean {
|
|
1676
|
+
const untruncatedStringToNodeIndexMap = new Map<string, number>();
|
|
1677
|
+
const truncatedStringToNodeIndexesMap =
|
|
1678
|
+
new Map<string, Array<{nodeIndex: number, length?: number, hash?: number}>>();
|
|
1679
|
+
const node = this.createNode(0);
|
|
1680
|
+
for (let i = 0; i < this.nodeCount; ++i) {
|
|
1681
|
+
node.nodeIndex = i * this.nodeFieldCount;
|
|
1682
|
+
const rawType = node.rawType();
|
|
1683
|
+
if (rawType !== this.nodeStringType && rawType !== this.nodeConsStringType) {
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
// Check whether the cons string is already "flattened", meaning
|
|
1687
|
+
// that one of its two parts is the empty string. If so, we should
|
|
1688
|
+
// skip it. We don't help anyone by reporting a flattened cons
|
|
1689
|
+
// string as a duplicate with its own content, since V8 controls
|
|
1690
|
+
// that behavior internally.
|
|
1691
|
+
if (node.isFlatConsString()) {
|
|
1692
|
+
continue;
|
|
1693
|
+
}
|
|
1694
|
+
// Skip string node used e.g. for encoding int values in the heap
|
|
1695
|
+
// snapshot. Real JS strings will have self size greater than 0.
|
|
1696
|
+
if (node.selfSize() === 0) {
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
const name = node.name();
|
|
1700
|
+
const truncated = node.nodeIsTruncatedString();
|
|
1701
|
+
if (truncated) {
|
|
1702
|
+
const length = node.nodeStringLength();
|
|
1703
|
+
const hash = node.nodeStringHash();
|
|
1704
|
+
let entries = truncatedStringToNodeIndexesMap.get(name);
|
|
1705
|
+
if (!entries) {
|
|
1706
|
+
entries = [];
|
|
1707
|
+
truncatedStringToNodeIndexesMap.set(name, entries);
|
|
1708
|
+
}
|
|
1709
|
+
const match = entries.find(e => e.length === length && e.hash === hash);
|
|
1710
|
+
if (match) {
|
|
1711
|
+
bitmap.setBit(match.nodeIndex / this.nodeFieldCount);
|
|
1712
|
+
bitmap.setBit(node.nodeIndex / this.nodeFieldCount);
|
|
1713
|
+
} else {
|
|
1714
|
+
entries.push({nodeIndex: node.nodeIndex, length, hash});
|
|
1715
|
+
}
|
|
1716
|
+
} else {
|
|
1717
|
+
const alreadyVisitedNodeIndex = untruncatedStringToNodeIndexMap.get(name);
|
|
1718
|
+
if (alreadyVisitedNodeIndex === undefined) {
|
|
1719
|
+
untruncatedStringToNodeIndexMap.set(name, node.nodeIndex);
|
|
1720
|
+
} else {
|
|
1721
|
+
bitmap.setBit(alreadyVisitedNodeIndex / this.nodeFieldCount);
|
|
1722
|
+
bitmap.setBit(node.nodeIndex / this.nodeFieldCount);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
return (node: HeapSnapshotNode): boolean => {
|
|
1727
|
+
const ordinal = node.nodeIndex / this.nodeFieldCount;
|
|
1728
|
+
return bitmap.getBit(ordinal);
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1596
1732
|
getAggregatesByClassKey(sortedIndexes: boolean, key?: string, filter?: ((arg0: HeapSnapshotNode) => boolean)):
|
|
1597
1733
|
Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo> {
|
|
1598
1734
|
let aggregates: Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo>;
|
|
@@ -2338,6 +2474,255 @@ export abstract class HeapSnapshot {
|
|
|
2338
2474
|
}
|
|
2339
2475
|
}
|
|
2340
2476
|
|
|
2477
|
+
private calculateNativeContextAttribution(): void {
|
|
2478
|
+
// Map from node ordinal to its attributed native context.
|
|
2479
|
+
// Value is either a native context ordinal (>= 0), NO_NATIVE_CONTEXT, or SHARED_NATIVE_CONTEXT.
|
|
2480
|
+
const attribution = new Int32Array(this.nodeCount).fill(NO_NATIVE_CONTEXT);
|
|
2481
|
+
|
|
2482
|
+
// First, try to infer a fixed native context for each object directly (e.g., via its map or direct links).
|
|
2483
|
+
// These direct attributions are considered "fixed" and will not be overwritten by the subsequent propagation phase.
|
|
2484
|
+
const isFixed = Platform.TypedArrayUtilities.createBitVector(this.nodeCount);
|
|
2485
|
+
const edgeTargets = this.buildInitEdgeTargets();
|
|
2486
|
+
this.#nativeContextOrdinals = [];
|
|
2487
|
+
for (let ordinal = 0; ordinal < this.nodeCount; ordinal++) {
|
|
2488
|
+
if (this.isNativeContext(ordinal)) {
|
|
2489
|
+
this.#nativeContextOrdinals.push(ordinal);
|
|
2490
|
+
attribution[ordinal] = ordinal;
|
|
2491
|
+
isFixed.setBit(ordinal);
|
|
2492
|
+
} else {
|
|
2493
|
+
const owner = this.inferFixedNativeContextForOrdinal(ordinal, edgeTargets);
|
|
2494
|
+
if (owner >= 0) {
|
|
2495
|
+
attribution[ordinal] = owner;
|
|
2496
|
+
isFixed.setBit(ordinal);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
// Propagate the fixed native context attributions to the rest of the nodes based on reachability.
|
|
2502
|
+
this.propagateNativeContextAttribution(attribution, isFixed);
|
|
2503
|
+
this.nodeNativeContextAttribution = attribution;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
private calculateNativeContextSizes(): void {
|
|
2507
|
+
const nodeFieldCount = this.nodeFieldCount;
|
|
2508
|
+
const node = this.createNode(0);
|
|
2509
|
+
const nativeContexts: HeapSnapshotModel.HeapSnapshotModel.NativeContextSize[] = [];
|
|
2510
|
+
const ordinalToInfo = new Map<number, HeapSnapshotModel.HeapSnapshotModel.NativeContextSize>();
|
|
2511
|
+
for (const ordinal of this.#nativeContextOrdinals) {
|
|
2512
|
+
node.nodeIndex = ordinal * nodeFieldCount;
|
|
2513
|
+
const info = {
|
|
2514
|
+
nodeId: node.id(),
|
|
2515
|
+
nodeIndex: node.nodeIndex,
|
|
2516
|
+
nodeName: node.name(),
|
|
2517
|
+
attributedSize: 0,
|
|
2518
|
+
retainedSize: node.retainedSize(),
|
|
2519
|
+
selfSize: node.selfSize(),
|
|
2520
|
+
};
|
|
2521
|
+
nativeContexts.push(info);
|
|
2522
|
+
ordinalToInfo.set(ordinal, info);
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
let sharedSize = 0;
|
|
2526
|
+
let noAttributionSize = 0;
|
|
2527
|
+
const selfSizeOffset = this.nodeSelfSizeOffset;
|
|
2528
|
+
const nodes = this.nodes;
|
|
2529
|
+
|
|
2530
|
+
for (let i = 0; i < this.nodeCount; ++i) {
|
|
2531
|
+
const ownerOrdinal = this.nodeNativeContextAttribution[i];
|
|
2532
|
+
const selfSize = nodes.getValue(i * nodeFieldCount + selfSizeOffset);
|
|
2533
|
+
if (ownerOrdinal === SHARED_NATIVE_CONTEXT) {
|
|
2534
|
+
sharedSize += selfSize;
|
|
2535
|
+
} else if (ownerOrdinal === NO_NATIVE_CONTEXT) {
|
|
2536
|
+
noAttributionSize += selfSize;
|
|
2537
|
+
} else {
|
|
2538
|
+
console.assert(ownerOrdinal >= 0, 'ownerOrdinal should be >= 0');
|
|
2539
|
+
const info = ordinalToInfo.get(ownerOrdinal);
|
|
2540
|
+
console.assert(info !== undefined, 'info should exist');
|
|
2541
|
+
if (info) {
|
|
2542
|
+
info.attributedSize += selfSize;
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
this.#nativeContextSizes = {
|
|
2548
|
+
nativeContexts,
|
|
2549
|
+
sharedSize,
|
|
2550
|
+
noAttributionSize,
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// Precomputes and maps specific outgoing edge targets for every node in the heap.
|
|
2555
|
+
// For each node ordinal, it stores the target ordinal of its:
|
|
2556
|
+
// - 'native_context' edge (in the returned 'nativeContext' array)
|
|
2557
|
+
// - 'map' edge (in the returned 'map' array)
|
|
2558
|
+
// This allows fast O(1) lookups of these key edges during attribution.
|
|
2559
|
+
private buildInitEdgeTargets(): {
|
|
2560
|
+
nativeContext: Int32Array,
|
|
2561
|
+
map: Int32Array,
|
|
2562
|
+
} {
|
|
2563
|
+
const {
|
|
2564
|
+
nodeCount,
|
|
2565
|
+
nodeFieldCount,
|
|
2566
|
+
containmentEdges,
|
|
2567
|
+
edgeFieldsCount,
|
|
2568
|
+
edgeTypeOffset,
|
|
2569
|
+
edgeNameOffset,
|
|
2570
|
+
edgeToNodeOffset,
|
|
2571
|
+
edgeInternalType,
|
|
2572
|
+
firstEdgeIndexes,
|
|
2573
|
+
strings,
|
|
2574
|
+
} = this;
|
|
2575
|
+
|
|
2576
|
+
const nativeContext = new Int32Array(nodeCount).fill(-1);
|
|
2577
|
+
const map = new Int32Array(nodeCount).fill(-1);
|
|
2578
|
+
|
|
2579
|
+
const nativeContextIdx = strings.indexOf('native_context');
|
|
2580
|
+
const mapIdx = strings.indexOf('map');
|
|
2581
|
+
|
|
2582
|
+
for (let ordinal = 0; ordinal < nodeCount; ordinal++) {
|
|
2583
|
+
const first = firstEdgeIndexes[ordinal];
|
|
2584
|
+
const last = firstEdgeIndexes[ordinal + 1];
|
|
2585
|
+
for (let edgeIndex = first; edgeIndex < last; edgeIndex += edgeFieldsCount) {
|
|
2586
|
+
const edgeType = containmentEdges.getValue(edgeIndex + edgeTypeOffset);
|
|
2587
|
+
if (edgeType !== edgeInternalType) {
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
const nameIdx = containmentEdges.getValue(edgeIndex + edgeNameOffset);
|
|
2592
|
+
const childNodeIndex = containmentEdges.getValue(edgeIndex + edgeToNodeOffset);
|
|
2593
|
+
const childOrdinal = childNodeIndex / nodeFieldCount;
|
|
2594
|
+
|
|
2595
|
+
if (nameIdx === nativeContextIdx && nativeContext[ordinal] === -1 && this.isNativeContext(childOrdinal)) {
|
|
2596
|
+
nativeContext[ordinal] = childOrdinal;
|
|
2597
|
+
} else if (nameIdx === mapIdx && map[ordinal] === -1) {
|
|
2598
|
+
map[ordinal] = childOrdinal;
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
return {nativeContext, map};
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
// Infers the native context for a node by looking at its Map.
|
|
2607
|
+
// In V8, objects point to their Map, and Maps point to their Meta-Map (the Map of the Map).
|
|
2608
|
+
// To save space, individual Maps do not have a direct link to the NativeContext.
|
|
2609
|
+
// Instead, the Meta-Map (which is unique per NativeContext) has a 'native_context' edge.
|
|
2610
|
+
// Thus, we can find the NativeContext of an object by traversing:
|
|
2611
|
+
// Object -> Map -> Meta-Map -> NativeContext.
|
|
2612
|
+
private inferFixedNativeContextForOrdinal(ordinal: number, edgeTargets: {
|
|
2613
|
+
nativeContext: Int32Array,
|
|
2614
|
+
map: Int32Array,
|
|
2615
|
+
}): number {
|
|
2616
|
+
const mapOrdinal = edgeTargets.map[ordinal];
|
|
2617
|
+
if (mapOrdinal >= 0) {
|
|
2618
|
+
const metaMapOrdinal = edgeTargets.map[mapOrdinal];
|
|
2619
|
+
if (metaMapOrdinal >= 0) {
|
|
2620
|
+
const mapNativeContextOrdinal = edgeTargets.nativeContext[metaMapOrdinal];
|
|
2621
|
+
if (mapNativeContextOrdinal >= 0) {
|
|
2622
|
+
return mapNativeContextOrdinal;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
return NO_NATIVE_CONTEXT;
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
private mergeNativeContextOwner(current: number, incoming: number): number {
|
|
2631
|
+
console.assert(incoming !== NO_NATIVE_CONTEXT, 'Incoming owner should not be NO_NATIVE_CONTEXT');
|
|
2632
|
+
if (current === SHARED_NATIVE_CONTEXT || incoming === SHARED_NATIVE_CONTEXT) {
|
|
2633
|
+
return SHARED_NATIVE_CONTEXT;
|
|
2634
|
+
}
|
|
2635
|
+
if (current === NO_NATIVE_CONTEXT) {
|
|
2636
|
+
return incoming;
|
|
2637
|
+
}
|
|
2638
|
+
if (current === incoming) {
|
|
2639
|
+
return current;
|
|
2640
|
+
}
|
|
2641
|
+
return SHARED_NATIVE_CONTEXT;
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
private propagateNativeContextAttribution(attribution: Int32Array,
|
|
2645
|
+
isFixed: Platform.TypedArrayUtilities.BitVector): void {
|
|
2646
|
+
const {
|
|
2647
|
+
nodeCount,
|
|
2648
|
+
containmentEdges,
|
|
2649
|
+
edgeFieldsCount,
|
|
2650
|
+
edgeTypeOffset,
|
|
2651
|
+
edgeToNodeOffset,
|
|
2652
|
+
edgeShortcutType,
|
|
2653
|
+
edgeWeakType,
|
|
2654
|
+
nodeFieldCount,
|
|
2655
|
+
firstEdgeIndexes,
|
|
2656
|
+
} = this;
|
|
2657
|
+
|
|
2658
|
+
// Initialize the queue with all nodes that have a fixed (directly inferred) native context.
|
|
2659
|
+
// Propagation will start from these "anchors".
|
|
2660
|
+
const queue: number[] = [];
|
|
2661
|
+
for (let ordinal = 0; ordinal < nodeCount; ordinal++) {
|
|
2662
|
+
if (isFixed.getBit(ordinal)) {
|
|
2663
|
+
queue.push(ordinal);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
let queueIndex = 0;
|
|
2668
|
+
while (queueIndex < queue.length) {
|
|
2669
|
+
const ordinal = queue[queueIndex];
|
|
2670
|
+
queueIndex++;
|
|
2671
|
+
|
|
2672
|
+
const current = attribution[ordinal];
|
|
2673
|
+
console.assert(current !== NO_NATIVE_CONTEXT, 'Queue should not contain unattributed nodes');
|
|
2674
|
+
|
|
2675
|
+
const first = firstEdgeIndexes[ordinal];
|
|
2676
|
+
const last = firstEdgeIndexes[ordinal + 1];
|
|
2677
|
+
for (let edgeIndex = first; edgeIndex < last; edgeIndex += edgeFieldsCount) {
|
|
2678
|
+
const edgeType = containmentEdges.getValue(edgeIndex + edgeTypeOffset);
|
|
2679
|
+
if (edgeType === edgeShortcutType || edgeType === edgeWeakType) {
|
|
2680
|
+
continue;
|
|
2681
|
+
}
|
|
2682
|
+
const childNodeIndex = containmentEdges.getValue(edgeIndex + edgeToNodeOffset);
|
|
2683
|
+
const childOrdinal = childNodeIndex / nodeFieldCount;
|
|
2684
|
+
|
|
2685
|
+
// Skip if it is a self-loop, or if the child node has a "fixed" attribution.
|
|
2686
|
+
// Fixed attributions are directly inferred and cannot be overwritten by propagation.
|
|
2687
|
+
if (childOrdinal === ordinal || isFixed.getBit(childOrdinal)) {
|
|
2688
|
+
continue;
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
// Merge the parent's native context owner into the child's owner.
|
|
2692
|
+
// Nodes can be visited multiple times: first, a node might be attributed to a specific
|
|
2693
|
+
// native context. If it is later reached by a different native context, the merge will
|
|
2694
|
+
// transition its owner to SHARED_NATIVE_CONTEXT.
|
|
2695
|
+
// If the owner changes (e.g., transitioning to SHARED), we queue the child again
|
|
2696
|
+
// to propagate the updated owner to its retainees.
|
|
2697
|
+
const merged = this.mergeNativeContextOwner(attribution[childOrdinal], current);
|
|
2698
|
+
if (merged !== attribution[childOrdinal]) {
|
|
2699
|
+
attribution[childOrdinal] = merged;
|
|
2700
|
+
queue.push(childOrdinal);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
getNativeContextSizes(): HeapSnapshotModel.HeapSnapshotModel.NativeContextSizes {
|
|
2707
|
+
return this.#nativeContextSizes;
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
nodeNativeContext(nodeIndex: number): number {
|
|
2711
|
+
const ordinal = nodeIndex / this.nodeFieldCount;
|
|
2712
|
+
const nativeContextOrdinal = this.nodeNativeContextAttribution[ordinal];
|
|
2713
|
+
if (nativeContextOrdinal < 0) {
|
|
2714
|
+
return nativeContextOrdinal;
|
|
2715
|
+
}
|
|
2716
|
+
return nativeContextOrdinal * this.nodeFieldCount;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
private isNativeContext(nodeOrdinal: number): boolean {
|
|
2720
|
+
const nameIdx = this.nodes.getValue(nodeOrdinal * this.nodeFieldCount + this.nodeNameOffset);
|
|
2721
|
+
const name = this.strings[nameIdx];
|
|
2722
|
+
return name === 'system / NativeContext' || name.startsWith('system / NativeContext / ') ||
|
|
2723
|
+
name === 'Detached system / NativeContext' || name.startsWith('Detached system / NativeContext / ');
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2341
2726
|
interfaceDefinitions(): string {
|
|
2342
2727
|
return JSON.stringify(this.#interfaceDefinitions ?? []);
|
|
2343
2728
|
}
|
|
@@ -2665,9 +3050,9 @@ export abstract class HeapSnapshot {
|
|
|
2665
3050
|
node.nodeIndex = nodeIndex;
|
|
2666
3051
|
node.setDetachedness(newState);
|
|
2667
3052
|
|
|
2668
|
-
if (newState === DOMLinkState.ATTACHED) {
|
|
3053
|
+
if (newState === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED) {
|
|
2669
3054
|
attached.push(nodeOrdinal);
|
|
2670
|
-
} else if (newState === DOMLinkState.DETACHED) {
|
|
3055
|
+
} else if (newState === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED) {
|
|
2671
3056
|
// Detached state: Rewire node name.
|
|
2672
3057
|
addDetachedPrefixToNodeName(snapshot, nodeIndex);
|
|
2673
3058
|
detached.push(nodeOrdinal);
|
|
@@ -2691,7 +3076,7 @@ export abstract class HeapSnapshot {
|
|
|
2691
3076
|
node.nodeIndex = nodeOrdinal * this.nodeFieldCount;
|
|
2692
3077
|
const state = node.detachedness();
|
|
2693
3078
|
// Bail out for objects that have no known state. For all other objects set that state.
|
|
2694
|
-
if (state === DOMLinkState.UNKNOWN) {
|
|
3079
|
+
if (state === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.UNKNOWN) {
|
|
2695
3080
|
continue;
|
|
2696
3081
|
}
|
|
2697
3082
|
processNode(this, nodeOrdinal, state);
|
|
@@ -2699,7 +3084,7 @@ export abstract class HeapSnapshot {
|
|
|
2699
3084
|
// 2. If the parent is attached, then the child is also attached.
|
|
2700
3085
|
while (attached.length !== 0) {
|
|
2701
3086
|
const nodeOrdinal = (attached.pop() as number);
|
|
2702
|
-
propagateState(this, nodeOrdinal, DOMLinkState.ATTACHED);
|
|
3087
|
+
propagateState(this, nodeOrdinal, HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED);
|
|
2703
3088
|
}
|
|
2704
3089
|
// 3. If the parent is not attached, then the child inherits the parent's state.
|
|
2705
3090
|
while (detached.length !== 0) {
|
|
@@ -2707,10 +3092,10 @@ export abstract class HeapSnapshot {
|
|
|
2707
3092
|
node.nodeIndex = nodeOrdinal * this.nodeFieldCount;
|
|
2708
3093
|
const nodeState = node.detachedness();
|
|
2709
3094
|
// Ignore if the node has been found through propagating forward attached state.
|
|
2710
|
-
if (nodeState === DOMLinkState.ATTACHED) {
|
|
3095
|
+
if (nodeState === HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED) {
|
|
2711
3096
|
continue;
|
|
2712
3097
|
}
|
|
2713
|
-
propagateState(this, nodeOrdinal, DOMLinkState.DETACHED);
|
|
3098
|
+
propagateState(this, nodeOrdinal, HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED);
|
|
2714
3099
|
}
|
|
2715
3100
|
|
|
2716
3101
|
console.timeEnd('propagateDOMState');
|