From ac6b48ef70b3746562ef21ddf34c18ffe7efc256 Mon Sep 17 00:00:00 2001
From: "mnaganov@chromium.org"
 <mnaganov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Thu, 4 Jun 2009 09:45:05 +0000
Subject: DevTools Profiler: reuse more code from WebKit Inspector.

This enables:
- links to source code from call graph tree;
- focusing and exlusion of functions;
- search (but it seems not fully-functional in WebKit Inspector for now).

Also moved to profiler_processor WebKit-specific code from V8/tools/profile_view.

BUG=none
TEST=none

Review URL: http://codereview.chromium.org/118230

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17626 0039d316-1c4b-4281-b951-d872f2087c98
---
 webkit/glue/devtools/js/devtools.html         |   2 +
 webkit/glue/devtools/js/devtools.js           |  95 -------------------
 webkit/glue/devtools/js/devtools_host_stub.js |   1 +
 webkit/glue/devtools/js/profiler_processor.js | 131 ++++++++++++++++++++++----
 webkit/webkit.gyp                             |   2 +
 5 files changed, 120 insertions(+), 111 deletions(-)

diff --git a/webkit/glue/devtools/js/devtools.html b/webkit/glue/devtools/js/devtools.html
index 922b584..b10e208 100644
--- a/webkit/glue/devtools/js/devtools.html
+++ b/webkit/glue/devtools/js/devtools.html
@@ -101,6 +101,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="ScriptView.js"></script>
     <script type="text/javascript" src="ProfileView.js"></script>
     <script type="text/javascript" src="ProfileDataGridTree.js"></script>    
+    <script type="text/javascript" src="BottomUpProfileDataGridTree.js"></script>    
+    <script type="text/javascript" src="TopDownProfileDataGridTree.js"></script>    
     <script type="text/javascript" src="devtools.js"></script>
     <script type="text/javascript" src="devtools_host_stub.js"></script>
 </head>
diff --git a/webkit/glue/devtools/js/devtools.js b/webkit/glue/devtools/js/devtools.js
index a21eeda..5ed05c2 100644
--- a/webkit/glue/devtools/js/devtools.js
+++ b/webkit/glue/devtools/js/devtools.js
@@ -851,101 +851,6 @@ WebInspector.Console.prototype._evalInInspectedWindow = function(expression) {
 })();
 
 
