summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--webkit/glue/devtools/js/devtools.css56
-rw-r--r--webkit/glue/devtools/js/devtools.js2
-rw-r--r--webkit/glue/devtools/js/devtools_host_stub.js5
-rw-r--r--webkit/glue/devtools/js/heap_profiler_panel.js403
-rw-r--r--webkit/glue/devtools/js/profiler_processor.js46
-rw-r--r--webkit/glue/devtools_strings.grd6
6 files changed, 454 insertions, 64 deletions
diff --git a/webkit/glue/devtools/js/devtools.css b/webkit/glue/devtools/js/devtools.css
index 827f415..f57464c 100644
--- a/webkit/glue/devtools/js/devtools.css
+++ b/webkit/glue/devtools/js/devtools.css
@@ -20,7 +20,57 @@ body.attached #toolbar {
}
-div.heap-profiler {
- overflow: scroll;
- -webkit-user-select: text;
+/* Heap Profiler Styles */
+
+#heap-snapshot-views {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 200px;
+ bottom: 0;
+}
+
+#heap-snapshot-status-bar-items {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 200px;
+ overflow: hidden;
+ border-left: 1px solid rgb(184, 184, 184);
+ margin-left: -1px;
+}
+
+.heap-snapshot-status-bar-item {
+ background-image: url(Images/focusButtons.png) !important;
+}
+
+.heap-snapshot-status-bar-item:active {
+ background-position: 32px 0;
+}
+
+.heap-snapshot-sidebar-tree-item .icon {
+ content: url(Images/profileIcon.png);
+}
+
+.heap-snapshot-sidebar-tree-item.small .icon {
+ content: url(Images/profileSmallIcon.png);
+}
+
+.heap-snapshot-view {
+ display: none;
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.heap-snapshot-view.visible {
+ display: block;
+}
+
+.heap-snapshot-view .data-grid {
+ border: none;
+ height: 100%;
}
diff --git a/webkit/glue/devtools/js/devtools.js b/webkit/glue/devtools/js/devtools.js
index eedf7aa..8306057 100644
--- a/webkit/glue/devtools/js/devtools.js
+++ b/webkit/glue/devtools/js/devtools.js
@@ -958,7 +958,6 @@ WebInspector.StylePropertiesSection.prototype._doesSelectorAffectSelectedNode =
};
-/* Hiding 'Heap' tab because it's not functional yet.
(function() {
var originalCreatePanels = WebInspector._createPanels;
WebInspector._createPanels = function() {
@@ -966,4 +965,3 @@ WebInspector.StylePropertiesSection.prototype._doesSelectorAffectSelectedNode =
this.panels.heap = new WebInspector.HeapProfilerPanel();
};
})();
-*/
diff --git a/webkit/glue/devtools/js/devtools_host_stub.js b/webkit/glue/devtools/js/devtools_host_stub.js
index 166f5b4..73255fe 100644
--- a/webkit/glue/devtools/js/devtools_host_stub.js
+++ b/webkit/glue/devtools/js/devtools_host_stub.js
@@ -40,6 +40,8 @@ RemoteDebuggerAgentStub.prototype.StartProfiling = function(modules) {
'heap-sample-begin,"Heap","allocated",' +
(new Date()).getTime() + '\n' +
'heap-sample-stats,"Heap","allocated",10000,1000\n' +
+ 'heap-js-cons-item,"foo",10,1000\n' +
+ 'heap-js-cons-item,"bar",20,2000\n' +
'heap-sample-end,"Heap","allocated"\n';
}
} else {
@@ -245,7 +247,8 @@ RemoteToolsAgentStub.prototype.ExecuteUtilityFunction = function(callId,
} catch (e) {
result = [ e.toString(), true ];
}
- } else if (functionName == 'InspectorController') {
+ } else if (functionName == 'InspectorController' ||
+ functionName == 'InjectedScript') {
// do nothing;
} else {
alert('Unexpected utility function:' + functionName);
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;
+};
diff --git a/webkit/glue/devtools/js/profiler_processor.js b/webkit/glue/devtools/js/profiler_processor.js
index 202cb44..351d0be 100644
--- a/webkit/glue/devtools/js/profiler_processor.js
+++ b/webkit/glue/devtools/js/profiler_processor.js
@@ -177,14 +177,13 @@ devtools.profiler.Processor = function() {
processor: this.processHeapSampleBegin_ },
'heap-sample-stats': { parsers: [null, null, parseInt, parseInt],
processor: this.processHeapSampleStats_ },
- 'heap-sample-item': { parsers: [null, parseInt, parseInt],
- processor: this.processHeapSampleItem_ },
'heap-js-cons-item': { parsers: [null, parseInt, parseInt],
processor: this.processHeapJsConsItem_ },
'heap-sample-end': { parsers: [null, null],
processor: this.processHeapSampleEnd_ },
// Not used in DevTools Profiler.
'shared-library': null,
+ 'heap-sample-item': null,
// Obsolete row types.
'code-allocate': null,
'begin-code-region': null,
@@ -233,6 +232,18 @@ devtools.profiler.Processor = function() {
* @type {number}
*/
this.ticksCount_ = 0;
+
+ /**
+ * The current heap snapshot.
+ * @type {string}
+ */
+ this.currentHeapSnapshot_ = null;
+
+ /**
+ * Next heap snapshot id.
+ * @type {number}
+ */
+ this.heapSnapshotId_ = 1;
};
goog.inherits(devtools.profiler.Processor, devtools.profiler.LogReader);
@@ -374,41 +385,36 @@ devtools.profiler.Processor.prototype.processTick_ = function(
devtools.profiler.Processor.prototype.processHeapSampleBegin_ = function(
space, state, ticks) {
if (space != 'Heap') return;
- this.recordHeapItems_ = true;
- WebInspector.panels.heap.addLogLine(
- 'heap-sample-begin,' + space + ',' + state + ',' + ticks);
+ this.currentHeapSnapshot_ = {
+ number: this.heapSnapshotId_++,
+ entries: [],
+ ticks: ticks
+ };
};
devtools.profiler.Processor.prototype.processHeapSampleStats_ = function(
space, state, capacity, used) {
if (space != 'Heap') return;
- WebInspector.panels.heap.addLogLine(
- 'heap-sample-stats,' + space + ',' + state + ',' + capacity + ',' + used);
-};
-
-
-devtools.profiler.Processor.prototype.processHeapSampleItem_ = function(
- item, number, size) {
- if (!this.recordHeapItems_) return;
- WebInspector.panels.heap.addLogLine(
- 'heap-sample-item,' + item + ',' + number + ',' + size);
+ this.currentHeapSnapshot_.capacity = capacity;
+ this.currentHeapSnapshot_.used = used;
};
devtools.profiler.Processor.prototype.processHeapJsConsItem_ = function(
item, number, size) {
- if (!this.recordHeapItems_) return;
- WebInspector.panels.heap.addLogLine(
- 'heap-js-cons-item,' + item + ',' + number + ',' + size);
+ if (!this.currentHeapSnapshot_) return;
+ this.currentHeapSnapshot_.entries.push({
+ cons: item, count: number, size: size
+ });
};
devtools.profiler.Processor.prototype.processHeapSampleEnd_ = function(
space, state) {
if (space != 'Heap') return;
- this.recordHeapItems_ = false;
- WebInspector.panels.heap.addLogLine('heap-sample-end,' + space + ',' + state);
+ WebInspector.panels.heap.addSnapshot(this.currentHeapSnapshot_);
+ this.currentHeapSnapshot_ = null;
};
diff --git a/webkit/glue/devtools_strings.grd b/webkit/glue/devtools_strings.grd
index 02ddc27..650ca36 100644
--- a/webkit/glue/devtools_strings.grd
+++ b/webkit/glue/devtools_strings.grd
@@ -46,6 +46,12 @@ Google Chrome Developer Tools. -->
<message name="IDS_WINDOW_HEADER" desc="DevTools window header.">
Developer Tools - <ph name="URL">%s<ex>http://www.example.com/</ex></ph>
</message>
+ <message name="IDS_HEAP_SNAPSHOT" desc="Heap snapshot title.">
+ Snapshot <ph name="COUNT">%d<ex>1</ex></ph>
+ </message>
+ <message name="IDS_HEAP_USAGE" desc="Heap usage.">
+ Used <ph name="USED">%1$s<ex>100MB</ex></ph> of <ph name="CAPACITY">%2$s<ex>200MB</ex></ph>
+ </message>
</messages>
</release>
</grit>