blob: d8efd0b265c09664fbf8f9b46074cafa09888c46 [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {Engine} from '../common/engine';
import {CallsiteInfo, CpuProfileSampleSelection} from '../common/state';
import {CpuProfileDetails} from '../frontend/globals';
import {Controller} from './controller';
import {globals} from './globals';
export interface CpuProfileControllerArgs {
engine: Engine;
}
export class CpuProfileController extends Controller<'main'> {
private lastSelectedSample?: CpuProfileSampleSelection;
private requestingData = false;
private queuedRunRequest = false;
constructor(private args: CpuProfileControllerArgs) {
super('main');
}
run() {
const selection = globals.state.currentSelection;
if (!selection || selection.kind !== 'CPU_PROFILE_SAMPLE') {
return;
}
const selectedSample = selection as CpuProfileSampleSelection;
if (!this.shouldRequestData(selectedSample)) {
return;
}
if (this.requestingData) {
this.queuedRunRequest = true;
return;
}
this.requestingData = true;
globals.publish('CpuProfileDetails', {});
this.lastSelectedSample = this.copyCpuProfileSample(selection);
this.getSampleData(selectedSample.id)
.then(sampleData => {
if (sampleData !== undefined && selectedSample &&
this.lastSelectedSample &&
this.lastSelectedSample.id === selectedSample.id) {
const cpuProfileDetails: CpuProfileDetails = {
id: selectedSample.id,
ts: selectedSample.ts,
utid: selectedSample.utid,
stack: sampleData,
};
globals.publish('CpuProfileDetails', cpuProfileDetails);
}
})
.finally(() => {
this.requestingData = false;
if (this.queuedRunRequest) {
this.queuedRunRequest = false;
this.run();
}
});
}
private copyCpuProfileSample(cpuProfileSample: CpuProfileSampleSelection):
CpuProfileSampleSelection {
return {
kind: cpuProfileSample.kind,
id: cpuProfileSample.id,
utid: cpuProfileSample.utid,
ts: cpuProfileSample.ts,
};
}
private shouldRequestData(selection: CpuProfileSampleSelection) {
return this.lastSelectedSample === undefined ||
(this.lastSelectedSample !== undefined &&
(this.lastSelectedSample.id !== selection.id));
}
async getSampleData(id: number) {
const sampleQuery = `SELECT samples.id, frame_name, mapping_name
FROM cpu_profile_stack_sample AS samples
LEFT JOIN
(
SELECT
callsite_id,
position,
spf.name AS frame_name,
stack_profile_mapping.name AS mapping_name
FROM
(
WITH
RECURSIVE
callsite_parser(callsite_id, current_id, position)
AS (
SELECT id, id, 0 FROM stack_profile_callsite
UNION
SELECT callsite_id, parent_id, position + 1
FROM callsite_parser
JOIN
stack_profile_callsite
ON stack_profile_callsite.id = current_id
WHERE stack_profile_callsite.depth > 0
)
SELECT *
FROM callsite_parser
) AS flattened_callsite
LEFT JOIN stack_profile_callsite AS spc
LEFT JOIN
(
SELECT
spf.id AS id,
spf.mapping AS mapping,
IFNULL(
(
SELECT name
FROM stack_profile_symbol symbol
WHERE symbol.symbol_set_id = spf.symbol_set_id
LIMIT 1
),
spf.name
) AS name
FROM stack_profile_frame spf
) AS spf
LEFT JOIN stack_profile_mapping
WHERE
flattened_callsite.current_id = spc.id
AND spc.frame_id = spf.id
AND spf.mapping = stack_profile_mapping.id
ORDER BY callsite_id, position
) AS frames
ON samples.callsite_id = frames.callsite_id
WHERE samples.id = ${id}
ORDER BY samples.id, frames.position DESC;`;
const callsites = await this.args.engine.query(sampleQuery);
if (callsites.numRecords < 1) {
return undefined;
}
const sampleData: CallsiteInfo[] = new Array();
for (let i = 0; i < callsites.numRecords; i++) {
const id = +callsites.columns[0].longValues![i];
const name = callsites.columns[1].stringValues![i];
const mapping = callsites.columns[2].stringValues![i];
sampleData.push({
id,
totalSize: 0,
depth: 0,
parentId: 0,
name,
selfSize: 0,
mapping,
merged: false
});
}
return sampleData;
}
}