-/**
- * We don't use WebKit's BottomUpProfileDataGridTree, instead using
- * our own (because BottomUpProfileDataGridTree's functionality is
- * implemented in profile_view.js for V8's Tick Processor).
- *
- * @param {WebInspector.ProfileView} profileView Profile view.
- * @param {devtools.profiler.ProfileView} profile Profile.
- */
-WebInspector.BottomUpProfileDataGridTree = function(profileView, profile) {
-  return WebInspector.buildProfileDataGridTree_(
-      profileView, profile.heavyProfile);
-};
-
-
-/**
- * We don't use WebKit's TopDownProfileDataGridTree, instead using
- * our own (because TopDownProfileDataGridTree's functionality is
- * implemented in profile_view.js for V8's Tick Processor).
- *
- * @param {WebInspector.ProfileView} profileView Profile view.
- * @param {devtools.profiler.ProfileView} profile Profile.
- */
-WebInspector.TopDownProfileDataGridTree = function(profileView, profile) {
-  return WebInspector.buildProfileDataGridTree_(
-      profileView, profile.treeProfile);
-};
-
-
-/**
- * A helper function, checks whether a profile node has visible children.
- *
- * @param {devtools.profiler.ProfileView.Node} profileNode Profile node.
- * @return {boolean} Whether a profile node has visible children.
- */
-WebInspector.nodeHasChildren_ = function(profileNode) {
-  var children = profileNode.children;
-  for (var i = 0, n = children.length; i < n; ++i) {
-    if (children[i].visible) {
-      return true;
-    }
-  }
-  return false;
-};
-
-
-/**
- * Common code for populating a profiler grid node or a tree with
- * given profile nodes.
- *
- * @param {WebInspector.ProfileDataGridNode|
- *     WebInspector.ProfileDataGridTree} viewNode Grid node or a tree.
- * @param {WebInspector.ProfileView} profileView Profile view.
- * @param {Array<devtools.profiler.ProfileView.Node>} children Profile nodes.
- * @param {WebInspector.ProfileDataGridTree} owningTree Grid tree.
- */
-WebInspector.populateNode_ = function(
-    viewNode, profileView, children, owningTree) {
-  for (var i = 0, n = children.length; i < n; ++i) {
-    var child = children[i];
-    if (child.visible) {
-      viewNode.appendChild(
-          new WebInspector.ProfileDataGridNode(
-              profileView, child, owningTree,
-              WebInspector.nodeHasChildren_(child)));
-    }
-  }
-};
-
-
-/**
- * A helper function for building a profile grid tree.
- *
- * @param {WebInspector.ProfileView} profileview Profile view.
- * @param {devtools.profiler.ProfileView} profile Profile.
- * @return {WebInspector.ProfileDataGridTree} Profile grid tree.
- */
-WebInspector.buildProfileDataGridTree_ = function(profileView, profile) {
-  var children = profile.head.children;
-  var dataGridTree = new WebInspector.ProfileDataGridTree(
-      profileView, profile.head);
-  WebInspector.populateNode_(dataGridTree, profileView, children, dataGridTree);
-  return dataGridTree;
-};
-
-
-/**
- * @override
- */
-WebInspector.ProfileDataGridNode.prototype._populate = function(event) {
-  var children = this.profileNode.children;
-  WebInspector.populateNode_(this, this.profileView, children, this.tree);
-  this.removeEventListener("populate", this._populate, this);
-};
-
-
 // As columns in data grid can't be changed after initialization,
 // we need to intercept the constructor and modify columns upon creation.
 (function InterceptDataGridForProfiler() {
diff --git a/webkit/glue/devtools/js/devtools_host_stub.js b/webkit/glue/devtools/js/devtools_host_stub.js
index fa9aba1..4538e83 100644
--- a/webkit/glue/devtools/js/devtools_host_stub.js
+++ b/webkit/glue/devtools/js/devtools_host_stub.js
@@ -244,6 +244,7 @@ RemoteToolsAgentStub.prototype.SetResourceTrackingEnabled = function(enabled, al
 
 
 RemoteDebuggerAgentStub.ProfilerLogBuffer =
+  'profiler,begin,1\n' +
   'profiler,resume\n' +
   'code-creation,LazyCompile,0x1000,256,"test1 http://aaa.js:1"\n' +
   'code-creation,LazyCompile,0x2000,256,"test2 http://bbb.js:2"\n' +
diff --git a/webkit/glue/devtools/js/profiler_processor.js b/webkit/glue/devtools/js/profiler_processor.js
index 117afc8..9f06acb3 100644
--- a/webkit/glue/devtools/js/profiler_processor.js
+++ b/webkit/glue/devtools/js/profiler_processor.js
@@ -11,6 +11,91 @@ goog.provide('devtools.profiler.Processor');
 
 
 /**
+ * Creates a Profile View builder object compatible with WebKit Profiler UI.
+ *
+ * @param {number} samplingRate Number of ms between profiler ticks.
+ * @constructor
+ */
+devtools.profiler.WebKitViewBuilder = function(samplingRate) {
+  devtools.profiler.ViewBuilder.call(this, samplingRate);
+};
+goog.inherits(devtools.profiler.WebKitViewBuilder,
+    devtools.profiler.ViewBuilder);
+
+
+/**
+ * @override
+ */
+devtools.profiler.WebKitViewBuilder.prototype.createViewNode = function(
+    funcName, totalTime, selfTime, head) {
+  return new devtools.profiler.WebKitViewNode(
+      funcName, totalTime, selfTime, head);
+};
+
+
+/**
+ * Constructs a Profile View node object for displaying in WebKit Profiler UI.
+ *
+ * @param {string} internalFuncName A fully qualified function name.
+ * @param {number} totalTime Amount of time that application spent in the
+ *     corresponding function and its descendants (not that depending on
+ *     profile they can be either callees or callers.)
+ * @param {number} selfTime Amount of time that application spent in the
+ *     corresponding function only.
+ * @param {devtools.profiler.ProfileView.Node} head Profile view head.
+ * @constructor
+ */
+devtools.profiler.WebKitViewNode = function(
+    internalFuncName, totalTime, selfTime, head) {
+  devtools.profiler.ProfileView.Node.call(this,
+      internalFuncName, totalTime, selfTime, head);
+  this.initFuncInfo_();
+  this.callUID = internalFuncName;
+};
+goog.inherits(devtools.profiler.WebKitViewNode,
+    devtools.profiler.ProfileView.Node);
+
+
+/**
+ * RegEx for stripping V8's prefixes of compiled functions.
+ */
+devtools.profiler.WebKitViewNode.FUNC_NAME_STRIP_RE =
+    /^(?:LazyCompile|Function): (.*)$/;
+
+
+/**
+ * RegEx for extracting script source URL and line number.
+ */
+devtools.profiler.WebKitViewNode.FUNC_NAME_PARSE_RE = /^([^ ]+) (.*):(\d+)$/;
+
+
+/**
+ * Inits 'functionName', 'url', and 'lineNumber' fields using 'internalFuncName'
+ * field.
+ * @private
+ */
+devtools.profiler.WebKitViewNode.prototype.initFuncInfo_ = function() {
+  var nodeAlias = devtools.profiler.WebKitViewNode;
+  this.functionName = this.internalFuncName;
+
+  var strippedName = nodeAlias.FUNC_NAME_STRIP_RE.exec(this.functionName);
+  if (strippedName) {
+    this.functionName = strippedName[1];
+  }
+
+  var parsedName = nodeAlias.FUNC_NAME_PARSE_RE.exec(this.functionName);
+  if (parsedName) {
+    this.functionName = parsedName[1];
+    this.url = parsedName[2];
+    this.lineNumber = parsedName[3];
+  } else {
+    this.url = '';
+    this.lineNumber = 0;
+  }
+};
+
+
+/**
  * Ancestor of a profile object that leaves out only JS-related functions.
  * @constructor
  */
@@ -78,7 +163,7 @@ devtools.profiler.Processor = function(newProfileCallback) {
 
   /**
    * Builder of profile views. Created during "profiler,begin" event processing.
-   * @type {devtools.profiler.ViewBuilder}
+   * @type {devtools.profiler.WebKitViewBuilder}
    */
   this.viewBuilder_ = null;
 
@@ -91,6 +176,19 @@ devtools.profiler.Processor = function(newProfileCallback) {
 
 
 /**
+ * An address for the fake "(program)" entry. WebKit's visualisation
+ * has assumptions on how the top of the call tree should look like,
+ * and we need to add a fake entry as the topmost function. This
+ * address is chosen because it's the end address of the first memory
+ * page, which is never used for code or data, but only as a guard
+ * page for catching AV errors.
+ *
+ * @type {number}
+ */
+devtools.profiler.Processor.PROGRAM_ENTRY = 0xffff;
+
+
+/**
  * A dispatch table for V8 profiler event log records.
  * @private
  */
@@ -206,21 +304,26 @@ devtools.profiler.Processor.prototype.dispatchLogRow_ = function(fields) {
 };
 
 
-devtools.profiler.Processor.prototype.processProfiler_ = function(state, params) {
+devtools.profiler.Processor.prototype.processProfiler_ = function(
+    state, params) {
   switch (state) {
-    case "resume":
+    case 'resume':
       if (this.currentProfile_ == null) {
         this.currentProfile_ = new devtools.profiler.JsProfile();
         this.profiles_.push(this.currentProfile_);
+        // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY
+        this.currentProfile_.addCode(
+          'Function', '(program)',
+          devtools.profiler.Processor.PROGRAM_ENTRY, 1);
       }
       break;
-    case "pause":
+    case 'pause':
       if (this.currentProfile_ != null) {
         this.newProfileCallback_(this.createProfileForView());
         this.currentProfile_ = null;
       }
       break;
-    case "begin":
+    case 'begin':
       var samplingRate = NaN;
       if (params.length > 0) {
         samplingRate = parseInt(params[0]);
@@ -228,12 +331,12 @@ devtools.profiler.Processor.prototype.processProfiler_ = function(state, params)
       if (isNaN(samplingRate)) {
         samplingRate = 1;
       }
-      this.viewBuilder_ = new devtools.profiler.ViewBuilder(samplingRate);
+      this.viewBuilder_ = new devtools.profiler.WebKitViewBuilder(samplingRate);
       break;
     // This event is valid but isn't used.
-    case "end": break;
+    case 'end': break;
     default:
-      throw new Error("unknown profiler state: " + state);
+      throw new Error('unknown profiler state: ' + state);
   }
 };
 
@@ -264,6 +367,8 @@ devtools.profiler.Processor.prototype.processTick_ = function(
       fullStack.push(parseInt(frame, 16));
     }
   }
+  // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY
+  fullStack.push(devtools.profiler.Processor.PROGRAM_ENTRY);
   this.currentProfile_.recordTick(fullStack);
 };
 
@@ -272,15 +377,9 @@ devtools.profiler.Processor.prototype.processTick_ = function(
  * Creates a profile for further displaying in ProfileView.
  */
 devtools.profiler.Processor.prototype.createProfileForView = function() {
-  var profile = new devtools.profiler.ProfileView();
+  var profile = this.viewBuilder_.buildView(
+      this.currentProfile_.getTopDownProfile());
   profile.uid = this.profileId_++;
   profile.title = UserInitiatedProfileName + '.' + profile.uid;
-  // A trick to cope with ProfileView.bottomUpProfileDataGridTree and
-  // ProfileView.topDownProfileDataGridTree behavior.
-  profile.head = profile;
-  profile.heavyProfile = this.viewBuilder_.buildView(
-      this.currentProfile_.getBottomUpProfile(), true);
-  profile.treeProfile = this.viewBuilder_.buildView(
-      this.currentProfile_.getTopDownProfile());
   return profile;
 };
diff --git a/webkit/webkit.gyp b/webkit/webkit.gyp
index 41acb49..6a977bb 100644
--- a/webkit/webkit.gyp
+++ b/webkit/webkit.gyp
@@ -4640,6 +4640,7 @@
             'inspector/DebuggerIPC.js',
             'inspector/DebuggerPanel.js',
             'inspector/DebuggerShell.js',
+            '../third_party/WebKit/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js',
             '../third_party/WebKit/WebCore/inspector/front-end/Breakpoint.js',
             '../third_party/WebKit/WebCore/inspector/front-end/BreakpointsSidebarPane.js',
             '../third_party/WebKit/WebCore/inspector/front-end/CallStackSidebarPane.js',
@@ -4681,6 +4682,7 @@
             '../third_party/WebKit/WebCore/inspector/front-end/SourceView.js',
             '../third_party/WebKit/WebCore/inspector/front-end/StylesSidebarPane.js',
             '../third_party/WebKit/WebCore/inspector/front-end/TextPrompt.js',
+            '../third_party/WebKit/WebCore/inspector/front-end/TopDownProfileDataGridTree.js',
             '../third_party/WebKit/WebCore/inspector/front-end/treeoutline.js',
             '../third_party/WebKit/WebCore/inspector/front-end/utilities.js',
             '../third_party/WebKit/WebCore/inspector/front-end/View.js',
-- 
cgit v1.1