diff options
Diffstat (limited to 'webkit/glue/devtools/js/heap_profiler_panel.js')
-rw-r--r-- | webkit/glue/devtools/js/heap_profiler_panel.js | 403 |
1 files changed, 365 insertions, 38 deletions
diff --git a/webkit/glue/devtools/js/heap_profiler_panel.js b/webkit/glue/devtools/js/heap_profiler_panel.js index df98b08..890740b 100644 --- a/webkit/glue/devtools/js/heap_profiler_panel.js +++ b/webkit/glue/devtools/js/heap_profiler_panel.js @@ -1,53 +1,380 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Heap profiler panel implementation. + */ + WebInspector.HeapProfilerPanel = function() { - WebInspector.Panel.call(this); + WebInspector.Panel.call(this); + + this.element.addStyleClass("heap-profiler"); + + this.sidebarElement = document.createElement("div"); + this.sidebarElement.id = "heap-snapshot-sidebar"; + this.sidebarElement.className = "sidebar"; + this.element.appendChild(this.sidebarElement); + + this.sidebarResizeElement = document.createElement("div"); + this.sidebarResizeElement.className = "sidebar-resizer-vertical"; + this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false); + this.element.appendChild(this.sidebarResizeElement); - this.element.addStyleClass('heap-profiler'); + this.sidebarTreeElement = document.createElement("ol"); + this.sidebarTreeElement.className = "sidebar-tree"; + this.sidebarElement.appendChild(this.sidebarTreeElement); - this.recordButton = document.createElement('button'); - this.recordButton.title = WebInspector.UIString('Take heap snapshot.'); - // TODO(mnaganov): Use an icon. - this.recordButton.textContent = "SS"; - this.recordButton.id = 'take-heap-snapshot-status-bar-item'; - this.recordButton.className = 'status-bar-item'; - this.recordButton.addEventListener('click', - this._takeSnapshotClicked.bind(this), - false); + this.sidebarTree = new TreeOutline(this.sidebarTreeElement); + + this.snapshotViews = document.createElement("div"); + this.snapshotViews.id = "heap-snapshot-views"; + this.element.appendChild(this.snapshotViews); + + this.snapshotButton = document.createElement("button"); + this.snapshotButton.title = WebInspector.UIString("Take heap snapshot."); + this.snapshotButton.className = "heap-snapshot-status-bar-item status-bar-item"; + this.snapshotButton.addEventListener("click", this._snapshotClicked.bind(this), false); + + this.snapshotViewStatusBarItemsContainer = document.createElement("div"); + this.snapshotViewStatusBarItemsContainer.id = "heap-snapshot-status-bar-items"; + + this.reset(); }; WebInspector.HeapProfilerPanel.prototype = { - toolbarItemClass: 'heap-profiler', + toolbarItemClass: "heap-profiler", + + get toolbarItemLabel() { + return WebInspector.UIString("Heap"); + }, + + get statusBarItems() { + return [this.snapshotButton, this.snapshotViewStatusBarItemsContainer]; + }, + + show: function() { + WebInspector.Panel.prototype.show.call(this); + this._updateSidebarWidth(); + }, + + reset: function() { + if (this._snapshots) { + var snapshotsLength = this._snapshots.length; + for (var i = 0; i < snapshotsLength; ++i) { + var snapshot = this._snapshots[i]; + delete snapshot._snapshotView; + } + } + + this._snapshots = []; + + this.sidebarTreeElement.removeStyleClass("some-expandable"); + + this.sidebarTree.removeChildren(); + this.snapshotViews.removeChildren(); + + this.snapshotViewStatusBarItemsContainer.removeChildren(); + }, + + handleKeyEvent: function(event) { + }, + + addSnapshot: function(snapshot) { + this._snapshots.push(snapshot); + + var sidebarParent = this.sidebarTree; + var snapshotsTreeElement = new WebInspector.HeapSnapshotSidebarTreeElement(snapshot); + snapshotsTreeElement.small = false; + snapshot._snapshotsTreeElement = snapshotsTreeElement; - get toolbarItemLabel() { - return WebInspector.UIString('Heap'); - }, + sidebarParent.appendChild(snapshotsTreeElement); + }, - get statusBarItems() { - return [this.recordButton]; - }, + showSnapshot: function(snapshot) { + if (!snapshot) + return; - show: function() { - WebInspector.Panel.prototype.show.call(this); - }, + if (this.visibleView) + this.visibleView.hide(); + var view = this.snapshotViewForSnapshot(snapshot); + view.show(this.snapshotViews); + this.visibleView = view; - reset: function() { - }, + this.snapshotViewStatusBarItemsContainer.removeChildren(); + var statusBarItems = view.statusBarItems; + for (var i = 0; i < statusBarItems.length; ++i) + this.snapshotViewStatusBarItemsContainer.appendChild(statusBarItems[i]); + }, - handleKeyEvent: function(event) { - }, + showView: function(view) + { + this.showSnapshot(view.snapshot); + }, - addLogLine: function(logLine) { - var line = document.createElement('div'); - line.innerHTML = logLine; - this.element.appendChild(line); - }, + snapshotViewForSnapshot: function(snapshot) + { + if (!snapshot) + return null; + if (!snapshot._snapshotView) + snapshot._snapshotView = new WebInspector.HeapSnapshotView(snapshot); + return snapshot._snapshotView; + }, - _takeSnapshotClicked: function() { - devtools.tools.getDebuggerAgent().startProfiling( - devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_HEAP_SNAPSHOT | - devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_HEAP_STATS | - devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_JS_CONSTRUCTORS); - } + closeVisibleView: function() + { + if (this.visibleView) + this.visibleView.hide(); + delete this.visibleView; + }, + + _snapshotClicked: function() { + devtools.tools.getDebuggerAgent().startProfiling( + devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_HEAP_SNAPSHOT | + devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_HEAP_STATS | + devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_JS_CONSTRUCTORS); + }, + + _startSidebarDragging: function(event) + { + WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); + }, + + _sidebarDragging: function(event) + { + this._updateSidebarWidth(event.pageX); + + event.preventDefault(); + }, + + _endSidebarDragging: function(event) + { + WebInspector.elementDragEnd(event); + }, + + _updateSidebarWidth: function(width) + { + if (this.sidebarElement.offsetWidth <= 0) { + // The stylesheet hasn"t loaded yet or the window is closed, + // so we can"t calculate what is need. Return early. + return; + } + + if (!("_currentSidebarWidth" in this)) + this._currentSidebarWidth = this.sidebarElement.offsetWidth; + if (typeof width === "undefined") + width = this._currentSidebarWidth; + + width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); + this._currentSidebarWidth = width; + this.sidebarElement.style.width = width + "px"; + this.snapshotViews.style.left = width + "px"; + this.snapshotViewStatusBarItemsContainer.style.left = width + "px"; + this.sidebarResizeElement.style.left = (width - 3) + "px"; + } }; -WebInspector.HeapProfilerPanel.prototype.__proto__ = - WebInspector.Panel.prototype; +WebInspector.HeapProfilerPanel.prototype.__proto__ = WebInspector.Panel.prototype; + +WebInspector.HeapSnapshotView = function(snapshot) +{ + WebInspector.View.call(this); + + this.element.addStyleClass("heap-snapshot-view"); + + var columns = { "cons": { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true }, + "count": { title: WebInspector.UIString("Count"), width: "54px", sortable: true }, + "size": { title: WebInspector.UIString("Size"), width: "72px", sort: "descending", sortable: true } }; + + this.dataGrid = new WebInspector.DataGrid(columns); + this.dataGrid.addEventListener("sorting changed", this._sortData, this); + this.element.appendChild(this.dataGrid.element); + + this.snapshot = snapshot; + this.snapshotDataGridList = this.createSnapshotDataGridList(); + this.snapshotDataGridList.sort(WebInspector.HeapSnapshotDataGridList.propertyComparator("objectsSize", false)); + + this.refresh(); +}; + +WebInspector.HeapSnapshotView.prototype = { + get statusBarItems() + { + return []; + }, + + get snapshot() + { + return this._snapshot; + }, + + set snapshot(snapshot) + { + this._snapshot = snapshot; + }, + + createSnapshotDataGridList: function() + { + if (!this._snapshotDataGridList) + this._snapshotDataGridList = new WebInspector.HeapSnapshotDataGridList(this, this.snapshot.entries); + return this._snapshotDataGridList; + }, + + refresh: function() + { + this.dataGrid.removeChildren(); + + var children = this.snapshotDataGridList.children; + var count = children.length; + for (var index = 0; index < count; ++index) + this.dataGrid.appendChild(children[index]); + }, + + _sortData: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortProperty = { + "cons": "constructorName", + "count": "objectsCount", + "size": "objectsSize" + }[sortColumnIdentifier]; + + this.snapshotDataGridList.sort(WebInspector.HeapSnapshotDataGridList.propertyComparator(sortProperty, sortAscending)); + + this.refresh(); + } +}; + +WebInspector.HeapSnapshotView.prototype.__proto__ = WebInspector.View.prototype; + +WebInspector.HeapSnapshotSidebarTreeElement = function(snapshot) +{ + this.snapshot = snapshot; + this._snapshotNumber = snapshot.number; + + WebInspector.SidebarTreeElement.call(this, "heap-snapshot-sidebar-tree-item", "", "", snapshot, false); + + this.refreshTitles(); +}; + +WebInspector.HeapSnapshotSidebarTreeElement.prototype = { + onselect: function() + { + WebInspector.panels.heap.showSnapshot(this.snapshot); + }, + + get mainTitle() + { + if (this._mainTitle) + return this._mainTitle; + return WebInspector.UIString("Snapshot %d", this._snapshotNumber); + }, + + set mainTitle(x) + { + this._mainTitle = x; + this.refreshTitles(); + }, + + get subtitle() + { + if (this._subTitle) + return this._subTitle; + return WebInspector.UIString("Used %s of %s", Number.bytesToString(this.snapshot.used), Number.bytesToString(this.snapshot.capacity)); + }, + + set subtitle(x) + { + this._subTitle = x; + this.refreshTitles(); + } +}; + +WebInspector.HeapSnapshotSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; + +WebInspector.HeapSnapshotDataGridNode = function(snapshotView, snapshotEntry, owningList) +{ + this.snapshotView = snapshotView; + this.snapshotEntry = snapshotEntry; + + WebInspector.DataGridNode.call(this, null, false); + + this.list = owningList; + this.lastComparator = null; + + this.constructorName = snapshotEntry.cons; + this.objectsCount = snapshotEntry.count; + this.objectsSize = snapshotEntry.size; +}; + +WebInspector.HeapSnapshotDataGridNode.prototype = { + get data() + { + var data = { + cons: this.constructorName, + count: this.objectsCount, + size: Number.bytesToString(this.objectsSize) + }; + return data; + } +}; + +WebInspector.HeapSnapshotDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; + +WebInspector.HeapSnapshotDataGridList = function(snapshotView, snapshotEntries) +{ + this.list = this; + this.snapshotView = snapshotView; + this.children = []; + this.lastComparator = null; + this.populateChildren(snapshotEntries); +}; + +WebInspector.HeapSnapshotDataGridList.prototype = { + appendChild: function(child) + { + this.insertChild(child, this.children.length); + }, + + insertChild: function(child, index) + { + this.children.splice(index, 0, child); + }, + + removeChildren: function() + { + this.children = []; + }, + + populateChildren: function(snapshotEntries) + { + var count = snapshotEntries.length; + for (var index = 0; index < count; ++index) + this.appendChild(new WebInspector.HeapSnapshotDataGridNode(this.snapshotView, snapshotEntries[index], this)); + }, + + sort: function(comparator, force) { + if (!force && this.lastComparator === comparator) + return; + + this.children.sort(comparator); + this.lastComparator = comparator; + } +}; + +WebInspector.HeapSnapshotDataGridList.propertyComparators = [{}, {}]; + +WebInspector.HeapSnapshotDataGridList.propertyComparator = function(property, isAscending) +{ + var comparator = this.propertyComparators[(isAscending ? 1 : 0)][property]; + if (!comparator) { + comparator = function(lhs, rhs) { + var l = lhs[property], r = rhs[property]; + var result = l < r ? -1 : (l > r ? 1 : 0); + return isAscending ? result : -result; + }; + this.propertyComparators[(isAscending ? 1 : 0)][property] = comparator; + } + return comparator; +}; |