openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
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

5 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;
}