xref: /aosp_15_r20/system/extras/simpleperf/scripts/inferno/script.js (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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}