Repository: google/pprof (language: go)

Issue:
New flamegraph:  'hover shows details on the top' from old flame-graph missing
### What version of pprof are you using?

github head.

### What operating system and processor architecture are you using?
Relveant  in this contetxt Chromium 122.0.6261.69

### What did you do?

Explore the flamegraph in the http UI.

The new flamegraph is nice, but it is missing one feature that was neat in the 'old' version: showing detailed information about a particular node (such long form of demangled name Edit: CPU % is already on the top right).

Currently, this information _is_ there, but only as a hover tooltip, i.e. it takes a second or so to show up.

I like to navigate quickly and hover over interesting nodes and want to see the details immediately, e.g. in the left top corner where it used to be in the 'old' flamegraph. The space on the top is is unused (currently, I don't know if you plan anything there), but it would be nice to have the 'old' feature back (or maybe I already can configure it somewhere and I don't know about it ?)

![hover-flamegraph2](https://github.com/google/pprof/assets/140937/bfaaf949-9ecd-487d-b584-34d4d2baa4a0)




Discussion / hints:
@ghemawat FYI, it's an older issue, but not sure if you saw it.
I'll take a look


Fix patch:
diff --git a/internal/driver/html/stacks.css b/internal/driver/html/stacks.css
index 34c54ebb..1df4f719 100644
--- a/internal/driver/html/stacks.css
+++ b/internal/driver/html/stacks.css
@@ -14,12 +14,26 @@ body {
   width: 100%;
   position: relative; /* Allows absolute positioning of child boxes */
 }
-/* Shows details of frame that is under the mouse */
+/* Holder for current frame details. */
 #current-details {
-  position: absolute;
-  top: 5px;
-  right: 5px;
+  position: relative;
+  background: #eee; /* Light grey gives better contrast with boxes */
   font-size: 12pt;
+  padding: 0 4px;
+  width: 100%;
+}
+/* Shows details of frame that is under the mouse */
+#current-details-left {
+  float: left;
+  max-width: 60%;
+  white-space: nowrap;
+  overflow-x: hidden;
+}
+#current-details-right {
+  float: right;
+  max-width: 40%;
+  white-space: nowrap;
+  overflow-x: hidden;
 }
 /* Background of a single flame-graph frame */
 .boxbg {
diff --git a/internal/driver/html/stacks.html b/internal/driver/html/stacks.html
index c2f8cf26..a4e4077e 100644
--- a/internal/driver/html/stacks.html
+++ b/internal/driver/html/stacks.html
@@ -8,9 +8,12 @@
 </head>
 <body>
   {{template "header" .}}
+  <div id="current-details">
+    <div id="current-details-left"></div>
+    <div id="current-details-right"> </div>
+  </div>
   <div id="stack-holder">
     <div id="stack-chart"></div>
-    <div id="current-details"></div>
   </div>
   <div id="action-menu" class="submenu">
     <span id="action-title"></span>
diff --git a/internal/driver/html/stacks.js b/internal/driver/html/stacks.js
index c89faf19..484c2d75 100644
--- a/internal/driver/html/stacks.js
+++ b/internal/driver/html/stacks.js
@@ -21,6 +21,7 @@ function stackViewer(stacks, nodes) {
   let actionMenuOn = false; // Is action menu visible?
   let actionTarget = null;  // Box on which action menu is operating.
   let diff = false;         // Are we displaying a diff?
+  let shown = 0;            // How many profile values are being displayed?
 
   for (const stack of stacks.Stacks) {
     if (stack.Value < 0) {
@@ -39,7 +40,8 @@ function stackViewer(stacks, nodes) {
   const search = find('search');
   const actions = find('action-menu');
   const actionTitle = find('action-title');
-  const detailBox = find('current-details');
+  const leftDetailBox = find('current-details-left');
+  const rightDetailBox = find('current-details-right');
 
   window.addEventListener('resize', render);
   window.addEventListener('popstate', render);
@@ -69,6 +71,7 @@ function stackViewer(stacks, nodes) {
     }});
 
   render();
+  clearDetails();
 
   // Helper functions follow:
 
@@ -176,17 +179,27 @@ function stackViewer(stacks, nodes) {
     if (actionMenuOn) return;
     const src = stacks.Sources[box.src];
     div.title = details(box) + ' │ ' + src.FullName + (src.Inlined ? "\n(inlined)" : "");
-    detailBox.innerText = summary(box.sumpos, box.sumneg);
+    leftDetailBox.innerText = src.FullName + (src.Inlined ? " (inlined)" : "");
+    let timing = summary(box.sumpos, box.sumneg);
+    if (box.self != 0) {
+      timing = "self " + unitText(box.self) + " │ " + timing;
+    }
+    rightDetailBox.innerText = timing;
     // Highlight all boxes that have the same source as box.
     toggleClass(box.src, 'hilite2', true);
   }
 
   function handleLeave(box) {
     if (actionMenuOn) return;
-    detailBox.innerText = '';
+    clearDetails();
     toggleClass(box.src, 'hilite2', false);
   }
 
+  function clearDetails() {
+    leftDetailBox.innerText = '';
+    rightDetailBox.innerText = percentText(shown);
+  }
+
   // Return list of sources that match the regexp given by the 'p' URL parameter.
   function urlPivots() {
     const pivots = [];
@@ -231,10 +244,14 @@ function stackViewer(stacks, nodes) {
     const x = PADDING;
     const y = 0;
 
+    // Show summary for pivots if we are actually pivoting.
+    const showPivotSummary = !(pivots.length == 1 && pivots[0] == 0);
+
+    shown = pos + neg;
     displayList.length = 0;
     renderStacks(0, xscale, x, y, places, +1);  // Callees
     renderStacks(0, xscale, x, y-ROW, places, -1);  // Callers (ROW left for separator)
-    display(xscale, pos, neg, displayList);
+    display(xscale, pos, neg, displayList, showPivotSummary);
   }
 
   // renderStacks creates boxes with top-left at x,y with children drawn as
@@ -372,7 +389,7 @@ function stackViewer(stacks, nodes) {
     return groups;
   }
 
-  function display(xscale, posTotal, negTotal, list) {
+  function display(xscale, posTotal, negTotal, list, showPivotSummary) {
     // Sort boxes so that text selection follows a predictable order.
     list.sort(function(a, b) {
       if (a.y != b.y) return a.y - b.y;
@@ -381,14 +398,15 @@ function stackViewer(stacks, nodes) {
 
     // Adjust Y coordinates so that zero is at top.
     let adjust = (list.length > 0) ? list[0].y : 0;
-    adjust -= ROW + 2*PADDING;  // Room for details
 
     const divs = [];
     for (const box of list) {
       box.y -= adjust;
       divs.push(drawBox(xscale, box));
     }
-    divs.push(drawSep(-adjust, posTotal, negTotal));
+    if (showPivotSummary) {
+      divs.push(drawSep(-adjust, posTotal, negTotal));
+    }
 
     const h = (list.length > 0 ?  list[list.length-1].y : 0) + 4*ROW;
     chart.style.height = h+'px';


Test patch: