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