/* * 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'; // Use IIFE to avoid leaking names to other scripts. $(document).ready(function() { function openHtml(name, attrs={}) { let s = `<${name} `; for (let key in attrs) { s += `${key}="${attrs[key]}" `; } s += '>'; return s; } function closeHtml(name) { return ``; } function getHtml(name, attrs={}) { let text; if ('text' in attrs) { text = attrs.text; delete attrs.text; } let s = openHtml(name, attrs); if (text) { s += text; } s += closeHtml(name); return s; } function getTableRow(cols, colName, attrs={}) { let s = openHtml('tr', attrs); for (let col of cols) { s += `<${colName}>${col}`; } s += ''; return s; } function toPercentageStr(percentage) { return percentage.toFixed(2) + '%'; } function getProcessName(pid) { let name = gProcesses[pid]; return name ? `${pid} (${name})`: pid.toString(); } function getThreadName(tid) { let name = gThreads[tid]; return name ? `${tid} (${name})`: tid.toString(); } function getLibName(libId) { return gLibList[libId]; } function getFuncName(funcId) { return gFunctionMap[funcId].f; } function getLibNameOfFunction(funcId) { return getLibName(gFunctionMap[funcId].l); } function getFuncSourceRange(funcId) { let func = gFunctionMap[funcId]; if (func.hasOwnProperty('s')) { return {fileId: func.s[0], startLine: func.s[1], endLine: func.s[2]}; } return null; } function getFuncDisassembly(funcId) { let func = gFunctionMap[funcId]; return func.hasOwnProperty('d') ? func.d : null; } function getSourceFilePath(sourceFileId) { return gSourceFiles[sourceFileId].path; } function getSourceCode(sourceFileId) { return gSourceFiles[sourceFileId].code; } function isClockEvent(eventInfo) { return eventInfo.eventName.includes('task-clock') || eventInfo.eventName.includes('cpu-clock'); } class TabManager { constructor(divContainer) { this.div = $('
', {id: 'tabs'}); this.div.appendTo(divContainer); this.div.append(getHtml('ul')); this.tabs = []; this.isDrawCalled = false; } addTab(title, tabObj) { let id = 'tab_' + this.div.children().length; let tabDiv = $('
', {id: id}); tabDiv.appendTo(this.div); this.div.children().first().append( getHtml('li', {text: getHtml('a', {href: '#' + id, text: title})})); tabObj.init(tabDiv); this.tabs.push(tabObj); if (this.isDrawCalled) { this.div.tabs('refresh'); } return tabObj; } findTab(title) { let links = this.div.find('li a'); for (let i = 0; i < links.length; ++i) { if (links.eq(i).text() == title) { return this.tabs[i]; } } return null; } draw() { this.div.tabs({ active: 0, }); this.tabs.forEach(function(tab) { tab.draw(); }); this.isDrawCalled = true; } setActive(tabObj) { for (let i = 0; i < this.tabs.length; ++i) { if (this.tabs[i] == tabObj) { this.div.tabs('option', 'active', i); break; } } } } // Show global information retrieved from the record file, including: // record time // machine type // Android version // record cmdline // total samples class RecordFileView { constructor(divContainer) { this.div = $('
'); this.div.appendTo(divContainer); } draw() { google.charts.setOnLoadCallback(() => this.realDraw()); } realDraw() { this.div.empty(); // Draw a table of 'Name', 'Value'. let rows = []; if (gRecordInfo.recordTime) { rows.push(['Record Time', gRecordInfo.recordTime]); } if (gRecordInfo.machineType) { rows.push(['Machine Type', gRecordInfo.machineType]); } if (gRecordInfo.androidVersion) { rows.push(['Android Version', gRecordInfo.androidVersion]); } if (gRecordInfo.recordCmdline) { rows.push(['Record cmdline', gRecordInfo.recordCmdline]); } rows.push(['Total Samples', '' + gRecordInfo.totalSamples]); let data = new google.visualization.DataTable(); data.addColumn('string', ''); data.addColumn('string', ''); data.addRows(rows); for (let i = 0; i < rows.length; ++i) { data.setProperty(i, 0, 'className', 'boldTableCell'); } let table = new google.visualization.Table(this.div.get(0)); table.draw(data, { width: '100%', sort: 'disable', allowHtml: true, cssClassNames: { 'tableCell': 'tableCell', }, }); } } // Show pieChart of event count percentage of each process, thread, library and function. class ChartView { constructor(divContainer, eventInfo) { this.id = divContainer.children().length; this.div = $('
', {id: 'chartstat_' + this.id}); this.div.appendTo(divContainer); this.eventInfo = eventInfo; this.processInfo = null; this.threadInfo = null; this.libInfo = null; this.states = { SHOW_EVENT_INFO: 1, SHOW_PROCESS_INFO: 2, SHOW_THREAD_INFO: 3, SHOW_LIB_INFO: 4, }; if (isClockEvent(this.eventInfo)) { this.getSampleWeight = function (eventCount) { return (eventCount / 1000000.0).toFixed(3) + ' ms'; } } else { this.getSampleWeight = (eventCount) => '' + eventCount; } } _getState() { if (this.libInfo) { return this.states.SHOW_LIB_INFO; } if (this.threadInfo) { return this.states.SHOW_THREAD_INFO; } if (this.processInfo) { return this.states.SHOW_PROCESS_INFO; } return this.states.SHOW_EVENT_INFO; } _goBack() { let state = this._getState(); if (state == this.states.SHOW_PROCESS_INFO) { this.processInfo = null; } else if (state == this.states.SHOW_THREAD_INFO) { this.threadInfo = null; } else if (state == this.states.SHOW_LIB_INFO) { this.libInfo = null; } this.draw(); } _selectHandler(chart) { let selectedItem = chart.getSelection()[0]; if (selectedItem) { let state = this._getState(); if (state == this.states.SHOW_EVENT_INFO) { this.processInfo = this.eventInfo.processes[selectedItem.row]; } else if (state == this.states.SHOW_PROCESS_INFO) { this.threadInfo = this.processInfo.threads[selectedItem.row]; } else if (state == this.states.SHOW_THREAD_INFO) { this.libInfo = this.threadInfo.libs[selectedItem.row]; } this.draw(); } } draw() { google.charts.setOnLoadCallback(() => this.realDraw()); } realDraw() { this.div.empty(); this._drawTitle(); this._drawPieChart(); } _drawTitle() { // Draw a table of 'Name', 'Event Count'. let rows = []; rows.push(['Event Type: ' + this.eventInfo.eventName, this.getSampleWeight(this.eventInfo.eventCount)]); if (this.processInfo) { rows.push(['Process: ' + getProcessName(this.processInfo.pid), this.getSampleWeight(this.processInfo.eventCount)]); } if (this.threadInfo) { rows.push(['Thread: ' + getThreadName(this.threadInfo.tid), this.getSampleWeight(this.threadInfo.eventCount)]); } if (this.libInfo) { rows.push(['Library: ' + getLibName(this.libInfo.libId), this.getSampleWeight(this.libInfo.eventCount)]); } let data = new google.visualization.DataTable(); data.addColumn('string', ''); data.addColumn('string', ''); data.addRows(rows); for (let i = 0; i < rows.length; ++i) { data.setProperty(i, 0, 'className', 'boldTableCell'); } let wrapperDiv = $('
'); wrapperDiv.appendTo(this.div); let table = new google.visualization.Table(wrapperDiv.get(0)); table.draw(data, { width: '100%', sort: 'disable', allowHtml: true, cssClassNames: { 'tableCell': 'tableCell', }, }); if (this._getState() != this.states.SHOW_EVENT_INFO) { let button = $('