1*288bf522SAndroid Build Coastguard Worker/* 2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project 3*288bf522SAndroid Build Coastguard Worker * 4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*288bf522SAndroid Build Coastguard Worker * 8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*288bf522SAndroid Build Coastguard Worker * 10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*288bf522SAndroid Build Coastguard Worker * limitations under the License. 15*288bf522SAndroid Build Coastguard Worker */ 16*288bf522SAndroid Build Coastguard Worker'use strict'; 17*288bf522SAndroid Build Coastguard Worker 18*288bf522SAndroid Build Coastguard Workerfunction flamegraphInit() { 19*288bf522SAndroid Build Coastguard Worker let flamegraph = document.getElementById('flamegraph_id'); 20*288bf522SAndroid Build Coastguard Worker let svgs = flamegraph.getElementsByTagName('svg'); 21*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < svgs.length; ++i) { 22*288bf522SAndroid Build Coastguard Worker createZoomHistoryStack(svgs[i]); 23*288bf522SAndroid Build Coastguard Worker adjust_text_size(svgs[i]); 24*288bf522SAndroid Build Coastguard Worker } 25*288bf522SAndroid Build Coastguard Worker 26*288bf522SAndroid Build Coastguard Worker function throttle(callback) { 27*288bf522SAndroid Build Coastguard Worker let running = false; 28*288bf522SAndroid Build Coastguard Worker return function() { 29*288bf522SAndroid Build Coastguard Worker if (!running) { 30*288bf522SAndroid Build Coastguard Worker running = true; 31*288bf522SAndroid Build Coastguard Worker window.requestAnimationFrame(function () { 32*288bf522SAndroid Build Coastguard Worker callback(); 33*288bf522SAndroid Build Coastguard Worker running = false; 34*288bf522SAndroid Build Coastguard Worker }); 35*288bf522SAndroid Build Coastguard Worker } 36*288bf522SAndroid Build Coastguard Worker }; 37*288bf522SAndroid Build Coastguard Worker } 38*288bf522SAndroid Build Coastguard Worker window.addEventListener('resize', throttle(function() { 39*288bf522SAndroid Build Coastguard Worker let flamegraph = document.getElementById('flamegraph_id'); 40*288bf522SAndroid Build Coastguard Worker let svgs = flamegraph.getElementsByTagName('svg'); 41*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < svgs.length; ++i) { 42*288bf522SAndroid Build Coastguard Worker adjust_text_size(svgs[i]); 43*288bf522SAndroid Build Coastguard Worker } 44*288bf522SAndroid Build Coastguard Worker })); 45*288bf522SAndroid Build Coastguard Worker} 46*288bf522SAndroid Build Coastguard Worker 47*288bf522SAndroid Build Coastguard Worker// Create a stack add the root svg element in it. 48*288bf522SAndroid Build Coastguard Workerfunction createZoomHistoryStack(svgElement) { 49*288bf522SAndroid Build Coastguard Worker svgElement.zoomStack = [svgElement.getElementById(svgElement.attributes['rootid'].value)]; 50*288bf522SAndroid Build Coastguard Worker} 51*288bf522SAndroid Build Coastguard Worker 52*288bf522SAndroid Build Coastguard Workerfunction adjust_node_text_size(x, svgWidth) { 53*288bf522SAndroid Build Coastguard Worker let title = x.getElementsByTagName('title')[0]; 54*288bf522SAndroid Build Coastguard Worker let text = x.getElementsByTagName('text')[0]; 55*288bf522SAndroid Build Coastguard Worker let rect = x.getElementsByTagName('rect')[0]; 56*288bf522SAndroid Build Coastguard Worker 57*288bf522SAndroid Build Coastguard Worker let width = parseFloat(rect.attributes['width'].value) * svgWidth * 0.01; 58*288bf522SAndroid Build Coastguard Worker 59*288bf522SAndroid Build Coastguard Worker // Don't even bother trying to find a best fit. The area is too small. 60*288bf522SAndroid Build Coastguard Worker if (width < 28) { 61*288bf522SAndroid Build Coastguard Worker text.textContent = ''; 62*288bf522SAndroid Build Coastguard Worker return; 63*288bf522SAndroid Build Coastguard Worker } 64*288bf522SAndroid Build Coastguard Worker // Remove dso and #samples which are here only for mouseover purposes. 65*288bf522SAndroid Build Coastguard Worker let methodName = title.textContent.split(' | ')[0]; 66*288bf522SAndroid Build Coastguard Worker 67*288bf522SAndroid Build Coastguard Worker let numCharacters; 68*288bf522SAndroid Build Coastguard Worker for (numCharacters = methodName.length; numCharacters > 4; numCharacters--) { 69*288bf522SAndroid Build Coastguard Worker // Avoid reflow by using hard-coded estimate instead of 70*288bf522SAndroid Build Coastguard Worker // text.getSubStringLength(0, numCharacters). 71*288bf522SAndroid Build Coastguard Worker if (numCharacters * 7.5 <= width) { 72*288bf522SAndroid Build Coastguard Worker break; 73*288bf522SAndroid Build Coastguard Worker } 74*288bf522SAndroid Build Coastguard Worker } 75*288bf522SAndroid Build Coastguard Worker 76*288bf522SAndroid Build Coastguard Worker if (numCharacters == methodName.length) { 77*288bf522SAndroid Build Coastguard Worker text.textContent = methodName; 78*288bf522SAndroid Build Coastguard Worker return; 79*288bf522SAndroid Build Coastguard Worker } 80*288bf522SAndroid Build Coastguard Worker 81*288bf522SAndroid Build Coastguard Worker text.textContent = methodName.substring(0, numCharacters-2) + '..'; 82*288bf522SAndroid Build Coastguard Worker} 83*288bf522SAndroid Build Coastguard Worker 84*288bf522SAndroid Build Coastguard Workerfunction adjust_text_size(svgElement) { 85*288bf522SAndroid Build Coastguard Worker let svgWidth = window.innerWidth; 86*288bf522SAndroid Build Coastguard Worker let x = svgElement.getElementsByTagName('g'); 87*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < x.length; i++) { 88*288bf522SAndroid Build Coastguard Worker adjust_node_text_size(x[i], svgWidth); 89*288bf522SAndroid Build Coastguard Worker } 90*288bf522SAndroid Build Coastguard Worker} 91*288bf522SAndroid Build Coastguard Worker 92*288bf522SAndroid Build Coastguard Workerfunction zoom(e) { 93*288bf522SAndroid Build Coastguard Worker let svgElement = e.ownerSVGElement; 94*288bf522SAndroid Build Coastguard Worker let zoomStack = svgElement.zoomStack; 95*288bf522SAndroid Build Coastguard Worker zoomStack.push(e); 96*288bf522SAndroid Build Coastguard Worker displaySVGElement(svgElement); 97*288bf522SAndroid Build Coastguard Worker select(e); 98*288bf522SAndroid Build Coastguard Worker 99*288bf522SAndroid Build Coastguard Worker // Show zoom out button. 100*288bf522SAndroid Build Coastguard Worker svgElement.getElementById('zoom_rect').style.display = 'block'; 101*288bf522SAndroid Build Coastguard Worker svgElement.getElementById('zoom_text').style.display = 'block'; 102*288bf522SAndroid Build Coastguard Worker} 103*288bf522SAndroid Build Coastguard Worker 104*288bf522SAndroid Build Coastguard Workerfunction displaySVGElement(svgElement) { 105*288bf522SAndroid Build Coastguard Worker let zoomStack = svgElement.zoomStack; 106*288bf522SAndroid Build Coastguard Worker let e = zoomStack[zoomStack.length - 1]; 107*288bf522SAndroid Build Coastguard Worker let clicked_rect = e.getElementsByTagName('rect')[0]; 108*288bf522SAndroid Build Coastguard Worker let clicked_origin_x; 109*288bf522SAndroid Build Coastguard Worker let clicked_origin_y = clicked_rect.attributes['oy'].value; 110*288bf522SAndroid Build Coastguard Worker let clicked_origin_width; 111*288bf522SAndroid Build Coastguard Worker 112*288bf522SAndroid Build Coastguard Worker if (zoomStack.length == 1) { 113*288bf522SAndroid Build Coastguard Worker // Show all nodes when zoomStack only contains the root node. 114*288bf522SAndroid Build Coastguard Worker // This is needed to show flamegraph containing more than one node at the root level. 115*288bf522SAndroid Build Coastguard Worker clicked_origin_x = 0; 116*288bf522SAndroid Build Coastguard Worker clicked_origin_width = 100; 117*288bf522SAndroid Build Coastguard Worker } else { 118*288bf522SAndroid Build Coastguard Worker clicked_origin_x = clicked_rect.attributes['ox'].value; 119*288bf522SAndroid Build Coastguard Worker clicked_origin_width = clicked_rect.attributes['owidth'].value; 120*288bf522SAndroid Build Coastguard Worker } 121*288bf522SAndroid Build Coastguard Worker 122*288bf522SAndroid Build Coastguard Worker 123*288bf522SAndroid Build Coastguard Worker let svgBox = svgElement.getBoundingClientRect(); 124*288bf522SAndroid Build Coastguard Worker let svgBoxHeight = svgBox.height; 125*288bf522SAndroid Build Coastguard Worker let svgBoxWidth = 100; 126*288bf522SAndroid Build Coastguard Worker let scaleFactor = svgBoxWidth / clicked_origin_width; 127*288bf522SAndroid Build Coastguard Worker 128*288bf522SAndroid Build Coastguard Worker let callsites = svgElement.getElementsByTagName('g'); 129*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < callsites.length; i++) { 130*288bf522SAndroid Build Coastguard Worker let text = callsites[i].getElementsByTagName('text')[0]; 131*288bf522SAndroid Build Coastguard Worker let rect = callsites[i].getElementsByTagName('rect')[0]; 132*288bf522SAndroid Build Coastguard Worker 133*288bf522SAndroid Build Coastguard Worker let rect_o_x = parseFloat(rect.attributes['ox'].value); 134*288bf522SAndroid Build Coastguard Worker let rect_o_y = parseFloat(rect.attributes['oy'].value); 135*288bf522SAndroid Build Coastguard Worker 136*288bf522SAndroid Build Coastguard Worker // Avoid multiple forced reflow by hiding nodes. 137*288bf522SAndroid Build Coastguard Worker if (rect_o_y > clicked_origin_y) { 138*288bf522SAndroid Build Coastguard Worker rect.style.display = 'none'; 139*288bf522SAndroid Build Coastguard Worker text.style.display = 'none'; 140*288bf522SAndroid Build Coastguard Worker continue; 141*288bf522SAndroid Build Coastguard Worker } 142*288bf522SAndroid Build Coastguard Worker rect.style.display = 'block'; 143*288bf522SAndroid Build Coastguard Worker text.style.display = 'block'; 144*288bf522SAndroid Build Coastguard Worker 145*288bf522SAndroid Build Coastguard Worker let newrec_x = rect.attributes['x'].value = (rect_o_x - clicked_origin_x) * scaleFactor + 146*288bf522SAndroid Build Coastguard Worker '%'; 147*288bf522SAndroid Build Coastguard Worker let newrec_y = rect.attributes['y'].value = rect_o_y + (svgBoxHeight - clicked_origin_y 148*288bf522SAndroid Build Coastguard Worker - 17 - 2); 149*288bf522SAndroid Build Coastguard Worker 150*288bf522SAndroid Build Coastguard Worker text.attributes['y'].value = newrec_y + 12; 151*288bf522SAndroid Build Coastguard Worker text.attributes['x'].value = newrec_x; 152*288bf522SAndroid Build Coastguard Worker 153*288bf522SAndroid Build Coastguard Worker rect.attributes['width'].value = (rect.attributes['owidth'].value * scaleFactor) + '%'; 154*288bf522SAndroid Build Coastguard Worker } 155*288bf522SAndroid Build Coastguard Worker 156*288bf522SAndroid Build Coastguard Worker adjust_text_size(svgElement); 157*288bf522SAndroid Build Coastguard Worker} 158*288bf522SAndroid Build Coastguard Worker 159*288bf522SAndroid Build Coastguard Workerfunction unzoom(e) { 160*288bf522SAndroid Build Coastguard Worker let svgOwner = e.ownerSVGElement; 161*288bf522SAndroid Build Coastguard Worker let stack = svgOwner.zoomStack; 162*288bf522SAndroid Build Coastguard Worker 163*288bf522SAndroid Build Coastguard Worker // Unhighlight whatever was selected. 164*288bf522SAndroid Build Coastguard Worker if (selected) { 165*288bf522SAndroid Build Coastguard Worker selected.classList.remove('s'); 166*288bf522SAndroid Build Coastguard Worker } 167*288bf522SAndroid Build Coastguard Worker 168*288bf522SAndroid Build Coastguard Worker // Stack management: Never remove the last element which is the flamegraph root. 169*288bf522SAndroid Build Coastguard Worker if (stack.length > 1) { 170*288bf522SAndroid Build Coastguard Worker let previouslySelected = stack.pop(); 171*288bf522SAndroid Build Coastguard Worker select(previouslySelected); 172*288bf522SAndroid Build Coastguard Worker } 173*288bf522SAndroid Build Coastguard Worker 174*288bf522SAndroid Build Coastguard Worker // Hide zoom out button. 175*288bf522SAndroid Build Coastguard Worker if (stack.length == 1) { 176*288bf522SAndroid Build Coastguard Worker svgOwner.getElementById('zoom_rect').style.display = 'none'; 177*288bf522SAndroid Build Coastguard Worker svgOwner.getElementById('zoom_text').style.display = 'none'; 178*288bf522SAndroid Build Coastguard Worker } 179*288bf522SAndroid Build Coastguard Worker 180*288bf522SAndroid Build Coastguard Worker displaySVGElement(svgOwner); 181*288bf522SAndroid Build Coastguard Worker} 182*288bf522SAndroid Build Coastguard Worker 183*288bf522SAndroid Build Coastguard Workerfunction search(e) { 184*288bf522SAndroid Build Coastguard Worker let term = prompt('Search for:', ''); 185*288bf522SAndroid Build Coastguard Worker let callsites = e.ownerSVGElement.getElementsByTagName('g'); 186*288bf522SAndroid Build Coastguard Worker 187*288bf522SAndroid Build Coastguard Worker if (!term) { 188*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < callsites.length; i++) { 189*288bf522SAndroid Build Coastguard Worker let rect = callsites[i].getElementsByTagName('rect')[0]; 190*288bf522SAndroid Build Coastguard Worker rect.attributes['fill'].value = rect.attributes['ofill'].value; 191*288bf522SAndroid Build Coastguard Worker } 192*288bf522SAndroid Build Coastguard Worker return; 193*288bf522SAndroid Build Coastguard Worker } 194*288bf522SAndroid Build Coastguard Worker 195*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < callsites.length; i++) { 196*288bf522SAndroid Build Coastguard Worker let title = callsites[i].getElementsByTagName('title')[0]; 197*288bf522SAndroid Build Coastguard Worker let rect = callsites[i].getElementsByTagName('rect')[0]; 198*288bf522SAndroid Build Coastguard Worker if (title.textContent.indexOf(term) != -1) { 199*288bf522SAndroid Build Coastguard Worker rect.attributes['fill'].value = 'rgb(230,100,230)'; 200*288bf522SAndroid Build Coastguard Worker } else { 201*288bf522SAndroid Build Coastguard Worker rect.attributes['fill'].value = rect.attributes['ofill'].value; 202*288bf522SAndroid Build Coastguard Worker } 203*288bf522SAndroid Build Coastguard Worker } 204*288bf522SAndroid Build Coastguard Worker} 205*288bf522SAndroid Build Coastguard Worker 206*288bf522SAndroid Build Coastguard Workerlet selected; 207*288bf522SAndroid Build Coastguard Workerdocument.addEventListener('keydown', (e) => { 208*288bf522SAndroid Build Coastguard Worker if (!selected) { 209*288bf522SAndroid Build Coastguard Worker return false; 210*288bf522SAndroid Build Coastguard Worker } 211*288bf522SAndroid Build Coastguard Worker 212*288bf522SAndroid Build Coastguard Worker let nav = selected.attributes['nav'].value.split(','); 213*288bf522SAndroid Build Coastguard Worker let navigation_index; 214*288bf522SAndroid Build Coastguard Worker switch (e.keyCode) { 215*288bf522SAndroid Build Coastguard Worker // case 38: // ARROW UP 216*288bf522SAndroid Build Coastguard Worker case 87: navigation_index = 0; break; // W 217*288bf522SAndroid Build Coastguard Worker 218*288bf522SAndroid Build Coastguard Worker // case 32 : // ARROW LEFT 219*288bf522SAndroid Build Coastguard Worker case 65: navigation_index = 1; break; // A 220*288bf522SAndroid Build Coastguard Worker 221*288bf522SAndroid Build Coastguard Worker // case 43: // ARROW DOWN 222*288bf522SAndroid Build Coastguard Worker case 68: navigation_index = 3; break; // S 223*288bf522SAndroid Build Coastguard Worker 224*288bf522SAndroid Build Coastguard Worker // case 39: // ARROW RIGHT 225*288bf522SAndroid Build Coastguard Worker case 83: navigation_index = 2; break; // D 226*288bf522SAndroid Build Coastguard Worker 227*288bf522SAndroid Build Coastguard Worker case 32: zoom(selected); return false; // SPACE 228*288bf522SAndroid Build Coastguard Worker 229*288bf522SAndroid Build Coastguard Worker case 8: // BACKSPACE 230*288bf522SAndroid Build Coastguard Worker unzoom(selected); return false; 231*288bf522SAndroid Build Coastguard Worker default: return true; 232*288bf522SAndroid Build Coastguard Worker } 233*288bf522SAndroid Build Coastguard Worker 234*288bf522SAndroid Build Coastguard Worker if (nav[navigation_index] == '0') { 235*288bf522SAndroid Build Coastguard Worker return false; 236*288bf522SAndroid Build Coastguard Worker } 237*288bf522SAndroid Build Coastguard Worker 238*288bf522SAndroid Build Coastguard Worker let target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]); 239*288bf522SAndroid Build Coastguard Worker select(target_element); 240*288bf522SAndroid Build Coastguard Worker return false; 241*288bf522SAndroid Build Coastguard Worker}); 242*288bf522SAndroid Build Coastguard Worker 243*288bf522SAndroid Build Coastguard Workerfunction select(e) { 244*288bf522SAndroid Build Coastguard Worker if (selected) { 245*288bf522SAndroid Build Coastguard Worker selected.classList.remove('s'); 246*288bf522SAndroid Build Coastguard Worker } 247*288bf522SAndroid Build Coastguard Worker selected = e; 248*288bf522SAndroid Build Coastguard Worker selected.classList.add('s'); 249*288bf522SAndroid Build Coastguard Worker 250*288bf522SAndroid Build Coastguard Worker // Update info bar 251*288bf522SAndroid Build Coastguard Worker let titleElement = selected.getElementsByTagName('title')[0]; 252*288bf522SAndroid Build Coastguard Worker let text = titleElement.textContent; 253*288bf522SAndroid Build Coastguard Worker 254*288bf522SAndroid Build Coastguard Worker // Parse title 255*288bf522SAndroid Build Coastguard Worker let method_and_info = text.split(' | '); 256*288bf522SAndroid Build Coastguard Worker let methodName = method_and_info[0]; 257*288bf522SAndroid Build Coastguard Worker let info = method_and_info[1]; 258*288bf522SAndroid Build Coastguard Worker 259*288bf522SAndroid Build Coastguard Worker // Parse info 260*288bf522SAndroid Build Coastguard Worker // '/system/lib64/libhwbinder.so (4 events: 0.28%)' 261*288bf522SAndroid Build Coastguard Worker let regexp = /(.*) \((.*)\)/g; 262*288bf522SAndroid Build Coastguard Worker let match = regexp.exec(info); 263*288bf522SAndroid Build Coastguard Worker if (match.length > 2) { 264*288bf522SAndroid Build Coastguard Worker let percentage = match[2]; 265*288bf522SAndroid Build Coastguard Worker // Write percentage 266*288bf522SAndroid Build Coastguard Worker let percentageTextElement = selected.ownerSVGElement.getElementById('percent_text'); 267*288bf522SAndroid Build Coastguard Worker percentageTextElement.textContent = percentage; 268*288bf522SAndroid Build Coastguard Worker // console.log("'" + percentage + "'") 269*288bf522SAndroid Build Coastguard Worker } 270*288bf522SAndroid Build Coastguard Worker 271*288bf522SAndroid Build Coastguard Worker // Set fields 272*288bf522SAndroid Build Coastguard Worker let barTextElement = selected.ownerSVGElement.getElementById('info_text'); 273*288bf522SAndroid Build Coastguard Worker barTextElement.textContent = methodName; 274*288bf522SAndroid Build Coastguard Worker}