Modify transactions presenter.
Change trace data type to HierarchyTreeNode.
Populate filters by query.
Bug: 411363817
Test: npm run test:unit:ci
Change-Id: If1767bd48db7753e743e9cc334bd304059a83291
diff --git a/tools/winscope/src/parsers/perfetto/utils.ts b/tools/winscope/src/parsers/perfetto/utils.ts
index 026caa4..5e86916 100644
--- a/tools/winscope/src/parsers/perfetto/utils.ts
+++ b/tools/winscope/src/parsers/perfetto/utils.ts
@@ -172,3 +172,30 @@
ORDER BY tbl.id;
`;
}
+
+export async function getDistinctValues(
+ traceProcessor: TraceProcessor,
+ tableName: string,
+ columns: string[],
+): Promise<string[]> {
+ const uniqueValueCol = 'unique_value';
+ const sql =
+ columns
+ .map((col) => {
+ return `SELECT DISTINCT ${col} AS ${uniqueValueCol} FROM ${tableName}`;
+ })
+ .join(' UNION ') + ` ORDER BY ${uniqueValueCol}`;
+
+ const rows = await traceProcessor.query(sql);
+ if (rows.numRows() === 0) {
+ return [];
+ }
+
+ const options: string[] = [];
+ for (const it = rows.iter({}); it.valid(); it.next()) {
+ const val = it.get(uniqueValueCol);
+ const option = val !== null && val !== undefined ? val.toString() : 'N/A';
+ options.push(option);
+ }
+ return options;
+}
diff --git a/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts b/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts
index dd49da0..10f24a4 100644
--- a/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts
+++ b/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts
@@ -21,7 +21,11 @@
import {SetFormatters} from 'parsers/operations/set_formatters';
import {AbstractParser} from 'parsers/perfetto/abstract_parser';
import {FakeProtoTransformer} from 'parsers/perfetto/fake_proto_transformer';
-import {queryArgs, queryVsyncId} from 'parsers/perfetto/utils';
+import {
+ getDistinctValues,
+ queryArgs,
+ queryVsyncId,
+} from 'parsers/perfetto/utils';
import {PropertyTreeBuilderFromProto} from 'parsers/property_tree_builder_from_proto';
import {PropertyTreeBuilderFromQueryRow} from 'parsers/property_tree_builder_from_query_row';
import {
@@ -31,6 +35,7 @@
import {TransactionType} from 'parsers/transactions/transaction_type';
import {perfetto} from 'protos/perfetto/trace/static';
import {
+ CustomQueryParamTypeMap,
CustomQueryParserResultTypeMap,
CustomQueryType,
VisitableParserCustomQuery,
@@ -38,6 +43,7 @@
import {EntriesRange} from 'trace/index_types';
import {TraceFile} from 'trace/trace_file';
import {TraceType} from 'trace/trace_type';
+import {TransactionColumnType} from 'trace/transaction_column_type';
import {
EnumFormatter,
FixedStringFormatter,
@@ -105,6 +111,7 @@
override async customQuery<Q extends CustomQueryType>(
type: Q,
entriesRange: EntriesRange,
+ param?: CustomQueryParamTypeMap[Q],
): Promise<CustomQueryParserResultTypeMap[Q]> {
return new VisitableParserCustomQuery(type)
.visit(CustomQueryType.VSYNCID, async () => {
@@ -116,6 +123,44 @@
ParserTransactions.createVsyncIdQuery,
);
})
+ .visit(CustomQueryType.LOG_TABLE_FILTER_VALUES, async () => {
+ let tableName: string;
+ let columns: string[];
+ switch (param) {
+ case TransactionColumnType.TRANSACTION_ID:
+ tableName = '__intrinsic_surfaceflinger_transaction';
+ columns = ['transaction_id'];
+ break;
+ case TransactionColumnType.VSYNC_ID:
+ tableName = 'surfaceflinger_transactions';
+ columns = ['vsync_id'];
+ break;
+ case TransactionColumnType.PID:
+ tableName = '__intrinsic_surfaceflinger_transaction';
+ columns = ['pid'];
+ break;
+ case TransactionColumnType.UID:
+ tableName = '__intrinsic_surfaceflinger_transaction';
+ columns = ['uid'];
+ break;
+ case TransactionColumnType.TRANSACTION_TYPE:
+ tableName = '__intrinsic_surfaceflinger_transaction';
+ columns = ['transaction_type'];
+ break;
+ case TransactionColumnType.LAYER_OR_DISPLAY_ID:
+ tableName = '__intrinsic_surfaceflinger_transaction';
+ columns = ['layer_id', 'display_id'];
+ break;
+ case TransactionColumnType.FLAGS:
+ tableName = '__intrinsic_surfaceflinger_transaction_flag';
+ columns = ['flag'];
+ break;
+
+ default:
+ throw new Error('unexpected transaction column type requested');
+ }
+ return getDistinctValues(this.traceProcessor, tableName, columns);
+ })
.getResult();
}
@@ -166,7 +211,9 @@
const argSetId = row.get('arg_set_id') ?? undefined;
let field: TamperedProtoField | undefined;
- const transactionType = assertDefined(row.get('transaction_type')) as string;
+ const transactionType = assertDefined(
+ row.get('transaction_type'),
+ ) as string;
const entryProtoType = assertDefined(
ParserTransactions.TransactionsTraceEntryField.tamperedMessageType,
);
diff --git a/tools/winscope/src/test/unit/mock_log_viewer_presenter.ts b/tools/winscope/src/test/unit/mock_log_viewer_presenter.ts
index 4cba678..142974d 100644
--- a/tools/winscope/src/test/unit/mock_log_viewer_presenter.ts
+++ b/tools/winscope/src/test/unit/mock_log_viewer_presenter.ts
@@ -135,10 +135,7 @@
return headers;
}
- protected override updateFiltersInHeaders(
- headers: LogHeader[],
- allEntries: LogEntry[],
- ) {
+ protected override async updateFiltersInHeaders(headers: LogHeader[]) {
for (const header of headers) {
if (header.spec === this.stringColumn) {
(assertDefined(header.filter) as LogSelectFilter).options = [
diff --git a/tools/winscope/src/trace/custom_query.ts b/tools/winscope/src/trace/custom_query.ts
index bd6eeb7..8d95da1 100644
--- a/tools/winscope/src/trace/custom_query.ts
+++ b/tools/winscope/src/trace/custom_query.ts
@@ -21,6 +21,7 @@
VIEW_CAPTURE_METADATA,
VSYNCID,
WM_WINDOWS_TOKEN_AND_TITLE,
+ LOG_TABLE_FILTER_VALUES,
}
export class ProcessParserResult {
@@ -53,6 +54,12 @@
): CustomQueryResultTypeMap<T>[CustomQueryType.WM_WINDOWS_TOKEN_AND_TITLE] {
return parserResult;
}
+
+ static [CustomQueryType.LOG_TABLE_FILTER_VALUES]<T>(
+ parserResult: CustomQueryParserResultTypeMap[CustomQueryType.LOG_TABLE_FILTER_VALUES],
+ ): CustomQueryResultTypeMap<T>[CustomQueryType.LOG_TABLE_FILTER_VALUES] {
+ return parserResult;
+ }
}
export interface CustomQueryParamTypeMap {
@@ -60,6 +67,7 @@
[CustomQueryType.VIEW_CAPTURE_METADATA]: never;
[CustomQueryType.VSYNCID]: never;
[CustomQueryType.WM_WINDOWS_TOKEN_AND_TITLE]: never;
+ [CustomQueryType.LOG_TABLE_FILTER_VALUES]: number;
}
export interface CustomQueryParserResultTypeMap {
@@ -73,6 +81,7 @@
token: string;
title: string;
}>;
+ [CustomQueryType.LOG_TABLE_FILTER_VALUES]: string[];
}
export interface CustomQueryResultTypeMap<T> {
@@ -86,6 +95,7 @@
token: string;
title: string;
}>;
+ [CustomQueryType.LOG_TABLE_FILTER_VALUES]: string[];
}
export class VisitableParserCustomQuery<Q extends CustomQueryType> {
diff --git a/tools/winscope/src/trace/transaction_column_type.ts b/tools/winscope/src/trace/transaction_column_type.ts
new file mode 100644
index 0000000..9288cd9
--- /dev/null
+++ b/tools/winscope/src/trace/transaction_column_type.ts
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+export enum TransactionColumnType {
+ TRANSACTION_ID,
+ VSYNC_ID,
+ PID,
+ UID,
+ TRANSACTION_TYPE,
+ LAYER_OR_DISPLAY_ID,
+ FLAGS,
+}
diff --git a/tools/winscope/src/viewers/common/abstract_log_viewer_presenter.ts b/tools/winscope/src/viewers/common/abstract_log_viewer_presenter.ts
index 8f8d1cf..fed60d0 100644
--- a/tools/winscope/src/viewers/common/abstract_log_viewer_presenter.ts
+++ b/tools/winscope/src/viewers/common/abstract_log_viewer_presenter.ts
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import {assertDefined} from 'common/assert_utils';
import {isElementVisible, KeyboardEventKey} from 'common/dom_utils';
import {FunctionUtils} from 'common/function_utils';
import {Timestamp} from 'common/time/time';
@@ -24,6 +25,7 @@
WinscopeEventType,
} from 'messaging/winscope_event';
import {EmitEvent} from 'messaging/winscope_event_emitter';
+import {CustomQueryType} from 'trace/custom_query';
import {Trace, TraceEntry} from 'trace/trace';
import {TraceEntryFinder} from 'trace/trace_entry_finder';
import {TRACE_INFO} from 'trace/trace_info';
@@ -32,6 +34,7 @@
import {PropertiesPresenter} from 'viewers/common/properties_presenter';
import {TextFilter} from 'viewers/common/text_filter';
import {UserOptions} from 'viewers/common/user_options';
+import {LogSelectFilter} from './log_filters';
import {LogPresenter} from './log_presenter';
import {LogEntry, LogHeader, UiDataLog} from './ui_data_log';
import {
@@ -324,7 +327,7 @@
const traceName = TRACE_INFO[this.trace.type].name;
const propertiesStartTime = Date.now();
- const tree = this.getPropertiesTree();
+ const tree = await this.getPropertiesTree();
this.propertiesPresenter.setPropertiesTree(tree);
if (updateDefaultAllowlist && this.updateDefaultAllowlist) {
this.updateDefaultAllowlist(tree);
@@ -362,19 +365,36 @@
this.uiData.scrollToIndex = this.logPresenter.getScrollToIndex();
}
- private getPropertiesTree(): PropertyTreeNode | undefined {
+ private async getPropertiesTree(): Promise<PropertyTreeNode | undefined> {
const entries = this.logPresenter.getFilteredEntries();
const selectedIndex = this.logPresenter.getSelectedIndex();
const currentIndex = this.logPresenter.getCurrentIndex();
if (selectedIndex !== undefined) {
- return entries.at(selectedIndex)?.propertiesTree;
+ const entry = entries.at(selectedIndex);
+ return (
+ entry?.propertiesTree ??
+ (entry?.getPropertiesTree ? await entry.getPropertiesTree() : undefined)
+ );
}
if (currentIndex !== undefined) {
- return entries.at(currentIndex)?.propertiesTree;
+ const entry = entries.at(currentIndex);
+ return (
+ entry?.propertiesTree ??
+ (entry?.getPropertiesTree ? await entry.getPropertiesTree() : undefined)
+ );
}
return undefined;
}
+ protected async updateFilterByCustomQuery(header: LogHeader) {
+ const filterValues = await this.trace.customQuery(
+ CustomQueryType.LOG_TABLE_FILTER_VALUES,
+ assertDefined(header.spec.columnType),
+ );
+ (header.filter as LogSelectFilter).options = filterValues;
+ return;
+ }
+
protected notifyViewChanged() {
this.notifyViewCallback(this.uiData);
}
@@ -384,9 +404,9 @@
headers: LogHeader[],
): Promise<LogEntry[]>;
protected initializeTraceSpecificData?(): Promise<void>;
- protected updateFiltersInHeaders?(
+ protected async updateFiltersInHeaders?(
headers: LogHeader[],
allEntries: LogEntry[],
- ): void;
+ ): Promise<void>;
protected updateDefaultAllowlist?(tree: PropertyTreeNode | undefined): void;
}
diff --git a/tools/winscope/src/viewers/common/abstract_log_viewer_presenter_test.ts b/tools/winscope/src/viewers/common/abstract_log_viewer_presenter_test.ts
index aa80b96..230d90f 100644
--- a/tools/winscope/src/viewers/common/abstract_log_viewer_presenter_test.ts
+++ b/tools/winscope/src/viewers/common/abstract_log_viewer_presenter_test.ts
@@ -18,6 +18,8 @@
import {TimestampConverterUtils} from 'common/time/test_utils';
import {TimeUtils} from 'common/time/time_utils';
import {TracePositionUpdate} from 'messaging/winscope_event';
+import {setNumRowsSpyQueryResult} from 'trace_processor/test_utils';
+import {TraceProcessor} from 'trace_processor/trace_processor';
import {
AbstractLogViewerPresenter,
NotifyLogViewCallbackType,
@@ -50,6 +52,9 @@
const presenter = await this.createPresenterWithEmptyTrace(
(newData: UiData) => (uiData = newData),
);
+ spyOn(TraceProcessor.prototype, 'queryAllRows').and.returnValue(
+ Promise.resolve(setNumRowsSpyQueryResult(0)),
+ );
await presenter.onAppEvent(
TracePositionUpdate.fromTimestamp(
TimestampConverterUtils.makeRealTimestamp(0n),
diff --git a/tools/winscope/src/viewers/common/log_viewer_presenter_test.ts b/tools/winscope/src/viewers/common/log_viewer_presenter_test.ts
index e123abb..7765a5b 100644
--- a/tools/winscope/src/viewers/common/log_viewer_presenter_test.ts
+++ b/tools/winscope/src/viewers/common/log_viewer_presenter_test.ts
@@ -538,7 +538,7 @@
});
it('is robust to empty trace', async () => {
- const trace = makeEmptyTrace(TraceType.TRANSACTIONS);
+ const trace = makeEmptyTrace(TraceType.TRANSITION);
const presenter = new MockPresenter(
trace,
new InMemoryStorage(),
diff --git a/tools/winscope/src/viewers/common/properties_presenter.ts b/tools/winscope/src/viewers/common/properties_presenter.ts
index 6df2f62..baa41cd 100644
--- a/tools/winscope/src/viewers/common/properties_presenter.ts
+++ b/tools/winscope/src/viewers/common/properties_presenter.ts
@@ -135,7 +135,7 @@
}
const predicatesKeepingChildren = [this.propertiesFilter];
- const predicatesDiscardingChildren = [];
+ const predicatesDiscardingChildren = [UiTreeUtils.isNotFromTP];
if (this.propertiesDenylist) {
predicatesDiscardingChildren.push(
diff --git a/tools/winscope/src/viewers/common/ui_data_log.ts b/tools/winscope/src/viewers/common/ui_data_log.ts
index d22a8bb..33e4d58 100644
--- a/tools/winscope/src/viewers/common/ui_data_log.ts
+++ b/tools/winscope/src/viewers/common/ui_data_log.ts
@@ -16,6 +16,7 @@
import {Timestamp} from 'common/time/time';
import {TraceEntry} from 'trace/trace';
+import {LazyPropertiesStrategyType} from 'trace/tree_node/properties_provider';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {TextFilter} from 'viewers/common/text_filter';
import {UserOptions} from 'viewers/common/user_options';
@@ -39,6 +40,7 @@
export interface ColumnSpec {
name: string;
cssClass: string;
+ columnType?: number;
canCopy?: boolean;
}
@@ -50,6 +52,7 @@
traceEntry: TraceEntry<object>;
fields: LogField[];
propertiesTree?: undefined | PropertyTreeNode;
+ getPropertiesTree?: LazyPropertiesStrategyType | undefined;
}
export interface LogField {
diff --git a/tools/winscope/src/viewers/common/ui_tree_utils.ts b/tools/winscope/src/viewers/common/ui_tree_utils.ts
index 46208ad..7662fdd 100644
--- a/tools/winscope/src/viewers/common/ui_tree_utils.ts
+++ b/tools/winscope/src/viewers/common/ui_tree_utils.ts
@@ -55,6 +55,12 @@
);
};
+ static isNotFromTP: TreeNodeFilter = (node: TreeNode) => {
+ return (
+ node instanceof UiPropertyTreeNode && node.source !== PropertySource.TP
+ );
+ };
+
static makeNodeFilter(predicate: StringFilterPredicate): TreeNodeFilter {
return (node: TreeNode) => {
return (
diff --git a/tools/winscope/src/viewers/viewer_input/presenter.ts b/tools/winscope/src/viewers/viewer_input/presenter.ts
index e7ffb3e..897629e 100644
--- a/tools/winscope/src/viewers/viewer_input/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_input/presenter.ts
@@ -238,7 +238,7 @@
return uniqueFieldValues;
}
- protected override updateFiltersInHeaders(
+ protected override async updateFiltersInHeaders(
headers: LogHeader[],
entries: LogEntry[],
) {
diff --git a/tools/winscope/src/viewers/viewer_protolog/presenter.ts b/tools/winscope/src/viewers/viewer_protolog/presenter.ts
index ba4d957..b4d190e 100644
--- a/tools/winscope/src/viewers/viewer_protolog/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_protolog/presenter.ts
@@ -121,7 +121,7 @@
return messages;
}
- protected override updateFiltersInHeaders(
+ protected override async updateFiltersInHeaders(
headers: LogHeader[],
allEntries: ProtologEntry[],
) {
diff --git a/tools/winscope/src/viewers/viewer_transactions/operations/set_root_display_name.ts b/tools/winscope/src/viewers/viewer_transactions/operations/set_root_display_name.ts
deleted file mode 100644
index e7d0ab9..0000000
--- a/tools/winscope/src/viewers/viewer_transactions/operations/set_root_display_name.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2024 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 {Operation} from 'trace/tree_node/operations/operation';
-import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
-
-export class SetRootDisplayNames implements Operation<UiPropertyTreeNode> {
- apply(node: UiPropertyTreeNode): void {
- if (node.id.includes('layerChanges')) {
- node.setDisplayName('LayerState');
- return;
- }
-
- if (
- node.id.includes('displayChanges') ||
- node.id.includes('addedDisplays')
- ) {
- node.setDisplayName('DisplayState');
- return;
- }
-
- if (node.id.includes('addedLayers')) {
- node.setDisplayName('LayerCreationArgs');
- return;
- }
-
- if (node.id.includes('destroyedLayers')) {
- node.setDisplayName('destroyedLayerId');
- return;
- }
-
- if (node.id.includes('removedDisplays')) {
- node.setDisplayName('removedDisplayId');
- return;
- }
-
- if (node.id.includes('destroyedLayerHandles')) {
- node.setDisplayName('destroyedLayerHandleId');
- return;
- }
- }
-}
diff --git a/tools/winscope/src/viewers/viewer_transactions/operations/set_root_display_name_test.ts b/tools/winscope/src/viewers/viewer_transactions/operations/set_root_display_name_test.ts
deleted file mode 100644
index 4b20f9a..0000000
--- a/tools/winscope/src/viewers/viewer_transactions/operations/set_root_display_name_test.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2024 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 {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
-import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
-import {SetRootDisplayNames} from './set_root_display_name';
-
-describe('SetRootDisplayNames', () => {
- let operation: SetRootDisplayNames;
-
- beforeEach(() => {
- operation = new SetRootDisplayNames();
- });
-
- it('sets display name LayerState', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder().setRootId('layerChanges').setName('0').build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('LayerState');
- });
-
- it('sets display name DisplayState for change in display', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder()
- .setRootId('displayChanges')
- .setName('0')
- .build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('DisplayState');
- });
-
- it('sets display name DisplayState for added display', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder().setRootId('addedDisplays').setName('0').build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('DisplayState');
- });
-
- it('sets display name LayerCreationArgs', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder().setRootId('addedLayers').setName('0').build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('LayerCreationArgs');
- });
-
- it('sets display name destroyedLayerId', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder()
- .setRootId('destroyedLayers')
- .setName('0')
- .build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('destroyedLayerId');
- });
-
- it('sets display name removedDisplayId', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder()
- .setRootId('removedDisplays')
- .setName('0')
- .build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('removedDisplayId');
- });
-
- it('sets display name destroyedLayerHandleId', () => {
- const propertyRoot = UiPropertyTreeNode.from(
- new PropertyTreeBuilder()
- .setRootId('destroyedLayerHandles')
- .setName('0')
- .build(),
- );
-
- operation.apply(propertyRoot);
- expect(propertyRoot.getDisplayName()).toEqual('destroyedLayerHandleId');
- });
-});
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter.ts b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
index 3a9dba01..057ec6e 100644
--- a/tools/winscope/src/viewers/viewer_transactions/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
@@ -17,7 +17,11 @@
import {assertDefined} from 'common/assert_utils';
import {PersistentStoreProxy} from 'common/store/persistent_store_proxy';
import {Store} from 'common/store/store';
+import {TransactionType} from 'parsers/transactions/transaction_type';
import {Trace} from 'trace/trace';
+import {TransactionColumnType} from 'trace/transaction_column_type';
+import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
+import {LazyPropertiesStrategyType} from 'trace/tree_node/properties_provider';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {
AbstractLogViewerPresenter,
@@ -29,24 +33,48 @@
import {TextFilter} from 'viewers/common/text_filter';
import {LogField, LogHeader} from 'viewers/common/ui_data_log';
import {UserOptions} from 'viewers/common/user_options';
-import {SetRootDisplayNames} from './operations/set_root_display_name';
-import {TransactionsEntry, TransactionsEntryType, UiData} from './ui_data';
+import {TransactionsEntry, UiData} from './ui_data';
export class Presenter extends AbstractLogViewerPresenter<
UiData,
- PropertyTreeNode
+ HierarchyTreeNode
> {
private static readonly COLUMNS = {
- id: {name: 'TX ID', cssClass: 'transaction-id right-align'},
- vsyncId: {name: 'VSYNC ID', cssClass: 'vsyncid right-align'},
- pid: {name: 'PID', cssClass: 'pid right-align'},
- uid: {name: 'UID', cssClass: 'uid right-align'},
- type: {name: 'TYPE', cssClass: 'transaction-type'},
+ id: {
+ name: 'TX ID',
+ cssClass: 'transaction-id right-align',
+ columnType: TransactionColumnType.TRANSACTION_ID,
+ },
+ vsyncId: {
+ name: 'VSYNC ID',
+ cssClass: 'vsyncid right-align',
+ columnType: TransactionColumnType.VSYNC_ID,
+ },
+ pid: {
+ name: 'PID',
+ cssClass: 'pid right-align',
+ columnType: TransactionColumnType.PID,
+ },
+ uid: {
+ name: 'UID',
+ cssClass: 'uid right-align',
+ columnType: TransactionColumnType.UID,
+ },
+ type: {
+ name: 'TYPE',
+ cssClass: 'transaction-type',
+ columnType: TransactionColumnType.TRANSACTION_TYPE,
+ },
layerOrDisplayId: {
name: 'LAYER/DISP ID',
cssClass: 'layer-or-display-id right-align',
+ columnType: TransactionColumnType.LAYER_OR_DISPLAY_ID,
},
- flags: {name: 'Flags', cssClass: 'flags'},
+ flags: {
+ name: 'Flags',
+ cssClass: 'flags',
+ columnType: TransactionColumnType.FLAGS,
+ },
};
protected override keepCalculated = true;
@@ -69,11 +97,10 @@
),
new TextFilter(),
[],
- [new SetRootDisplayNames()],
);
constructor(
- trace: Trace<PropertyTreeNode>,
+ trace: Trace<HierarchyTreeNode>,
readonly storage: Store,
notifyViewCallback: NotifyLogViewCallbackType<UiData>,
) {
@@ -148,288 +175,84 @@
) {
const entry = this.trace.getEntry(traceIndex);
const entryNode = entryProtos[traceIndex];
- const vsyncId = Number(
- assertDefined(entryNode.getChildByName('vsyncId')).getValue(),
- );
+ const vsyncId = entryNode.getEagerPropertyByName('vsyncId')?.getValue();
- for (const transactionState of assertDefined(
- entryNode.getChildByName('transactions'),
- ).getAllChildren()) {
- const transactionId = assertDefined(
- transactionState.getChildByName('transactionId'),
+ for (const transactionNode of entryNode.getAllChildren()) {
+ const transactionType = assertDefined(
+ transactionNode.getEagerPropertyByName('transactionType'),
).formattedValue();
- const pid = assertDefined(
- transactionState.getChildByName('pid'),
- ).formattedValue();
- const uid = assertDefined(
- transactionState.getChildByName('uid'),
- ).formattedValue();
- const layerChanges = assertDefined(
- transactionState.getChildByName('layerChanges'),
- ).getAllChildren();
+ const transactionId = transactionNode
+ .getEagerPropertyByName('transactionId')
+ ?.formattedValue();
+ const pid = transactionNode
+ .getEagerPropertyByName('pid')
+ ?.formattedValue();
+ const uid = transactionNode
+ .getEagerPropertyByName('uid')
+ ?.formattedValue();
+ const layerId = transactionNode
+ .getEagerPropertyByName('layerId')
+ ?.formattedValue();
+ const displayId = transactionNode
+ .getEagerPropertyByName('displayId')
+ ?.formattedValue();
+ const flags = transactionNode
+ .getEagerPropertyByName('flagsId')
+ ?.formattedValue();
- for (const layerState of layerChanges) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: transactionId},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: pid},
- {spec: Presenter.COLUMNS.uid, value: uid},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.LAYER_CHANGED,
- },
- {
- spec: Presenter.COLUMNS.layerOrDisplayId,
- value: assertDefined(
- layerState.getChildByName('layerId'),
- ).formattedValue(),
- },
- {
- spec: Presenter.COLUMNS.flags,
- value: assertDefined(
- layerState.getChildByName('what'),
- ).formattedValue(),
- },
- ];
- entries.push(new TransactionsEntry(entry, fields, layerState));
+ let getPropertiesTree: LazyPropertiesStrategyType | undefined;
+ switch (transactionType) {
+ case TransactionType.LAYER_CHANGED:
+ case TransactionType.DISPLAY_CHANGED:
+ case TransactionType.LAYER_ADDED:
+ case TransactionType.DISPLAY_ADDED:
+ getPropertiesTree = async () => {
+ return await transactionNode.getAllProperties();
+ };
+ break;
+
+ default:
+ // do nothing
+ break;
}
- const displayChanges = assertDefined(
- transactionState.getChildByName('displayChanges'),
- ).getAllChildren();
- for (const displayState of displayChanges) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: transactionId},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: pid},
- {spec: Presenter.COLUMNS.uid, value: uid},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.DISPLAY_CHANGED,
- },
- {
- spec: Presenter.COLUMNS.layerOrDisplayId,
- value: assertDefined(
- displayState.getChildByName('id'),
- ).formattedValue(),
- },
- {
- spec: Presenter.COLUMNS.flags,
- value: assertDefined(
- displayState.getChildByName('what'),
- ).formattedValue(),
- },
- ];
- entries.push(new TransactionsEntry(entry, fields, displayState));
- }
+ const layerOrDisplayId =
+ (layerId?.length ?? 0) > 0
+ ? assertDefined(layerId)
+ : displayId ?? Presenter.VALUE_NA;
- if (layerChanges.length === 0 && displayChanges.length === 0) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: transactionId},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: pid},
- {spec: Presenter.COLUMNS.uid, value: uid},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.NO_OP,
- },
- {spec: Presenter.COLUMNS.layerOrDisplayId, value: ''},
- {spec: Presenter.COLUMNS.flags, value: ''},
- ];
- entries.push(new TransactionsEntry(entry, fields, undefined));
- }
- }
-
- for (const layerCreationArgs of assertDefined(
- entryNode.getChildByName('addedLayers'),
- ).getAllChildren()) {
const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: ''},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: Presenter.VALUE_NA},
- {spec: Presenter.COLUMNS.uid, value: Presenter.VALUE_NA},
+ {
+ spec: Presenter.COLUMNS.id,
+ value: transactionId ?? Presenter.VALUE_NA,
+ },
+ {spec: Presenter.COLUMNS.vsyncId, value: assertDefined(vsyncId)},
+ {spec: Presenter.COLUMNS.pid, value: pid ?? Presenter.VALUE_NA},
+ {spec: Presenter.COLUMNS.uid, value: uid ?? Presenter.VALUE_NA},
{
spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.LAYER_ADDED,
+ value: transactionType,
},
{
spec: Presenter.COLUMNS.layerOrDisplayId,
- value: assertDefined(
- layerCreationArgs.getChildByName('layerId'),
- ).formattedValue(),
- },
- {spec: Presenter.COLUMNS.flags, value: ''},
- ];
- entries.push(new TransactionsEntry(entry, fields, layerCreationArgs));
- }
-
- for (const destroyedLayerId of assertDefined(
- entryNode.getChildByName('destroyedLayers'),
- ).getAllChildren()) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: ''},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: Presenter.VALUE_NA},
- {spec: Presenter.COLUMNS.uid, value: Presenter.VALUE_NA},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.LAYER_DESTROYED,
- },
- {
- spec: Presenter.COLUMNS.layerOrDisplayId,
- value: destroyedLayerId.formattedValue(),
- },
- {spec: Presenter.COLUMNS.flags, value: ''},
- ];
- entries.push(new TransactionsEntry(entry, fields, destroyedLayerId));
- }
-
- for (const displayState of assertDefined(
- entryNode.getChildByName('addedDisplays'),
- ).getAllChildren()) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: ''},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: Presenter.VALUE_NA},
- {spec: Presenter.COLUMNS.uid, value: Presenter.VALUE_NA},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.DISPLAY_ADDED,
- },
- {
- spec: Presenter.COLUMNS.layerOrDisplayId,
- value: assertDefined(
- displayState.getChildByName('id'),
- ).formattedValue(),
+ value: layerOrDisplayId,
},
{
spec: Presenter.COLUMNS.flags,
- value: assertDefined(
- displayState.getChildByName('what'),
- ).formattedValue(),
+ value: flags ?? Presenter.VALUE_NA,
},
];
- entries.push(new TransactionsEntry(entry, fields, displayState));
- }
-
- for (const removedDisplayId of assertDefined(
- entryNode.getChildByName('removedDisplays'),
- ).getAllChildren()) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: ''},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: Presenter.VALUE_NA},
- {spec: Presenter.COLUMNS.uid, value: Presenter.VALUE_NA},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.DISPLAY_REMOVED,
- },
- {
- spec: Presenter.COLUMNS.layerOrDisplayId,
- value: removedDisplayId.formattedValue(),
- },
- {spec: Presenter.COLUMNS.flags, value: ''},
- ];
- entries.push(new TransactionsEntry(entry, fields, removedDisplayId));
- }
-
- for (const destroyedLayerHandleId of assertDefined(
- entryNode.getChildByName('destroyedLayerHandles'),
- ).getAllChildren()) {
- const fields: LogField[] = [
- {spec: Presenter.COLUMNS.id, value: ''},
- {spec: Presenter.COLUMNS.vsyncId, value: vsyncId},
- {spec: Presenter.COLUMNS.pid, value: Presenter.VALUE_NA},
- {spec: Presenter.COLUMNS.uid, value: Presenter.VALUE_NA},
- {
- spec: Presenter.COLUMNS.type,
- value: TransactionsEntryType.LAYER_HANDLE_DESTROYED,
- },
- {
- spec: Presenter.COLUMNS.layerOrDisplayId,
- value: destroyedLayerHandleId.formattedValue(),
- },
- {spec: Presenter.COLUMNS.flags, value: ''},
- ];
- entries.push(
- new TransactionsEntry(entry, fields, destroyedLayerHandleId),
- );
+ entries.push(new TransactionsEntry(entry, fields, getPropertiesTree));
}
}
-
return entries;
}
- protected override updateFiltersInHeaders(
- headers: LogHeader[],
- allEntries: TransactionsEntry[],
- ) {
+ protected override async updateFiltersInHeaders(headers: LogHeader[]) {
for (const header of headers) {
- if (header.spec === Presenter.COLUMNS.flags) {
- (assertDefined(header.filter) as LogSelectFilter).options =
- this.getUniqueUiDataEntryValues(
- allEntries,
- (entry: TransactionsEntry) =>
- assertDefined(
- entry.fields.find((f) => f.spec === header.spec)
- ?.value as string,
- )
- .split('|')
- .map((flag) => flag.trim()),
- );
- } else {
- (assertDefined(header.filter) as LogSelectFilter).options =
- this.getUniqueUiDataEntryValues(
- allEntries,
- (entry: TransactionsEntry) =>
- assertDefined(
- entry.fields.find((f) => f.spec === header.spec),
- ).value.toString(),
- );
- }
+ this.updateFilterByCustomQuery(header);
}
}
-
- private getUniqueUiDataEntryValues<T>(
- entries: TransactionsEntry[],
- getValue: (entry: TransactionsEntry) => T | T[],
- ): T[] {
- const uniqueValues = new Set<T>();
- entries.forEach((entry: TransactionsEntry) => {
- const value = getValue(entry);
- if (Array.isArray(value)) {
- value.forEach((val) => uniqueValues.add(val));
- } else {
- uniqueValues.add(value);
- }
- });
-
- const result = [...uniqueValues];
-
- result.sort((a, b) => {
- const aIsNumber = !isNaN(Number(a));
- const bIsNumber = !isNaN(Number(b));
-
- if (aIsNumber && bIsNumber) {
- return Number(a) - Number(b);
- } else if (aIsNumber) {
- return 1; // place number after strings in the result
- } else if (bIsNumber) {
- return -1; // place number after strings in the result
- }
-
- // a and b are both strings
- if (a < b) {
- return -1;
- } else if (a > b) {
- return 1;
- } else {
- return 0;
- }
- });
-
- return result;
- }
}
const layerChangeFlagToPropertiesMap = new Map([
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
index 28bc0ee..b2bdfd7 100644
--- a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
@@ -23,7 +23,8 @@
import {makeEmptyTrace} from 'test/unit/trace_utils';
import {Trace} from 'trace/trace';
import {TraceType} from 'trace/trace_type';
-import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {TransactionColumnType} from 'trace/transaction_column_type';
+import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
import {NotifyLogViewCallbackType} from 'viewers/common/abstract_log_viewer_presenter';
import {AbstractLogViewerPresenterTest} from 'viewers/common/abstract_log_viewer_presenter_test';
import {LogSelectFilter} from 'viewers/common/log_filters';
@@ -35,33 +36,53 @@
override readonly expectedHeaders = [
{
header: new LogHeader(
- {name: 'TX ID', cssClass: 'transaction-id right-align'},
+ {
+ name: 'TX ID',
+ cssClass: 'transaction-id right-align',
+ columnType: TransactionColumnType.TRANSACTION_ID,
+ },
new LogSelectFilter(Array.from({length: 1295}, () => '')),
),
},
{
header: new LogHeader(
- {name: 'VSYNC ID', cssClass: 'vsyncid right-align'},
- new LogSelectFilter(Array.from({length: 710}, () => '')),
+ {
+ name: 'VSYNC ID',
+ cssClass: 'vsyncid right-align',
+ columnType: TransactionColumnType.VSYNC_ID,
+ },
+ new LogSelectFilter(Array.from({length: 712}, () => '')),
),
},
{
header: new LogHeader(
- {name: 'PID', cssClass: 'pid right-align'},
+ {
+ name: 'PID',
+ cssClass: 'pid right-align',
+ columnType: TransactionColumnType.PID,
+ },
new LogSelectFilter(Array.from({length: 8}, () => '')),
),
options: ['N/A', '0', '515', '1593', '2022', '2322', '2463', '3300'],
},
{
header: new LogHeader(
- {name: 'UID', cssClass: 'uid right-align'},
+ {
+ name: 'UID',
+ cssClass: 'uid right-align',
+ columnType: TransactionColumnType.UID,
+ },
new LogSelectFilter(Array.from({length: 6}, () => '')),
),
options: ['N/A', '1000', '1003', '10169', '10235', '10239'],
},
{
header: new LogHeader(
- {name: 'TYPE', cssClass: 'transaction-type'},
+ {
+ name: 'TYPE',
+ cssClass: 'transaction-type',
+ columnType: TransactionColumnType.TRANSACTION_TYPE,
+ },
new LogSelectFilter(Array.from({length: 6}, () => '')),
),
options: [
@@ -70,28 +91,72 @@
'LAYER_CHANGED',
'LAYER_DESTROYED',
'LAYER_HANDLE_DESTROYED',
- 'NO_OP',
+ 'NOOP',
],
},
{
header: new LogHeader(
- {name: 'LAYER/DISP ID', cssClass: 'layer-or-display-id right-align'},
- new LogSelectFilter(Array.from({length: 117}, () => '')),
+ {
+ name: 'LAYER/DISP ID',
+ cssClass: 'layer-or-display-id right-align',
+ columnType: TransactionColumnType.LAYER_OR_DISPLAY_ID,
+ },
+ new LogSelectFilter(Array.from({length: 116}, () => '')),
),
+ options: [
+ 'N/A',
+ ...Array.from({length: 114}, (_, i) => `${i + 1}`),
+ '4294967295',
+ ],
},
{
header: new LogHeader(
- {name: 'Flags', cssClass: 'flags'},
+ {
+ name: 'Flags',
+ cssClass: 'flags',
+ columnType: TransactionColumnType.FLAGS,
+ },
new LogSelectFilter(
- Array.from({length: 30}, () => ''),
+ Array.from({length: 29}, () => ''),
true,
'250',
'100%',
),
),
+ options: [
+ 'eAcquireFenceChanged',
+ 'eAlphaChanged',
+ 'eAutoRefreshChanged',
+ 'eBackgroundBlurRadiusChanged',
+ 'eBufferChanged',
+ 'eBufferCropChanged',
+ 'eBufferTransformChanged',
+ 'eColorChanged',
+ 'eColorSpaceAgnosticChanged',
+ 'eCornerRadiusChanged',
+ 'eCropChanged',
+ 'eDataspaceChanged',
+ 'eDestinationFrameChanged',
+ 'eDisplayProjectionChanged',
+ 'eFlagsChanged',
+ 'eFrameRateSelectionPriority',
+ 'eHasListenerCallbacksChanged',
+ 'eHdrMetadataChanged',
+ 'eInputInfoChanged',
+ 'eLayerChanged',
+ 'eLayerStackChanged',
+ 'eMatrixChanged',
+ 'eMetadataChanged',
+ 'ePositionChanged',
+ 'eProducerDisconnect',
+ 'eRelativeLayerChanged',
+ 'eReparent',
+ 'eSurfaceDamageRegionChanged',
+ 'eTransformToDisplayInverseChanged',
+ ],
},
];
- private trace: Trace<PropertyTreeNode> | undefined;
+ private trace: Trace<HierarchyTreeNode> | undefined;
private positionUpdate: TracePositionUpdate | undefined;
override executeSpecializedTests() {
@@ -162,8 +227,8 @@
const parser = await new LegacyParserProvider()
.addFilename('traces/elapsed_and_real_timestamp/Transactions.pb')
.setConvertToPerfetto(true)
- .getParser<PropertyTreeNode>();
- this.trace = new TraceBuilder<PropertyTreeNode>()
+ .getParser<HierarchyTreeNode>();
+ this.trace = new TraceBuilder<HierarchyTreeNode>()
.setType(TraceType.TRANSACTIONS)
.setParser(parser)
.build();
diff --git a/tools/winscope/src/viewers/viewer_transactions/ui_data.ts b/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
index f5bfe1a..2646cde 100644
--- a/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/ui_data.ts
@@ -15,7 +15,8 @@
*/
import {TraceEntry} from 'trace/trace';
-import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
+import {LazyPropertiesStrategyType} from 'trace/tree_node/properties_provider';
import {
LogEntry,
LogField,
@@ -45,19 +46,8 @@
export class TransactionsEntry implements LogEntry {
constructor(
- public traceEntry: TraceEntry<PropertyTreeNode>,
+ public traceEntry: TraceEntry<HierarchyTreeNode>,
public fields: LogField[],
- public propertiesTree: PropertyTreeNode | undefined,
+ public getPropertiesTree: LazyPropertiesStrategyType | undefined,
) {}
}
-
-export enum TransactionsEntryType {
- DISPLAY_ADDED = 'DISPLAY_ADDED',
- DISPLAY_REMOVED = 'DISPLAY_REMOVED',
- DISPLAY_CHANGED = 'DISPLAY_CHANGED',
- LAYER_ADDED = 'LAYER_ADDED',
- LAYER_DESTROYED = 'LAYER_DESTROYED',
- LAYER_CHANGED = 'LAYER_CHANGED',
- LAYER_HANDLE_DESTROYED = 'LAYER_HANDLE_DESTROYED',
- NO_OP = 'NO_OP',
-}
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
index ce1f62e..ed0244c 100644
--- a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
@@ -18,21 +18,21 @@
import {Trace} from 'trace/trace';
import {Traces} from 'trace/traces';
import {TraceType} from 'trace/trace_type';
-import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
import {AbstractViewer} from 'viewers/abstract_viewer';
import {ViewerComponent} from 'viewers/components/viewer_component';
import {Presenter} from './presenter';
import {UiData} from './ui_data';
-export class ViewerTransactions extends AbstractViewer<PropertyTreeNode> {
+export class ViewerTransactions extends AbstractViewer<HierarchyTreeNode> {
static readonly DEPENDENCIES: TraceType[] = [TraceType.TRANSACTIONS];
- constructor(trace: Trace<PropertyTreeNode>, traces: Traces, store: Store) {
+ constructor(trace: Trace<HierarchyTreeNode>, traces: Traces, store: Store) {
super(trace, traces, 'viewer-transactions', store);
}
protected override initializePresenter(
- trace: Trace<PropertyTreeNode>,
+ trace: Trace<HierarchyTreeNode>,
traces: Traces,
store: Store,
): Presenter {
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts
index d1b75fb..864ed53 100644
--- a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component.ts
@@ -51,7 +51,7 @@
[traceType]="${TraceType.TRANSACTIONS}"
[isProtoDump]="false"
[textFilter]="inputData?.propertiesFilter"
- placeholderText="No current or selected transaction."
+ placeholderText="No current or selected transaction with additional properties."
(collapseButtonClicked)="sections.onCollapseStateChange(CollapsibleSectionType.PROPERTIES, true)"
[class.collapsed]="sections.isSectionCollapsed(CollapsibleSectionType.PROPERTIES)"></properties-view>
</div>
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
index 4a0a3f6..0f9c87a 100644
--- a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
@@ -17,10 +17,11 @@
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
import {TimestampConverterUtils} from 'common/time/test_utils';
import {DOMTestHelper} from 'test/unit/dom_test_utils';
+import {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TraceType} from 'trace/trace_type';
-import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
import {AbstractLogViewerComponentTest} from 'viewers/common/abstract_log_viewer_component_test';
import {LogSelectFilter} from 'viewers/common/log_filters';
import {LogHeader} from 'viewers/common/ui_data_log';
@@ -36,7 +37,7 @@
protected override readonly propertiesSectionTitle =
'PROPERTIES - PROTO DUMP';
protected override readonly propertiesPlaceholder =
- 'No current or selected transaction.';
+ 'No current or selected transaction with additional properties.';
protected override checkTimestampInTable(
dom: DOMTestHelper<ViewerTransactionsComponent>,
@@ -52,6 +53,11 @@
ViewerTransactionsComponent,
]
> {
+ const hierarchyTree = new HierarchyTreeBuilder()
+ .setId('Transactions')
+ .setName('tree')
+ .build();
+
const propertiesTree = new PropertyTreeBuilder()
.setRootId('Transactions')
.setName('tree')
@@ -60,15 +66,15 @@
const ts = TimestampConverterUtils.makeElapsedTimestamp(1n);
- const trace = new TraceBuilder<PropertyTreeNode>()
- .setEntries([propertiesTree, propertiesTree])
+ const trace = new TraceBuilder<HierarchyTreeNode>()
+ .setEntries([hierarchyTree, hierarchyTree])
.setTimestamps([ts, ts])
.build();
const entry1 = new TransactionsEntry(
trace.getEntry(0),
Array.from({length: 7}, () => this.testField),
- propertiesTree,
+ async () => propertiesTree,
);
const uiData = new UiData(
@@ -88,6 +94,11 @@
protected override async setUpTestEnvironmentForScroll(): Promise<
[DOMTestHelper<ViewerTransactionsComponent>, CdkVirtualScrollViewport]
> {
+ const hierarchyTree = new HierarchyTreeBuilder()
+ .setId('Transactions')
+ .setName('tree')
+ .build();
+
const propertiesTree = new PropertyTreeBuilder()
.setRootId('Transactions')
.setName('tree')
@@ -96,9 +107,9 @@
const ts = TimestampConverterUtils.makeElapsedTimestamp(1n);
- const trace = new TraceBuilder<PropertyTreeNode>()
+ const trace = new TraceBuilder<HierarchyTreeNode>()
.setType(TraceType.TRANSACTIONS)
- .setEntries([propertiesTree, propertiesTree])
+ .setEntries([hierarchyTree, hierarchyTree])
.setTimestamps([ts, ts])
.build();
@@ -124,7 +135,7 @@
value: i % 2 === 0 ? shortMessage : longMessage,
},
]),
- propertiesTree,
+ async () => propertiesTree,
);
uiData.entries.push(entry);
}
diff --git a/tools/winscope/src/viewers/viewer_transitions/presenter.ts b/tools/winscope/src/viewers/viewer_transitions/presenter.ts
index 210ae45..301e0dd 100644
--- a/tools/winscope/src/viewers/viewer_transitions/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/presenter.ts
@@ -141,7 +141,7 @@
return transitions;
}
- protected override updateFiltersInHeaders(headers: LogHeader[]) {
+ protected override async updateFiltersInHeaders(headers: LogHeader[]) {
headers.forEach((header) => {
if (!(header.filter instanceof LogSelectFilter)) return;
header.filter.options = this.getUniqueUiDataEntryValues(header.spec);