You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					1339 lines
				
				46 KiB
			
		
		
			
		
	
	
					1339 lines
				
				46 KiB
			| 
								 
											6 years ago
										 
									 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (C) 2017 The Android Open Source Project
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Licensed under the Apache License, Version 2.0 (the "License");
							 | 
						||
| 
								 | 
							
								 * you may not use this file except in compliance with the License.
							 | 
						||
| 
								 | 
							
								 * You may obtain a copy of the License at
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *      http://www.apache.org/licenses/LICENSE-2.0
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Unless required by applicable law or agreed to in writing, software
							 | 
						||
| 
								 | 
							
								 * distributed under the License is distributed on an "AS IS" BASIS,
							 | 
						||
| 
								 | 
							
								 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
							 | 
						||
| 
								 | 
							
								 * See the License for the specific language governing permissions and
							 | 
						||
| 
								 | 
							
								 * limitations under the License.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Use IIFE to avoid leaking names to other scripts.
							 | 
						||
| 
								 | 
							
								$(document).ready(function() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function openHtml(name, attrs={}) {
							 | 
						||
| 
								 | 
							
								    let s = `<${name} `;
							 | 
						||
| 
								 | 
							
								    for (let key in attrs) {
							 | 
						||
| 
								 | 
							
								        s += `${key}="${attrs[key]}" `;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    s += '>';
							 | 
						||
| 
								 | 
							
								    return s;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function closeHtml(name) {
							 | 
						||
| 
								 | 
							
								    return `</${name}>`;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getHtml(name, attrs={}) {
							 | 
						||
| 
								 | 
							
								    let text;
							 | 
						||
| 
								 | 
							
								    if ('text' in attrs) {
							 | 
						||
| 
								 | 
							
								        text = attrs.text;
							 | 
						||
| 
								 | 
							
								        delete attrs.text;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let s = openHtml(name, attrs);
							 | 
						||
| 
								 | 
							
								    if (text) {
							 | 
						||
| 
								 | 
							
								        s += text;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    s += closeHtml(name);
							 | 
						||
| 
								 | 
							
								    return s;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getTableRow(cols, colName, attrs={}) {
							 | 
						||
| 
								 | 
							
								    let s = openHtml('tr', attrs);
							 | 
						||
| 
								 | 
							
								    for (let col of cols) {
							 | 
						||
| 
								 | 
							
								        s += `<${colName}>${col}</${colName}>`;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    s += '</tr>';
							 | 
						||
| 
								 | 
							
								    return s;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function toPercentageStr(percentage) {
							 | 
						||
| 
								 | 
							
								    return percentage.toFixed(2) + '%';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getProcessName(pid) {
							 | 
						||
| 
								 | 
							
								    let name = gProcesses[pid];
							 | 
						||
| 
								 | 
							
								    return name ? `${pid} (${name})`: pid.toString();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getThreadName(tid) {
							 | 
						||
| 
								 | 
							
								    let name = gThreads[tid];
							 | 
						||
| 
								 | 
							
								    return name ? `${tid} (${name})`: tid.toString();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getLibName(libId) {
							 | 
						||
| 
								 | 
							
								    return gLibList[libId];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getFuncName(funcId) {
							 | 
						||
| 
								 | 
							
								    return gFunctionMap[funcId].f;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getLibNameOfFunction(funcId) {
							 | 
						||
| 
								 | 
							
								    return getLibName(gFunctionMap[funcId].l);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getFuncSourceRange(funcId) {
							 | 
						||
| 
								 | 
							
								    let func = gFunctionMap[funcId];
							 | 
						||
| 
								 | 
							
								    if (func.hasOwnProperty('s')) {
							 | 
						||
| 
								 | 
							
								        return {fileId: func.s[0], startLine: func.s[1], endLine: func.s[2]};
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return null;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getFuncDisassembly(funcId) {
							 | 
						||
| 
								 | 
							
								    let func = gFunctionMap[funcId];
							 | 
						||
| 
								 | 
							
								    return func.hasOwnProperty('d') ? func.d : null;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getSourceFilePath(sourceFileId) {
							 | 
						||
| 
								 | 
							
								    return gSourceFiles[sourceFileId].path;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getSourceCode(sourceFileId) {
							 | 
						||
| 
								 | 
							
								    return gSourceFiles[sourceFileId].code;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isClockEvent(eventInfo) {
							 | 
						||
| 
								 | 
							
								    return eventInfo.eventName.includes('task-clock') ||
							 | 
						||
| 
								 | 
							
								            eventInfo.eventName.includes('cpu-clock');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class TabManager {
							 | 
						||
| 
								 | 
							
								    constructor(divContainer) {
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>', {id: 'tabs'});
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('ul'));
							 | 
						||
| 
								 | 
							
								        this.tabs = [];
							 | 
						||
| 
								 | 
							
								        this.isDrawCalled = false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    addTab(title, tabObj) {
							 | 
						||
| 
								 | 
							
								        let id = 'tab_' + this.div.children().length;
							 | 
						||
| 
								 | 
							
								        let tabDiv = $('<div>', {id: id});
							 | 
						||
| 
								 | 
							
								        tabDiv.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								        this.div.children().first().append(
							 | 
						||
| 
								 | 
							
								            getHtml('li', {text: getHtml('a', {href: '#' + id, text: title})}));
							 | 
						||
| 
								 | 
							
								        tabObj.init(tabDiv);
							 | 
						||
| 
								 | 
							
								        this.tabs.push(tabObj);
							 | 
						||
| 
								 | 
							
								        if (this.isDrawCalled) {
							 | 
						||
| 
								 | 
							
								            this.div.tabs('refresh');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return tabObj;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    findTab(title) {
							 | 
						||
| 
								 | 
							
								        let links = this.div.find('li a');
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < links.length; ++i) {
							 | 
						||
| 
								 | 
							
								            if (links.eq(i).text() == title) {
							 | 
						||
| 
								 | 
							
								                return this.tabs[i];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        this.div.tabs({
							 | 
						||
| 
								 | 
							
								            active: 0,
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        this.tabs.forEach(function(tab) {
							 | 
						||
| 
								 | 
							
								            tab.draw();
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        this.isDrawCalled = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    setActive(tabObj) {
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < this.tabs.length; ++i) {
							 | 
						||
| 
								 | 
							
								            if (this.tabs[i] == tabObj) {
							 | 
						||
| 
								 | 
							
								                this.div.tabs('option', 'active', i);
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Show global information retrieved from the record file, including:
							 | 
						||
| 
								 | 
							
								//   record time
							 | 
						||
| 
								 | 
							
								//   machine type
							 | 
						||
| 
								 | 
							
								//   Android version
							 | 
						||
| 
								 | 
							
								//   record cmdline
							 | 
						||
| 
								 | 
							
								//   total samples
							 | 
						||
| 
								 | 
							
								class RecordFileView {
							 | 
						||
| 
								 | 
							
								    constructor(divContainer) {
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>');
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        google.charts.setOnLoadCallback(() => this.realDraw());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    realDraw() {
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        // Draw a table of 'Name', 'Value'.
							 | 
						||
| 
								 | 
							
								        let rows = [];
							 | 
						||
| 
								 | 
							
								        if (gRecordInfo.recordTime) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Record Time', gRecordInfo.recordTime]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (gRecordInfo.machineType) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Machine Type', gRecordInfo.machineType]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (gRecordInfo.androidVersion) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Android Version', gRecordInfo.androidVersion]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (gRecordInfo.recordCmdline) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Record cmdline', gRecordInfo.recordCmdline]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        rows.push(['Total Samples', '' + gRecordInfo.totalSamples]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let data = new google.visualization.DataTable();
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', '');
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', '');
							 | 
						||
| 
								 | 
							
								        data.addRows(rows);
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < rows.length; ++i) {
							 | 
						||
| 
								 | 
							
								            data.setProperty(i, 0, 'className', 'boldTableCell');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let table = new google.visualization.Table(this.div.get(0));
							 | 
						||
| 
								 | 
							
								        table.draw(data, {
							 | 
						||
| 
								 | 
							
								            width: '100%',
							 | 
						||
| 
								 | 
							
								            sort: 'disable',
							 | 
						||
| 
								 | 
							
								            allowHtml: true,
							 | 
						||
| 
								 | 
							
								            cssClassNames: {
							 | 
						||
| 
								 | 
							
								                'tableCell': 'tableCell',
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Show pieChart of event count percentage of each process, thread, library and function.
							 | 
						||
| 
								 | 
							
								class ChartView {
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, eventInfo) {
							 | 
						||
| 
								 | 
							
								        this.id = divContainer.children().length;
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>', {id: 'chartstat_' + this.id});
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.eventInfo = eventInfo;
							 | 
						||
| 
								 | 
							
								        this.processInfo = null;
							 | 
						||
| 
								 | 
							
								        this.threadInfo = null;
							 | 
						||
| 
								 | 
							
								        this.libInfo = null;
							 | 
						||
| 
								 | 
							
								        this.states = {
							 | 
						||
| 
								 | 
							
								            SHOW_EVENT_INFO: 1,
							 | 
						||
| 
								 | 
							
								            SHOW_PROCESS_INFO: 2,
							 | 
						||
| 
								 | 
							
								            SHOW_THREAD_INFO: 3,
							 | 
						||
| 
								 | 
							
								            SHOW_LIB_INFO: 4,
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        if (isClockEvent(this.eventInfo)) {
							 | 
						||
| 
								 | 
							
								            this.getSampleWeight = function (eventCount) {
							 | 
						||
| 
								 | 
							
								                return (eventCount / 1000000.0).toFixed(3) + ' ms';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            this.getSampleWeight = (eventCount) => '' + eventCount;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _getState() {
							 | 
						||
| 
								 | 
							
								        if (this.libInfo) {
							 | 
						||
| 
								 | 
							
								            return this.states.SHOW_LIB_INFO;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.threadInfo) {
							 | 
						||
| 
								 | 
							
								            return this.states.SHOW_THREAD_INFO;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.processInfo) {
							 | 
						||
| 
								 | 
							
								            return this.states.SHOW_PROCESS_INFO;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return this.states.SHOW_EVENT_INFO;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _goBack() {
							 | 
						||
| 
								 | 
							
								        let state = this._getState();
							 | 
						||
| 
								 | 
							
								        if (state == this.states.SHOW_PROCESS_INFO) {
							 | 
						||
| 
								 | 
							
								            this.processInfo = null;
							 | 
						||
| 
								 | 
							
								        } else if (state == this.states.SHOW_THREAD_INFO) {
							 | 
						||
| 
								 | 
							
								            this.threadInfo = null;
							 | 
						||
| 
								 | 
							
								        } else if (state == this.states.SHOW_LIB_INFO) {
							 | 
						||
| 
								 | 
							
								            this.libInfo = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.draw();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _selectHandler(chart) {
							 | 
						||
| 
								 | 
							
								        let selectedItem = chart.getSelection()[0];
							 | 
						||
| 
								 | 
							
								        if (selectedItem) {
							 | 
						||
| 
								 | 
							
								            let state = this._getState();
							 | 
						||
| 
								 | 
							
								            if (state == this.states.SHOW_EVENT_INFO) {
							 | 
						||
| 
								 | 
							
								                this.processInfo = this.eventInfo.processes[selectedItem.row];
							 | 
						||
| 
								 | 
							
								            } else if (state == this.states.SHOW_PROCESS_INFO) {
							 | 
						||
| 
								 | 
							
								                this.threadInfo = this.processInfo.threads[selectedItem.row];
							 | 
						||
| 
								 | 
							
								            } else if (state == this.states.SHOW_THREAD_INFO) {
							 | 
						||
| 
								 | 
							
								                this.libInfo = this.threadInfo.libs[selectedItem.row];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            this.draw();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        google.charts.setOnLoadCallback(() => this.realDraw());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    realDraw() {
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        this._drawTitle();
							 | 
						||
| 
								 | 
							
								        this._drawPieChart();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _drawTitle() {
							 | 
						||
| 
								 | 
							
								        // Draw a table of 'Name', 'Event Count'.
							 | 
						||
| 
								 | 
							
								        let rows = [];
							 | 
						||
| 
								 | 
							
								        rows.push(['Event Type: ' + this.eventInfo.eventName,
							 | 
						||
| 
								 | 
							
								                   this.getSampleWeight(this.eventInfo.eventCount)]);
							 | 
						||
| 
								 | 
							
								        if (this.processInfo) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Process: ' + getProcessName(this.processInfo.pid),
							 | 
						||
| 
								 | 
							
								                       this.getSampleWeight(this.processInfo.eventCount)]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.threadInfo) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Thread: ' + getThreadName(this.threadInfo.tid),
							 | 
						||
| 
								 | 
							
								                       this.getSampleWeight(this.threadInfo.eventCount)]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.libInfo) {
							 | 
						||
| 
								 | 
							
								            rows.push(['Library: ' + getLibName(this.libInfo.libId),
							 | 
						||
| 
								 | 
							
								                       this.getSampleWeight(this.libInfo.eventCount)]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let data = new google.visualization.DataTable();
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', '');
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', '');
							 | 
						||
| 
								 | 
							
								        data.addRows(rows);
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < rows.length; ++i) {
							 | 
						||
| 
								 | 
							
								            data.setProperty(i, 0, 'className', 'boldTableCell');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let wrapperDiv = $('<div>');
							 | 
						||
| 
								 | 
							
								        wrapperDiv.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								        let table = new google.visualization.Table(wrapperDiv.get(0));
							 | 
						||
| 
								 | 
							
								        table.draw(data, {
							 | 
						||
| 
								 | 
							
								            width: '100%',
							 | 
						||
| 
								 | 
							
								            sort: 'disable',
							 | 
						||
| 
								 | 
							
								            allowHtml: true,
							 | 
						||
| 
								 | 
							
								            cssClassNames: {
							 | 
						||
| 
								 | 
							
								                'tableCell': 'tableCell',
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        if (this._getState() != this.states.SHOW_EVENT_INFO) {
							 | 
						||
| 
								 | 
							
								            let button = $('<button>', {text: 'Back'});
							 | 
						||
| 
								 | 
							
								            button.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								            button.button().click(() => this._goBack());
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _drawPieChart() {
							 | 
						||
| 
								 | 
							
								        let state = this._getState();
							 | 
						||
| 
								 | 
							
								        let title = null;
							 | 
						||
| 
								 | 
							
								        let firstColumn = null;
							 | 
						||
| 
								 | 
							
								        let rows = [];
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        function getItem(name, eventCount, totalEventCount) {
							 | 
						||
| 
								 | 
							
								            let sampleWeight = thisObj.getSampleWeight(eventCount);
							 | 
						||
| 
								 | 
							
								            let percent = (eventCount * 100.0 / totalEventCount).toFixed(2) + '%';
							 | 
						||
| 
								 | 
							
								            return [name, eventCount, getHtml('pre', {text: name}) +
							 | 
						||
| 
								 | 
							
								                        getHtml('b', {text: `${sampleWeight} (${percent})`})];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (state == this.states.SHOW_EVENT_INFO) {
							 | 
						||
| 
								 | 
							
								            title = 'Processes in event type ' + this.eventInfo.eventName;
							 | 
						||
| 
								 | 
							
								            firstColumn = 'Process';
							 | 
						||
| 
								 | 
							
								            for (let process of this.eventInfo.processes) {
							 | 
						||
| 
								 | 
							
								                rows.push(getItem('Process: ' + getProcessName(process.pid), process.eventCount,
							 | 
						||
| 
								 | 
							
								                                  this.eventInfo.eventCount));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else if (state == this.states.SHOW_PROCESS_INFO) {
							 | 
						||
| 
								 | 
							
								            title = 'Threads in process ' + getProcessName(this.processInfo.pid);
							 | 
						||
| 
								 | 
							
								            firstColumn = 'Thread';
							 | 
						||
| 
								 | 
							
								            for (let thread of this.processInfo.threads) {
							 | 
						||
| 
								 | 
							
								                rows.push(getItem('Thread: ' + getThreadName(thread.tid), thread.eventCount,
							 | 
						||
| 
								 | 
							
								                                  this.processInfo.eventCount));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else if (state == this.states.SHOW_THREAD_INFO) {
							 | 
						||
| 
								 | 
							
								            title = 'Libraries in thread ' + getThreadName(this.threadInfo.tid);
							 | 
						||
| 
								 | 
							
								            firstColumn = 'Library';
							 | 
						||
| 
								 | 
							
								            for (let lib of this.threadInfo.libs) {
							 | 
						||
| 
								 | 
							
								                rows.push(getItem('Library: ' + getLibName(lib.libId), lib.eventCount,
							 | 
						||
| 
								 | 
							
								                                  this.threadInfo.eventCount));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else if (state == this.states.SHOW_LIB_INFO) {
							 | 
						||
| 
								 | 
							
								            title = 'Functions in library ' + getLibName(this.libInfo.libId);
							 | 
						||
| 
								 | 
							
								            firstColumn = 'Function';
							 | 
						||
| 
								 | 
							
								            for (let func of this.libInfo.functions) {
							 | 
						||
| 
								 | 
							
								                rows.push(getItem('Function: ' + getFuncName(func.g.f), func.g.e,
							 | 
						||
| 
								 | 
							
								                                  this.libInfo.eventCount));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let data = new google.visualization.DataTable();
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', firstColumn);
							 | 
						||
| 
								 | 
							
								        data.addColumn('number', 'EventCount');
							 | 
						||
| 
								 | 
							
								        data.addColumn({type: 'string', role: 'tooltip', p: {html: true}});
							 | 
						||
| 
								 | 
							
								        data.addRows(rows);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let wrapperDiv = $('<div>');
							 | 
						||
| 
								 | 
							
								        wrapperDiv.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								        let chart = new google.visualization.PieChart(wrapperDiv.get(0));
							 | 
						||
| 
								 | 
							
								        chart.draw(data, {
							 | 
						||
| 
								 | 
							
								            title: title,
							 | 
						||
| 
								 | 
							
								            width: 1000,
							 | 
						||
| 
								 | 
							
								            height: 600,
							 | 
						||
| 
								 | 
							
								            tooltip: {isHtml: true},
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        google.visualization.events.addListener(chart, 'select', () => this._selectHandler(chart));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ChartStatTab {
							 | 
						||
| 
								 | 
							
								    constructor() {
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    init(div) {
							 | 
						||
| 
								 | 
							
								        this.div = div;
							 | 
						||
| 
								 | 
							
								        this.recordFileView = new RecordFileView(this.div);
							 | 
						||
| 
								 | 
							
								        this.chartViews = [];
							 | 
						||
| 
								 | 
							
								        for (let eventInfo of gSampleInfo) {
							 | 
						||
| 
								 | 
							
								            this.chartViews.push(new ChartView(this.div, eventInfo));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        this.recordFileView.draw();
							 | 
						||
| 
								 | 
							
								        for (let charView of this.chartViews) {
							 | 
						||
| 
								 | 
							
								            charView.draw();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SampleTableTab {
							 | 
						||
| 
								 | 
							
								    constructor() {
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    init(div) {
							 | 
						||
| 
								 | 
							
								        this.div = div;
							 | 
						||
| 
								 | 
							
								        this.selectorView = null;
							 | 
						||
| 
								 | 
							
								        this.sampleTableViews = [];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        this.selectorView = new SampleTableWeightSelectorView(this.div, gSampleInfo[0],
							 | 
						||
| 
								 | 
							
								                                                              () => this.onSampleWeightChange());
							 | 
						||
| 
								 | 
							
								        this.selectorView.draw();
							 | 
						||
| 
								 | 
							
								        for (let eventInfo of gSampleInfo) {
							 | 
						||
| 
								 | 
							
								            this.div.append(getHtml('hr'));
							 | 
						||
| 
								 | 
							
								            this.sampleTableViews.push(new SampleTableView(this.div, eventInfo));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.onSampleWeightChange();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    onSampleWeightChange() {
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < gSampleInfo.length; ++i) {
							 | 
						||
| 
								 | 
							
								            let sampleWeightFunction = this.selectorView.getSampleWeightFunction(gSampleInfo[i]);
							 | 
						||
| 
								 | 
							
								            let sampleWeightSuffix = this.selectorView.getSampleWeightSuffix(gSampleInfo[i]);
							 | 
						||
| 
								 | 
							
								            this.sampleTableViews[i].draw(sampleWeightFunction, sampleWeightSuffix);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Select the way to show sample weight in SampleTableTab.
							 | 
						||
| 
								 | 
							
								// 1. Show percentage of event count.
							 | 
						||
| 
								 | 
							
								// 2. Show event count (For cpu-clock and task-clock events, it is time in ms).
							 | 
						||
| 
								 | 
							
								class SampleTableWeightSelectorView {
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, firstEventInfo, onSelectChange) {
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>');
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.onSelectChange = onSelectChange;
							 | 
						||
| 
								 | 
							
								        this.options = {
							 | 
						||
| 
								 | 
							
								            SHOW_PERCENT: 0,
							 | 
						||
| 
								 | 
							
								            SHOW_EVENT_COUNT: 1,
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        if (isClockEvent(firstEventInfo)) {
							 | 
						||
| 
								 | 
							
								            this.curOption = this.options.SHOW_EVENT_COUNT;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            this.curOption = this.options.SHOW_PERCENT;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        let options = ['Show percentage of event count', 'Show event count'];
							 | 
						||
| 
								 | 
							
								        let optionStr = '';
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < options.length; ++i) {
							 | 
						||
| 
								 | 
							
								            optionStr += getHtml('option', {value: i, text: options[i]});
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('select', {text: optionStr}));
							 | 
						||
| 
								 | 
							
								        let selectMenu = this.div.children().last();
							 | 
						||
| 
								 | 
							
								        selectMenu.children().eq(this.curOption).attr('selected', 'selected');
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        selectMenu.selectmenu({
							 | 
						||
| 
								 | 
							
								            change: function() {
							 | 
						||
| 
								 | 
							
								                thisObj.curOption = this.value;
							 | 
						||
| 
								 | 
							
								                thisObj.onSelectChange();
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            width: '100%',
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    getSampleWeightFunction(eventInfo) {
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.SHOW_PERCENT) {
							 | 
						||
| 
								 | 
							
								            return function(eventCount) {
							 | 
						||
| 
								 | 
							
								                return (eventCount * 100.0 / eventInfo.eventCount).toFixed(2) + '%';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (isClockEvent(eventInfo)) {
							 | 
						||
| 
								 | 
							
								            return (eventCount) => (eventCount / 1000000.0).toFixed(3);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return (eventCount) => '' + eventCount;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    getSampleWeightSuffix(eventInfo) {
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.SHOW_EVENT_COUNT && isClockEvent(eventInfo)) {
							 | 
						||
| 
								 | 
							
								            return ' ms';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return '';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SampleTableView {
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, eventInfo) {
							 | 
						||
| 
								 | 
							
								        this.id = divContainer.children().length;
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>');
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.eventInfo = eventInfo;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw(getSampleWeight, sampleWeightSuffix) {
							 | 
						||
| 
								 | 
							
								        // Draw a table of 'Total', 'Self', 'Samples', 'Process', 'Thread', 'Library', 'Function'.
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        let eventInfo = this.eventInfo;
							 | 
						||
| 
								 | 
							
								        let sampleWeight = getSampleWeight(eventInfo.eventCount);
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('p', {text: `Sample table for event ${eventInfo.eventName}, ` +
							 | 
						||
| 
								 | 
							
								                `total count ${sampleWeight}${sampleWeightSuffix}`}));
							 | 
						||
| 
								 | 
							
								        let tableId = 'sampleTable_' + this.id;
							 | 
						||
| 
								 | 
							
								        let valueSuffix = sampleWeightSuffix.length > 0 ? `(in${sampleWeightSuffix})` : '';
							 | 
						||
| 
								 | 
							
								        let titles = ['Total' + valueSuffix, 'Self' + valueSuffix, 'Samples',
							 | 
						||
| 
								 | 
							
								                      'Process', 'Thread', 'Library', 'Function'];
							 | 
						||
| 
								 | 
							
								        let tableStr = openHtml('table', {id: tableId, cellspacing: '0', width: '100%'}) +
							 | 
						||
| 
								 | 
							
								                        getHtml('thead', {text: getTableRow(titles, 'th')}) +
							 | 
						||
| 
								 | 
							
								                        getHtml('tfoot', {text: getTableRow(titles, 'th')}) +
							 | 
						||
| 
								 | 
							
								                        openHtml('tbody');
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < eventInfo.processes.length; ++i) {
							 | 
						||
| 
								 | 
							
								            let processInfo = eventInfo.processes[i];
							 | 
						||
| 
								 | 
							
								            let processName = getProcessName(processInfo.pid);
							 | 
						||
| 
								 | 
							
								            for (let j = 0; j < processInfo.threads.length; ++j) {
							 | 
						||
| 
								 | 
							
								                let threadInfo = processInfo.threads[j];
							 | 
						||
| 
								 | 
							
								                let threadName = getThreadName(threadInfo.tid);
							 | 
						||
| 
								 | 
							
								                for (let k = 0; k < threadInfo.libs.length; ++k) {
							 | 
						||
| 
								 | 
							
								                    let lib = threadInfo.libs[k];
							 | 
						||
| 
								 | 
							
								                    let libName = getLibName(lib.libId);
							 | 
						||
| 
								 | 
							
								                    for (let t = 0; t < lib.functions.length; ++t) {
							 | 
						||
| 
								 | 
							
								                        let func = lib.functions[t];
							 | 
						||
| 
								 | 
							
								                        let key = [i, j, k, t].join('_');
							 | 
						||
| 
								 | 
							
								                        let totalValue = getSampleWeight(func.g.s);
							 | 
						||
| 
								 | 
							
								                        let selfValue = getSampleWeight(func.g.e);
							 | 
						||
| 
								 | 
							
								                        tableStr += getTableRow([totalValue, selfValue, func.c,
							 | 
						||
| 
								 | 
							
								                                                 processName, threadName, libName,
							 | 
						||
| 
								 | 
							
								                                                 getFuncName(func.g.f)], 'td', {key: key});
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        tableStr += closeHtml('tbody') + closeHtml('table');
							 | 
						||
| 
								 | 
							
								        this.div.append(tableStr);
							 | 
						||
| 
								 | 
							
								        let table = this.div.find(`table#${tableId}`).dataTable({
							 | 
						||
| 
								 | 
							
								            lengthMenu: [10, 20, 50, 100, -1],
							 | 
						||
| 
								 | 
							
								            processing: true,
							 | 
						||
| 
								 | 
							
								            order: [0, 'desc'],
							 | 
						||
| 
								 | 
							
								            responsive: true,
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        table.find('tr').css('cursor', 'pointer');
							 | 
						||
| 
								 | 
							
								        table.on('click', 'tr', function() {
							 | 
						||
| 
								 | 
							
								            let key = this.getAttribute('key');
							 | 
						||
| 
								 | 
							
								            if (!key) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            let indexes = key.split('_');
							 | 
						||
| 
								 | 
							
								            let processInfo = eventInfo.processes[indexes[0]];
							 | 
						||
| 
								 | 
							
								            let threadInfo = processInfo.threads[indexes[1]];
							 | 
						||
| 
								 | 
							
								            let lib = threadInfo.libs[indexes[2]];
							 | 
						||
| 
								 | 
							
								            let func = lib.functions[indexes[3]];
							 | 
						||
| 
								 | 
							
								            FunctionTab.showFunction(eventInfo, processInfo, threadInfo, lib, func);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Show embedded flamegraph generated by inferno.
							 | 
						||
| 
								 | 
							
								class FlameGraphTab {
							 | 
						||
| 
								 | 
							
								    constructor() {
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    init(div) {
							 | 
						||
| 
								 | 
							
								        this.div = div;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        $('div#flamegraph_id').appendTo(this.div).css('display', 'block');
							 | 
						||
| 
								 | 
							
								        flamegraphInit();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// FunctionTab: show information of a function.
							 | 
						||
| 
								 | 
							
								// 1. Show the callgrpah and reverse callgraph of a function as flamegraphs.
							 | 
						||
| 
								 | 
							
								// 2. Show the annotated source code of the function.
							 | 
						||
| 
								 | 
							
								class FunctionTab {
							 | 
						||
| 
								 | 
							
								    static showFunction(eventInfo, processInfo, threadInfo, lib, func) {
							 | 
						||
| 
								 | 
							
								        let title = 'Function';
							 | 
						||
| 
								 | 
							
								        let tab = gTabs.findTab(title);
							 | 
						||
| 
								 | 
							
								        if (!tab) {
							 | 
						||
| 
								 | 
							
								            tab = gTabs.addTab(title, new FunctionTab());
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        tab.setFunction(eventInfo, processInfo, threadInfo, lib, func);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    constructor() {
							 | 
						||
| 
								 | 
							
								        this.func = null;
							 | 
						||
| 
								 | 
							
								        this.selectPercent = 'thread';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    init(div) {
							 | 
						||
| 
								 | 
							
								        this.div = div;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    setFunction(eventInfo, processInfo, threadInfo, lib, func) {
							 | 
						||
| 
								 | 
							
								        this.eventInfo = eventInfo;
							 | 
						||
| 
								 | 
							
								        this.processInfo = processInfo;
							 | 
						||
| 
								 | 
							
								        this.threadInfo = threadInfo;
							 | 
						||
| 
								 | 
							
								        this.lib = lib;
							 | 
						||
| 
								 | 
							
								        this.func = func;
							 | 
						||
| 
								 | 
							
								        this.selectorView = null;
							 | 
						||
| 
								 | 
							
								        this.callgraphView = null;
							 | 
						||
| 
								 | 
							
								        this.reverseCallgraphView = null;
							 | 
						||
| 
								 | 
							
								        this.sourceCodeView = null;
							 | 
						||
| 
								 | 
							
								        this.disassemblyView = null;
							 | 
						||
| 
								 | 
							
								        this.draw();
							 | 
						||
| 
								 | 
							
								        gTabs.setActive(this);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        if (!this.func) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        this._drawTitle();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.selectorView = new FunctionSampleWeightSelectorView(this.div, this.eventInfo,
							 | 
						||
| 
								 | 
							
								            this.processInfo, this.threadInfo, () => this.onSampleWeightChange());
							 | 
						||
| 
								 | 
							
								        this.selectorView.draw();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('hr'));
							 | 
						||
| 
								 | 
							
								        let funcName = getFuncName(this.func.g.f);
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('b', {text: `Functions called by ${funcName}`}) + '<br/>');
							 | 
						||
| 
								 | 
							
								        this.callgraphView = new FlameGraphView(this.div, this.func.g, false);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('hr'));
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('b', {text: `Functions calling ${funcName}`}) + '<br/>');
							 | 
						||
| 
								 | 
							
								        this.reverseCallgraphView = new FlameGraphView(this.div, this.func.rg, true);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let sourceFiles = collectSourceFilesForFunction(this.func);
							 | 
						||
| 
								 | 
							
								        if (sourceFiles) {
							 | 
						||
| 
								 | 
							
								            this.div.append(getHtml('hr'));
							 | 
						||
| 
								 | 
							
								            this.div.append(getHtml('b', {text: 'SourceCode:'}) + '<br/>');
							 | 
						||
| 
								 | 
							
								            this.sourceCodeView = new SourceCodeView(this.div, sourceFiles);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let disassembly = collectDisassemblyForFunction(this.func);
							 | 
						||
| 
								 | 
							
								        if (disassembly) {
							 | 
						||
| 
								 | 
							
								            this.div.append(getHtml('hr'));
							 | 
						||
| 
								 | 
							
								            this.div.append(getHtml('b', {text: 'Disassembly:'}) + '<br/>');
							 | 
						||
| 
								 | 
							
								            this.disassemblyView = new DisassemblyView(this.div, disassembly);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.onSampleWeightChange();  // Manually set sample weight function for the first time.
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _drawTitle() {
							 | 
						||
| 
								 | 
							
								        let eventName = this.eventInfo.eventName;
							 | 
						||
| 
								 | 
							
								        let processName = getProcessName(this.processInfo.pid);
							 | 
						||
| 
								 | 
							
								        let threadName = getThreadName(this.threadInfo.tid);
							 | 
						||
| 
								 | 
							
								        let libName = getLibName(this.lib.libId);
							 | 
						||
| 
								 | 
							
								        let funcName = getFuncName(this.func.g.f);
							 | 
						||
| 
								 | 
							
								        // Draw a table of 'Name', 'Value'.
							 | 
						||
| 
								 | 
							
								        let rows = [];
							 | 
						||
| 
								 | 
							
								        rows.push(['Event Type', eventName]);
							 | 
						||
| 
								 | 
							
								        rows.push(['Process', processName]);
							 | 
						||
| 
								 | 
							
								        rows.push(['Thread', threadName]);
							 | 
						||
| 
								 | 
							
								        rows.push(['Library', libName]);
							 | 
						||
| 
								 | 
							
								        rows.push(['Function', getHtml('pre', {text: funcName})]);
							 | 
						||
| 
								 | 
							
								        let data = new google.visualization.DataTable();
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', '');
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', '');
							 | 
						||
| 
								 | 
							
								        data.addRows(rows);
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < rows.length; ++i) {
							 | 
						||
| 
								 | 
							
								            data.setProperty(i, 0, 'className', 'boldTableCell');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let wrapperDiv = $('<div>');
							 | 
						||
| 
								 | 
							
								        wrapperDiv.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								        let table = new google.visualization.Table(wrapperDiv.get(0));
							 | 
						||
| 
								 | 
							
								        table.draw(data, {
							 | 
						||
| 
								 | 
							
								            width: '100%',
							 | 
						||
| 
								 | 
							
								            sort: 'disable',
							 | 
						||
| 
								 | 
							
								            allowHtml: true,
							 | 
						||
| 
								 | 
							
								            cssClassNames: {
							 | 
						||
| 
								 | 
							
								                'tableCell': 'tableCell',
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    onSampleWeightChange() {
							 | 
						||
| 
								 | 
							
								        let sampleWeightFunction = this.selectorView.getSampleWeightFunction();
							 | 
						||
| 
								 | 
							
								        if (this.callgraphView) {
							 | 
						||
| 
								 | 
							
								            this.callgraphView.draw(sampleWeightFunction);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.reverseCallgraphView) {
							 | 
						||
| 
								 | 
							
								            this.reverseCallgraphView.draw(sampleWeightFunction);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.sourceCodeView) {
							 | 
						||
| 
								 | 
							
								            this.sourceCodeView.draw(sampleWeightFunction);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.disassemblyView) {
							 | 
						||
| 
								 | 
							
								            this.disassemblyView.draw(sampleWeightFunction);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Select the way to show sample weight in FunctionTab.
							 | 
						||
| 
								 | 
							
								// 1. Show percentage of event count relative to all processes.
							 | 
						||
| 
								 | 
							
								// 2. Show percentage of event count relative to the current process.
							 | 
						||
| 
								 | 
							
								// 3. Show percentage of event count relative to the current thread.
							 | 
						||
| 
								 | 
							
								// 4. Show absolute event count.
							 | 
						||
| 
								 | 
							
								// 5. Show event count in milliseconds, only possible for cpu-clock or task-clock events.
							 | 
						||
| 
								 | 
							
								class FunctionSampleWeightSelectorView {
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, eventInfo, processInfo, threadInfo, onSelectChange) {
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>');
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.onSelectChange = onSelectChange;
							 | 
						||
| 
								 | 
							
								        this.eventCountForAllProcesses = eventInfo.eventCount;
							 | 
						||
| 
								 | 
							
								        this.eventCountForProcess = processInfo.eventCount;
							 | 
						||
| 
								 | 
							
								        this.eventCountForThread = threadInfo.eventCount;
							 | 
						||
| 
								 | 
							
								        this.options = {
							 | 
						||
| 
								 | 
							
								            PERCENT_TO_ALL_PROCESSES: 0,
							 | 
						||
| 
								 | 
							
								            PERCENT_TO_CUR_PROCESS: 1,
							 | 
						||
| 
								 | 
							
								            PERCENT_TO_CUR_THREAD: 2,
							 | 
						||
| 
								 | 
							
								            RAW_EVENT_COUNT: 3,
							 | 
						||
| 
								 | 
							
								            EVENT_COUNT_IN_TIME: 4,
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        let name = eventInfo.eventName;
							 | 
						||
| 
								 | 
							
								        this.supportEventCountInTime = isClockEvent(eventInfo);
							 | 
						||
| 
								 | 
							
								        if (this.supportEventCountInTime) {
							 | 
						||
| 
								 | 
							
								            this.curOption = this.options.EVENT_COUNT_IN_TIME;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            this.curOption = this.options.PERCENT_TO_CUR_THREAD;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw() {
							 | 
						||
| 
								 | 
							
								        let options = [];
							 | 
						||
| 
								 | 
							
								        options.push('Show percentage of event count relative to all processes.');
							 | 
						||
| 
								 | 
							
								        options.push('Show percentage of event count relative to the current process.');
							 | 
						||
| 
								 | 
							
								        options.push('Show percentage of event count relative to the current thread.');
							 | 
						||
| 
								 | 
							
								        options.push('Show event count.');
							 | 
						||
| 
								 | 
							
								        if (this.supportEventCountInTime) {
							 | 
						||
| 
								 | 
							
								            options.push('Show event count in milliseconds.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let optionStr = '';
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < options.length; ++i) {
							 | 
						||
| 
								 | 
							
								            optionStr += getHtml('option', {value: i, text: options[i]});
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.div.append(getHtml('select', {text: optionStr}));
							 | 
						||
| 
								 | 
							
								        let selectMenu = this.div.children().last();
							 | 
						||
| 
								 | 
							
								        selectMenu.children().eq(this.curOption).attr('selected', 'selected');
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        selectMenu.selectmenu({
							 | 
						||
| 
								 | 
							
								            change: function() {
							 | 
						||
| 
								 | 
							
								                thisObj.curOption = this.value;
							 | 
						||
| 
								 | 
							
								                thisObj.onSelectChange();
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            width: '100%',
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    getSampleWeightFunction() {
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.PERCENT_TO_ALL_PROCESSES) {
							 | 
						||
| 
								 | 
							
								            return function(eventCount) {
							 | 
						||
| 
								 | 
							
								                let percent = eventCount * 100.0 / thisObj.eventCountForAllProcesses;
							 | 
						||
| 
								 | 
							
								                return percent.toFixed(2) + '%';
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.PERCENT_TO_CUR_PROCESS) {
							 | 
						||
| 
								 | 
							
								            return function(eventCount) {
							 | 
						||
| 
								 | 
							
								                let percent = eventCount * 100.0 / thisObj.eventCountForProcess;
							 | 
						||
| 
								 | 
							
								                return percent.toFixed(2) + '%';
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.PERCENT_TO_CUR_THREAD) {
							 | 
						||
| 
								 | 
							
								            return function(eventCount) {
							 | 
						||
| 
								 | 
							
								                let percent = eventCount * 100.0 / thisObj.eventCountForThread;
							 | 
						||
| 
								 | 
							
								                return percent.toFixed(2) + '%';
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.RAW_EVENT_COUNT) {
							 | 
						||
| 
								 | 
							
								            return function(eventCount) {
							 | 
						||
| 
								 | 
							
								                return '' + eventCount;
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.curOption == this.options.EVENT_COUNT_IN_TIME) {
							 | 
						||
| 
								 | 
							
								            return function(eventCount) {
							 | 
						||
| 
								 | 
							
								                let timeInMs = eventCount / 1000000.0;
							 | 
						||
| 
								 | 
							
								                return timeInMs.toFixed(3) + ' ms';
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Given a callgraph, show the flamegraph.
							 | 
						||
| 
								 | 
							
								class FlameGraphView {
							 | 
						||
| 
								 | 
							
								    // If reverseOrder is false, the root of the flamegraph is at the bottom,
							 | 
						||
| 
								 | 
							
								    // otherwise it is at the top.
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, callgraph, reverseOrder) {
							 | 
						||
| 
								 | 
							
								        this.id = divContainer.children().length;
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>', {id: 'fg_' + this.id});
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.callgraph = callgraph;
							 | 
						||
| 
								 | 
							
								        this.reverseOrder = reverseOrder;
							 | 
						||
| 
								 | 
							
								        this.sampleWeightFunction = null;
							 | 
						||
| 
								 | 
							
								        this.svgWidth = $(window).width();
							 | 
						||
| 
								 | 
							
								        this.svgNodeHeight = 17;
							 | 
						||
| 
								 | 
							
								        this.fontSize = 12;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function getMaxDepth(node) {
							 | 
						||
| 
								 | 
							
								            let depth = 0;
							 | 
						||
| 
								 | 
							
								            for (let child of node.c) {
							 | 
						||
| 
								 | 
							
								                depth = Math.max(depth, getMaxDepth(child));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return depth + 1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.maxDepth = getMaxDepth(this.callgraph);
							 | 
						||
| 
								 | 
							
								        this.svgHeight = this.svgNodeHeight * (this.maxDepth + 3);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw(sampleWeightFunction) {
							 | 
						||
| 
								 | 
							
								        this.sampleWeightFunction = sampleWeightFunction;
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        this.div.css('width', '100%').css('height', this.svgHeight + 'px');
							 | 
						||
| 
								 | 
							
								        let svgStr = '<svg xmlns="http://www.w3.org/2000/svg" \
							 | 
						||
| 
								 | 
							
								        xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" \
							 | 
						||
| 
								 | 
							
								        width="100%" height="100%" style="border: 1px solid black; font-family: Monospace;"> \
							 | 
						||
| 
								 | 
							
								        </svg>';
							 | 
						||
| 
								 | 
							
								        this.div.append(svgStr);
							 | 
						||
| 
								 | 
							
								        this.svg = this.div.find('svg');
							 | 
						||
| 
								 | 
							
								        this._renderBackground();
							 | 
						||
| 
								 | 
							
								        this._renderSvgNodes(this.callgraph, 0, 0);
							 | 
						||
| 
								 | 
							
								        this._renderUnzoomNode();
							 | 
						||
| 
								 | 
							
								        this._renderInfoNode();
							 | 
						||
| 
								 | 
							
								        this._renderPercentNode();
							 | 
						||
| 
								 | 
							
								        // Make the added nodes in the svg visible.
							 | 
						||
| 
								 | 
							
								        this.div.html(this.div.html());
							 | 
						||
| 
								 | 
							
								        this.svg = this.div.find('svg');
							 | 
						||
| 
								 | 
							
								        this._adjustTextSize();
							 | 
						||
| 
								 | 
							
								        this._enableZoom();
							 | 
						||
| 
								 | 
							
								        this._enableInfo();
							 | 
						||
| 
								 | 
							
								        this._adjustTextSizeOnResize();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _renderBackground() {
							 | 
						||
| 
								 | 
							
								        this.svg.append(`<defs > <linearGradient id="background_gradient_${this.id}"
							 | 
						||
| 
								 | 
							
								                                  y1="0" y2="1" x1="0" x2="0" > \
							 | 
						||
| 
								 | 
							
								                                  <stop stop-color="#eeeeee" offset="5%" /> \
							 | 
						||
| 
								 | 
							
								                                  <stop stop-color="#efefb1" offset="90%" /> \
							 | 
						||
| 
								 | 
							
								                                  </linearGradient> \
							 | 
						||
| 
								 | 
							
								                         </defs> \
							 | 
						||
| 
								 | 
							
								                         <rect x="0" y="0" width="100%" height="100%" \
							 | 
						||
| 
								 | 
							
								                           fill="url(#background_gradient_${this.id})" />`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _getYForDepth(depth) {
							 | 
						||
| 
								 | 
							
								        if (this.reverseOrder) {
							 | 
						||
| 
								 | 
							
								            return (depth + 3) * this.svgNodeHeight;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return this.svgHeight - (depth + 1) * this.svgNodeHeight;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _getWidthPercentage(eventCount) {
							 | 
						||
| 
								 | 
							
								        return eventCount * 100.0 / this.callgraph.s;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _getHeatColor(widthPercentage) {
							 | 
						||
| 
								 | 
							
								        return {
							 | 
						||
| 
								 | 
							
								            r: Math.floor(245 + 10 * (1 - widthPercentage * 0.01)),
							 | 
						||
| 
								 | 
							
								            g: Math.floor(110 + 105 * (1 - widthPercentage * 0.01)),
							 | 
						||
| 
								 | 
							
								            b: 100,
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _renderSvgNodes(callNode, depth, xOffset) {
							 | 
						||
| 
								 | 
							
								        let x = xOffset;
							 | 
						||
| 
								 | 
							
								        let y = this._getYForDepth(depth);
							 | 
						||
| 
								 | 
							
								        let width = this._getWidthPercentage(callNode.s);
							 | 
						||
| 
								 | 
							
								        if (width < 0.1) {
							 | 
						||
| 
								 | 
							
								            return xOffset;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let color = this._getHeatColor(width);
							 | 
						||
| 
								 | 
							
								        let borderColor = {};
							 | 
						||
| 
								 | 
							
								        for (let key in color) {
							 | 
						||
| 
								 | 
							
								            borderColor[key] = Math.max(0, color[key] - 50);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let funcName = getFuncName(callNode.f);
							 | 
						||
| 
								 | 
							
								        let libName = getLibNameOfFunction(callNode.f);
							 | 
						||
| 
								 | 
							
								        let sampleWeight = this.sampleWeightFunction(callNode.s);
							 | 
						||
| 
								 | 
							
								        let title = funcName + ' | ' + libName + ' (' + callNode.s + ' events: ' +
							 | 
						||
| 
								 | 
							
								                    sampleWeight + ')';
							 | 
						||
| 
								 | 
							
								        this.svg.append(`<g> <title>${title}</title> <rect x="${x}%" y="${y}" ox="${x}" \
							 | 
						||
| 
								 | 
							
								                        depth="${depth}" width="${width}%" owidth="${width}" height="15.0" \
							 | 
						||
| 
								 | 
							
								                        ofill="rgb(${color.r},${color.g},${color.b})" \
							 | 
						||
| 
								 | 
							
								                        fill="rgb(${color.r},${color.g},${color.b})" \
							 | 
						||
| 
								 | 
							
								                        style="stroke:rgb(${borderColor.r},${borderColor.g},${borderColor.b})"/> \
							 | 
						||
| 
								 | 
							
								                        <text x="${x}%" y="${y + 12}" font-size="${this.fontSize}" \
							 | 
						||
| 
								 | 
							
								                        font-family="Monospace"></text></g>`);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let childXOffset = xOffset;
							 | 
						||
| 
								 | 
							
								        for (let child of callNode.c) {
							 | 
						||
| 
								 | 
							
								            childXOffset = this._renderSvgNodes(child, depth + 1, childXOffset);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return xOffset + width;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _renderUnzoomNode() {
							 | 
						||
| 
								 | 
							
								        this.svg.append(`<rect id="zoom_rect_${this.id}" style="display:none;stroke:rgb(0,0,0);" \
							 | 
						||
| 
								 | 
							
								        rx="10" ry="10" x="10" y="10" width="80" height="30" \
							 | 
						||
| 
								 | 
							
								        fill="rgb(255,255,255)"/> \
							 | 
						||
| 
								 | 
							
								         <text id="zoom_text_${this.id}" x="19" y="30" style="display:none">Zoom out</text>`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _renderInfoNode() {
							 | 
						||
| 
								 | 
							
								        this.svg.append(`<clipPath id="info_clip_path_${this.id}"> \
							 | 
						||
| 
								 | 
							
								                         <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10" \
							 | 
						||
| 
								 | 
							
								                         width="789" height="30" fill="rgb(255,255,255)"/> \
							 | 
						||
| 
								 | 
							
								                         </clipPath> \
							 | 
						||
| 
								 | 
							
								                         <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10" \
							 | 
						||
| 
								 | 
							
								                         width="799" height="30" fill="rgb(255,255,255)"/> \
							 | 
						||
| 
								 | 
							
								                         <text clip-path="url(#info_clip_path_${this.id})" \
							 | 
						||
| 
								 | 
							
								                         id="info_text_${this.id}" x="128" y="30"></text>`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _renderPercentNode() {
							 | 
						||
| 
								 | 
							
								        this.svg.append(`<rect style="stroke:rgb(0,0,0);" rx="10" ry="10" \
							 | 
						||
| 
								 | 
							
								                         x="934" y="10" width="150" height="30" \
							 | 
						||
| 
								 | 
							
								                         fill="rgb(255,255,255)"/> \
							 | 
						||
| 
								 | 
							
								                         <text id="percent_text_${this.id}" text-anchor="end" \
							 | 
						||
| 
								 | 
							
								                         x="1074" y="30"></text>`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _adjustTextSizeForNode(g) {
							 | 
						||
| 
								 | 
							
								        let text = g.find('text');
							 | 
						||
| 
								 | 
							
								        let width = parseFloat(g.find('rect').attr('width')) * this.svgWidth * 0.01;
							 | 
						||
| 
								 | 
							
								        if (width < 28) {
							 | 
						||
| 
								 | 
							
								            text.text('');
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let methodName = g.find('title').text().split(' | ')[0];
							 | 
						||
| 
								 | 
							
								        let numCharacters;
							 | 
						||
| 
								 | 
							
								        for (numCharacters = methodName.length; numCharacters > 4; numCharacters--) {
							 | 
						||
| 
								 | 
							
								            if (numCharacters * 7.5 <= width) {
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (numCharacters == methodName.length) {
							 | 
						||
| 
								 | 
							
								            text.text(methodName);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            text.text(methodName.substring(0, numCharacters - 2) + '..');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _adjustTextSize() {
							 | 
						||
| 
								 | 
							
								        this.svgWidth = $(window).width();
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        this.svg.find('g').each(function(_, g) {
							 | 
						||
| 
								 | 
							
								            thisObj._adjustTextSizeForNode($(g));
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _enableZoom() {
							 | 
						||
| 
								 | 
							
								        this.zoomStack = [this.svg.find('g').first().get(0)];
							 | 
						||
| 
								 | 
							
								        this.svg.find('g').css('cursor', 'pointer').click(zoom);
							 | 
						||
| 
								 | 
							
								        this.svg.find(`#zoom_rect_${this.id}`).css('cursor', 'pointer').click(unzoom);
							 | 
						||
| 
								 | 
							
								        this.svg.find(`#zoom_text_${this.id}`).css('cursor', 'pointer').click(unzoom);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        function zoom() {
							 | 
						||
| 
								 | 
							
								            thisObj.zoomStack.push(this);
							 | 
						||
| 
								 | 
							
								            displayFromElement(this);
							 | 
						||
| 
								 | 
							
								            thisObj.svg.find(`#zoom_rect_${thisObj.id}`).css('display', 'block');
							 | 
						||
| 
								 | 
							
								            thisObj.svg.find(`#zoom_text_${thisObj.id}`).css('display', 'block');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function unzoom() {
							 | 
						||
| 
								 | 
							
								            if (thisObj.zoomStack.length > 1) {
							 | 
						||
| 
								 | 
							
								                thisObj.zoomStack.pop();
							 | 
						||
| 
								 | 
							
								                displayFromElement(thisObj.zoomStack[thisObj.zoomStack.length - 1]);
							 | 
						||
| 
								 | 
							
								                if (thisObj.zoomStack.length == 1) {
							 | 
						||
| 
								 | 
							
								                    thisObj.svg.find(`#zoom_rect_${thisObj.id}`).css('display', 'none');
							 | 
						||
| 
								 | 
							
								                    thisObj.svg.find(`#zoom_text_${thisObj.id}`).css('display', 'none');
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function displayFromElement(g) {
							 | 
						||
| 
								 | 
							
								            g = $(g);
							 | 
						||
| 
								 | 
							
								            let clickedRect = g.find('rect');
							 | 
						||
| 
								 | 
							
								            let clickedOriginX = parseFloat(clickedRect.attr('ox'));
							 | 
						||
| 
								 | 
							
								            let clickedDepth = parseInt(clickedRect.attr('depth'));
							 | 
						||
| 
								 | 
							
								            let clickedOriginWidth = parseFloat(clickedRect.attr('owidth'));
							 | 
						||
| 
								 | 
							
								            let scaleFactor = 100.0 / clickedOriginWidth;
							 | 
						||
| 
								 | 
							
								            thisObj.svg.find('g').each(function(_, g) {
							 | 
						||
| 
								 | 
							
								                g = $(g);
							 | 
						||
| 
								 | 
							
								                let text = g.find('text');
							 | 
						||
| 
								 | 
							
								                let rect = g.find('rect');
							 | 
						||
| 
								 | 
							
								                let depth = parseInt(rect.attr('depth'));
							 | 
						||
| 
								 | 
							
								                let ox = parseFloat(rect.attr('ox'));
							 | 
						||
| 
								 | 
							
								                let owidth = parseFloat(rect.attr('owidth'));
							 | 
						||
| 
								 | 
							
								                if (depth < clickedDepth || ox < clickedOriginX - 1e-9 ||
							 | 
						||
| 
								 | 
							
								                    ox + owidth > clickedOriginX + clickedOriginWidth + 1e-9) {
							 | 
						||
| 
								 | 
							
								                    rect.css('display', 'none');
							 | 
						||
| 
								 | 
							
								                    text.css('display', 'none');
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    rect.css('display', 'block');
							 | 
						||
| 
								 | 
							
								                    text.css('display', 'block');
							 | 
						||
| 
								 | 
							
								                    let nx = (ox - clickedOriginX) * scaleFactor + '%';
							 | 
						||
| 
								 | 
							
								                    let ny = thisObj._getYForDepth(depth - clickedDepth);
							 | 
						||
| 
								 | 
							
								                    rect.attr('x', nx);
							 | 
						||
| 
								 | 
							
								                    rect.attr('y', ny);
							 | 
						||
| 
								 | 
							
								                    rect.attr('width', owidth * scaleFactor + '%');
							 | 
						||
| 
								 | 
							
								                    text.attr('x', nx);
							 | 
						||
| 
								 | 
							
								                    text.attr('y', ny + 12);
							 | 
						||
| 
								 | 
							
								                    thisObj._adjustTextSizeForNode(g);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _enableInfo() {
							 | 
						||
| 
								 | 
							
								        this.selected = null;
							 | 
						||
| 
								 | 
							
								        let thisObj = this;
							 | 
						||
| 
								 | 
							
								        this.svg.find('g').on('mouseenter', function() {
							 | 
						||
| 
								 | 
							
								            if (thisObj.selected) {
							 | 
						||
| 
								 | 
							
								                thisObj.selected.css('stroke-width', '0');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Mark current node.
							 | 
						||
| 
								 | 
							
								            let g = $(this);
							 | 
						||
| 
								 | 
							
								            thisObj.selected = g;
							 | 
						||
| 
								 | 
							
								            g.css('stroke', 'black').css('stroke-width', '0.5');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // Parse title.
							 | 
						||
| 
								 | 
							
								            let title = g.find('title').text();
							 | 
						||
| 
								 | 
							
								            let methodAndInfo = title.split(' | ');
							 | 
						||
| 
								 | 
							
								            thisObj.svg.find(`#info_text_${thisObj.id}`).text(methodAndInfo[0]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // Parse percentage.
							 | 
						||
| 
								 | 
							
								            // '/system/lib64/libhwbinder.so (4 events: 0.28%)'
							 | 
						||
| 
								 | 
							
								            let regexp = /.* \(.*:\s+(.*)\)/g;
							 | 
						||
| 
								 | 
							
								            let match = regexp.exec(methodAndInfo[1]);
							 | 
						||
| 
								 | 
							
								            let percentage = '';
							 | 
						||
| 
								 | 
							
								            if (match && match.length > 1) {
							 | 
						||
| 
								 | 
							
								                percentage = match[1];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            thisObj.svg.find(`#percent_text_${thisObj.id}`).text(percentage);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _adjustTextSizeOnResize() {
							 | 
						||
| 
								 | 
							
								        function throttle(callback) {
							 | 
						||
| 
								 | 
							
								            let running = false;
							 | 
						||
| 
								 | 
							
								            return function() {
							 | 
						||
| 
								 | 
							
								                if (!running) {
							 | 
						||
| 
								 | 
							
								                    running = true;
							 | 
						||
| 
								 | 
							
								                    window.requestAnimationFrame(function () {
							 | 
						||
| 
								 | 
							
								                        callback();
							 | 
						||
| 
								 | 
							
								                        running = false;
							 | 
						||
| 
								 | 
							
								                    });
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $(window).resize(throttle(() => this._adjustTextSize()));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SourceFile {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    constructor(fileId) {
							 | 
						||
| 
								 | 
							
								        this.path = getSourceFilePath(fileId);
							 | 
						||
| 
								 | 
							
								        this.code = getSourceCode(fileId);
							 | 
						||
| 
								 | 
							
								        this.showLines = {};  // map from line number to {eventCount, subtreeEventCount}.
							 | 
						||
| 
								 | 
							
								        this.hasCount = false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    addLineRange(startLine, endLine) {
							 | 
						||
| 
								 | 
							
								        for (let i = startLine; i <= endLine; ++i) {
							 | 
						||
| 
								 | 
							
								            if (i in this.showLines || !(i in this.code)) {
							 | 
						||
| 
								 | 
							
								                continue;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            this.showLines[i] = {eventCount: 0, subtreeEventCount: 0};
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    addLineCount(lineNumber, eventCount, subtreeEventCount) {
							 | 
						||
| 
								 | 
							
								        let line = this.showLines[lineNumber];
							 | 
						||
| 
								 | 
							
								        if (line) {
							 | 
						||
| 
								 | 
							
								            line.eventCount += eventCount;
							 | 
						||
| 
								 | 
							
								            line.subtreeEventCount += subtreeEventCount;
							 | 
						||
| 
								 | 
							
								            this.hasCount = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Return a list of SourceFile related to a function.
							 | 
						||
| 
								 | 
							
								function collectSourceFilesForFunction(func) {
							 | 
						||
| 
								 | 
							
								    if (!func.hasOwnProperty('s')) {
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let hitLines = func.s;
							 | 
						||
| 
								 | 
							
								    let sourceFiles = {};  // map from sourceFileId to SourceFile.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function getFile(fileId) {
							 | 
						||
| 
								 | 
							
								        let file = sourceFiles[fileId];
							 | 
						||
| 
								 | 
							
								        if (!file) {
							 | 
						||
| 
								 | 
							
								            file = sourceFiles[fileId] = new SourceFile(fileId);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return file;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Show lines for the function.
							 | 
						||
| 
								 | 
							
								    let funcRange = getFuncSourceRange(func.g.f);
							 | 
						||
| 
								 | 
							
								    if (funcRange) {
							 | 
						||
| 
								 | 
							
								        let file = getFile(funcRange.fileId);
							 | 
						||
| 
								 | 
							
								        file.addLineRange(funcRange.startLine);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Show lines for hitLines.
							 | 
						||
| 
								 | 
							
								    for (let hitLine of hitLines) {
							 | 
						||
| 
								 | 
							
								        let file = getFile(hitLine.f);
							 | 
						||
| 
								 | 
							
								        file.addLineRange(hitLine.l - 5, hitLine.l + 5);
							 | 
						||
| 
								 | 
							
								        file.addLineCount(hitLine.l, hitLine.e, hitLine.s);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let result = [];
							 | 
						||
| 
								 | 
							
								    // Show the source file containing the function before other source files.
							 | 
						||
| 
								 | 
							
								    if (funcRange) {
							 | 
						||
| 
								 | 
							
								        let file = getFile(funcRange.fileId);
							 | 
						||
| 
								 | 
							
								        if (file.hasCount) {
							 | 
						||
| 
								 | 
							
								            result.push(file);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        delete sourceFiles[funcRange.fileId];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    for (let fileId in sourceFiles) {
							 | 
						||
| 
								 | 
							
								        let file = sourceFiles[fileId];
							 | 
						||
| 
								 | 
							
								        if (file.hasCount) {
							 | 
						||
| 
								 | 
							
								            result.push(file);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return result.length > 0 ? result : null;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Show annotated source code of a function.
							 | 
						||
| 
								 | 
							
								class SourceCodeView {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, sourceFiles) {
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>');
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.sourceFiles = sourceFiles;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw(sampleWeightFunction) {
							 | 
						||
| 
								 | 
							
								        google.charts.setOnLoadCallback(() => this.realDraw(sampleWeightFunction));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    realDraw(sampleWeightFunction) {
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        // For each file, draw a table of 'Line', 'Total', 'Self', 'Code'.
							 | 
						||
| 
								 | 
							
								        for (let sourceFile of this.sourceFiles) {
							 | 
						||
| 
								 | 
							
								            let rows = [];
							 | 
						||
| 
								 | 
							
								            let lineNumbers = Object.keys(sourceFile.showLines);
							 | 
						||
| 
								 | 
							
								            lineNumbers.sort((a, b) => a - b);
							 | 
						||
| 
								 | 
							
								            for (let lineNumber of lineNumbers) {
							 | 
						||
| 
								 | 
							
								                let code = getHtml('pre', {text: sourceFile.code[lineNumber]});
							 | 
						||
| 
								 | 
							
								                let countInfo = sourceFile.showLines[lineNumber];
							 | 
						||
| 
								 | 
							
								                let totalValue = '';
							 | 
						||
| 
								 | 
							
								                let selfValue = '';
							 | 
						||
| 
								 | 
							
								                if (countInfo.subtreeEventCount != 0) {
							 | 
						||
| 
								 | 
							
								                    totalValue = sampleWeightFunction(countInfo.subtreeEventCount);
							 | 
						||
| 
								 | 
							
								                    selfValue = sampleWeightFunction(countInfo.eventCount);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                rows.push([lineNumber, totalValue, selfValue, code]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            let data = new google.visualization.DataTable();
							 | 
						||
| 
								 | 
							
								            data.addColumn('string', 'Line');
							 | 
						||
| 
								 | 
							
								            data.addColumn('string', 'Total');
							 | 
						||
| 
								 | 
							
								            data.addColumn('string', 'Self');
							 | 
						||
| 
								 | 
							
								            data.addColumn('string', 'Code');
							 | 
						||
| 
								 | 
							
								            data.addRows(rows);
							 | 
						||
| 
								 | 
							
								            for (let i = 0; i < rows.length; ++i) {
							 | 
						||
| 
								 | 
							
								                data.setProperty(i, 0, 'className', 'colForLine');
							 | 
						||
| 
								 | 
							
								                for (let j = 1; j <= 2; ++j) {
							 | 
						||
| 
								 | 
							
								                    data.setProperty(i, j, 'className', 'colForCount');
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            this.div.append(getHtml('pre', {text: sourceFile.path}));
							 | 
						||
| 
								 | 
							
								            let wrapperDiv = $('<div>');
							 | 
						||
| 
								 | 
							
								            wrapperDiv.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								            let table = new google.visualization.Table(wrapperDiv.get(0));
							 | 
						||
| 
								 | 
							
								            table.draw(data, {
							 | 
						||
| 
								 | 
							
								                width: '100%',
							 | 
						||
| 
								 | 
							
								                sort: 'disable',
							 | 
						||
| 
								 | 
							
								                frozenColumns: 3,
							 | 
						||
| 
								 | 
							
								                allowHtml: true,
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Return a list of disassembly related to a function.
							 | 
						||
| 
								 | 
							
								function collectDisassemblyForFunction(func) {
							 | 
						||
| 
								 | 
							
								    if (!func.hasOwnProperty('a')) {
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let hitAddrs = func.a;
							 | 
						||
| 
								 | 
							
								    let rawCode = getFuncDisassembly(func.g.f);
							 | 
						||
| 
								 | 
							
								    if (!rawCode) {
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Annotate disassembly with event count information.
							 | 
						||
| 
								 | 
							
								    let annotatedCode = [];
							 | 
						||
| 
								 | 
							
								    let codeForLastAddr = null;
							 | 
						||
| 
								 | 
							
								    let hitAddrPos = 0;
							 | 
						||
| 
								 | 
							
								    let hasCount = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function addEventCount(addr) {
							 | 
						||
| 
								 | 
							
								        while (hitAddrPos < hitAddrs.length && hitAddrs[hitAddrPos].a < addr) {
							 | 
						||
| 
								 | 
							
								            if (codeForLastAddr) {
							 | 
						||
| 
								 | 
							
								                codeForLastAddr.eventCount += hitAddrs[hitAddrPos].e;
							 | 
						||
| 
								 | 
							
								                codeForLastAddr.subtreeEventCount += hitAddrs[hitAddrPos].s;
							 | 
						||
| 
								 | 
							
								                hasCount = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            hitAddrPos++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let line of rawCode) {
							 | 
						||
| 
								 | 
							
								        let code = line[0];
							 | 
						||
| 
								 | 
							
								        let addr = line[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        addEventCount(addr);
							 | 
						||
| 
								 | 
							
								        let item = {code: code, eventCount: 0, subtreeEventCount: 0};
							 | 
						||
| 
								 | 
							
								        annotatedCode.push(item);
							 | 
						||
| 
								 | 
							
								        // Objdump sets addr to 0 when a disassembly line is not associated with an addr.
							 | 
						||
| 
								 | 
							
								        if (addr != 0) {
							 | 
						||
| 
								 | 
							
								            codeForLastAddr = item;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    addEventCount(Number.MAX_VALUE);
							 | 
						||
| 
								 | 
							
								    return hasCount ? annotatedCode : null;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Show annotated disassembly of a function.
							 | 
						||
| 
								 | 
							
								class DisassemblyView {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    constructor(divContainer, disassembly) {
							 | 
						||
| 
								 | 
							
								        this.div = $('<div>');
							 | 
						||
| 
								 | 
							
								        this.div.appendTo(divContainer);
							 | 
						||
| 
								 | 
							
								        this.disassembly = disassembly;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    draw(sampleWeightFunction) {
							 | 
						||
| 
								 | 
							
								        google.charts.setOnLoadCallback(() => this.realDraw(sampleWeightFunction));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    realDraw(sampleWeightFunction) {
							 | 
						||
| 
								 | 
							
								        this.div.empty();
							 | 
						||
| 
								 | 
							
								        // Draw a table of 'Total', 'Self', 'Code'.
							 | 
						||
| 
								 | 
							
								        let rows = [];
							 | 
						||
| 
								 | 
							
								        for (let line of this.disassembly) {
							 | 
						||
| 
								 | 
							
								            let code = getHtml('pre', {text: line.code});
							 | 
						||
| 
								 | 
							
								            let totalValue = '';
							 | 
						||
| 
								 | 
							
								            let selfValue = '';
							 | 
						||
| 
								 | 
							
								            if (line.subtreeEventCount != 0) {
							 | 
						||
| 
								 | 
							
								                totalValue = sampleWeightFunction(line.subtreeEventCount);
							 | 
						||
| 
								 | 
							
								                selfValue = sampleWeightFunction(line.eventCount);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            rows.push([totalValue, selfValue, code]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let data = new google.visualization.DataTable();
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', 'Total');
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', 'Self');
							 | 
						||
| 
								 | 
							
								        data.addColumn('string', 'Code');
							 | 
						||
| 
								 | 
							
								        data.addRows(rows);
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < rows.length; ++i) {
							 | 
						||
| 
								 | 
							
								            for (let j = 0; j < 2; ++j) {
							 | 
						||
| 
								 | 
							
								                data.setProperty(i, j, 'className', 'colForCount');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let wrapperDiv = $('<div>');
							 | 
						||
| 
								 | 
							
								        wrapperDiv.appendTo(this.div);
							 | 
						||
| 
								 | 
							
								        let table = new google.visualization.Table(wrapperDiv.get(0));
							 | 
						||
| 
								 | 
							
								        table.draw(data, {
							 | 
						||
| 
								 | 
							
								            width: '100%',
							 | 
						||
| 
								 | 
							
								            sort: 'disable',
							 | 
						||
| 
								 | 
							
								            frozenColumns: 2,
							 | 
						||
| 
								 | 
							
								            allowHtml: true,
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function initGlobalObjects() {
							 | 
						||
| 
								 | 
							
								    gTabs = new TabManager($('div#report_content'));
							 | 
						||
| 
								 | 
							
								    let recordData = $('#record_data').text();
							 | 
						||
| 
								 | 
							
								    gRecordInfo = JSON.parse(recordData);
							 | 
						||
| 
								 | 
							
								    gProcesses = gRecordInfo.processNames;
							 | 
						||
| 
								 | 
							
								    gThreads = gRecordInfo.threadNames;
							 | 
						||
| 
								 | 
							
								    gLibList = gRecordInfo.libList;
							 | 
						||
| 
								 | 
							
								    gFunctionMap = gRecordInfo.functionMap;
							 | 
						||
| 
								 | 
							
								    gSampleInfo = gRecordInfo.sampleInfo;
							 | 
						||
| 
								 | 
							
								    gSourceFiles = gRecordInfo.sourceFiles;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function createTabs() {
							 | 
						||
| 
								 | 
							
								    gTabs.addTab('Chart Statistics', new ChartStatTab());
							 | 
						||
| 
								 | 
							
								    gTabs.addTab('Sample Table', new SampleTableTab());
							 | 
						||
| 
								 | 
							
								    gTabs.addTab('Flamegraph', new FlameGraphTab());
							 | 
						||
| 
								 | 
							
								    gTabs.draw();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let gTabs;
							 | 
						||
| 
								 | 
							
								let gRecordInfo;
							 | 
						||
| 
								 | 
							
								let gProcesses;
							 | 
						||
| 
								 | 
							
								let gThreads;
							 | 
						||
| 
								 | 
							
								let gLibList;
							 | 
						||
| 
								 | 
							
								let gFunctionMap;
							 | 
						||
| 
								 | 
							
								let gSampleInfo;
							 | 
						||
| 
								 | 
							
								let gSourceFiles;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								initGlobalObjects();
							 | 
						||
| 
								 | 
							
								createTabs();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								});
							 |