You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					274 lines
				
				8.6 KiB
			
		
		
			
		
	
	
					274 lines
				
				8.6 KiB
			| 
								 
											6 years ago
										 
									 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (C) 2017 The Android Open Source Project
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Licensed under the Apache License, Version 2.0 (the "License");
							 | 
						||
| 
								 | 
							
								 * you may not use this file except in compliance with the License.
							 | 
						||
| 
								 | 
							
								 * You may obtain a copy of the License at
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *      http://www.apache.org/licenses/LICENSE-2.0
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Unless required by applicable law or agreed to in writing, software
							 | 
						||
| 
								 | 
							
								 * distributed under the License is distributed on an "AS IS" BASIS,
							 | 
						||
| 
								 | 
							
								 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
							 | 
						||
| 
								 | 
							
								 * See the License for the specific language governing permissions and
							 | 
						||
| 
								 | 
							
								 * limitations under the License.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function flamegraphInit() {
							 | 
						||
| 
								 | 
							
								    let flamegraph = document.getElementById('flamegraph_id');
							 | 
						||
| 
								 | 
							
								    let svgs = flamegraph.getElementsByTagName('svg');
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < svgs.length; ++i) {
							 | 
						||
| 
								 | 
							
								        createZoomHistoryStack(svgs[i]);
							 | 
						||
| 
								 | 
							
								        adjust_text_size(svgs[i]);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function throttle(callback) {
							 | 
						||
| 
								 | 
							
								        let running = false;
							 | 
						||
| 
								 | 
							
								        return function() {
							 | 
						||
| 
								 | 
							
								            if (!running) {
							 | 
						||
| 
								 | 
							
								                running = true;
							 | 
						||
| 
								 | 
							
								                window.requestAnimationFrame(function () {
							 | 
						||
| 
								 | 
							
								                    callback();
							 | 
						||
| 
								 | 
							
								                    running = false;
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    window.addEventListener('resize', throttle(function() {
							 | 
						||
| 
								 | 
							
								        let flamegraph = document.getElementById('flamegraph_id');
							 | 
						||
| 
								 | 
							
								        let svgs = flamegraph.getElementsByTagName('svg');
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < svgs.length; ++i) {
							 | 
						||
| 
								 | 
							
								            adjust_text_size(svgs[i]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Create a stack add the root svg element in it.
							 | 
						||
| 
								 | 
							
								function createZoomHistoryStack(svgElement) {
							 | 
						||
| 
								 | 
							
								    svgElement.zoomStack = [svgElement.getElementById(svgElement.attributes['rootid'].value)];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function adjust_node_text_size(x, svgWidth) {
							 | 
						||
| 
								 | 
							
								    let title = x.getElementsByTagName('title')[0];
							 | 
						||
| 
								 | 
							
								    let text = x.getElementsByTagName('text')[0];
							 | 
						||
| 
								 | 
							
								    let rect = x.getElementsByTagName('rect')[0];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let width = parseFloat(rect.attributes['width'].value) * svgWidth * 0.01;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Don't even bother trying to find a best fit. The area is too small.
							 | 
						||
| 
								 | 
							
								    if (width < 28) {
							 | 
						||
| 
								 | 
							
								        text.textContent = '';
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Remove dso and #samples which are here only for mouseover purposes.
							 | 
						||
| 
								 | 
							
								    let methodName = title.textContent.split(' | ')[0];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let numCharacters;
							 | 
						||
| 
								 | 
							
								    for (numCharacters = methodName.length; numCharacters > 4; numCharacters--) {
							 | 
						||
| 
								 | 
							
								        // Avoid reflow by using hard-coded estimate instead of
							 | 
						||
| 
								 | 
							
								        // text.getSubStringLength(0, numCharacters).
							 | 
						||
| 
								 | 
							
								        if (numCharacters * 7.5 <= width) {
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (numCharacters == methodName.length) {
							 | 
						||
| 
								 | 
							
								        text.textContent = methodName;
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    text.textContent = methodName.substring(0, numCharacters-2) + '..';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function adjust_text_size(svgElement) {
							 | 
						||
| 
								 | 
							
								    let svgWidth = window.innerWidth;
							 | 
						||
| 
								 | 
							
								    let x = svgElement.getElementsByTagName('g');
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < x.length; i++) {
							 | 
						||
| 
								 | 
							
								        adjust_node_text_size(x[i], svgWidth);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function zoom(e) {
							 | 
						||
| 
								 | 
							
								    let svgElement = e.ownerSVGElement;
							 | 
						||
| 
								 | 
							
								    let zoomStack = svgElement.zoomStack;
							 | 
						||
| 
								 | 
							
								    zoomStack.push(e);
							 | 
						||
| 
								 | 
							
								    displaySVGElement(svgElement);
							 | 
						||
| 
								 | 
							
								    select(e);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Show zoom out button.
							 | 
						||
| 
								 | 
							
								    svgElement.getElementById('zoom_rect').style.display = 'block';
							 | 
						||
| 
								 | 
							
								    svgElement.getElementById('zoom_text').style.display = 'block';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function displaySVGElement(svgElement) {
							 | 
						||
| 
								 | 
							
								    let zoomStack = svgElement.zoomStack;
							 | 
						||
| 
								 | 
							
								    let e = zoomStack[zoomStack.length - 1];
							 | 
						||
| 
								 | 
							
								    let clicked_rect = e.getElementsByTagName('rect')[0];
							 | 
						||
| 
								 | 
							
								    let clicked_origin_x;
							 | 
						||
| 
								 | 
							
								    let clicked_origin_y = clicked_rect.attributes['oy'].value;
							 | 
						||
| 
								 | 
							
								    let clicked_origin_width;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (zoomStack.length == 1) {
							 | 
						||
| 
								 | 
							
								        // Show all nodes when zoomStack only contains the root node.
							 | 
						||
| 
								 | 
							
								        // This is needed to show flamegraph containing more than one node at the root level.
							 | 
						||
| 
								 | 
							
								        clicked_origin_x = 0;
							 | 
						||
| 
								 | 
							
								        clicked_origin_width = 100;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        clicked_origin_x = clicked_rect.attributes['ox'].value;
							 | 
						||
| 
								 | 
							
								        clicked_origin_width = clicked_rect.attributes['owidth'].value;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let svgBox = svgElement.getBoundingClientRect();
							 | 
						||
| 
								 | 
							
								    let svgBoxHeight = svgBox.height;
							 | 
						||
| 
								 | 
							
								    let svgBoxWidth = 100;
							 | 
						||
| 
								 | 
							
								    let scaleFactor = svgBoxWidth / clicked_origin_width;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let callsites = svgElement.getElementsByTagName('g');
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < callsites.length; i++) {
							 | 
						||
| 
								 | 
							
								        let text = callsites[i].getElementsByTagName('text')[0];
							 | 
						||
| 
								 | 
							
								        let rect = callsites[i].getElementsByTagName('rect')[0];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let rect_o_x = parseFloat(rect.attributes['ox'].value);
							 | 
						||
| 
								 | 
							
								        let rect_o_y = parseFloat(rect.attributes['oy'].value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Avoid multiple forced reflow by hiding nodes.
							 | 
						||
| 
								 | 
							
								        if (rect_o_y > clicked_origin_y) {
							 | 
						||
| 
								 | 
							
								            rect.style.display = 'none';
							 | 
						||
| 
								 | 
							
								            text.style.display = 'none';
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        rect.style.display = 'block';
							 | 
						||
| 
								 | 
							
								        text.style.display = 'block';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let newrec_x = rect.attributes['x'].value = (rect_o_x - clicked_origin_x) * scaleFactor +
							 | 
						||
| 
								 | 
							
								                                                    '%';
							 | 
						||
| 
								 | 
							
								        let newrec_y = rect.attributes['y'].value = rect_o_y + (svgBoxHeight - clicked_origin_y
							 | 
						||
| 
								 | 
							
								                                                            - 17 - 2);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text.attributes['y'].value = newrec_y + 12;
							 | 
						||
| 
								 | 
							
								        text.attributes['x'].value = newrec_x;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        rect.attributes['width'].value = (rect.attributes['owidth'].value * scaleFactor) + '%';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    adjust_text_size(svgElement);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function unzoom(e) {
							 | 
						||
| 
								 | 
							
								    let svgOwner = e.ownerSVGElement;
							 | 
						||
| 
								 | 
							
								    let stack = svgOwner.zoomStack;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Unhighlight whatever was selected.
							 | 
						||
| 
								 | 
							
								    if (selected) {
							 | 
						||
| 
								 | 
							
								        selected.classList.remove('s');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Stack management: Never remove the last element which is the flamegraph root.
							 | 
						||
| 
								 | 
							
								    if (stack.length > 1) {
							 | 
						||
| 
								 | 
							
								        let previouslySelected = stack.pop();
							 | 
						||
| 
								 | 
							
								        select(previouslySelected);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Hide zoom out button.
							 | 
						||
| 
								 | 
							
								    if (stack.length == 1) {
							 | 
						||
| 
								 | 
							
								        svgOwner.getElementById('zoom_rect').style.display = 'none';
							 | 
						||
| 
								 | 
							
								        svgOwner.getElementById('zoom_text').style.display = 'none';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    displaySVGElement(svgOwner);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function search(e) {
							 | 
						||
| 
								 | 
							
								    let term = prompt('Search for:', '');
							 | 
						||
| 
								 | 
							
								    let callsites = e.ownerSVGElement.getElementsByTagName('g');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!term) {
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < callsites.length; i++) {
							 | 
						||
| 
								 | 
							
								            let rect = callsites[i].getElementsByTagName('rect')[0];
							 | 
						||
| 
								 | 
							
								            rect.attributes['fill'].value = rect.attributes['ofill'].value;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < callsites.length; i++) {
							 | 
						||
| 
								 | 
							
								        let title = callsites[i].getElementsByTagName('title')[0];
							 | 
						||
| 
								 | 
							
								        let rect = callsites[i].getElementsByTagName('rect')[0];
							 | 
						||
| 
								 | 
							
								        if (title.textContent.indexOf(term) != -1) {
							 | 
						||
| 
								 | 
							
								            rect.attributes['fill'].value = 'rgb(230,100,230)';
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            rect.attributes['fill'].value = rect.attributes['ofill'].value;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let selected;
							 | 
						||
| 
								 | 
							
								document.addEventListener('keydown', (e) => {
							 | 
						||
| 
								 | 
							
								    if (!selected) {
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let nav = selected.attributes['nav'].value.split(',');
							 | 
						||
| 
								 | 
							
								    let navigation_index;
							 | 
						||
| 
								 | 
							
								    switch (e.keyCode) {
							 | 
						||
| 
								 | 
							
								    // case 38: // ARROW UP
							 | 
						||
| 
								 | 
							
								    case 87: navigation_index = 0; break; // W
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // case 32 : // ARROW LEFT
							 | 
						||
| 
								 | 
							
								    case 65: navigation_index = 1; break; // A
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // case 43: // ARROW DOWN
							 | 
						||
| 
								 | 
							
								    case 68: navigation_index = 3; break; // S
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // case 39: // ARROW RIGHT
							 | 
						||
| 
								 | 
							
								    case 83: navigation_index = 2; break; // D
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 32: zoom(selected); return false; // SPACE
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 8: // BACKSPACE
							 | 
						||
| 
								 | 
							
								        unzoom(selected); return false;
							 | 
						||
| 
								 | 
							
								    default: return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (nav[navigation_index] == '0') {
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]);
							 | 
						||
| 
								 | 
							
								    select(target_element);
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function select(e) {
							 | 
						||
| 
								 | 
							
								    if (selected) {
							 | 
						||
| 
								 | 
							
								        selected.classList.remove('s');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    selected = e;
							 | 
						||
| 
								 | 
							
								    selected.classList.add('s');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Update info bar
							 | 
						||
| 
								 | 
							
								    let titleElement = selected.getElementsByTagName('title')[0];
							 | 
						||
| 
								 | 
							
								    let text = titleElement.textContent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Parse title
							 | 
						||
| 
								 | 
							
								    let method_and_info = text.split(' | ');
							 | 
						||
| 
								 | 
							
								    let methodName = method_and_info[0];
							 | 
						||
| 
								 | 
							
								    let info = method_and_info[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Parse info
							 | 
						||
| 
								 | 
							
								    // '/system/lib64/libhwbinder.so (4 events: 0.28%)'
							 | 
						||
| 
								 | 
							
								    let regexp = /(.*) \((.*)\)/g;
							 | 
						||
| 
								 | 
							
								    let match = regexp.exec(info);
							 | 
						||
| 
								 | 
							
								    if (match.length > 2) {
							 | 
						||
| 
								 | 
							
								        let percentage = match[2];
							 | 
						||
| 
								 | 
							
								        // Write percentage
							 | 
						||
| 
								 | 
							
								        let percentageTextElement = selected.ownerSVGElement.getElementById('percent_text');
							 | 
						||
| 
								 | 
							
								        percentageTextElement.textContent = percentage;
							 | 
						||
| 
								 | 
							
								    // console.log("'" + percentage + "'")
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Set fields
							 | 
						||
| 
								 | 
							
								    let barTextElement = selected.ownerSVGElement.getElementById('info_text');
							 | 
						||
| 
								 | 
							
								    barTextElement.textContent = methodName;
							 | 
						||
| 
								 | 
							
								}
							 |