| // Copyright (C) 2025 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 m from 'mithril'; |
| import type {Engine} from '../../../trace_processor/engine'; |
| import { |
| NUM_NULL, |
| STR_NULL, |
| UNKNOWN, |
| } from '../../../trace_processor/query_result'; |
| import {Section} from '../../../widgets/section'; |
| import {CodeSnippet} from '../../../widgets/code_snippet'; |
| import {EmptyState} from '../../../widgets/empty_state'; |
| import {getTraceInfos} from '../utils'; |
| |
| export interface ConfigEntry { |
| readonly traceId?: number; |
| readonly traceIndex?: number; |
| readonly sessionName?: string; |
| readonly configText: string; |
| } |
| |
| export interface ConfigData { |
| readonly configs: ReadonlyArray<ConfigEntry>; |
| } |
| |
| export async function loadConfigData(engine: Engine): Promise<ConfigData> { |
| const configResult = await engine.query(` |
| INCLUDE PERFETTO MODULE std.traceinfo.trace; |
| SELECT |
| trace_id as traceId, |
| extract_metadata_for_trace(trace_id, 'trace_config_pbtxt') as configText, |
| unique_session_name as sessionName |
| FROM _metadata_by_trace |
| WHERE configText IS NOT NULL |
| ORDER BY traceId; |
| `); |
| |
| const traceInfos = await getTraceInfos(engine); |
| const configs: ConfigEntry[] = []; |
| const it = configResult.iter({ |
| traceId: NUM_NULL, |
| configText: UNKNOWN, |
| sessionName: STR_NULL, |
| }); |
| for (; it.valid(); it.next()) { |
| const traceId = it.traceId; |
| const info = traceId !== null ? traceInfos.get(traceId) : undefined; |
| configs.push({ |
| traceId: traceId ?? undefined, |
| traceIndex: info?.traceIndex ?? undefined, |
| configText: String(it.configText), |
| sessionName: it.sessionName ?? undefined, |
| }); |
| } |
| |
| return {configs}; |
| } |
| |
| export interface ConfigTabAttrs { |
| data: ConfigData; |
| } |
| |
| export class ConfigTab implements m.ClassComponent<ConfigTabAttrs> { |
| view({attrs}: m.CVnode<ConfigTabAttrs>) { |
| const configs = attrs.data.configs; |
| if (configs.length === 0) { |
| return m( |
| '.pf-trace-info-page__tab-content', |
| m( |
| Section, |
| { |
| title: 'Trace Configuration', |
| subtitle: 'TraceConfig protobuf used to record this trace', |
| }, |
| m(EmptyState, { |
| icon: 'settings', |
| title: 'No trace configuration available', |
| }), |
| ), |
| ); |
| } |
| |
| return m( |
| '.pf-trace-info-page__tab-content', |
| configs.map((config, index) => { |
| let title = 'Trace Configuration'; |
| if ( |
| configs.length > 1 || |
| config.sessionName || |
| config.traceIndex !== undefined |
| ) { |
| const indexPart = |
| config.traceIndex !== undefined ? ` ${config.traceIndex}` : ''; |
| const namePart = config.sessionName ? `: ${config.sessionName}` : ''; |
| title = `Trace Configuration${indexPart}${namePart}`; |
| } |
| return m( |
| Section, |
| { |
| key: config.traceId ?? index, |
| title, |
| subtitle: 'TraceConfig protobuf used to record this trace', |
| }, |
| m(CodeSnippet, { |
| text: config.configText, |
| language: 'prototext', |
| downloadFileName: |
| config.traceIndex !== undefined |
| ? `config_${config.traceIndex}.txtpb` |
| : 'config.txtpb', |
| }), |
| ); |
| }), |
| ); |
| } |
| } |