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 Worker// Use IIFE to avoid leaking names to other scripts. 19*288bf522SAndroid Build Coastguard Worker(function () { 20*288bf522SAndroid Build Coastguard Worker 21*288bf522SAndroid Build Coastguard Workerfunction getTimeInMs() { 22*288bf522SAndroid Build Coastguard Worker return new Date().getTime(); 23*288bf522SAndroid Build Coastguard Worker} 24*288bf522SAndroid Build Coastguard Worker 25*288bf522SAndroid Build Coastguard Workerclass TimeLog { 26*288bf522SAndroid Build Coastguard Worker constructor() { 27*288bf522SAndroid Build Coastguard Worker this.start = getTimeInMs(); 28*288bf522SAndroid Build Coastguard Worker } 29*288bf522SAndroid Build Coastguard Worker 30*288bf522SAndroid Build Coastguard Worker log(name) { 31*288bf522SAndroid Build Coastguard Worker let end = getTimeInMs(); 32*288bf522SAndroid Build Coastguard Worker console.log(name, end - this.start, 'ms'); 33*288bf522SAndroid Build Coastguard Worker this.start = end; 34*288bf522SAndroid Build Coastguard Worker } 35*288bf522SAndroid Build Coastguard Worker} 36*288bf522SAndroid Build Coastguard Worker 37*288bf522SAndroid Build Coastguard Workerclass ProgressBar { 38*288bf522SAndroid Build Coastguard Worker constructor() { 39*288bf522SAndroid Build Coastguard Worker let str = ` 40*288bf522SAndroid Build Coastguard Worker <div class="modal" tabindex="-1" role="dialog"> 41*288bf522SAndroid Build Coastguard Worker <div class="modal-dialog" role="document"> 42*288bf522SAndroid Build Coastguard Worker <div class="modal-content"> 43*288bf522SAndroid Build Coastguard Worker <div class="modal-header"><h5 class="modal-title">Loading page...</h5></div> 44*288bf522SAndroid Build Coastguard Worker <div class="modal-body"> 45*288bf522SAndroid Build Coastguard Worker <div class="progress"> 46*288bf522SAndroid Build Coastguard Worker <div class="progress-bar" role="progressbar" 47*288bf522SAndroid Build Coastguard Worker style="width: 0%" aria-valuenow="0" aria-valuemin="0" 48*288bf522SAndroid Build Coastguard Worker aria-valuemax="100">0%</div> 49*288bf522SAndroid Build Coastguard Worker </div> 50*288bf522SAndroid Build Coastguard Worker </div> 51*288bf522SAndroid Build Coastguard Worker </div> 52*288bf522SAndroid Build Coastguard Worker </div> 53*288bf522SAndroid Build Coastguard Worker </div> 54*288bf522SAndroid Build Coastguard Worker `; 55*288bf522SAndroid Build Coastguard Worker this.modal = $(str).appendTo($('body')); 56*288bf522SAndroid Build Coastguard Worker this.progress = 0; 57*288bf522SAndroid Build Coastguard Worker this.shownCallback = null; 58*288bf522SAndroid Build Coastguard Worker this.modal.on('shown.bs.modal', () => this._onShown()); 59*288bf522SAndroid Build Coastguard Worker // Shorten progress bar update time. 60*288bf522SAndroid Build Coastguard Worker this.modal.find('.progress-bar').css('transition-duration', '0ms'); 61*288bf522SAndroid Build Coastguard Worker this.shown = false; 62*288bf522SAndroid Build Coastguard Worker } 63*288bf522SAndroid Build Coastguard Worker 64*288bf522SAndroid Build Coastguard Worker // progress is [0-100]. Return a Promise resolved when the update is shown. 65*288bf522SAndroid Build Coastguard Worker updateAsync(text, progress) { 66*288bf522SAndroid Build Coastguard Worker progress = parseInt(progress); // Truncate float number to integer. 67*288bf522SAndroid Build Coastguard Worker return this.showAsync().then(() => { 68*288bf522SAndroid Build Coastguard Worker if (text) { 69*288bf522SAndroid Build Coastguard Worker this.modal.find('.modal-title').text(text); 70*288bf522SAndroid Build Coastguard Worker } 71*288bf522SAndroid Build Coastguard Worker this.progress = progress; 72*288bf522SAndroid Build Coastguard Worker this.modal.find('.progress-bar').css('width', progress + '%') 73*288bf522SAndroid Build Coastguard Worker .attr('aria-valuenow', progress).text(progress + '%'); 74*288bf522SAndroid Build Coastguard Worker // Leave 100ms for the progess bar to update. 75*288bf522SAndroid Build Coastguard Worker return createPromise((resolve) => setTimeout(resolve, 100)); 76*288bf522SAndroid Build Coastguard Worker }); 77*288bf522SAndroid Build Coastguard Worker } 78*288bf522SAndroid Build Coastguard Worker 79*288bf522SAndroid Build Coastguard Worker showAsync() { 80*288bf522SAndroid Build Coastguard Worker if (this.shown) { 81*288bf522SAndroid Build Coastguard Worker return createPromise(); 82*288bf522SAndroid Build Coastguard Worker } 83*288bf522SAndroid Build Coastguard Worker return createPromise((resolve) => { 84*288bf522SAndroid Build Coastguard Worker this.shownCallback = resolve; 85*288bf522SAndroid Build Coastguard Worker this.modal.modal({ 86*288bf522SAndroid Build Coastguard Worker show: true, 87*288bf522SAndroid Build Coastguard Worker keyboard: false, 88*288bf522SAndroid Build Coastguard Worker backdrop: false, 89*288bf522SAndroid Build Coastguard Worker }); 90*288bf522SAndroid Build Coastguard Worker }); 91*288bf522SAndroid Build Coastguard Worker } 92*288bf522SAndroid Build Coastguard Worker 93*288bf522SAndroid Build Coastguard Worker _onShown() { 94*288bf522SAndroid Build Coastguard Worker this.shown = true; 95*288bf522SAndroid Build Coastguard Worker if (this.shownCallback) { 96*288bf522SAndroid Build Coastguard Worker let callback = this.shownCallback; 97*288bf522SAndroid Build Coastguard Worker this.shownCallback = null; 98*288bf522SAndroid Build Coastguard Worker callback(); 99*288bf522SAndroid Build Coastguard Worker } 100*288bf522SAndroid Build Coastguard Worker } 101*288bf522SAndroid Build Coastguard Worker 102*288bf522SAndroid Build Coastguard Worker hide() { 103*288bf522SAndroid Build Coastguard Worker this.shown = false; 104*288bf522SAndroid Build Coastguard Worker this.modal.modal('hide'); 105*288bf522SAndroid Build Coastguard Worker } 106*288bf522SAndroid Build Coastguard Worker} 107*288bf522SAndroid Build Coastguard Worker 108*288bf522SAndroid Build Coastguard Workerfunction openHtml(name, attrs={}) { 109*288bf522SAndroid Build Coastguard Worker let s = `<${name} `; 110*288bf522SAndroid Build Coastguard Worker for (let key in attrs) { 111*288bf522SAndroid Build Coastguard Worker s += `${key}="${attrs[key]}" `; 112*288bf522SAndroid Build Coastguard Worker } 113*288bf522SAndroid Build Coastguard Worker s += '>'; 114*288bf522SAndroid Build Coastguard Worker return s; 115*288bf522SAndroid Build Coastguard Worker} 116*288bf522SAndroid Build Coastguard Worker 117*288bf522SAndroid Build Coastguard Workerfunction closeHtml(name) { 118*288bf522SAndroid Build Coastguard Worker return `</${name}>`; 119*288bf522SAndroid Build Coastguard Worker} 120*288bf522SAndroid Build Coastguard Worker 121*288bf522SAndroid Build Coastguard Workerfunction getHtml(name, attrs={}) { 122*288bf522SAndroid Build Coastguard Worker let text; 123*288bf522SAndroid Build Coastguard Worker if ('text' in attrs) { 124*288bf522SAndroid Build Coastguard Worker text = attrs.text; 125*288bf522SAndroid Build Coastguard Worker delete attrs.text; 126*288bf522SAndroid Build Coastguard Worker } 127*288bf522SAndroid Build Coastguard Worker let s = openHtml(name, attrs); 128*288bf522SAndroid Build Coastguard Worker if (text) { 129*288bf522SAndroid Build Coastguard Worker s += text; 130*288bf522SAndroid Build Coastguard Worker } 131*288bf522SAndroid Build Coastguard Worker s += closeHtml(name); 132*288bf522SAndroid Build Coastguard Worker return s; 133*288bf522SAndroid Build Coastguard Worker} 134*288bf522SAndroid Build Coastguard Worker 135*288bf522SAndroid Build Coastguard Workerfunction getTableRow(cols, colName, attrs={}) { 136*288bf522SAndroid Build Coastguard Worker let s = openHtml('tr', attrs); 137*288bf522SAndroid Build Coastguard Worker for (let col of cols) { 138*288bf522SAndroid Build Coastguard Worker s += `<${colName}>${col}</${colName}>`; 139*288bf522SAndroid Build Coastguard Worker } 140*288bf522SAndroid Build Coastguard Worker s += '</tr>'; 141*288bf522SAndroid Build Coastguard Worker return s; 142*288bf522SAndroid Build Coastguard Worker} 143*288bf522SAndroid Build Coastguard Worker 144*288bf522SAndroid Build Coastguard Workerfunction getProcessName(pid) { 145*288bf522SAndroid Build Coastguard Worker let name = gProcesses[pid]; 146*288bf522SAndroid Build Coastguard Worker return name ? `${pid} (${name})`: pid.toString(); 147*288bf522SAndroid Build Coastguard Worker} 148*288bf522SAndroid Build Coastguard Worker 149*288bf522SAndroid Build Coastguard Workerfunction getThreadName(tid) { 150*288bf522SAndroid Build Coastguard Worker let name = gThreads[tid]; 151*288bf522SAndroid Build Coastguard Worker return name ? `${tid} (${name})`: tid.toString(); 152*288bf522SAndroid Build Coastguard Worker} 153*288bf522SAndroid Build Coastguard Worker 154*288bf522SAndroid Build Coastguard Workerfunction getLibName(libId) { 155*288bf522SAndroid Build Coastguard Worker return gLibList[libId]; 156*288bf522SAndroid Build Coastguard Worker} 157*288bf522SAndroid Build Coastguard Worker 158*288bf522SAndroid Build Coastguard Workerfunction getFuncName(funcId) { 159*288bf522SAndroid Build Coastguard Worker return gFunctionMap[funcId].f; 160*288bf522SAndroid Build Coastguard Worker} 161*288bf522SAndroid Build Coastguard Worker 162*288bf522SAndroid Build Coastguard Workerfunction getLibNameOfFunction(funcId) { 163*288bf522SAndroid Build Coastguard Worker return getLibName(gFunctionMap[funcId].l); 164*288bf522SAndroid Build Coastguard Worker} 165*288bf522SAndroid Build Coastguard Worker 166*288bf522SAndroid Build Coastguard Workerfunction getFuncSourceRange(funcId) { 167*288bf522SAndroid Build Coastguard Worker let func = gFunctionMap[funcId]; 168*288bf522SAndroid Build Coastguard Worker if (func.hasOwnProperty('s')) { 169*288bf522SAndroid Build Coastguard Worker return {fileId: func.s[0], startLine: func.s[1], endLine: func.s[2]}; 170*288bf522SAndroid Build Coastguard Worker } 171*288bf522SAndroid Build Coastguard Worker return null; 172*288bf522SAndroid Build Coastguard Worker} 173*288bf522SAndroid Build Coastguard Worker 174*288bf522SAndroid Build Coastguard Workerfunction getFuncDisassembly(funcId) { 175*288bf522SAndroid Build Coastguard Worker let func = gFunctionMap[funcId]; 176*288bf522SAndroid Build Coastguard Worker return func.hasOwnProperty('d') ? func.d : null; 177*288bf522SAndroid Build Coastguard Worker} 178*288bf522SAndroid Build Coastguard Worker 179*288bf522SAndroid Build Coastguard Workerfunction getSourceFilePath(sourceFileId) { 180*288bf522SAndroid Build Coastguard Worker return gSourceFiles[sourceFileId].path; 181*288bf522SAndroid Build Coastguard Worker} 182*288bf522SAndroid Build Coastguard Worker 183*288bf522SAndroid Build Coastguard Workerfunction getSourceCode(sourceFileId) { 184*288bf522SAndroid Build Coastguard Worker return gSourceFiles[sourceFileId].code; 185*288bf522SAndroid Build Coastguard Worker} 186*288bf522SAndroid Build Coastguard Worker 187*288bf522SAndroid Build Coastguard Workerfunction isClockEvent(eventInfo) { 188*288bf522SAndroid Build Coastguard Worker return eventInfo.eventName.includes('task-clock') || 189*288bf522SAndroid Build Coastguard Worker eventInfo.eventName.includes('cpu-clock'); 190*288bf522SAndroid Build Coastguard Worker} 191*288bf522SAndroid Build Coastguard Worker 192*288bf522SAndroid Build Coastguard Workerlet createId = function() { 193*288bf522SAndroid Build Coastguard Worker let currentId = 0; 194*288bf522SAndroid Build Coastguard Worker return () => `id${++currentId}`; 195*288bf522SAndroid Build Coastguard Worker}(); 196*288bf522SAndroid Build Coastguard Worker 197*288bf522SAndroid Build Coastguard Workerclass TabManager { 198*288bf522SAndroid Build Coastguard Worker constructor(divContainer) { 199*288bf522SAndroid Build Coastguard Worker let id = createId(); 200*288bf522SAndroid Build Coastguard Worker divContainer.append(`<ul class="nav nav-pills mb-3 mt-3 ml-3" id="${id}" role="tablist"> 201*288bf522SAndroid Build Coastguard Worker </ul><hr/><div class="tab-content" id="${id}Content"></div>`); 202*288bf522SAndroid Build Coastguard Worker this.ul = divContainer.find(`#${id}`); 203*288bf522SAndroid Build Coastguard Worker this.content = divContainer.find(`#${id}Content`); 204*288bf522SAndroid Build Coastguard Worker // Map from title to [tabObj, drawn=false|true]. 205*288bf522SAndroid Build Coastguard Worker this.tabs = new Map(); 206*288bf522SAndroid Build Coastguard Worker this.tabActiveCallback = null; 207*288bf522SAndroid Build Coastguard Worker } 208*288bf522SAndroid Build Coastguard Worker 209*288bf522SAndroid Build Coastguard Worker addTab(title, tabObj) { 210*288bf522SAndroid Build Coastguard Worker let id = createId(); 211*288bf522SAndroid Build Coastguard Worker this.content.append(`<div class="tab-pane" id="${id}" role="tabpanel" 212*288bf522SAndroid Build Coastguard Worker aria-labelledby="${id}-tab"></div>`); 213*288bf522SAndroid Build Coastguard Worker this.ul.append(` 214*288bf522SAndroid Build Coastguard Worker <li class="nav-item"> 215*288bf522SAndroid Build Coastguard Worker <a class="nav-link" id="${id}-tab" data-toggle="pill" href="#${id}" role="tab" 216*288bf522SAndroid Build Coastguard Worker aria-controls="${id}" aria-selected="false">${title}</a> 217*288bf522SAndroid Build Coastguard Worker </li>`); 218*288bf522SAndroid Build Coastguard Worker tabObj.init(this.content.find(`#${id}`)); 219*288bf522SAndroid Build Coastguard Worker this.tabs.set(title, [tabObj, false]); 220*288bf522SAndroid Build Coastguard Worker this.ul.find(`#${id}-tab`).on('shown.bs.tab', () => this.onTabActive(title)); 221*288bf522SAndroid Build Coastguard Worker return tabObj; 222*288bf522SAndroid Build Coastguard Worker } 223*288bf522SAndroid Build Coastguard Worker 224*288bf522SAndroid Build Coastguard Worker setActiveAsync(title) { 225*288bf522SAndroid Build Coastguard Worker let tabObj = this.findTab(title); 226*288bf522SAndroid Build Coastguard Worker return createPromise((resolve) => { 227*288bf522SAndroid Build Coastguard Worker this.tabActiveCallback = resolve; 228*288bf522SAndroid Build Coastguard Worker let id = tabObj.div.attr('id') + '-tab'; 229*288bf522SAndroid Build Coastguard Worker this.ul.find(`#${id}`).tab('show'); 230*288bf522SAndroid Build Coastguard Worker }); 231*288bf522SAndroid Build Coastguard Worker } 232*288bf522SAndroid Build Coastguard Worker 233*288bf522SAndroid Build Coastguard Worker onTabActive(title) { 234*288bf522SAndroid Build Coastguard Worker let array = this.tabs.get(title); 235*288bf522SAndroid Build Coastguard Worker let tabObj = array[0]; 236*288bf522SAndroid Build Coastguard Worker let drawn = array[1]; 237*288bf522SAndroid Build Coastguard Worker if (!drawn) { 238*288bf522SAndroid Build Coastguard Worker tabObj.draw(); 239*288bf522SAndroid Build Coastguard Worker array[1] = true; 240*288bf522SAndroid Build Coastguard Worker } 241*288bf522SAndroid Build Coastguard Worker if (this.tabActiveCallback) { 242*288bf522SAndroid Build Coastguard Worker let callback = this.tabActiveCallback; 243*288bf522SAndroid Build Coastguard Worker this.tabActiveCallback = null; 244*288bf522SAndroid Build Coastguard Worker callback(); 245*288bf522SAndroid Build Coastguard Worker } 246*288bf522SAndroid Build Coastguard Worker } 247*288bf522SAndroid Build Coastguard Worker 248*288bf522SAndroid Build Coastguard Worker findTab(title) { 249*288bf522SAndroid Build Coastguard Worker let array = this.tabs.get(title); 250*288bf522SAndroid Build Coastguard Worker return array ? array[0] : null; 251*288bf522SAndroid Build Coastguard Worker } 252*288bf522SAndroid Build Coastguard Worker} 253*288bf522SAndroid Build Coastguard Worker 254*288bf522SAndroid Build Coastguard Workerfunction createEventTabs(id) { 255*288bf522SAndroid Build Coastguard Worker let ul = `<ul class="nav nav-pills mb-3 mt-3 ml-3" id="${id}" role="tablist">`; 256*288bf522SAndroid Build Coastguard Worker let content = `<div class="tab-content" id="${id}Content">`; 257*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < gSampleInfo.length; ++i) { 258*288bf522SAndroid Build Coastguard Worker let subId = id + '_' + i; 259*288bf522SAndroid Build Coastguard Worker let title = gSampleInfo[i].eventName; 260*288bf522SAndroid Build Coastguard Worker ul += ` 261*288bf522SAndroid Build Coastguard Worker <li class="nav-item"> 262*288bf522SAndroid Build Coastguard Worker <a class="nav-link" id="${subId}-tab" data-toggle="pill" href="#${subId}" role="tab" 263*288bf522SAndroid Build Coastguard Worker aria-controls="${subId}" aria-selected="${i == 0 ? "true" : "false"}">${title}</a> 264*288bf522SAndroid Build Coastguard Worker </li>`; 265*288bf522SAndroid Build Coastguard Worker content += ` 266*288bf522SAndroid Build Coastguard Worker <div class="tab-pane" id="${subId}" role="tabpanel" aria-labelledby="${subId}-tab"> 267*288bf522SAndroid Build Coastguard Worker </div>`; 268*288bf522SAndroid Build Coastguard Worker } 269*288bf522SAndroid Build Coastguard Worker ul += '</ul>'; 270*288bf522SAndroid Build Coastguard Worker content += '</div>'; 271*288bf522SAndroid Build Coastguard Worker return ul + content; 272*288bf522SAndroid Build Coastguard Worker} 273*288bf522SAndroid Build Coastguard Worker 274*288bf522SAndroid Build Coastguard Workerfunction createViewsForEvents(div, createViewCallback) { 275*288bf522SAndroid Build Coastguard Worker let views = []; 276*288bf522SAndroid Build Coastguard Worker if (gSampleInfo.length == 1) { 277*288bf522SAndroid Build Coastguard Worker views.push(createViewCallback(div, gSampleInfo[0])); 278*288bf522SAndroid Build Coastguard Worker } else if (gSampleInfo.length > 1) { 279*288bf522SAndroid Build Coastguard Worker // If more than one event, draw them in tabs. 280*288bf522SAndroid Build Coastguard Worker let id = createId(); 281*288bf522SAndroid Build Coastguard Worker div.append(createEventTabs(id)); 282*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < gSampleInfo.length; ++i) { 283*288bf522SAndroid Build Coastguard Worker let subId = id + '_' + i; 284*288bf522SAndroid Build Coastguard Worker views.push(createViewCallback(div.find(`#${subId}`), gSampleInfo[i])); 285*288bf522SAndroid Build Coastguard Worker } 286*288bf522SAndroid Build Coastguard Worker div.find(`#${id}_0-tab`).tab('show'); 287*288bf522SAndroid Build Coastguard Worker } 288*288bf522SAndroid Build Coastguard Worker return views; 289*288bf522SAndroid Build Coastguard Worker} 290*288bf522SAndroid Build Coastguard Worker 291*288bf522SAndroid Build Coastguard Worker// Return a promise to draw views. 292*288bf522SAndroid Build Coastguard Workerfunction drawViewsAsync(views, totalProgress, drawViewCallback) { 293*288bf522SAndroid Build Coastguard Worker if (views.length == 0) { 294*288bf522SAndroid Build Coastguard Worker return createPromise(); 295*288bf522SAndroid Build Coastguard Worker } 296*288bf522SAndroid Build Coastguard Worker let drawPos = 0; 297*288bf522SAndroid Build Coastguard Worker let eachProgress = totalProgress / views.length; 298*288bf522SAndroid Build Coastguard Worker function drawAsync() { 299*288bf522SAndroid Build Coastguard Worker if (drawPos == views.length) { 300*288bf522SAndroid Build Coastguard Worker return createPromise(); 301*288bf522SAndroid Build Coastguard Worker } 302*288bf522SAndroid Build Coastguard Worker return drawViewCallback(views[drawPos++], eachProgress).then(drawAsync); 303*288bf522SAndroid Build Coastguard Worker } 304*288bf522SAndroid Build Coastguard Worker return drawAsync(); 305*288bf522SAndroid Build Coastguard Worker} 306*288bf522SAndroid Build Coastguard Worker 307*288bf522SAndroid Build Coastguard Worker// Show global information retrieved from the record file, including: 308*288bf522SAndroid Build Coastguard Worker// record time 309*288bf522SAndroid Build Coastguard Worker// machine type 310*288bf522SAndroid Build Coastguard Worker// Android version 311*288bf522SAndroid Build Coastguard Worker// record cmdline 312*288bf522SAndroid Build Coastguard Worker// total samples 313*288bf522SAndroid Build Coastguard Workerclass RecordFileView { 314*288bf522SAndroid Build Coastguard Worker constructor(divContainer) { 315*288bf522SAndroid Build Coastguard Worker this.div = $('<div>'); 316*288bf522SAndroid Build Coastguard Worker this.div.appendTo(divContainer); 317*288bf522SAndroid Build Coastguard Worker } 318*288bf522SAndroid Build Coastguard Worker 319*288bf522SAndroid Build Coastguard Worker draw() { 320*288bf522SAndroid Build Coastguard Worker google.charts.setOnLoadCallback(() => this.realDraw()); 321*288bf522SAndroid Build Coastguard Worker } 322*288bf522SAndroid Build Coastguard Worker 323*288bf522SAndroid Build Coastguard Worker realDraw() { 324*288bf522SAndroid Build Coastguard Worker this.div.empty(); 325*288bf522SAndroid Build Coastguard Worker // Draw a table of 'Name', 'Value'. 326*288bf522SAndroid Build Coastguard Worker let rows = []; 327*288bf522SAndroid Build Coastguard Worker if (gRecordInfo.recordTime) { 328*288bf522SAndroid Build Coastguard Worker rows.push(['Record Time', gRecordInfo.recordTime]); 329*288bf522SAndroid Build Coastguard Worker } 330*288bf522SAndroid Build Coastguard Worker if (gRecordInfo.machineType) { 331*288bf522SAndroid Build Coastguard Worker rows.push(['Machine Type', gRecordInfo.machineType]); 332*288bf522SAndroid Build Coastguard Worker } 333*288bf522SAndroid Build Coastguard Worker if (gRecordInfo.androidVersion) { 334*288bf522SAndroid Build Coastguard Worker rows.push(['Android Version', gRecordInfo.androidVersion]); 335*288bf522SAndroid Build Coastguard Worker } 336*288bf522SAndroid Build Coastguard Worker if (gRecordInfo.androidBuildFingerprint) { 337*288bf522SAndroid Build Coastguard Worker rows.push(['Build Fingerprint', gRecordInfo.androidBuildFingerprint]); 338*288bf522SAndroid Build Coastguard Worker } 339*288bf522SAndroid Build Coastguard Worker if (gRecordInfo.kernelVersion) { 340*288bf522SAndroid Build Coastguard Worker rows.push(['Kernel Version', gRecordInfo.kernelVersion]); 341*288bf522SAndroid Build Coastguard Worker } 342*288bf522SAndroid Build Coastguard Worker if (gRecordInfo.recordCmdline) { 343*288bf522SAndroid Build Coastguard Worker rows.push(['Record cmdline', gRecordInfo.recordCmdline]); 344*288bf522SAndroid Build Coastguard Worker } 345*288bf522SAndroid Build Coastguard Worker rows.push(['Total Samples', '' + gRecordInfo.totalSamples]); 346*288bf522SAndroid Build Coastguard Worker 347*288bf522SAndroid Build Coastguard Worker let data = new google.visualization.DataTable(); 348*288bf522SAndroid Build Coastguard Worker data.addColumn('string', ''); 349*288bf522SAndroid Build Coastguard Worker data.addColumn('string', ''); 350*288bf522SAndroid Build Coastguard Worker data.addRows(rows); 351*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < rows.length; ++i) { 352*288bf522SAndroid Build Coastguard Worker data.setProperty(i, 0, 'className', 'boldTableCell'); 353*288bf522SAndroid Build Coastguard Worker } 354*288bf522SAndroid Build Coastguard Worker let table = new google.visualization.Table(this.div.get(0)); 355*288bf522SAndroid Build Coastguard Worker table.draw(data, { 356*288bf522SAndroid Build Coastguard Worker width: '100%', 357*288bf522SAndroid Build Coastguard Worker sort: 'disable', 358*288bf522SAndroid Build Coastguard Worker allowHtml: true, 359*288bf522SAndroid Build Coastguard Worker cssClassNames: { 360*288bf522SAndroid Build Coastguard Worker 'tableCell': 'tableCell', 361*288bf522SAndroid Build Coastguard Worker }, 362*288bf522SAndroid Build Coastguard Worker }); 363*288bf522SAndroid Build Coastguard Worker } 364*288bf522SAndroid Build Coastguard Worker} 365*288bf522SAndroid Build Coastguard Worker 366*288bf522SAndroid Build Coastguard Worker// Show pieChart of event count percentage of each process, thread, library and function. 367*288bf522SAndroid Build Coastguard Workerclass ChartView { 368*288bf522SAndroid Build Coastguard Worker constructor(divContainer, eventInfo) { 369*288bf522SAndroid Build Coastguard Worker this.div = $('<div>').appendTo(divContainer); 370*288bf522SAndroid Build Coastguard Worker this.eventInfo = eventInfo; 371*288bf522SAndroid Build Coastguard Worker this.processInfo = null; 372*288bf522SAndroid Build Coastguard Worker this.threadInfo = null; 373*288bf522SAndroid Build Coastguard Worker this.libInfo = null; 374*288bf522SAndroid Build Coastguard Worker this.states = { 375*288bf522SAndroid Build Coastguard Worker SHOW_EVENT_INFO: 1, 376*288bf522SAndroid Build Coastguard Worker SHOW_PROCESS_INFO: 2, 377*288bf522SAndroid Build Coastguard Worker SHOW_THREAD_INFO: 3, 378*288bf522SAndroid Build Coastguard Worker SHOW_LIB_INFO: 4, 379*288bf522SAndroid Build Coastguard Worker }; 380*288bf522SAndroid Build Coastguard Worker if (isClockEvent(this.eventInfo)) { 381*288bf522SAndroid Build Coastguard Worker this.getSampleWeight = function (eventCount) { 382*288bf522SAndroid Build Coastguard Worker return (eventCount / 1000000.0).toFixed(3).toLocaleString() + ' ms'; 383*288bf522SAndroid Build Coastguard Worker }; 384*288bf522SAndroid Build Coastguard Worker } else { 385*288bf522SAndroid Build Coastguard Worker this.getSampleWeight = (eventCount) => eventCount.toLocaleString(); 386*288bf522SAndroid Build Coastguard Worker } 387*288bf522SAndroid Build Coastguard Worker } 388*288bf522SAndroid Build Coastguard Worker 389*288bf522SAndroid Build Coastguard Worker _getState() { 390*288bf522SAndroid Build Coastguard Worker if (this.libInfo) { 391*288bf522SAndroid Build Coastguard Worker return this.states.SHOW_LIB_INFO; 392*288bf522SAndroid Build Coastguard Worker } 393*288bf522SAndroid Build Coastguard Worker if (this.threadInfo) { 394*288bf522SAndroid Build Coastguard Worker return this.states.SHOW_THREAD_INFO; 395*288bf522SAndroid Build Coastguard Worker } 396*288bf522SAndroid Build Coastguard Worker if (this.processInfo) { 397*288bf522SAndroid Build Coastguard Worker return this.states.SHOW_PROCESS_INFO; 398*288bf522SAndroid Build Coastguard Worker } 399*288bf522SAndroid Build Coastguard Worker return this.states.SHOW_EVENT_INFO; 400*288bf522SAndroid Build Coastguard Worker } 401*288bf522SAndroid Build Coastguard Worker 402*288bf522SAndroid Build Coastguard Worker _goBack() { 403*288bf522SAndroid Build Coastguard Worker let state = this._getState(); 404*288bf522SAndroid Build Coastguard Worker if (state == this.states.SHOW_PROCESS_INFO) { 405*288bf522SAndroid Build Coastguard Worker this.processInfo = null; 406*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_THREAD_INFO) { 407*288bf522SAndroid Build Coastguard Worker this.threadInfo = null; 408*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_LIB_INFO) { 409*288bf522SAndroid Build Coastguard Worker this.libInfo = null; 410*288bf522SAndroid Build Coastguard Worker } 411*288bf522SAndroid Build Coastguard Worker this.draw(); 412*288bf522SAndroid Build Coastguard Worker } 413*288bf522SAndroid Build Coastguard Worker 414*288bf522SAndroid Build Coastguard Worker _selectHandler(chart) { 415*288bf522SAndroid Build Coastguard Worker let selectedItem = chart.getSelection()[0]; 416*288bf522SAndroid Build Coastguard Worker if (selectedItem) { 417*288bf522SAndroid Build Coastguard Worker let state = this._getState(); 418*288bf522SAndroid Build Coastguard Worker if (state == this.states.SHOW_EVENT_INFO) { 419*288bf522SAndroid Build Coastguard Worker this.processInfo = this.eventInfo.processes[selectedItem.row]; 420*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_PROCESS_INFO) { 421*288bf522SAndroid Build Coastguard Worker this.threadInfo = this.processInfo.threads[selectedItem.row]; 422*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_THREAD_INFO) { 423*288bf522SAndroid Build Coastguard Worker this.libInfo = this.threadInfo.libs[selectedItem.row]; 424*288bf522SAndroid Build Coastguard Worker } 425*288bf522SAndroid Build Coastguard Worker this.draw(); 426*288bf522SAndroid Build Coastguard Worker } 427*288bf522SAndroid Build Coastguard Worker } 428*288bf522SAndroid Build Coastguard Worker 429*288bf522SAndroid Build Coastguard Worker draw() { 430*288bf522SAndroid Build Coastguard Worker google.charts.setOnLoadCallback(() => this.realDraw()); 431*288bf522SAndroid Build Coastguard Worker } 432*288bf522SAndroid Build Coastguard Worker 433*288bf522SAndroid Build Coastguard Worker realDraw() { 434*288bf522SAndroid Build Coastguard Worker this.div.empty(); 435*288bf522SAndroid Build Coastguard Worker this._drawTitle(); 436*288bf522SAndroid Build Coastguard Worker this._drawPieChart(); 437*288bf522SAndroid Build Coastguard Worker } 438*288bf522SAndroid Build Coastguard Worker 439*288bf522SAndroid Build Coastguard Worker _drawTitle() { 440*288bf522SAndroid Build Coastguard Worker // Draw a table of 'Name', 'Event Count'. 441*288bf522SAndroid Build Coastguard Worker let rows = []; 442*288bf522SAndroid Build Coastguard Worker rows.push(['Event Type: ' + this.eventInfo.eventName, 443*288bf522SAndroid Build Coastguard Worker this.getSampleWeight(this.eventInfo.eventCount)]); 444*288bf522SAndroid Build Coastguard Worker if (this.processInfo) { 445*288bf522SAndroid Build Coastguard Worker rows.push(['Process: ' + getProcessName(this.processInfo.pid), 446*288bf522SAndroid Build Coastguard Worker this.getSampleWeight(this.processInfo.eventCount)]); 447*288bf522SAndroid Build Coastguard Worker } 448*288bf522SAndroid Build Coastguard Worker if (this.threadInfo) { 449*288bf522SAndroid Build Coastguard Worker rows.push(['Thread: ' + getThreadName(this.threadInfo.tid), 450*288bf522SAndroid Build Coastguard Worker this.getSampleWeight(this.threadInfo.eventCount)]); 451*288bf522SAndroid Build Coastguard Worker } 452*288bf522SAndroid Build Coastguard Worker if (this.libInfo) { 453*288bf522SAndroid Build Coastguard Worker rows.push(['Library: ' + getLibName(this.libInfo.libId), 454*288bf522SAndroid Build Coastguard Worker this.getSampleWeight(this.libInfo.eventCount)]); 455*288bf522SAndroid Build Coastguard Worker } 456*288bf522SAndroid Build Coastguard Worker let data = new google.visualization.DataTable(); 457*288bf522SAndroid Build Coastguard Worker data.addColumn('string', ''); 458*288bf522SAndroid Build Coastguard Worker data.addColumn('string', ''); 459*288bf522SAndroid Build Coastguard Worker data.addRows(rows); 460*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < rows.length; ++i) { 461*288bf522SAndroid Build Coastguard Worker data.setProperty(i, 0, 'className', 'boldTableCell'); 462*288bf522SAndroid Build Coastguard Worker } 463*288bf522SAndroid Build Coastguard Worker let wrapperDiv = $('<div>'); 464*288bf522SAndroid Build Coastguard Worker wrapperDiv.appendTo(this.div); 465*288bf522SAndroid Build Coastguard Worker let table = new google.visualization.Table(wrapperDiv.get(0)); 466*288bf522SAndroid Build Coastguard Worker table.draw(data, { 467*288bf522SAndroid Build Coastguard Worker width: '100%', 468*288bf522SAndroid Build Coastguard Worker sort: 'disable', 469*288bf522SAndroid Build Coastguard Worker allowHtml: true, 470*288bf522SAndroid Build Coastguard Worker cssClassNames: { 471*288bf522SAndroid Build Coastguard Worker 'tableCell': 'tableCell', 472*288bf522SAndroid Build Coastguard Worker }, 473*288bf522SAndroid Build Coastguard Worker }); 474*288bf522SAndroid Build Coastguard Worker if (this._getState() != this.states.SHOW_EVENT_INFO) { 475*288bf522SAndroid Build Coastguard Worker $('<button type="button" class="btn btn-primary">Back</button>').appendTo(this.div) 476*288bf522SAndroid Build Coastguard Worker .click(() => this._goBack()); 477*288bf522SAndroid Build Coastguard Worker } 478*288bf522SAndroid Build Coastguard Worker } 479*288bf522SAndroid Build Coastguard Worker 480*288bf522SAndroid Build Coastguard Worker _drawPieChart() { 481*288bf522SAndroid Build Coastguard Worker let state = this._getState(); 482*288bf522SAndroid Build Coastguard Worker let title = null; 483*288bf522SAndroid Build Coastguard Worker let firstColumn = null; 484*288bf522SAndroid Build Coastguard Worker let rows = []; 485*288bf522SAndroid Build Coastguard Worker let thisObj = this; 486*288bf522SAndroid Build Coastguard Worker function getItem(name, eventCount, totalEventCount) { 487*288bf522SAndroid Build Coastguard Worker let sampleWeight = thisObj.getSampleWeight(eventCount); 488*288bf522SAndroid Build Coastguard Worker let percent = (eventCount * 100.0 / totalEventCount).toFixed(2) + '%'; 489*288bf522SAndroid Build Coastguard Worker return [name, eventCount, getHtml('pre', {text: name}) + 490*288bf522SAndroid Build Coastguard Worker getHtml('b', {text: `${sampleWeight} (${percent})`})]; 491*288bf522SAndroid Build Coastguard Worker } 492*288bf522SAndroid Build Coastguard Worker 493*288bf522SAndroid Build Coastguard Worker if (state == this.states.SHOW_EVENT_INFO) { 494*288bf522SAndroid Build Coastguard Worker title = 'Processes in event type ' + this.eventInfo.eventName; 495*288bf522SAndroid Build Coastguard Worker firstColumn = 'Process'; 496*288bf522SAndroid Build Coastguard Worker for (let process of this.eventInfo.processes) { 497*288bf522SAndroid Build Coastguard Worker rows.push(getItem('Process: ' + getProcessName(process.pid), process.eventCount, 498*288bf522SAndroid Build Coastguard Worker this.eventInfo.eventCount)); 499*288bf522SAndroid Build Coastguard Worker } 500*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_PROCESS_INFO) { 501*288bf522SAndroid Build Coastguard Worker title = 'Threads in process ' + getProcessName(this.processInfo.pid); 502*288bf522SAndroid Build Coastguard Worker firstColumn = 'Thread'; 503*288bf522SAndroid Build Coastguard Worker for (let thread of this.processInfo.threads) { 504*288bf522SAndroid Build Coastguard Worker rows.push(getItem('Thread: ' + getThreadName(thread.tid), thread.eventCount, 505*288bf522SAndroid Build Coastguard Worker this.processInfo.eventCount)); 506*288bf522SAndroid Build Coastguard Worker } 507*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_THREAD_INFO) { 508*288bf522SAndroid Build Coastguard Worker title = 'Libraries in thread ' + getThreadName(this.threadInfo.tid); 509*288bf522SAndroid Build Coastguard Worker firstColumn = 'Library'; 510*288bf522SAndroid Build Coastguard Worker for (let lib of this.threadInfo.libs) { 511*288bf522SAndroid Build Coastguard Worker rows.push(getItem('Library: ' + getLibName(lib.libId), lib.eventCount, 512*288bf522SAndroid Build Coastguard Worker this.threadInfo.eventCount)); 513*288bf522SAndroid Build Coastguard Worker } 514*288bf522SAndroid Build Coastguard Worker } else if (state == this.states.SHOW_LIB_INFO) { 515*288bf522SAndroid Build Coastguard Worker title = 'Functions in library ' + getLibName(this.libInfo.libId); 516*288bf522SAndroid Build Coastguard Worker firstColumn = 'Function'; 517*288bf522SAndroid Build Coastguard Worker for (let func of this.libInfo.functions) { 518*288bf522SAndroid Build Coastguard Worker rows.push(getItem('Function: ' + getFuncName(func.f), func.c[1], 519*288bf522SAndroid Build Coastguard Worker this.libInfo.eventCount)); 520*288bf522SAndroid Build Coastguard Worker } 521*288bf522SAndroid Build Coastguard Worker } 522*288bf522SAndroid Build Coastguard Worker let data = new google.visualization.DataTable(); 523*288bf522SAndroid Build Coastguard Worker data.addColumn('string', firstColumn); 524*288bf522SAndroid Build Coastguard Worker data.addColumn('number', 'EventCount'); 525*288bf522SAndroid Build Coastguard Worker data.addColumn({type: 'string', role: 'tooltip', p: {html: true}}); 526*288bf522SAndroid Build Coastguard Worker data.addRows(rows); 527*288bf522SAndroid Build Coastguard Worker 528*288bf522SAndroid Build Coastguard Worker let wrapperDiv = $('<div>'); 529*288bf522SAndroid Build Coastguard Worker wrapperDiv.appendTo(this.div); 530*288bf522SAndroid Build Coastguard Worker let chart = new google.visualization.PieChart(wrapperDiv.get(0)); 531*288bf522SAndroid Build Coastguard Worker chart.draw(data, { 532*288bf522SAndroid Build Coastguard Worker title: title, 533*288bf522SAndroid Build Coastguard Worker width: 1000, 534*288bf522SAndroid Build Coastguard Worker height: 600, 535*288bf522SAndroid Build Coastguard Worker tooltip: {isHtml: true}, 536*288bf522SAndroid Build Coastguard Worker }); 537*288bf522SAndroid Build Coastguard Worker google.visualization.events.addListener(chart, 'select', () => this._selectHandler(chart)); 538*288bf522SAndroid Build Coastguard Worker } 539*288bf522SAndroid Build Coastguard Worker} 540*288bf522SAndroid Build Coastguard Worker 541*288bf522SAndroid Build Coastguard Worker 542*288bf522SAndroid Build Coastguard Workerclass ChartStatTab { 543*288bf522SAndroid Build Coastguard Worker init(div) { 544*288bf522SAndroid Build Coastguard Worker this.div = div; 545*288bf522SAndroid Build Coastguard Worker } 546*288bf522SAndroid Build Coastguard Worker 547*288bf522SAndroid Build Coastguard Worker draw() { 548*288bf522SAndroid Build Coastguard Worker new RecordFileView(this.div).draw(); 549*288bf522SAndroid Build Coastguard Worker let views = createViewsForEvents(this.div, (div, eventInfo) => { 550*288bf522SAndroid Build Coastguard Worker return new ChartView(div, eventInfo); 551*288bf522SAndroid Build Coastguard Worker }); 552*288bf522SAndroid Build Coastguard Worker for (let view of views) { 553*288bf522SAndroid Build Coastguard Worker view.draw(); 554*288bf522SAndroid Build Coastguard Worker } 555*288bf522SAndroid Build Coastguard Worker } 556*288bf522SAndroid Build Coastguard Worker} 557*288bf522SAndroid Build Coastguard Worker 558*288bf522SAndroid Build Coastguard Worker 559*288bf522SAndroid Build Coastguard Workerclass SampleTableTab { 560*288bf522SAndroid Build Coastguard Worker init(div) { 561*288bf522SAndroid Build Coastguard Worker this.div = div; 562*288bf522SAndroid Build Coastguard Worker } 563*288bf522SAndroid Build Coastguard Worker 564*288bf522SAndroid Build Coastguard Worker draw() { 565*288bf522SAndroid Build Coastguard Worker let views = []; 566*288bf522SAndroid Build Coastguard Worker createPromise() 567*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Draw SampleTable...', 0)) 568*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 569*288bf522SAndroid Build Coastguard Worker this.div.empty(); 570*288bf522SAndroid Build Coastguard Worker views = createViewsForEvents(this.div, (div, eventInfo) => { 571*288bf522SAndroid Build Coastguard Worker return new SampleTableView(div, eventInfo); 572*288bf522SAndroid Build Coastguard Worker }); 573*288bf522SAndroid Build Coastguard Worker })) 574*288bf522SAndroid Build Coastguard Worker .then(() => drawViewsAsync(views, 100, (view, progress) => view.drawAsync(progress))) 575*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 576*288bf522SAndroid Build Coastguard Worker } 577*288bf522SAndroid Build Coastguard Worker} 578*288bf522SAndroid Build Coastguard Worker 579*288bf522SAndroid Build Coastguard Worker// Select the way to show sample weight in SampleTableTab. 580*288bf522SAndroid Build Coastguard Worker// 1. Show percentage of event count. 581*288bf522SAndroid Build Coastguard Worker// 2. Show event count (For cpu-clock and task-clock events, it is time in ms). 582*288bf522SAndroid Build Coastguard Workerclass SampleTableWeightSelectorView { 583*288bf522SAndroid Build Coastguard Worker constructor(divContainer, eventInfo, onSelectChange) { 584*288bf522SAndroid Build Coastguard Worker let options = new Map(); 585*288bf522SAndroid Build Coastguard Worker options.set('percent', 'Show percentage of event count'); 586*288bf522SAndroid Build Coastguard Worker options.set('event_count', 'Show event count'); 587*288bf522SAndroid Build Coastguard Worker if (isClockEvent(eventInfo)) { 588*288bf522SAndroid Build Coastguard Worker options.set('event_count_in_ms', 'Show event count in milliseconds'); 589*288bf522SAndroid Build Coastguard Worker } 590*288bf522SAndroid Build Coastguard Worker let buttons = []; 591*288bf522SAndroid Build Coastguard Worker options.forEach((value, key) => { 592*288bf522SAndroid Build Coastguard Worker buttons.push(`<button type="button" class="dropdown-item" key="${key}">${value} 593*288bf522SAndroid Build Coastguard Worker </button>`); 594*288bf522SAndroid Build Coastguard Worker }); 595*288bf522SAndroid Build Coastguard Worker this.curOption = 'percent'; 596*288bf522SAndroid Build Coastguard Worker this.eventCount = eventInfo.eventCount; 597*288bf522SAndroid Build Coastguard Worker let id = createId(); 598*288bf522SAndroid Build Coastguard Worker let str = ` 599*288bf522SAndroid Build Coastguard Worker <div class="dropdown"> 600*288bf522SAndroid Build Coastguard Worker <button type="button" class="btn btn-primary dropdown-toggle" id="${id}" 601*288bf522SAndroid Build Coastguard Worker data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" 602*288bf522SAndroid Build Coastguard Worker >${options.get(this.curOption)}</button> 603*288bf522SAndroid Build Coastguard Worker <div class="dropdown-menu" aria-labelledby="${id}">${buttons.join('')}</div> 604*288bf522SAndroid Build Coastguard Worker </div> 605*288bf522SAndroid Build Coastguard Worker `; 606*288bf522SAndroid Build Coastguard Worker divContainer.append(str); 607*288bf522SAndroid Build Coastguard Worker divContainer.children().last().on('hidden.bs.dropdown', (e) => { 608*288bf522SAndroid Build Coastguard Worker if (e.clickEvent) { 609*288bf522SAndroid Build Coastguard Worker let button = $(e.clickEvent.target); 610*288bf522SAndroid Build Coastguard Worker let newOption = button.attr('key'); 611*288bf522SAndroid Build Coastguard Worker if (newOption && this.curOption != newOption) { 612*288bf522SAndroid Build Coastguard Worker this.curOption = newOption; 613*288bf522SAndroid Build Coastguard Worker divContainer.find(`#${id}`).text(options.get(this.curOption)); 614*288bf522SAndroid Build Coastguard Worker onSelectChange(); 615*288bf522SAndroid Build Coastguard Worker } 616*288bf522SAndroid Build Coastguard Worker } 617*288bf522SAndroid Build Coastguard Worker }); 618*288bf522SAndroid Build Coastguard Worker } 619*288bf522SAndroid Build Coastguard Worker 620*288bf522SAndroid Build Coastguard Worker getSampleWeightFunction() { 621*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'percent') { 622*288bf522SAndroid Build Coastguard Worker return (eventCount) => (eventCount * 100.0 / this.eventCount).toFixed(2) + '%'; 623*288bf522SAndroid Build Coastguard Worker } 624*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'event_count') { 625*288bf522SAndroid Build Coastguard Worker return (eventCount) => eventCount.toLocaleString(); 626*288bf522SAndroid Build Coastguard Worker } 627*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'event_count_in_ms') { 628*288bf522SAndroid Build Coastguard Worker return (eventCount) => (eventCount / 1000000.0).toFixed(3).toLocaleString(); 629*288bf522SAndroid Build Coastguard Worker } 630*288bf522SAndroid Build Coastguard Worker } 631*288bf522SAndroid Build Coastguard Worker 632*288bf522SAndroid Build Coastguard Worker getSampleWeightSuffix() { 633*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'event_count_in_ms') { 634*288bf522SAndroid Build Coastguard Worker return ' ms'; 635*288bf522SAndroid Build Coastguard Worker } 636*288bf522SAndroid Build Coastguard Worker return ''; 637*288bf522SAndroid Build Coastguard Worker } 638*288bf522SAndroid Build Coastguard Worker} 639*288bf522SAndroid Build Coastguard Worker 640*288bf522SAndroid Build Coastguard Worker 641*288bf522SAndroid Build Coastguard Workerclass SampleTableView { 642*288bf522SAndroid Build Coastguard Worker constructor(divContainer, eventInfo) { 643*288bf522SAndroid Build Coastguard Worker this.id = createId(); 644*288bf522SAndroid Build Coastguard Worker this.div = $('<div>', {id: this.id}).appendTo(divContainer); 645*288bf522SAndroid Build Coastguard Worker this.eventInfo = eventInfo; 646*288bf522SAndroid Build Coastguard Worker this.selectorView = null; 647*288bf522SAndroid Build Coastguard Worker this.tableDiv = null; 648*288bf522SAndroid Build Coastguard Worker } 649*288bf522SAndroid Build Coastguard Worker 650*288bf522SAndroid Build Coastguard Worker drawAsync(totalProgress) { 651*288bf522SAndroid Build Coastguard Worker return createPromise() 652*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 653*288bf522SAndroid Build Coastguard Worker this.div.empty(); 654*288bf522SAndroid Build Coastguard Worker this.selectorView = new SampleTableWeightSelectorView( 655*288bf522SAndroid Build Coastguard Worker this.div, this.eventInfo, () => this.onSampleWeightChange()); 656*288bf522SAndroid Build Coastguard Worker this.tableDiv = $('<div>').appendTo(this.div); 657*288bf522SAndroid Build Coastguard Worker })) 658*288bf522SAndroid Build Coastguard Worker .then(() => this._drawSampleTable(totalProgress)); 659*288bf522SAndroid Build Coastguard Worker } 660*288bf522SAndroid Build Coastguard Worker 661*288bf522SAndroid Build Coastguard Worker // Return a promise to draw SampleTable. 662*288bf522SAndroid Build Coastguard Worker _drawSampleTable(totalProgress) { 663*288bf522SAndroid Build Coastguard Worker let eventInfo = this.eventInfo; 664*288bf522SAndroid Build Coastguard Worker let data = []; 665*288bf522SAndroid Build Coastguard Worker return createPromise() 666*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 667*288bf522SAndroid Build Coastguard Worker this.tableDiv.empty(); 668*288bf522SAndroid Build Coastguard Worker let getSampleWeight = this.selectorView.getSampleWeightFunction(); 669*288bf522SAndroid Build Coastguard Worker let sampleWeightSuffix = this.selectorView.getSampleWeightSuffix(); 670*288bf522SAndroid Build Coastguard Worker // Draw a table of 'Total', 'Self', 'Samples', 'Process', 'Thread', 'Library', 671*288bf522SAndroid Build Coastguard Worker // 'Function'. 672*288bf522SAndroid Build Coastguard Worker let valueSuffix = sampleWeightSuffix.length > 0 ? `(in${sampleWeightSuffix})` : ''; 673*288bf522SAndroid Build Coastguard Worker let titles = ['Total' + valueSuffix, 'Self' + valueSuffix, 'Samples', 'Process', 674*288bf522SAndroid Build Coastguard Worker 'Thread', 'Library', 'Function', 'HideKey']; 675*288bf522SAndroid Build Coastguard Worker this.tableDiv.append(` 676*288bf522SAndroid Build Coastguard Worker <table cellspacing="0" class="table table-striped table-bordered" 677*288bf522SAndroid Build Coastguard Worker style="width:100%"> 678*288bf522SAndroid Build Coastguard Worker <thead>${getTableRow(titles, 'th')}</thead> 679*288bf522SAndroid Build Coastguard Worker <tbody></tbody> 680*288bf522SAndroid Build Coastguard Worker <tfoot>${getTableRow(titles, 'th')}</tfoot> 681*288bf522SAndroid Build Coastguard Worker </table>`); 682*288bf522SAndroid Build Coastguard Worker for (let [i, process] of eventInfo.processes.entries()) { 683*288bf522SAndroid Build Coastguard Worker let processName = getProcessName(process.pid); 684*288bf522SAndroid Build Coastguard Worker for (let [j, thread] of process.threads.entries()) { 685*288bf522SAndroid Build Coastguard Worker let threadName = getThreadName(thread.tid); 686*288bf522SAndroid Build Coastguard Worker for (let [k, lib] of thread.libs.entries()) { 687*288bf522SAndroid Build Coastguard Worker let libName = getLibName(lib.libId); 688*288bf522SAndroid Build Coastguard Worker for (let [t, func] of lib.functions.entries()) { 689*288bf522SAndroid Build Coastguard Worker let totalValue = getSampleWeight(func.c[2]); 690*288bf522SAndroid Build Coastguard Worker let selfValue = getSampleWeight(func.c[1]); 691*288bf522SAndroid Build Coastguard Worker let key = [i, j, k, t].join('_'); 692*288bf522SAndroid Build Coastguard Worker data.push([totalValue, selfValue, func.c[0], processName, 693*288bf522SAndroid Build Coastguard Worker threadName, libName, getFuncName(func.f), key]) 694*288bf522SAndroid Build Coastguard Worker } 695*288bf522SAndroid Build Coastguard Worker } 696*288bf522SAndroid Build Coastguard Worker } 697*288bf522SAndroid Build Coastguard Worker } 698*288bf522SAndroid Build Coastguard Worker })) 699*288bf522SAndroid Build Coastguard Worker .then(addProgress(totalProgress / 2)) 700*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 701*288bf522SAndroid Build Coastguard Worker let table = this.tableDiv.find('table'); 702*288bf522SAndroid Build Coastguard Worker let dataTable = table.DataTable({ 703*288bf522SAndroid Build Coastguard Worker lengthMenu: [10, 20, 50, 100, -1], 704*288bf522SAndroid Build Coastguard Worker pageLength: 100, 705*288bf522SAndroid Build Coastguard Worker order: [[0, 'desc'], [1, 'desc'], [2, 'desc']], 706*288bf522SAndroid Build Coastguard Worker data: data, 707*288bf522SAndroid Build Coastguard Worker responsive: true, 708*288bf522SAndroid Build Coastguard Worker columnDefs: [ 709*288bf522SAndroid Build Coastguard Worker { orderSequence: [ 'desc' ], className: 'textRight', targets: [0, 1, 2] }, 710*288bf522SAndroid Build Coastguard Worker ], 711*288bf522SAndroid Build Coastguard Worker }); 712*288bf522SAndroid Build Coastguard Worker dataTable.column(7).visible(false); 713*288bf522SAndroid Build Coastguard Worker 714*288bf522SAndroid Build Coastguard Worker table.find('tr').css('cursor', 'pointer'); 715*288bf522SAndroid Build Coastguard Worker table.on('click', 'tr', function() { 716*288bf522SAndroid Build Coastguard Worker let data = dataTable.row(this).data(); 717*288bf522SAndroid Build Coastguard Worker if (!data) { 718*288bf522SAndroid Build Coastguard Worker // A row in header or footer. 719*288bf522SAndroid Build Coastguard Worker return; 720*288bf522SAndroid Build Coastguard Worker } 721*288bf522SAndroid Build Coastguard Worker let key = data[7]; 722*288bf522SAndroid Build Coastguard Worker if (!key) { 723*288bf522SAndroid Build Coastguard Worker return; 724*288bf522SAndroid Build Coastguard Worker } 725*288bf522SAndroid Build Coastguard Worker let indexes = key.split('_'); 726*288bf522SAndroid Build Coastguard Worker let processInfo = eventInfo.processes[indexes[0]]; 727*288bf522SAndroid Build Coastguard Worker let threadInfo = processInfo.threads[indexes[1]]; 728*288bf522SAndroid Build Coastguard Worker let lib = threadInfo.libs[indexes[2]]; 729*288bf522SAndroid Build Coastguard Worker let func = lib.functions[indexes[3]]; 730*288bf522SAndroid Build Coastguard Worker FunctionTab.showFunction(eventInfo, processInfo, threadInfo, lib, func); 731*288bf522SAndroid Build Coastguard Worker }); 732*288bf522SAndroid Build Coastguard Worker })); 733*288bf522SAndroid Build Coastguard Worker } 734*288bf522SAndroid Build Coastguard Worker 735*288bf522SAndroid Build Coastguard Worker onSampleWeightChange() { 736*288bf522SAndroid Build Coastguard Worker createPromise() 737*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Draw SampleTable...', 0)) 738*288bf522SAndroid Build Coastguard Worker .then(() => this._drawSampleTable(100)) 739*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 740*288bf522SAndroid Build Coastguard Worker } 741*288bf522SAndroid Build Coastguard Worker} 742*288bf522SAndroid Build Coastguard Worker 743*288bf522SAndroid Build Coastguard Worker 744*288bf522SAndroid Build Coastguard Worker// Show embedded flamegraph generated by inferno. 745*288bf522SAndroid Build Coastguard Workerclass FlameGraphTab { 746*288bf522SAndroid Build Coastguard Worker init(div) { 747*288bf522SAndroid Build Coastguard Worker this.div = div; 748*288bf522SAndroid Build Coastguard Worker } 749*288bf522SAndroid Build Coastguard Worker 750*288bf522SAndroid Build Coastguard Worker draw() { 751*288bf522SAndroid Build Coastguard Worker let views = []; 752*288bf522SAndroid Build Coastguard Worker createPromise() 753*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Draw Flamegraph...', 0)) 754*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 755*288bf522SAndroid Build Coastguard Worker this.div.empty(); 756*288bf522SAndroid Build Coastguard Worker views = createViewsForEvents(this.div, (div, eventInfo) => { 757*288bf522SAndroid Build Coastguard Worker return new FlameGraphViewList(div, eventInfo); 758*288bf522SAndroid Build Coastguard Worker }); 759*288bf522SAndroid Build Coastguard Worker })) 760*288bf522SAndroid Build Coastguard Worker .then(() => drawViewsAsync(views, 100, (view, progress) => view.drawAsync(progress))) 761*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 762*288bf522SAndroid Build Coastguard Worker } 763*288bf522SAndroid Build Coastguard Worker} 764*288bf522SAndroid Build Coastguard Worker 765*288bf522SAndroid Build Coastguard Worker// Show FlameGraphs for samples in an event type, used in FlameGraphTab. 766*288bf522SAndroid Build Coastguard Worker// 1. Draw 10 FlameGraphs at one time, and use a "More" button to show more FlameGraphs. 767*288bf522SAndroid Build Coastguard Worker// 2. First draw background of Flamegraphs, then draw details in idle time. 768*288bf522SAndroid Build Coastguard Workerclass FlameGraphViewList { 769*288bf522SAndroid Build Coastguard Worker constructor(div, eventInfo) { 770*288bf522SAndroid Build Coastguard Worker this.div = div; 771*288bf522SAndroid Build Coastguard Worker this.eventInfo = eventInfo; 772*288bf522SAndroid Build Coastguard Worker this.selectorView = null; 773*288bf522SAndroid Build Coastguard Worker this.flamegraphDiv = null; 774*288bf522SAndroid Build Coastguard Worker this.flamegraphs = []; 775*288bf522SAndroid Build Coastguard Worker this.moreButton = null; 776*288bf522SAndroid Build Coastguard Worker } 777*288bf522SAndroid Build Coastguard Worker 778*288bf522SAndroid Build Coastguard Worker drawAsync(totalProgress) { 779*288bf522SAndroid Build Coastguard Worker this.div.empty(); 780*288bf522SAndroid Build Coastguard Worker this.selectorView = new SampleWeightSelectorView(this.div, this.eventInfo, 781*288bf522SAndroid Build Coastguard Worker () => this.onSampleWeightChange()); 782*288bf522SAndroid Build Coastguard Worker this.flamegraphDiv = $('<div>').appendTo(this.div); 783*288bf522SAndroid Build Coastguard Worker return this._drawMoreFlameGraphs(10, totalProgress); 784*288bf522SAndroid Build Coastguard Worker } 785*288bf522SAndroid Build Coastguard Worker 786*288bf522SAndroid Build Coastguard Worker // Return a promise to draw flamegraphs. 787*288bf522SAndroid Build Coastguard Worker _drawMoreFlameGraphs(moreCount, progress) { 788*288bf522SAndroid Build Coastguard Worker let initProgress = progress / (1 + moreCount); 789*288bf522SAndroid Build Coastguard Worker let newFlamegraphs = []; 790*288bf522SAndroid Build Coastguard Worker return createPromise() 791*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 792*288bf522SAndroid Build Coastguard Worker if (this.moreButton) { 793*288bf522SAndroid Build Coastguard Worker this.moreButton.hide(); 794*288bf522SAndroid Build Coastguard Worker } 795*288bf522SAndroid Build Coastguard Worker let pId = 0; 796*288bf522SAndroid Build Coastguard Worker let tId = 0; 797*288bf522SAndroid Build Coastguard Worker let newCount = this.flamegraphs.length + moreCount; 798*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < newCount; ++i) { 799*288bf522SAndroid Build Coastguard Worker if (pId == this.eventInfo.processes.length) { 800*288bf522SAndroid Build Coastguard Worker break; 801*288bf522SAndroid Build Coastguard Worker } 802*288bf522SAndroid Build Coastguard Worker let process = this.eventInfo.processes[pId]; 803*288bf522SAndroid Build Coastguard Worker let thread = process.threads[tId]; 804*288bf522SAndroid Build Coastguard Worker if (i >= this.flamegraphs.length) { 805*288bf522SAndroid Build Coastguard Worker let title = `Process ${getProcessName(process.pid)} ` + 806*288bf522SAndroid Build Coastguard Worker `Thread ${getThreadName(thread.tid)} ` + 807*288bf522SAndroid Build Coastguard Worker `(Samples: ${thread.sampleCount})`; 808*288bf522SAndroid Build Coastguard Worker let totalCount = {countForProcess: process.eventCount, 809*288bf522SAndroid Build Coastguard Worker countForThread: thread.eventCount}; 810*288bf522SAndroid Build Coastguard Worker let flamegraph = new FlameGraphView(this.flamegraphDiv, title, totalCount, 811*288bf522SAndroid Build Coastguard Worker thread.g.c, false); 812*288bf522SAndroid Build Coastguard Worker flamegraph.draw(); 813*288bf522SAndroid Build Coastguard Worker newFlamegraphs.push(flamegraph); 814*288bf522SAndroid Build Coastguard Worker } 815*288bf522SAndroid Build Coastguard Worker tId++; 816*288bf522SAndroid Build Coastguard Worker if (tId == process.threads.length) { 817*288bf522SAndroid Build Coastguard Worker pId++; 818*288bf522SAndroid Build Coastguard Worker tId = 0; 819*288bf522SAndroid Build Coastguard Worker } 820*288bf522SAndroid Build Coastguard Worker } 821*288bf522SAndroid Build Coastguard Worker if (pId < this.eventInfo.processes.length) { 822*288bf522SAndroid Build Coastguard Worker // Show "More" Button. 823*288bf522SAndroid Build Coastguard Worker if (!this.moreButton) { 824*288bf522SAndroid Build Coastguard Worker this.div.append(` 825*288bf522SAndroid Build Coastguard Worker <div style="text-align:center"> 826*288bf522SAndroid Build Coastguard Worker <button type="button" class="btn btn-primary">More</button> 827*288bf522SAndroid Build Coastguard Worker </div>`); 828*288bf522SAndroid Build Coastguard Worker this.moreButton = this.div.children().last().find('button'); 829*288bf522SAndroid Build Coastguard Worker this.moreButton.click(() => { 830*288bf522SAndroid Build Coastguard Worker createPromise().then(updateProgress('Draw FlameGraph...', 0)) 831*288bf522SAndroid Build Coastguard Worker .then(() => this._drawMoreFlameGraphs(10, 100)) 832*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 833*288bf522SAndroid Build Coastguard Worker }); 834*288bf522SAndroid Build Coastguard Worker this.moreButton.hide(); 835*288bf522SAndroid Build Coastguard Worker } 836*288bf522SAndroid Build Coastguard Worker } else if (this.moreButton) { 837*288bf522SAndroid Build Coastguard Worker this.moreButton.remove(); 838*288bf522SAndroid Build Coastguard Worker this.moreButton = null; 839*288bf522SAndroid Build Coastguard Worker } 840*288bf522SAndroid Build Coastguard Worker for (let flamegraph of newFlamegraphs) { 841*288bf522SAndroid Build Coastguard Worker this.flamegraphs.push(flamegraph); 842*288bf522SAndroid Build Coastguard Worker } 843*288bf522SAndroid Build Coastguard Worker })) 844*288bf522SAndroid Build Coastguard Worker .then(addProgress(initProgress)) 845*288bf522SAndroid Build Coastguard Worker .then(() => this.drawDetails(newFlamegraphs, progress - initProgress)); 846*288bf522SAndroid Build Coastguard Worker } 847*288bf522SAndroid Build Coastguard Worker 848*288bf522SAndroid Build Coastguard Worker drawDetails(flamegraphs, totalProgress) { 849*288bf522SAndroid Build Coastguard Worker return createPromise() 850*288bf522SAndroid Build Coastguard Worker .then(() => drawViewsAsync(flamegraphs, totalProgress, (view, progress) => { 851*288bf522SAndroid Build Coastguard Worker return createPromise() 852*288bf522SAndroid Build Coastguard Worker .then(wait(() => view.drawDetails(this.selectorView.getSampleWeightFunction()))) 853*288bf522SAndroid Build Coastguard Worker .then(addProgress(progress)); 854*288bf522SAndroid Build Coastguard Worker })) 855*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 856*288bf522SAndroid Build Coastguard Worker if (this.moreButton) { 857*288bf522SAndroid Build Coastguard Worker this.moreButton.show(); 858*288bf522SAndroid Build Coastguard Worker } 859*288bf522SAndroid Build Coastguard Worker })); 860*288bf522SAndroid Build Coastguard Worker } 861*288bf522SAndroid Build Coastguard Worker 862*288bf522SAndroid Build Coastguard Worker onSampleWeightChange() { 863*288bf522SAndroid Build Coastguard Worker createPromise().then(updateProgress('Draw FlameGraph...', 0)) 864*288bf522SAndroid Build Coastguard Worker .then(() => this.drawDetails(this.flamegraphs, 100)) 865*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 866*288bf522SAndroid Build Coastguard Worker } 867*288bf522SAndroid Build Coastguard Worker} 868*288bf522SAndroid Build Coastguard Worker 869*288bf522SAndroid Build Coastguard Worker// FunctionTab: show information of a function. 870*288bf522SAndroid Build Coastguard Worker// 1. Show the callgrpah and reverse callgraph of a function as flamegraphs. 871*288bf522SAndroid Build Coastguard Worker// 2. Show the annotated source code of the function. 872*288bf522SAndroid Build Coastguard Workerclass FunctionTab { 873*288bf522SAndroid Build Coastguard Worker static showFunction(eventInfo, processInfo, threadInfo, lib, func) { 874*288bf522SAndroid Build Coastguard Worker let title = 'Function'; 875*288bf522SAndroid Build Coastguard Worker let tab = gTabs.findTab(title); 876*288bf522SAndroid Build Coastguard Worker if (!tab) { 877*288bf522SAndroid Build Coastguard Worker tab = gTabs.addTab(title, new FunctionTab()); 878*288bf522SAndroid Build Coastguard Worker } 879*288bf522SAndroid Build Coastguard Worker gTabs.setActiveAsync(title) 880*288bf522SAndroid Build Coastguard Worker .then(() => tab.setFunction(eventInfo, processInfo, threadInfo, lib, func)); 881*288bf522SAndroid Build Coastguard Worker } 882*288bf522SAndroid Build Coastguard Worker 883*288bf522SAndroid Build Coastguard Worker constructor() { 884*288bf522SAndroid Build Coastguard Worker this.func = null; 885*288bf522SAndroid Build Coastguard Worker this.selectPercent = 'thread'; 886*288bf522SAndroid Build Coastguard Worker } 887*288bf522SAndroid Build Coastguard Worker 888*288bf522SAndroid Build Coastguard Worker init(div) { 889*288bf522SAndroid Build Coastguard Worker this.div = div; 890*288bf522SAndroid Build Coastguard Worker } 891*288bf522SAndroid Build Coastguard Worker 892*288bf522SAndroid Build Coastguard Worker setFunction(eventInfo, processInfo, threadInfo, lib, func) { 893*288bf522SAndroid Build Coastguard Worker this.eventInfo = eventInfo; 894*288bf522SAndroid Build Coastguard Worker this.processInfo = processInfo; 895*288bf522SAndroid Build Coastguard Worker this.threadInfo = threadInfo; 896*288bf522SAndroid Build Coastguard Worker this.lib = lib; 897*288bf522SAndroid Build Coastguard Worker this.func = func; 898*288bf522SAndroid Build Coastguard Worker this.selectorView = null; 899*288bf522SAndroid Build Coastguard Worker this.views = []; 900*288bf522SAndroid Build Coastguard Worker this.redraw(); 901*288bf522SAndroid Build Coastguard Worker } 902*288bf522SAndroid Build Coastguard Worker 903*288bf522SAndroid Build Coastguard Worker redraw() { 904*288bf522SAndroid Build Coastguard Worker if (!this.func) { 905*288bf522SAndroid Build Coastguard Worker return; 906*288bf522SAndroid Build Coastguard Worker } 907*288bf522SAndroid Build Coastguard Worker createPromise() 908*288bf522SAndroid Build Coastguard Worker .then(updateProgress("Draw Function...", 0)) 909*288bf522SAndroid Build Coastguard Worker .then(wait(() => { 910*288bf522SAndroid Build Coastguard Worker this.div.empty(); 911*288bf522SAndroid Build Coastguard Worker this._drawTitle(); 912*288bf522SAndroid Build Coastguard Worker 913*288bf522SAndroid Build Coastguard Worker this.selectorView = new SampleWeightSelectorView(this.div, this.eventInfo, 914*288bf522SAndroid Build Coastguard Worker () => this.onSampleWeightChange()); 915*288bf522SAndroid Build Coastguard Worker let funcId = this.func.f; 916*288bf522SAndroid Build Coastguard Worker let funcName = getFuncName(funcId); 917*288bf522SAndroid Build Coastguard Worker function getNodesMatchingFuncId(root) { 918*288bf522SAndroid Build Coastguard Worker let nodes = []; 919*288bf522SAndroid Build Coastguard Worker function recursiveFn(node) { 920*288bf522SAndroid Build Coastguard Worker if (node.f == funcId) { 921*288bf522SAndroid Build Coastguard Worker nodes.push(node); 922*288bf522SAndroid Build Coastguard Worker } else { 923*288bf522SAndroid Build Coastguard Worker for (let child of node.c) { 924*288bf522SAndroid Build Coastguard Worker recursiveFn(child); 925*288bf522SAndroid Build Coastguard Worker } 926*288bf522SAndroid Build Coastguard Worker } 927*288bf522SAndroid Build Coastguard Worker } 928*288bf522SAndroid Build Coastguard Worker recursiveFn(root); 929*288bf522SAndroid Build Coastguard Worker return nodes; 930*288bf522SAndroid Build Coastguard Worker } 931*288bf522SAndroid Build Coastguard Worker let totalCount = {countForProcess: this.processInfo.eventCount, 932*288bf522SAndroid Build Coastguard Worker countForThread: this.threadInfo.eventCount}; 933*288bf522SAndroid Build Coastguard Worker let callgraphView = new FlameGraphView( 934*288bf522SAndroid Build Coastguard Worker this.div, `Functions called by ${funcName}`, totalCount, 935*288bf522SAndroid Build Coastguard Worker getNodesMatchingFuncId(this.threadInfo.g), false); 936*288bf522SAndroid Build Coastguard Worker callgraphView.draw(); 937*288bf522SAndroid Build Coastguard Worker this.views.push(callgraphView); 938*288bf522SAndroid Build Coastguard Worker let reverseCallgraphView = new FlameGraphView( 939*288bf522SAndroid Build Coastguard Worker this.div, `Functions calling ${funcName}`, totalCount, 940*288bf522SAndroid Build Coastguard Worker getNodesMatchingFuncId(this.threadInfo.rg), true); 941*288bf522SAndroid Build Coastguard Worker reverseCallgraphView.draw(); 942*288bf522SAndroid Build Coastguard Worker this.views.push(reverseCallgraphView); 943*288bf522SAndroid Build Coastguard Worker let sourceFiles = collectSourceFilesForFunction(this.func); 944*288bf522SAndroid Build Coastguard Worker if (sourceFiles) { 945*288bf522SAndroid Build Coastguard Worker this.div.append(getHtml('hr')); 946*288bf522SAndroid Build Coastguard Worker this.div.append(getHtml('b', {text: 'SourceCode:'}) + '<br/>'); 947*288bf522SAndroid Build Coastguard Worker this.views.push(new SourceCodeView(this.div, sourceFiles, totalCount)); 948*288bf522SAndroid Build Coastguard Worker } 949*288bf522SAndroid Build Coastguard Worker 950*288bf522SAndroid Build Coastguard Worker let disassembly = collectDisassemblyForFunction(this.func); 951*288bf522SAndroid Build Coastguard Worker if (disassembly) { 952*288bf522SAndroid Build Coastguard Worker this.div.append(getHtml('hr')); 953*288bf522SAndroid Build Coastguard Worker this.div.append(getHtml('b', {text: 'Disassembly:'}) + '<br/>'); 954*288bf522SAndroid Build Coastguard Worker this.views.push(new DisassemblyView(this.div, disassembly, totalCount)); 955*288bf522SAndroid Build Coastguard Worker } 956*288bf522SAndroid Build Coastguard Worker })) 957*288bf522SAndroid Build Coastguard Worker .then(addProgress(25)) 958*288bf522SAndroid Build Coastguard Worker .then(() => this.drawDetails(75)) 959*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 960*288bf522SAndroid Build Coastguard Worker } 961*288bf522SAndroid Build Coastguard Worker 962*288bf522SAndroid Build Coastguard Worker draw() {} 963*288bf522SAndroid Build Coastguard Worker 964*288bf522SAndroid Build Coastguard Worker _drawTitle() { 965*288bf522SAndroid Build Coastguard Worker let eventName = this.eventInfo.eventName; 966*288bf522SAndroid Build Coastguard Worker let processName = getProcessName(this.processInfo.pid); 967*288bf522SAndroid Build Coastguard Worker let threadName = getThreadName(this.threadInfo.tid); 968*288bf522SAndroid Build Coastguard Worker let libName = getLibName(this.lib.libId); 969*288bf522SAndroid Build Coastguard Worker let funcName = getFuncName(this.func.f); 970*288bf522SAndroid Build Coastguard Worker // Draw a table of 'Name', 'Value'. 971*288bf522SAndroid Build Coastguard Worker let rows = []; 972*288bf522SAndroid Build Coastguard Worker rows.push(['Event Type', eventName]); 973*288bf522SAndroid Build Coastguard Worker rows.push(['Process', processName]); 974*288bf522SAndroid Build Coastguard Worker rows.push(['Thread', threadName]); 975*288bf522SAndroid Build Coastguard Worker rows.push(['Library', libName]); 976*288bf522SAndroid Build Coastguard Worker rows.push(['Function', getHtml('pre', {text: funcName})]); 977*288bf522SAndroid Build Coastguard Worker let data = new google.visualization.DataTable(); 978*288bf522SAndroid Build Coastguard Worker data.addColumn('string', ''); 979*288bf522SAndroid Build Coastguard Worker data.addColumn('string', ''); 980*288bf522SAndroid Build Coastguard Worker data.addRows(rows); 981*288bf522SAndroid Build Coastguard Worker for (let i = 0; i < rows.length; ++i) { 982*288bf522SAndroid Build Coastguard Worker data.setProperty(i, 0, 'className', 'boldTableCell'); 983*288bf522SAndroid Build Coastguard Worker } 984*288bf522SAndroid Build Coastguard Worker let wrapperDiv = $('<div>'); 985*288bf522SAndroid Build Coastguard Worker wrapperDiv.appendTo(this.div); 986*288bf522SAndroid Build Coastguard Worker let table = new google.visualization.Table(wrapperDiv.get(0)); 987*288bf522SAndroid Build Coastguard Worker table.draw(data, { 988*288bf522SAndroid Build Coastguard Worker width: '100%', 989*288bf522SAndroid Build Coastguard Worker sort: 'disable', 990*288bf522SAndroid Build Coastguard Worker allowHtml: true, 991*288bf522SAndroid Build Coastguard Worker cssClassNames: { 992*288bf522SAndroid Build Coastguard Worker 'tableCell': 'tableCell', 993*288bf522SAndroid Build Coastguard Worker }, 994*288bf522SAndroid Build Coastguard Worker }); 995*288bf522SAndroid Build Coastguard Worker } 996*288bf522SAndroid Build Coastguard Worker 997*288bf522SAndroid Build Coastguard Worker onSampleWeightChange() { 998*288bf522SAndroid Build Coastguard Worker createPromise() 999*288bf522SAndroid Build Coastguard Worker .then(updateProgress("Draw Function...", 0)) 1000*288bf522SAndroid Build Coastguard Worker .then(() => this.drawDetails(100)) 1001*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 1002*288bf522SAndroid Build Coastguard Worker } 1003*288bf522SAndroid Build Coastguard Worker 1004*288bf522SAndroid Build Coastguard Worker drawDetails(totalProgress) { 1005*288bf522SAndroid Build Coastguard Worker let sampleWeightFunction = this.selectorView.getSampleWeightFunction(); 1006*288bf522SAndroid Build Coastguard Worker return drawViewsAsync(this.views, totalProgress, (view, progress) => { 1007*288bf522SAndroid Build Coastguard Worker return createPromise() 1008*288bf522SAndroid Build Coastguard Worker .then(wait(() => view.drawDetails(sampleWeightFunction))) 1009*288bf522SAndroid Build Coastguard Worker .then(addProgress(progress)); 1010*288bf522SAndroid Build Coastguard Worker }); 1011*288bf522SAndroid Build Coastguard Worker } 1012*288bf522SAndroid Build Coastguard Worker} 1013*288bf522SAndroid Build Coastguard Worker 1014*288bf522SAndroid Build Coastguard Worker 1015*288bf522SAndroid Build Coastguard Worker// Select the way to show sample weight in FlamegraphTab and FunctionTab. 1016*288bf522SAndroid Build Coastguard Worker// 1. Show percentage of event count relative to all processes. 1017*288bf522SAndroid Build Coastguard Worker// 2. Show percentage of event count relative to the current process. 1018*288bf522SAndroid Build Coastguard Worker// 3. Show percentage of event count relative to the current thread. 1019*288bf522SAndroid Build Coastguard Worker// 4. Show absolute event count. 1020*288bf522SAndroid Build Coastguard Worker// 5. Show event count in milliseconds, only possible for cpu-clock or task-clock events. 1021*288bf522SAndroid Build Coastguard Workerclass SampleWeightSelectorView { 1022*288bf522SAndroid Build Coastguard Worker constructor(divContainer, eventInfo, onSelectChange) { 1023*288bf522SAndroid Build Coastguard Worker let options = new Map(); 1024*288bf522SAndroid Build Coastguard Worker options.set('percent_to_all', 'Show percentage of event count relative to all processes'); 1025*288bf522SAndroid Build Coastguard Worker options.set('percent_to_process', 1026*288bf522SAndroid Build Coastguard Worker 'Show percentage of event count relative to the current process'); 1027*288bf522SAndroid Build Coastguard Worker options.set('percent_to_thread', 1028*288bf522SAndroid Build Coastguard Worker 'Show percentage of event count relative to the current thread'); 1029*288bf522SAndroid Build Coastguard Worker options.set('event_count', 'Show event count'); 1030*288bf522SAndroid Build Coastguard Worker if (isClockEvent(eventInfo)) { 1031*288bf522SAndroid Build Coastguard Worker options.set('event_count_in_ms', 'Show event count in milliseconds'); 1032*288bf522SAndroid Build Coastguard Worker } 1033*288bf522SAndroid Build Coastguard Worker let buttons = []; 1034*288bf522SAndroid Build Coastguard Worker options.forEach((value, key) => { 1035*288bf522SAndroid Build Coastguard Worker buttons.push(`<button type="button" class="dropdown-item" key="${key}">${value} 1036*288bf522SAndroid Build Coastguard Worker </button>`); 1037*288bf522SAndroid Build Coastguard Worker }); 1038*288bf522SAndroid Build Coastguard Worker this.curOption = 'percent_to_all'; 1039*288bf522SAndroid Build Coastguard Worker let id = createId(); 1040*288bf522SAndroid Build Coastguard Worker let str = ` 1041*288bf522SAndroid Build Coastguard Worker <div class="dropdown"> 1042*288bf522SAndroid Build Coastguard Worker <button type="button" class="btn btn-primary dropdown-toggle" id="${id}" 1043*288bf522SAndroid Build Coastguard Worker data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" 1044*288bf522SAndroid Build Coastguard Worker >${options.get(this.curOption)}</button> 1045*288bf522SAndroid Build Coastguard Worker <div class="dropdown-menu" aria-labelledby="${id}">${buttons.join('')}</div> 1046*288bf522SAndroid Build Coastguard Worker </div> 1047*288bf522SAndroid Build Coastguard Worker `; 1048*288bf522SAndroid Build Coastguard Worker divContainer.append(str); 1049*288bf522SAndroid Build Coastguard Worker divContainer.children().last().on('hidden.bs.dropdown', (e) => { 1050*288bf522SAndroid Build Coastguard Worker if (e.clickEvent) { 1051*288bf522SAndroid Build Coastguard Worker let button = $(e.clickEvent.target); 1052*288bf522SAndroid Build Coastguard Worker let newOption = button.attr('key'); 1053*288bf522SAndroid Build Coastguard Worker if (newOption && this.curOption != newOption) { 1054*288bf522SAndroid Build Coastguard Worker this.curOption = newOption; 1055*288bf522SAndroid Build Coastguard Worker divContainer.find(`#${id}`).text(options.get(this.curOption)); 1056*288bf522SAndroid Build Coastguard Worker onSelectChange(); 1057*288bf522SAndroid Build Coastguard Worker } 1058*288bf522SAndroid Build Coastguard Worker } 1059*288bf522SAndroid Build Coastguard Worker }); 1060*288bf522SAndroid Build Coastguard Worker this.countForAllProcesses = eventInfo.eventCount; 1061*288bf522SAndroid Build Coastguard Worker } 1062*288bf522SAndroid Build Coastguard Worker 1063*288bf522SAndroid Build Coastguard Worker getSampleWeightFunction() { 1064*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'percent_to_all') { 1065*288bf522SAndroid Build Coastguard Worker let countForAllProcesses = this.countForAllProcesses; 1066*288bf522SAndroid Build Coastguard Worker return function(eventCount, _) { 1067*288bf522SAndroid Build Coastguard Worker let percent = eventCount * 100.0 / countForAllProcesses; 1068*288bf522SAndroid Build Coastguard Worker return percent.toFixed(2) + '%'; 1069*288bf522SAndroid Build Coastguard Worker }; 1070*288bf522SAndroid Build Coastguard Worker } 1071*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'percent_to_process') { 1072*288bf522SAndroid Build Coastguard Worker return function(eventCount, totalCount) { 1073*288bf522SAndroid Build Coastguard Worker let percent = eventCount * 100.0 / totalCount.countForProcess; 1074*288bf522SAndroid Build Coastguard Worker return percent.toFixed(2) + '%'; 1075*288bf522SAndroid Build Coastguard Worker }; 1076*288bf522SAndroid Build Coastguard Worker } 1077*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'percent_to_thread') { 1078*288bf522SAndroid Build Coastguard Worker return function(eventCount, totalCount) { 1079*288bf522SAndroid Build Coastguard Worker let percent = eventCount * 100.0 / totalCount.countForThread; 1080*288bf522SAndroid Build Coastguard Worker return percent.toFixed(2) + '%'; 1081*288bf522SAndroid Build Coastguard Worker }; 1082*288bf522SAndroid Build Coastguard Worker } 1083*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'event_count') { 1084*288bf522SAndroid Build Coastguard Worker return function(eventCount, _) { 1085*288bf522SAndroid Build Coastguard Worker return eventCount.toLocaleString(); 1086*288bf522SAndroid Build Coastguard Worker }; 1087*288bf522SAndroid Build Coastguard Worker } 1088*288bf522SAndroid Build Coastguard Worker if (this.curOption == 'event_count_in_ms') { 1089*288bf522SAndroid Build Coastguard Worker return function(eventCount, _) { 1090*288bf522SAndroid Build Coastguard Worker let timeInMs = eventCount / 1000000.0; 1091*288bf522SAndroid Build Coastguard Worker return timeInMs.toFixed(3).toLocaleString() + ' ms'; 1092*288bf522SAndroid Build Coastguard Worker }; 1093*288bf522SAndroid Build Coastguard Worker } 1094*288bf522SAndroid Build Coastguard Worker } 1095*288bf522SAndroid Build Coastguard Worker} 1096*288bf522SAndroid Build Coastguard Worker 1097*288bf522SAndroid Build Coastguard Worker// Given a callgraph, show the flamegraph. 1098*288bf522SAndroid Build Coastguard Workerclass FlameGraphView { 1099*288bf522SAndroid Build Coastguard Worker constructor(divContainer, title, totalCount, initNodes, reverseOrder) { 1100*288bf522SAndroid Build Coastguard Worker this.id = createId(); 1101*288bf522SAndroid Build Coastguard Worker this.div = $('<div>', {id: this.id, 1102*288bf522SAndroid Build Coastguard Worker style: 'font-family: Monospace; font-size: 12px'}); 1103*288bf522SAndroid Build Coastguard Worker this.div.appendTo(divContainer); 1104*288bf522SAndroid Build Coastguard Worker this.title = title; 1105*288bf522SAndroid Build Coastguard Worker this.totalCount = totalCount; 1106*288bf522SAndroid Build Coastguard Worker this.reverseOrder = reverseOrder; 1107*288bf522SAndroid Build Coastguard Worker this.sampleWeightFunction = null; 1108*288bf522SAndroid Build Coastguard Worker this.svgNodeHeight = 17; 1109*288bf522SAndroid Build Coastguard Worker this.initNodes = initNodes; 1110*288bf522SAndroid Build Coastguard Worker this.sumCount = 0; 1111*288bf522SAndroid Build Coastguard Worker for (let node of initNodes) { 1112*288bf522SAndroid Build Coastguard Worker this.sumCount += node.s; 1113*288bf522SAndroid Build Coastguard Worker } 1114*288bf522SAndroid Build Coastguard Worker this.maxDepth = this._getMaxDepth(this.initNodes); 1115*288bf522SAndroid Build Coastguard Worker this.svgHeight = this.svgNodeHeight * (this.maxDepth + 3); 1116*288bf522SAndroid Build Coastguard Worker this.svgStr = null; 1117*288bf522SAndroid Build Coastguard Worker this.svgDiv = null; 1118*288bf522SAndroid Build Coastguard Worker this.svg = null; 1119*288bf522SAndroid Build Coastguard Worker } 1120*288bf522SAndroid Build Coastguard Worker 1121*288bf522SAndroid Build Coastguard Worker _getMaxDepth(nodes) { 1122*288bf522SAndroid Build Coastguard Worker let isArray = Array.isArray(nodes); 1123*288bf522SAndroid Build Coastguard Worker let sumCount; 1124*288bf522SAndroid Build Coastguard Worker if (isArray) { 1125*288bf522SAndroid Build Coastguard Worker sumCount = nodes.reduce((acc, cur) => acc + cur.s, 0); 1126*288bf522SAndroid Build Coastguard Worker } else { 1127*288bf522SAndroid Build Coastguard Worker sumCount = nodes.s; 1128*288bf522SAndroid Build Coastguard Worker } 1129*288bf522SAndroid Build Coastguard Worker let width = this._getWidthPercentage(sumCount); 1130*288bf522SAndroid Build Coastguard Worker if (width < 0.1) { 1131*288bf522SAndroid Build Coastguard Worker return 0; 1132*288bf522SAndroid Build Coastguard Worker } 1133*288bf522SAndroid Build Coastguard Worker let children = isArray ? this._splitChildrenForNodes(nodes) : nodes.c; 1134*288bf522SAndroid Build Coastguard Worker let childDepth = 0; 1135*288bf522SAndroid Build Coastguard Worker for (let child of children) { 1136*288bf522SAndroid Build Coastguard Worker childDepth = Math.max(childDepth, this._getMaxDepth(child)); 1137*288bf522SAndroid Build Coastguard Worker } 1138*288bf522SAndroid Build Coastguard Worker return childDepth + 1; 1139*288bf522SAndroid Build Coastguard Worker } 1140*288bf522SAndroid Build Coastguard Worker 1141*288bf522SAndroid Build Coastguard Worker draw() { 1142*288bf522SAndroid Build Coastguard Worker // Only draw skeleton. 1143*288bf522SAndroid Build Coastguard Worker this.div.empty(); 1144*288bf522SAndroid Build Coastguard Worker this.div.append(`<p><b>${this.title}</b></p>`); 1145*288bf522SAndroid Build Coastguard Worker this.svgStr = []; 1146*288bf522SAndroid Build Coastguard Worker this._renderBackground(); 1147*288bf522SAndroid Build Coastguard Worker this.svgStr.push('</svg></div>'); 1148*288bf522SAndroid Build Coastguard Worker this.div.append(this.svgStr.join('')); 1149*288bf522SAndroid Build Coastguard Worker this.svgDiv = this.div.children().last(); 1150*288bf522SAndroid Build Coastguard Worker this.div.append('<br/><br/>'); 1151*288bf522SAndroid Build Coastguard Worker } 1152*288bf522SAndroid Build Coastguard Worker 1153*288bf522SAndroid Build Coastguard Worker drawDetails(sampleWeightFunction) { 1154*288bf522SAndroid Build Coastguard Worker this.sampleWeightFunction = sampleWeightFunction; 1155*288bf522SAndroid Build Coastguard Worker this.svgStr = []; 1156*288bf522SAndroid Build Coastguard Worker this._renderBackground(); 1157*288bf522SAndroid Build Coastguard Worker this._renderSvgNodes(); 1158*288bf522SAndroid Build Coastguard Worker this._renderUnzoomNode(); 1159*288bf522SAndroid Build Coastguard Worker this._renderInfoNode(); 1160*288bf522SAndroid Build Coastguard Worker this._renderPercentNode(); 1161*288bf522SAndroid Build Coastguard Worker this._renderSearchNode(); 1162*288bf522SAndroid Build Coastguard Worker // It is much faster to add html content to svgStr than adding it directly to svgDiv. 1163*288bf522SAndroid Build Coastguard Worker this.svgDiv.html(this.svgStr.join('')); 1164*288bf522SAndroid Build Coastguard Worker this.svgStr = []; 1165*288bf522SAndroid Build Coastguard Worker this.svg = this.svgDiv.find('svg'); 1166*288bf522SAndroid Build Coastguard Worker this._adjustTextSize(); 1167*288bf522SAndroid Build Coastguard Worker this._enableZoom(); 1168*288bf522SAndroid Build Coastguard Worker this._enableInfo(); 1169*288bf522SAndroid Build Coastguard Worker this._enableSearch(); 1170*288bf522SAndroid Build Coastguard Worker this._adjustTextSizeOnResize(); 1171*288bf522SAndroid Build Coastguard Worker } 1172*288bf522SAndroid Build Coastguard Worker 1173*288bf522SAndroid Build Coastguard Worker _renderBackground() { 1174*288bf522SAndroid Build Coastguard Worker this.svgStr.push(` 1175*288bf522SAndroid Build Coastguard Worker <div style="width: 100%; height: ${this.svgHeight}px;"> 1176*288bf522SAndroid Build Coastguard Worker <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 1177*288bf522SAndroid Build Coastguard Worker version="1.1" width="100%" height="100%" style="border: 1px solid black; "> 1178*288bf522SAndroid Build Coastguard Worker <defs > <linearGradient id="background_gradient_${this.id}" 1179*288bf522SAndroid Build Coastguard Worker y1="0" y2="1" x1="0" x2="0" > 1180*288bf522SAndroid Build Coastguard Worker <stop stop-color="#eeeeee" offset="5%" /> 1181*288bf522SAndroid Build Coastguard Worker <stop stop-color="#efefb1" offset="90%" /> 1182*288bf522SAndroid Build Coastguard Worker </linearGradient> 1183*288bf522SAndroid Build Coastguard Worker </defs> 1184*288bf522SAndroid Build Coastguard Worker <rect x="0" y="0" width="100%" height="100%" 1185*288bf522SAndroid Build Coastguard Worker fill="url(#background_gradient_${this.id})" />`); 1186*288bf522SAndroid Build Coastguard Worker } 1187*288bf522SAndroid Build Coastguard Worker 1188*288bf522SAndroid Build Coastguard Worker _getYForDepth(depth) { 1189*288bf522SAndroid Build Coastguard Worker if (this.reverseOrder) { 1190*288bf522SAndroid Build Coastguard Worker return (depth + 3) * this.svgNodeHeight; 1191*288bf522SAndroid Build Coastguard Worker } 1192*288bf522SAndroid Build Coastguard Worker return this.svgHeight - (depth + 1) * this.svgNodeHeight; 1193*288bf522SAndroid Build Coastguard Worker } 1194*288bf522SAndroid Build Coastguard Worker 1195*288bf522SAndroid Build Coastguard Worker _getWidthPercentage(eventCount) { 1196*288bf522SAndroid Build Coastguard Worker return eventCount * 100.0 / this.sumCount; 1197*288bf522SAndroid Build Coastguard Worker } 1198*288bf522SAndroid Build Coastguard Worker 1199*288bf522SAndroid Build Coastguard Worker _getHeatColor(widthPercentage) { 1200*288bf522SAndroid Build Coastguard Worker return { 1201*288bf522SAndroid Build Coastguard Worker r: Math.floor(245 + 10 * (1 - widthPercentage * 0.01)), 1202*288bf522SAndroid Build Coastguard Worker g: Math.floor(110 + 105 * (1 - widthPercentage * 0.01)), 1203*288bf522SAndroid Build Coastguard Worker b: 100, 1204*288bf522SAndroid Build Coastguard Worker }; 1205*288bf522SAndroid Build Coastguard Worker } 1206*288bf522SAndroid Build Coastguard Worker 1207*288bf522SAndroid Build Coastguard Worker _renderSvgNodes() { 1208*288bf522SAndroid Build Coastguard Worker let fakeNodes = [{c: this.initNodes}]; 1209*288bf522SAndroid Build Coastguard Worker let children = this._splitChildrenForNodes(fakeNodes); 1210*288bf522SAndroid Build Coastguard Worker let xOffset = 0; 1211*288bf522SAndroid Build Coastguard Worker for (let child of children) { 1212*288bf522SAndroid Build Coastguard Worker xOffset = this._renderSvgNodesWithSameRoot(child, 0, xOffset); 1213*288bf522SAndroid Build Coastguard Worker } 1214*288bf522SAndroid Build Coastguard Worker } 1215*288bf522SAndroid Build Coastguard Worker 1216*288bf522SAndroid Build Coastguard Worker // Return an array of children nodes, with children having the same functionId merged in a 1217*288bf522SAndroid Build Coastguard Worker // subarray. 1218*288bf522SAndroid Build Coastguard Worker _splitChildrenForNodes(nodes) { 1219*288bf522SAndroid Build Coastguard Worker let map = new Map(); 1220*288bf522SAndroid Build Coastguard Worker for (let node of nodes) { 1221*288bf522SAndroid Build Coastguard Worker for (let child of node.c) { 1222*288bf522SAndroid Build Coastguard Worker let funcName = getFuncName(child.f); 1223*288bf522SAndroid Build Coastguard Worker let subNodes = map.get(funcName); 1224*288bf522SAndroid Build Coastguard Worker if (subNodes) { 1225*288bf522SAndroid Build Coastguard Worker subNodes.push(child); 1226*288bf522SAndroid Build Coastguard Worker } else { 1227*288bf522SAndroid Build Coastguard Worker map.set(funcName, [child]); 1228*288bf522SAndroid Build Coastguard Worker } 1229*288bf522SAndroid Build Coastguard Worker } 1230*288bf522SAndroid Build Coastguard Worker } 1231*288bf522SAndroid Build Coastguard Worker const funcNames = [...map.keys()].sort(); 1232*288bf522SAndroid Build Coastguard Worker let res = []; 1233*288bf522SAndroid Build Coastguard Worker funcNames.forEach(function (funcName) { 1234*288bf522SAndroid Build Coastguard Worker const subNodes = map.get(funcName); 1235*288bf522SAndroid Build Coastguard Worker res.push(subNodes.length == 1 ? subNodes[0] : subNodes); 1236*288bf522SAndroid Build Coastguard Worker }); 1237*288bf522SAndroid Build Coastguard Worker return res; 1238*288bf522SAndroid Build Coastguard Worker } 1239*288bf522SAndroid Build Coastguard Worker 1240*288bf522SAndroid Build Coastguard Worker // nodes can be a CallNode, or an array of CallNodes with the same functionId. 1241*288bf522SAndroid Build Coastguard Worker _renderSvgNodesWithSameRoot(nodes, depth, xOffset) { 1242*288bf522SAndroid Build Coastguard Worker let x = xOffset; 1243*288bf522SAndroid Build Coastguard Worker let y = this._getYForDepth(depth); 1244*288bf522SAndroid Build Coastguard Worker let isArray = Array.isArray(nodes); 1245*288bf522SAndroid Build Coastguard Worker let funcId; 1246*288bf522SAndroid Build Coastguard Worker let sumCount; 1247*288bf522SAndroid Build Coastguard Worker if (isArray) { 1248*288bf522SAndroid Build Coastguard Worker funcId = nodes[0].f; 1249*288bf522SAndroid Build Coastguard Worker sumCount = nodes.reduce((acc, cur) => acc + cur.s, 0); 1250*288bf522SAndroid Build Coastguard Worker } else { 1251*288bf522SAndroid Build Coastguard Worker funcId = nodes.f; 1252*288bf522SAndroid Build Coastguard Worker sumCount = nodes.s; 1253*288bf522SAndroid Build Coastguard Worker } 1254*288bf522SAndroid Build Coastguard Worker let width = this._getWidthPercentage(sumCount); 1255*288bf522SAndroid Build Coastguard Worker if (width < 0.1) { 1256*288bf522SAndroid Build Coastguard Worker return xOffset; 1257*288bf522SAndroid Build Coastguard Worker } 1258*288bf522SAndroid Build Coastguard Worker let color = this._getHeatColor(width); 1259*288bf522SAndroid Build Coastguard Worker let borderColor = {}; 1260*288bf522SAndroid Build Coastguard Worker for (let key in color) { 1261*288bf522SAndroid Build Coastguard Worker borderColor[key] = Math.max(0, color[key] - 50); 1262*288bf522SAndroid Build Coastguard Worker } 1263*288bf522SAndroid Build Coastguard Worker let funcName = getFuncName(funcId); 1264*288bf522SAndroid Build Coastguard Worker let libName = getLibNameOfFunction(funcId); 1265*288bf522SAndroid Build Coastguard Worker let sampleWeight = this.sampleWeightFunction(sumCount, this.totalCount); 1266*288bf522SAndroid Build Coastguard Worker let title = funcName + ' | ' + libName + ' (' + sumCount + ' events: ' + 1267*288bf522SAndroid Build Coastguard Worker sampleWeight + ')'; 1268*288bf522SAndroid Build Coastguard Worker this.svgStr.push(`<g><title>${title}</title> <rect x="${x}%" y="${y}" ox="${x}" 1269*288bf522SAndroid Build Coastguard Worker depth="${depth}" width="${width}%" owidth="${width}" height="15.0" 1270*288bf522SAndroid Build Coastguard Worker ofill="rgb(${color.r},${color.g},${color.b})" 1271*288bf522SAndroid Build Coastguard Worker fill="rgb(${color.r},${color.g},${color.b})" 1272*288bf522SAndroid Build Coastguard Worker style="stroke:rgb(${borderColor.r},${borderColor.g},${borderColor.b})"/> 1273*288bf522SAndroid Build Coastguard Worker <text x="${x}%" y="${y + 12}"></text></g>`); 1274*288bf522SAndroid Build Coastguard Worker 1275*288bf522SAndroid Build Coastguard Worker let children = isArray ? this._splitChildrenForNodes(nodes) : nodes.c; 1276*288bf522SAndroid Build Coastguard Worker let childXOffset = xOffset; 1277*288bf522SAndroid Build Coastguard Worker for (let child of children) { 1278*288bf522SAndroid Build Coastguard Worker childXOffset = this._renderSvgNodesWithSameRoot(child, depth + 1, childXOffset); 1279*288bf522SAndroid Build Coastguard Worker } 1280*288bf522SAndroid Build Coastguard Worker return xOffset + width; 1281*288bf522SAndroid Build Coastguard Worker } 1282*288bf522SAndroid Build Coastguard Worker 1283*288bf522SAndroid Build Coastguard Worker _renderUnzoomNode() { 1284*288bf522SAndroid Build Coastguard Worker this.svgStr.push(`<rect id="zoom_rect_${this.id}" style="display:none;stroke:rgb(0,0,0);" 1285*288bf522SAndroid Build Coastguard Worker rx="10" ry="10" x="10" y="10" width="80" height="30" 1286*288bf522SAndroid Build Coastguard Worker fill="rgb(255,255,255)"/> 1287*288bf522SAndroid Build Coastguard Worker <text id="zoom_text_${this.id}" x="19" y="30" style="display:none">Zoom out</text>`); 1288*288bf522SAndroid Build Coastguard Worker } 1289*288bf522SAndroid Build Coastguard Worker 1290*288bf522SAndroid Build Coastguard Worker _renderInfoNode() { 1291*288bf522SAndroid Build Coastguard Worker this.svgStr.push(`<clipPath id="info_clip_path_${this.id}"> 1292*288bf522SAndroid Build Coastguard Worker <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10" 1293*288bf522SAndroid Build Coastguard Worker width="789" height="30" fill="rgb(255,255,255)"/> 1294*288bf522SAndroid Build Coastguard Worker </clipPath> 1295*288bf522SAndroid Build Coastguard Worker <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10" 1296*288bf522SAndroid Build Coastguard Worker width="799" height="30" fill="rgb(255,255,255)"/> 1297*288bf522SAndroid Build Coastguard Worker <text clip-path="url(#info_clip_path_${this.id})" 1298*288bf522SAndroid Build Coastguard Worker id="info_text_${this.id}" x="128" y="30"></text>`); 1299*288bf522SAndroid Build Coastguard Worker } 1300*288bf522SAndroid Build Coastguard Worker 1301*288bf522SAndroid Build Coastguard Worker _renderPercentNode() { 1302*288bf522SAndroid Build Coastguard Worker this.svgStr.push(`<rect style="stroke:rgb(0,0,0);" rx="10" ry="10" 1303*288bf522SAndroid Build Coastguard Worker x="934" y="10" width="150" height="30" 1304*288bf522SAndroid Build Coastguard Worker fill="rgb(255,255,255)"/> 1305*288bf522SAndroid Build Coastguard Worker <text id="percent_text_${this.id}" text-anchor="end" 1306*288bf522SAndroid Build Coastguard Worker x="1074" y="30"></text>`); 1307*288bf522SAndroid Build Coastguard Worker } 1308*288bf522SAndroid Build Coastguard Worker 1309*288bf522SAndroid Build Coastguard Worker _renderSearchNode() { 1310*288bf522SAndroid Build Coastguard Worker this.svgStr.push(`<rect style="stroke:rgb(0,0,0); rx="10" ry="10" 1311*288bf522SAndroid Build Coastguard Worker x="1150" y="10" width="80" height="30" 1312*288bf522SAndroid Build Coastguard Worker fill="rgb(255,255,255)" class="search"/> 1313*288bf522SAndroid Build Coastguard Worker <text x="1160" y="30" class="search">Search</text>`); 1314*288bf522SAndroid Build Coastguard Worker } 1315*288bf522SAndroid Build Coastguard Worker 1316*288bf522SAndroid Build Coastguard Worker _adjustTextSizeForNode(g) { 1317*288bf522SAndroid Build Coastguard Worker let text = g.find('text'); 1318*288bf522SAndroid Build Coastguard Worker let width = parseFloat(g.find('rect').attr('width')) * this.svgWidth * 0.01; 1319*288bf522SAndroid Build Coastguard Worker if (width < 28) { 1320*288bf522SAndroid Build Coastguard Worker text.text(''); 1321*288bf522SAndroid Build Coastguard Worker return; 1322*288bf522SAndroid Build Coastguard Worker } 1323*288bf522SAndroid Build Coastguard Worker let methodName = g.find('title').text().split(' | ')[0]; 1324*288bf522SAndroid Build Coastguard Worker let numCharacters; 1325*288bf522SAndroid Build Coastguard Worker for (numCharacters = methodName.length; numCharacters > 4; numCharacters--) { 1326*288bf522SAndroid Build Coastguard Worker if (numCharacters * 7.5 <= width) { 1327*288bf522SAndroid Build Coastguard Worker break; 1328*288bf522SAndroid Build Coastguard Worker } 1329*288bf522SAndroid Build Coastguard Worker } 1330*288bf522SAndroid Build Coastguard Worker if (numCharacters == methodName.length) { 1331*288bf522SAndroid Build Coastguard Worker text.text(methodName); 1332*288bf522SAndroid Build Coastguard Worker } else { 1333*288bf522SAndroid Build Coastguard Worker text.text(methodName.substring(0, numCharacters - 2) + '..'); 1334*288bf522SAndroid Build Coastguard Worker } 1335*288bf522SAndroid Build Coastguard Worker } 1336*288bf522SAndroid Build Coastguard Worker 1337*288bf522SAndroid Build Coastguard Worker _adjustTextSize() { 1338*288bf522SAndroid Build Coastguard Worker this.svgWidth = $(window).width(); 1339*288bf522SAndroid Build Coastguard Worker let thisObj = this; 1340*288bf522SAndroid Build Coastguard Worker this.svg.find('g').each(function(_, g) { 1341*288bf522SAndroid Build Coastguard Worker thisObj._adjustTextSizeForNode($(g)); 1342*288bf522SAndroid Build Coastguard Worker }); 1343*288bf522SAndroid Build Coastguard Worker } 1344*288bf522SAndroid Build Coastguard Worker 1345*288bf522SAndroid Build Coastguard Worker _enableZoom() { 1346*288bf522SAndroid Build Coastguard Worker this.zoomStack = [null]; 1347*288bf522SAndroid Build Coastguard Worker this.svg.find('g').css('cursor', 'pointer').click(zoom); 1348*288bf522SAndroid Build Coastguard Worker this.svg.find(`#zoom_rect_${this.id}`).css('cursor', 'pointer').click(unzoom); 1349*288bf522SAndroid Build Coastguard Worker this.svg.find(`#zoom_text_${this.id}`).css('cursor', 'pointer').click(unzoom); 1350*288bf522SAndroid Build Coastguard Worker 1351*288bf522SAndroid Build Coastguard Worker let thisObj = this; 1352*288bf522SAndroid Build Coastguard Worker function zoom() { 1353*288bf522SAndroid Build Coastguard Worker thisObj.zoomStack.push(this); 1354*288bf522SAndroid Build Coastguard Worker displayFromElement(this); 1355*288bf522SAndroid Build Coastguard Worker thisObj.svg.find(`#zoom_rect_${thisObj.id}`).css('display', 'block'); 1356*288bf522SAndroid Build Coastguard Worker thisObj.svg.find(`#zoom_text_${thisObj.id}`).css('display', 'block'); 1357*288bf522SAndroid Build Coastguard Worker } 1358*288bf522SAndroid Build Coastguard Worker 1359*288bf522SAndroid Build Coastguard Worker function unzoom() { 1360*288bf522SAndroid Build Coastguard Worker if (thisObj.zoomStack.length > 1) { 1361*288bf522SAndroid Build Coastguard Worker thisObj.zoomStack.pop(); 1362*288bf522SAndroid Build Coastguard Worker displayFromElement(thisObj.zoomStack[thisObj.zoomStack.length - 1]); 1363*288bf522SAndroid Build Coastguard Worker if (thisObj.zoomStack.length == 1) { 1364*288bf522SAndroid Build Coastguard Worker thisObj.svg.find(`#zoom_rect_${thisObj.id}`).css('display', 'none'); 1365*288bf522SAndroid Build Coastguard Worker thisObj.svg.find(`#zoom_text_${thisObj.id}`).css('display', 'none'); 1366*288bf522SAndroid Build Coastguard Worker } 1367*288bf522SAndroid Build Coastguard Worker } 1368*288bf522SAndroid Build Coastguard Worker } 1369*288bf522SAndroid Build Coastguard Worker 1370*288bf522SAndroid Build Coastguard Worker function displayFromElement(g) { 1371*288bf522SAndroid Build Coastguard Worker let clickedOriginX = 0; 1372*288bf522SAndroid Build Coastguard Worker let clickedDepth = 0; 1373*288bf522SAndroid Build Coastguard Worker let clickedOriginWidth = 100; 1374*288bf522SAndroid Build Coastguard Worker let scaleFactor = 1; 1375*288bf522SAndroid Build Coastguard Worker if (g) { 1376*288bf522SAndroid Build Coastguard Worker g = $(g); 1377*288bf522SAndroid Build Coastguard Worker let clickedRect = g.find('rect'); 1378*288bf522SAndroid Build Coastguard Worker clickedOriginX = parseFloat(clickedRect.attr('ox')); 1379*288bf522SAndroid Build Coastguard Worker clickedDepth = parseInt(clickedRect.attr('depth')); 1380*288bf522SAndroid Build Coastguard Worker clickedOriginWidth = parseFloat(clickedRect.attr('owidth')); 1381*288bf522SAndroid Build Coastguard Worker scaleFactor = 100.0 / clickedOriginWidth; 1382*288bf522SAndroid Build Coastguard Worker } 1383*288bf522SAndroid Build Coastguard Worker thisObj.svg.find('g').each(function(_, g) { 1384*288bf522SAndroid Build Coastguard Worker g = $(g); 1385*288bf522SAndroid Build Coastguard Worker let text = g.find('text'); 1386*288bf522SAndroid Build Coastguard Worker let rect = g.find('rect'); 1387*288bf522SAndroid Build Coastguard Worker let depth = parseInt(rect.attr('depth')); 1388*288bf522SAndroid Build Coastguard Worker let ox = parseFloat(rect.attr('ox')); 1389*288bf522SAndroid Build Coastguard Worker let owidth = parseFloat(rect.attr('owidth')); 1390*288bf522SAndroid Build Coastguard Worker if (depth < clickedDepth || ox < clickedOriginX - 1e-9 || 1391*288bf522SAndroid Build Coastguard Worker ox + owidth > clickedOriginX + clickedOriginWidth + 1e-9) { 1392*288bf522SAndroid Build Coastguard Worker rect.css('display', 'none'); 1393*288bf522SAndroid Build Coastguard Worker text.css('display', 'none'); 1394*288bf522SAndroid Build Coastguard Worker } else { 1395*288bf522SAndroid Build Coastguard Worker rect.css('display', 'block'); 1396*288bf522SAndroid Build Coastguard Worker text.css('display', 'block'); 1397*288bf522SAndroid Build Coastguard Worker let nx = (ox - clickedOriginX) * scaleFactor + '%'; 1398*288bf522SAndroid Build Coastguard Worker let ny = thisObj._getYForDepth(depth - clickedDepth); 1399*288bf522SAndroid Build Coastguard Worker rect.attr('x', nx); 1400*288bf522SAndroid Build Coastguard Worker rect.attr('y', ny); 1401*288bf522SAndroid Build Coastguard Worker rect.attr('width', owidth * scaleFactor + '%'); 1402*288bf522SAndroid Build Coastguard Worker text.attr('x', nx); 1403*288bf522SAndroid Build Coastguard Worker text.attr('y', ny + 12); 1404*288bf522SAndroid Build Coastguard Worker thisObj._adjustTextSizeForNode(g); 1405*288bf522SAndroid Build Coastguard Worker } 1406*288bf522SAndroid Build Coastguard Worker }); 1407*288bf522SAndroid Build Coastguard Worker } 1408*288bf522SAndroid Build Coastguard Worker } 1409*288bf522SAndroid Build Coastguard Worker 1410*288bf522SAndroid Build Coastguard Worker _enableInfo() { 1411*288bf522SAndroid Build Coastguard Worker this.selected = null; 1412*288bf522SAndroid Build Coastguard Worker let thisObj = this; 1413*288bf522SAndroid Build Coastguard Worker this.svg.find('g').on('mouseenter', function() { 1414*288bf522SAndroid Build Coastguard Worker if (thisObj.selected) { 1415*288bf522SAndroid Build Coastguard Worker thisObj.selected.css('stroke-width', '0'); 1416*288bf522SAndroid Build Coastguard Worker } 1417*288bf522SAndroid Build Coastguard Worker // Mark current node. 1418*288bf522SAndroid Build Coastguard Worker let g = $(this); 1419*288bf522SAndroid Build Coastguard Worker thisObj.selected = g; 1420*288bf522SAndroid Build Coastguard Worker g.css('stroke', 'black').css('stroke-width', '0.5'); 1421*288bf522SAndroid Build Coastguard Worker 1422*288bf522SAndroid Build Coastguard Worker // Parse title. 1423*288bf522SAndroid Build Coastguard Worker let title = g.find('title').text(); 1424*288bf522SAndroid Build Coastguard Worker let methodAndInfo = title.split(' | '); 1425*288bf522SAndroid Build Coastguard Worker thisObj.svg.find(`#info_text_${thisObj.id}`).text(methodAndInfo[0]); 1426*288bf522SAndroid Build Coastguard Worker 1427*288bf522SAndroid Build Coastguard Worker // Parse percentage. 1428*288bf522SAndroid Build Coastguard Worker // '/system/lib64/libhwbinder.so (4 events: 0.28%)' 1429*288bf522SAndroid Build Coastguard Worker let regexp = /.* \(.*:\s+(.*)\)/g; 1430*288bf522SAndroid Build Coastguard Worker let match = regexp.exec(methodAndInfo[1]); 1431*288bf522SAndroid Build Coastguard Worker let percentage = ''; 1432*288bf522SAndroid Build Coastguard Worker if (match && match.length > 1) { 1433*288bf522SAndroid Build Coastguard Worker percentage = match[1]; 1434*288bf522SAndroid Build Coastguard Worker } 1435*288bf522SAndroid Build Coastguard Worker thisObj.svg.find(`#percent_text_${thisObj.id}`).text(percentage); 1436*288bf522SAndroid Build Coastguard Worker }); 1437*288bf522SAndroid Build Coastguard Worker } 1438*288bf522SAndroid Build Coastguard Worker 1439*288bf522SAndroid Build Coastguard Worker _enableSearch() { 1440*288bf522SAndroid Build Coastguard Worker this.svg.find('.search').css('cursor', 'pointer').click(() => { 1441*288bf522SAndroid Build Coastguard Worker let term = prompt('Search for:', ''); 1442*288bf522SAndroid Build Coastguard Worker if (!term) { 1443*288bf522SAndroid Build Coastguard Worker this.svg.find('g > rect').each(function() { 1444*288bf522SAndroid Build Coastguard Worker this.attributes['fill'].value = this.attributes['ofill'].value; 1445*288bf522SAndroid Build Coastguard Worker }); 1446*288bf522SAndroid Build Coastguard Worker } else { 1447*288bf522SAndroid Build Coastguard Worker this.svg.find('g').each(function() { 1448*288bf522SAndroid Build Coastguard Worker let title = this.getElementsByTagName('title')[0]; 1449*288bf522SAndroid Build Coastguard Worker let rect = this.getElementsByTagName('rect')[0]; 1450*288bf522SAndroid Build Coastguard Worker if (title.textContent.indexOf(term) != -1) { 1451*288bf522SAndroid Build Coastguard Worker rect.attributes['fill'].value = 'rgb(230,100,230)'; 1452*288bf522SAndroid Build Coastguard Worker } else { 1453*288bf522SAndroid Build Coastguard Worker rect.attributes['fill'].value = rect.attributes['ofill'].value; 1454*288bf522SAndroid Build Coastguard Worker } 1455*288bf522SAndroid Build Coastguard Worker }); 1456*288bf522SAndroid Build Coastguard Worker } 1457*288bf522SAndroid Build Coastguard Worker }); 1458*288bf522SAndroid Build Coastguard Worker } 1459*288bf522SAndroid Build Coastguard Worker 1460*288bf522SAndroid Build Coastguard Worker _adjustTextSizeOnResize() { 1461*288bf522SAndroid Build Coastguard Worker function throttle(callback) { 1462*288bf522SAndroid Build Coastguard Worker let running = false; 1463*288bf522SAndroid Build Coastguard Worker return function() { 1464*288bf522SAndroid Build Coastguard Worker if (!running) { 1465*288bf522SAndroid Build Coastguard Worker running = true; 1466*288bf522SAndroid Build Coastguard Worker window.requestAnimationFrame(function () { 1467*288bf522SAndroid Build Coastguard Worker callback(); 1468*288bf522SAndroid Build Coastguard Worker running = false; 1469*288bf522SAndroid Build Coastguard Worker }); 1470*288bf522SAndroid Build Coastguard Worker } 1471*288bf522SAndroid Build Coastguard Worker }; 1472*288bf522SAndroid Build Coastguard Worker } 1473*288bf522SAndroid Build Coastguard Worker $(window).resize(throttle(() => this._adjustTextSize())); 1474*288bf522SAndroid Build Coastguard Worker } 1475*288bf522SAndroid Build Coastguard Worker} 1476*288bf522SAndroid Build Coastguard Worker 1477*288bf522SAndroid Build Coastguard Worker 1478*288bf522SAndroid Build Coastguard Workerclass SourceFile { 1479*288bf522SAndroid Build Coastguard Worker 1480*288bf522SAndroid Build Coastguard Worker constructor(fileId) { 1481*288bf522SAndroid Build Coastguard Worker this.path = getSourceFilePath(fileId); 1482*288bf522SAndroid Build Coastguard Worker this.code = getSourceCode(fileId); 1483*288bf522SAndroid Build Coastguard Worker this.showLines = {}; // map from line number to {eventCount, subtreeEventCount}. 1484*288bf522SAndroid Build Coastguard Worker this.hasCount = false; 1485*288bf522SAndroid Build Coastguard Worker } 1486*288bf522SAndroid Build Coastguard Worker 1487*288bf522SAndroid Build Coastguard Worker addLineRange(startLine, endLine) { 1488*288bf522SAndroid Build Coastguard Worker for (let i = startLine; i <= endLine; ++i) { 1489*288bf522SAndroid Build Coastguard Worker if (i in this.showLines || !(i in this.code)) { 1490*288bf522SAndroid Build Coastguard Worker continue; 1491*288bf522SAndroid Build Coastguard Worker } 1492*288bf522SAndroid Build Coastguard Worker this.showLines[i] = {eventCount: 0, subtreeEventCount: 0}; 1493*288bf522SAndroid Build Coastguard Worker } 1494*288bf522SAndroid Build Coastguard Worker } 1495*288bf522SAndroid Build Coastguard Worker 1496*288bf522SAndroid Build Coastguard Worker addLineCount(lineNumber, eventCount, subtreeEventCount) { 1497*288bf522SAndroid Build Coastguard Worker let line = this.showLines[lineNumber]; 1498*288bf522SAndroid Build Coastguard Worker if (line) { 1499*288bf522SAndroid Build Coastguard Worker line.eventCount += eventCount; 1500*288bf522SAndroid Build Coastguard Worker line.subtreeEventCount += subtreeEventCount; 1501*288bf522SAndroid Build Coastguard Worker this.hasCount = true; 1502*288bf522SAndroid Build Coastguard Worker } 1503*288bf522SAndroid Build Coastguard Worker } 1504*288bf522SAndroid Build Coastguard Worker} 1505*288bf522SAndroid Build Coastguard Worker 1506*288bf522SAndroid Build Coastguard Worker// Return a list of SourceFile related to a function. 1507*288bf522SAndroid Build Coastguard Workerfunction collectSourceFilesForFunction(func) { 1508*288bf522SAndroid Build Coastguard Worker if (!func.hasOwnProperty('s')) { 1509*288bf522SAndroid Build Coastguard Worker return null; 1510*288bf522SAndroid Build Coastguard Worker } 1511*288bf522SAndroid Build Coastguard Worker let hitLines = func.s; 1512*288bf522SAndroid Build Coastguard Worker let sourceFiles = {}; // map from sourceFileId to SourceFile. 1513*288bf522SAndroid Build Coastguard Worker 1514*288bf522SAndroid Build Coastguard Worker function getFile(fileId) { 1515*288bf522SAndroid Build Coastguard Worker let file = sourceFiles[fileId]; 1516*288bf522SAndroid Build Coastguard Worker if (!file) { 1517*288bf522SAndroid Build Coastguard Worker file = sourceFiles[fileId] = new SourceFile(fileId); 1518*288bf522SAndroid Build Coastguard Worker } 1519*288bf522SAndroid Build Coastguard Worker return file; 1520*288bf522SAndroid Build Coastguard Worker } 1521*288bf522SAndroid Build Coastguard Worker 1522*288bf522SAndroid Build Coastguard Worker // Show lines for the function. 1523*288bf522SAndroid Build Coastguard Worker let funcRange = getFuncSourceRange(func.f); 1524*288bf522SAndroid Build Coastguard Worker if (funcRange) { 1525*288bf522SAndroid Build Coastguard Worker let file = getFile(funcRange.fileId); 1526*288bf522SAndroid Build Coastguard Worker file.addLineRange(funcRange.startLine); 1527*288bf522SAndroid Build Coastguard Worker } 1528*288bf522SAndroid Build Coastguard Worker 1529*288bf522SAndroid Build Coastguard Worker // Show lines for hitLines. 1530*288bf522SAndroid Build Coastguard Worker for (let hitLine of hitLines) { 1531*288bf522SAndroid Build Coastguard Worker let file = getFile(hitLine.f); 1532*288bf522SAndroid Build Coastguard Worker file.addLineRange(hitLine.l - 5, hitLine.l + 5); 1533*288bf522SAndroid Build Coastguard Worker file.addLineCount(hitLine.l, hitLine.e, hitLine.s); 1534*288bf522SAndroid Build Coastguard Worker } 1535*288bf522SAndroid Build Coastguard Worker 1536*288bf522SAndroid Build Coastguard Worker let result = []; 1537*288bf522SAndroid Build Coastguard Worker // Show the source file containing the function before other source files. 1538*288bf522SAndroid Build Coastguard Worker if (funcRange) { 1539*288bf522SAndroid Build Coastguard Worker let file = getFile(funcRange.fileId); 1540*288bf522SAndroid Build Coastguard Worker if (file.hasCount) { 1541*288bf522SAndroid Build Coastguard Worker result.push(file); 1542*288bf522SAndroid Build Coastguard Worker } 1543*288bf522SAndroid Build Coastguard Worker delete sourceFiles[funcRange.fileId]; 1544*288bf522SAndroid Build Coastguard Worker } 1545*288bf522SAndroid Build Coastguard Worker for (let fileId in sourceFiles) { 1546*288bf522SAndroid Build Coastguard Worker let file = sourceFiles[fileId]; 1547*288bf522SAndroid Build Coastguard Worker if (file.hasCount) { 1548*288bf522SAndroid Build Coastguard Worker result.push(file); 1549*288bf522SAndroid Build Coastguard Worker } 1550*288bf522SAndroid Build Coastguard Worker } 1551*288bf522SAndroid Build Coastguard Worker return result.length > 0 ? result : null; 1552*288bf522SAndroid Build Coastguard Worker} 1553*288bf522SAndroid Build Coastguard Worker 1554*288bf522SAndroid Build Coastguard Worker// Show annotated source code of a function. 1555*288bf522SAndroid Build Coastguard Workerclass SourceCodeView { 1556*288bf522SAndroid Build Coastguard Worker 1557*288bf522SAndroid Build Coastguard Worker constructor(divContainer, sourceFiles, totalCount) { 1558*288bf522SAndroid Build Coastguard Worker this.div = $('<div>'); 1559*288bf522SAndroid Build Coastguard Worker this.div.appendTo(divContainer); 1560*288bf522SAndroid Build Coastguard Worker this.sourceFiles = sourceFiles; 1561*288bf522SAndroid Build Coastguard Worker this.totalCount = totalCount; 1562*288bf522SAndroid Build Coastguard Worker } 1563*288bf522SAndroid Build Coastguard Worker 1564*288bf522SAndroid Build Coastguard Worker drawDetails(sampleWeightFunction) { 1565*288bf522SAndroid Build Coastguard Worker google.charts.setOnLoadCallback(() => this.realDraw(sampleWeightFunction)); 1566*288bf522SAndroid Build Coastguard Worker } 1567*288bf522SAndroid Build Coastguard Worker 1568*288bf522SAndroid Build Coastguard Worker realDraw(sampleWeightFunction) { 1569*288bf522SAndroid Build Coastguard Worker this.div.empty(); 1570*288bf522SAndroid Build Coastguard Worker // For each file, draw a table of 'Line', 'Total', 'Self', 'Code'. 1571*288bf522SAndroid Build Coastguard Worker for (let sourceFile of this.sourceFiles) { 1572*288bf522SAndroid Build Coastguard Worker let rows = []; 1573*288bf522SAndroid Build Coastguard Worker let lineNumbers = Object.keys(sourceFile.showLines); 1574*288bf522SAndroid Build Coastguard Worker lineNumbers.sort((a, b) => a - b); 1575*288bf522SAndroid Build Coastguard Worker for (let lineNumber of lineNumbers) { 1576*288bf522SAndroid Build Coastguard Worker let code = getHtml('pre', {text: sourceFile.code[lineNumber]}); 1577*288bf522SAndroid Build Coastguard Worker let countInfo = sourceFile.showLines[lineNumber]; 1578*288bf522SAndroid Build Coastguard Worker let totalValue = ''; 1579*288bf522SAndroid Build Coastguard Worker let selfValue = ''; 1580*288bf522SAndroid Build Coastguard Worker if (countInfo.subtreeEventCount != 0) { 1581*288bf522SAndroid Build Coastguard Worker totalValue = sampleWeightFunction(countInfo.subtreeEventCount, this.totalCount); 1582*288bf522SAndroid Build Coastguard Worker selfValue = sampleWeightFunction(countInfo.eventCount, this.totalCount); 1583*288bf522SAndroid Build Coastguard Worker } 1584*288bf522SAndroid Build Coastguard Worker rows.push([lineNumber, totalValue, selfValue, code]); 1585*288bf522SAndroid Build Coastguard Worker } 1586*288bf522SAndroid Build Coastguard Worker 1587*288bf522SAndroid Build Coastguard Worker let data = new google.visualization.DataTable(); 1588*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Line'); 1589*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Total'); 1590*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Self'); 1591*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Code'); 1592*288bf522SAndroid Build Coastguard Worker data.addRows(rows); 1593*288bf522SAndroid Build Coastguard Worker data.setColumnProperty(0, 'className', 'colForLine'); 1594*288bf522SAndroid Build Coastguard Worker data.setColumnProperty(1, 'className', 'colForCount'); 1595*288bf522SAndroid Build Coastguard Worker data.setColumnProperty(2, 'className', 'colForCount'); 1596*288bf522SAndroid Build Coastguard Worker this.div.append(getHtml('pre', {text: sourceFile.path})); 1597*288bf522SAndroid Build Coastguard Worker let wrapperDiv = $('<div>'); 1598*288bf522SAndroid Build Coastguard Worker wrapperDiv.appendTo(this.div); 1599*288bf522SAndroid Build Coastguard Worker let table = new google.visualization.Table(wrapperDiv.get(0)); 1600*288bf522SAndroid Build Coastguard Worker table.draw(data, { 1601*288bf522SAndroid Build Coastguard Worker width: '100%', 1602*288bf522SAndroid Build Coastguard Worker sort: 'disable', 1603*288bf522SAndroid Build Coastguard Worker frozenColumns: 3, 1604*288bf522SAndroid Build Coastguard Worker allowHtml: true, 1605*288bf522SAndroid Build Coastguard Worker }); 1606*288bf522SAndroid Build Coastguard Worker } 1607*288bf522SAndroid Build Coastguard Worker } 1608*288bf522SAndroid Build Coastguard Worker} 1609*288bf522SAndroid Build Coastguard Worker 1610*288bf522SAndroid Build Coastguard Worker// Return a list of disassembly related to a function. 1611*288bf522SAndroid Build Coastguard Workerfunction collectDisassemblyForFunction(func) { 1612*288bf522SAndroid Build Coastguard Worker if (!func.hasOwnProperty('a')) { 1613*288bf522SAndroid Build Coastguard Worker return null; 1614*288bf522SAndroid Build Coastguard Worker } 1615*288bf522SAndroid Build Coastguard Worker let hitAddrs = func.a; 1616*288bf522SAndroid Build Coastguard Worker let rawCode = getFuncDisassembly(func.f); 1617*288bf522SAndroid Build Coastguard Worker if (!rawCode) { 1618*288bf522SAndroid Build Coastguard Worker return null; 1619*288bf522SAndroid Build Coastguard Worker } 1620*288bf522SAndroid Build Coastguard Worker 1621*288bf522SAndroid Build Coastguard Worker // Annotate disassembly with event count information. 1622*288bf522SAndroid Build Coastguard Worker let annotatedCode = []; 1623*288bf522SAndroid Build Coastguard Worker let codeForLastAddr = null; 1624*288bf522SAndroid Build Coastguard Worker let hitAddrPos = 0; 1625*288bf522SAndroid Build Coastguard Worker let hasCount = false; 1626*288bf522SAndroid Build Coastguard Worker 1627*288bf522SAndroid Build Coastguard Worker function addEventCount(addr) { 1628*288bf522SAndroid Build Coastguard Worker while (hitAddrPos < hitAddrs.length && BigInt(hitAddrs[hitAddrPos].a) < addr) { 1629*288bf522SAndroid Build Coastguard Worker if (codeForLastAddr) { 1630*288bf522SAndroid Build Coastguard Worker codeForLastAddr.eventCount += hitAddrs[hitAddrPos].e; 1631*288bf522SAndroid Build Coastguard Worker codeForLastAddr.subtreeEventCount += hitAddrs[hitAddrPos].s; 1632*288bf522SAndroid Build Coastguard Worker hasCount = true; 1633*288bf522SAndroid Build Coastguard Worker } 1634*288bf522SAndroid Build Coastguard Worker hitAddrPos++; 1635*288bf522SAndroid Build Coastguard Worker } 1636*288bf522SAndroid Build Coastguard Worker } 1637*288bf522SAndroid Build Coastguard Worker 1638*288bf522SAndroid Build Coastguard Worker for (let line of rawCode) { 1639*288bf522SAndroid Build Coastguard Worker let code = line[0]; 1640*288bf522SAndroid Build Coastguard Worker let addr = BigInt(line[1]); 1641*288bf522SAndroid Build Coastguard Worker 1642*288bf522SAndroid Build Coastguard Worker addEventCount(addr); 1643*288bf522SAndroid Build Coastguard Worker let item = {code: code, eventCount: 0, subtreeEventCount: 0}; 1644*288bf522SAndroid Build Coastguard Worker annotatedCode.push(item); 1645*288bf522SAndroid Build Coastguard Worker // Objdump sets addr to 0 when a disassembly line is not associated with an addr. 1646*288bf522SAndroid Build Coastguard Worker if (addr != 0) { 1647*288bf522SAndroid Build Coastguard Worker codeForLastAddr = item; 1648*288bf522SAndroid Build Coastguard Worker } 1649*288bf522SAndroid Build Coastguard Worker } 1650*288bf522SAndroid Build Coastguard Worker addEventCount(Number.MAX_VALUE); 1651*288bf522SAndroid Build Coastguard Worker return hasCount ? annotatedCode : null; 1652*288bf522SAndroid Build Coastguard Worker} 1653*288bf522SAndroid Build Coastguard Worker 1654*288bf522SAndroid Build Coastguard Worker// Show annotated disassembly of a function. 1655*288bf522SAndroid Build Coastguard Workerclass DisassemblyView { 1656*288bf522SAndroid Build Coastguard Worker 1657*288bf522SAndroid Build Coastguard Worker constructor(divContainer, disassembly, totalCount) { 1658*288bf522SAndroid Build Coastguard Worker this.div = $('<div>'); 1659*288bf522SAndroid Build Coastguard Worker this.div.appendTo(divContainer); 1660*288bf522SAndroid Build Coastguard Worker this.disassembly = disassembly; 1661*288bf522SAndroid Build Coastguard Worker this.totalCount = totalCount; 1662*288bf522SAndroid Build Coastguard Worker } 1663*288bf522SAndroid Build Coastguard Worker 1664*288bf522SAndroid Build Coastguard Worker drawDetails(sampleWeightFunction) { 1665*288bf522SAndroid Build Coastguard Worker google.charts.setOnLoadCallback(() => this.realDraw(sampleWeightFunction)); 1666*288bf522SAndroid Build Coastguard Worker } 1667*288bf522SAndroid Build Coastguard Worker 1668*288bf522SAndroid Build Coastguard Worker realDraw(sampleWeightFunction) { 1669*288bf522SAndroid Build Coastguard Worker this.div.empty(); 1670*288bf522SAndroid Build Coastguard Worker // Draw a table of 'Total', 'Self', 'Code'. 1671*288bf522SAndroid Build Coastguard Worker let rows = []; 1672*288bf522SAndroid Build Coastguard Worker for (let line of this.disassembly) { 1673*288bf522SAndroid Build Coastguard Worker let code = getHtml('pre', {text: line.code}); 1674*288bf522SAndroid Build Coastguard Worker let totalValue = ''; 1675*288bf522SAndroid Build Coastguard Worker let selfValue = ''; 1676*288bf522SAndroid Build Coastguard Worker if (line.subtreeEventCount != 0) { 1677*288bf522SAndroid Build Coastguard Worker totalValue = sampleWeightFunction(line.subtreeEventCount, this.totalCount); 1678*288bf522SAndroid Build Coastguard Worker selfValue = sampleWeightFunction(line.eventCount, this.totalCount); 1679*288bf522SAndroid Build Coastguard Worker } 1680*288bf522SAndroid Build Coastguard Worker rows.push([totalValue, selfValue, code]); 1681*288bf522SAndroid Build Coastguard Worker } 1682*288bf522SAndroid Build Coastguard Worker let data = new google.visualization.DataTable(); 1683*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Total'); 1684*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Self'); 1685*288bf522SAndroid Build Coastguard Worker data.addColumn('string', 'Code'); 1686*288bf522SAndroid Build Coastguard Worker data.addRows(rows); 1687*288bf522SAndroid Build Coastguard Worker data.setColumnProperty(0, 'className', 'colForCount'); 1688*288bf522SAndroid Build Coastguard Worker data.setColumnProperty(1, 'className', 'colForCount'); 1689*288bf522SAndroid Build Coastguard Worker let wrapperDiv = $('<div>'); 1690*288bf522SAndroid Build Coastguard Worker wrapperDiv.appendTo(this.div); 1691*288bf522SAndroid Build Coastguard Worker let table = new google.visualization.Table(wrapperDiv.get(0)); 1692*288bf522SAndroid Build Coastguard Worker table.draw(data, { 1693*288bf522SAndroid Build Coastguard Worker width: '100%', 1694*288bf522SAndroid Build Coastguard Worker sort: 'disable', 1695*288bf522SAndroid Build Coastguard Worker frozenColumns: 2, 1696*288bf522SAndroid Build Coastguard Worker allowHtml: true, 1697*288bf522SAndroid Build Coastguard Worker }); 1698*288bf522SAndroid Build Coastguard Worker } 1699*288bf522SAndroid Build Coastguard Worker} 1700*288bf522SAndroid Build Coastguard Worker 1701*288bf522SAndroid Build Coastguard Worker 1702*288bf522SAndroid Build Coastguard Workerfunction initGlobalObjects() { 1703*288bf522SAndroid Build Coastguard Worker let recordData = $('#record_data').text(); 1704*288bf522SAndroid Build Coastguard Worker gRecordInfo = JSON.parse(recordData); 1705*288bf522SAndroid Build Coastguard Worker gProcesses = gRecordInfo.processNames; 1706*288bf522SAndroid Build Coastguard Worker gThreads = gRecordInfo.threadNames; 1707*288bf522SAndroid Build Coastguard Worker gLibList = gRecordInfo.libList; 1708*288bf522SAndroid Build Coastguard Worker gFunctionMap = gRecordInfo.functionMap; 1709*288bf522SAndroid Build Coastguard Worker gSampleInfo = gRecordInfo.sampleInfo; 1710*288bf522SAndroid Build Coastguard Worker gSourceFiles = gRecordInfo.sourceFiles; 1711*288bf522SAndroid Build Coastguard Worker} 1712*288bf522SAndroid Build Coastguard Worker 1713*288bf522SAndroid Build Coastguard Workerfunction createTabs() { 1714*288bf522SAndroid Build Coastguard Worker gTabs = new TabManager($('div#report_content')); 1715*288bf522SAndroid Build Coastguard Worker gTabs.addTab('Chart Statistics', new ChartStatTab()); 1716*288bf522SAndroid Build Coastguard Worker gTabs.addTab('Sample Table', new SampleTableTab()); 1717*288bf522SAndroid Build Coastguard Worker gTabs.addTab('Flamegraph', new FlameGraphTab()); 1718*288bf522SAndroid Build Coastguard Worker} 1719*288bf522SAndroid Build Coastguard Worker 1720*288bf522SAndroid Build Coastguard Worker// Global draw objects 1721*288bf522SAndroid Build Coastguard Workerlet gTabs; 1722*288bf522SAndroid Build Coastguard Workerlet gProgressBar = new ProgressBar(); 1723*288bf522SAndroid Build Coastguard Worker 1724*288bf522SAndroid Build Coastguard Worker// Gobal Json Data 1725*288bf522SAndroid Build Coastguard Workerlet gRecordInfo; 1726*288bf522SAndroid Build Coastguard Workerlet gProcesses; 1727*288bf522SAndroid Build Coastguard Workerlet gThreads; 1728*288bf522SAndroid Build Coastguard Workerlet gLibList; 1729*288bf522SAndroid Build Coastguard Workerlet gFunctionMap; 1730*288bf522SAndroid Build Coastguard Workerlet gSampleInfo; 1731*288bf522SAndroid Build Coastguard Workerlet gSourceFiles; 1732*288bf522SAndroid Build Coastguard Worker 1733*288bf522SAndroid Build Coastguard Workerfunction updateProgress(text, progress) { 1734*288bf522SAndroid Build Coastguard Worker return () => gProgressBar.updateAsync(text, progress); 1735*288bf522SAndroid Build Coastguard Worker} 1736*288bf522SAndroid Build Coastguard Worker 1737*288bf522SAndroid Build Coastguard Workerfunction addProgress(progress) { 1738*288bf522SAndroid Build Coastguard Worker return () => gProgressBar.updateAsync(null, gProgressBar.progress + progress); 1739*288bf522SAndroid Build Coastguard Worker} 1740*288bf522SAndroid Build Coastguard Worker 1741*288bf522SAndroid Build Coastguard Workerfunction hideProgress() { 1742*288bf522SAndroid Build Coastguard Worker return () => gProgressBar.hide(); 1743*288bf522SAndroid Build Coastguard Worker} 1744*288bf522SAndroid Build Coastguard Worker 1745*288bf522SAndroid Build Coastguard Workerfunction createPromise(callback) { 1746*288bf522SAndroid Build Coastguard Worker if (callback) { 1747*288bf522SAndroid Build Coastguard Worker return new Promise((resolve, _) => callback(resolve)); 1748*288bf522SAndroid Build Coastguard Worker } 1749*288bf522SAndroid Build Coastguard Worker return new Promise((resolve,_) => resolve()); 1750*288bf522SAndroid Build Coastguard Worker} 1751*288bf522SAndroid Build Coastguard Worker 1752*288bf522SAndroid Build Coastguard Workerfunction waitDocumentReady() { 1753*288bf522SAndroid Build Coastguard Worker return createPromise((resolve) => $(document).ready(resolve)); 1754*288bf522SAndroid Build Coastguard Worker} 1755*288bf522SAndroid Build Coastguard Worker 1756*288bf522SAndroid Build Coastguard Workerfunction wait(functionCall) { 1757*288bf522SAndroid Build Coastguard Worker return () => { 1758*288bf522SAndroid Build Coastguard Worker functionCall(); 1759*288bf522SAndroid Build Coastguard Worker return createPromise(); 1760*288bf522SAndroid Build Coastguard Worker }; 1761*288bf522SAndroid Build Coastguard Worker} 1762*288bf522SAndroid Build Coastguard Worker 1763*288bf522SAndroid Build Coastguard WorkercreatePromise() 1764*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Load page...', 0)) 1765*288bf522SAndroid Build Coastguard Worker .then(waitDocumentReady) 1766*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Parse Json data...', 20)) 1767*288bf522SAndroid Build Coastguard Worker .then(wait(initGlobalObjects)) 1768*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Create tabs...', 30)) 1769*288bf522SAndroid Build Coastguard Worker .then(wait(createTabs)) 1770*288bf522SAndroid Build Coastguard Worker .then(updateProgress('Draw ChartStat...', 40)) 1771*288bf522SAndroid Build Coastguard Worker .then(() => gTabs.setActiveAsync('Chart Statistics')) 1772*288bf522SAndroid Build Coastguard Worker .then(updateProgress(null, 100)) 1773*288bf522SAndroid Build Coastguard Worker .then(hideProgress()); 1774*288bf522SAndroid Build Coastguard Worker})(); 1775