Merge "Add Microsoft press link"
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index d402e1f..70c47fc 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -55,6 +55,7 @@
"../../protos/perfetto/common:cpp",
"../../protos/perfetto/config:cpp",
"../../protos/perfetto/config/ftrace:cpp",
+ "../../protos/perfetto/config/sys_stats:cpp",
"../android_stats",
"../base",
"../base:version",
diff --git a/src/perfetto_cmd/config.cc b/src/perfetto_cmd/config.cc
index 56a8f06..1313dcf 100644
--- a/src/perfetto_cmd/config.cc
+++ b/src/perfetto_cmd/config.cc
@@ -24,6 +24,7 @@
#include "perfetto/tracing/core/trace_config.h"
#include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+#include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
namespace perfetto {
namespace {
@@ -132,6 +133,18 @@
frame_timeline->mutable_config()->set_name(
"android.surfaceflinger.frametimeline");
}
+
+ // For the disk category, add the diskstat data source
+ // to figure out disk io statistics.
+ if (category == "disk") {
+ protos::gen::SysStatsConfig cfg;
+ cfg.set_diskstat_period_ms(1000);
+
+ auto* sys_stats_ds = config->add_data_sources();
+ sys_stats_ds->mutable_config()->set_name("linux.sys_stats");
+ sys_stats_ds->mutable_config()->set_sys_stats_config_raw(
+ cfg.SerializeAsString());
+ }
}
config->set_duration_ms(static_cast<unsigned int>(duration_ms));
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index 5470643..ccf4776 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -792,7 +792,7 @@
user-select: none;
}
-.pivot-table-redux {
+.pivot-table {
user-select: text;
padding: 10px;
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index 880b11d..32568d3 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -29,3 +29,4 @@
@import "widgets/button";
@import "widgets/checkbox";
@import "widgets/text_input";
+@import "widgets/empty_state";
diff --git a/ui/src/assets/widgets/empty_state.scss b/ui/src/assets/widgets/empty_state.scss
new file mode 100644
index 0000000..46ab9bc
--- /dev/null
+++ b/ui/src/assets/widgets/empty_state.scss
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 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 "theme";
+
+.pf-empty-state {
+ display: inline-flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 10px;
+ user-select: none;
+
+ & > i {
+ margin: auto;
+ font-size: 5em; // Size of the icon is relative to the font size
+ color: $pf-minimal-foreground;
+ margin-bottom: 10px;
+ }
+
+ .pf-empty-state-header {
+ margin-bottom: 10px;
+ color: $pf-minimal-foreground;
+ }
+
+ .pf-empty-state-detail {
+ margin: auto;
+ }
+}
diff --git a/ui/src/assets/widgets_page.scss b/ui/src/assets/widgets_page.scss
index 653c6a0..beff40c 100644
--- a/ui/src/assets/widgets_page.scss
+++ b/ui/src/assets/widgets_page.scss
@@ -15,6 +15,7 @@
.widgets-page {
padding: 20px;
font-size: 16px;
+ overflow: auto;
h1 {
margin: 32px 0 0 0;
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 2909c73..38e9d44 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -23,7 +23,7 @@
TableColumn,
tableColumnEquals,
toggleEnabled,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_types';
import {randomColor} from './colorizer';
import {
@@ -44,7 +44,7 @@
LogsPagination,
NewEngineMode,
OmniboxState,
- PivotTableReduxResult,
+ PivotTableResult,
PrimaryTrackSortKey,
ProfileType,
RecordingTarget,
@@ -1031,25 +1031,23 @@
}
},
- togglePivotTableRedux(state: StateDraft, args: {areaId: string|null}) {
- state.nonSerializableState.pivotTableRedux.selectionArea =
- args.areaId === null ?
+ togglePivotTable(state: StateDraft, args: {areaId: string|null}) {
+ state.nonSerializableState.pivotTable.selectionArea = args.areaId === null ?
undefined :
{areaId: args.areaId, tracks: globals.state.areas[args.areaId].tracks};
if (args.areaId !==
- state.nonSerializableState.pivotTableRedux.selectionArea?.areaId) {
- state.nonSerializableState.pivotTableRedux.queryResult = null;
+ state.nonSerializableState.pivotTable.selectionArea?.areaId) {
+ state.nonSerializableState.pivotTable.queryResult = null;
}
},
setPivotStateQueryResult(
- state: StateDraft, args: {queryResult: PivotTableReduxResult|null}) {
- state.nonSerializableState.pivotTableRedux.queryResult = args.queryResult;
+ state: StateDraft, args: {queryResult: PivotTableResult|null}) {
+ state.nonSerializableState.pivotTable.queryResult = args.queryResult;
},
- setPivotTableReduxConstrainToArea(
- state: StateDraft, args: {constrain: boolean}) {
- state.nonSerializableState.pivotTableRedux.constrainToArea = args.constrain;
+ setPivotTableConstrainToArea(state: StateDraft, args: {constrain: boolean}) {
+ state.nonSerializableState.pivotTable.constrainToArea = args.constrain;
},
dismissFlamegraphModal(state: StateDraft, _: {}) {
@@ -1058,41 +1056,40 @@
addPivotTableAggregation(
state: StateDraft, args: {aggregation: Aggregation, after: number}) {
- state.nonSerializableState.pivotTableRedux.selectedAggregations.splice(
+ state.nonSerializableState.pivotTable.selectedAggregations.splice(
args.after, 0, args.aggregation);
},
removePivotTableAggregation(state: StateDraft, args: {index: number}) {
- state.nonSerializableState.pivotTableRedux.selectedAggregations.splice(
+ state.nonSerializableState.pivotTable.selectedAggregations.splice(
args.index, 1);
},
setPivotTableQueryRequested(
state: StateDraft, args: {queryRequested: boolean}) {
- state.nonSerializableState.pivotTableRedux.queryRequested =
- args.queryRequested;
+ state.nonSerializableState.pivotTable.queryRequested = args.queryRequested;
},
setPivotTablePivotSelected(
state: StateDraft, args: {column: TableColumn, selected: boolean}) {
toggleEnabled(
tableColumnEquals,
- state.nonSerializableState.pivotTableRedux.selectedPivots,
+ state.nonSerializableState.pivotTable.selectedPivots,
args.column,
args.selected);
},
setPivotTableAggregationFunction(
state: StateDraft, args: {index: number, function: AggregationFunction}) {
- state.nonSerializableState.pivotTableRedux.selectedAggregations[args.index]
+ state.nonSerializableState.pivotTable.selectedAggregations[args.index]
.aggregationFunction = args.function;
},
setPivotTableSortColumn(
state: StateDraft,
args: {aggregationIndex: number, order: SortDirection}) {
- state.nonSerializableState.pivotTableRedux.selectedAggregations =
- state.nonSerializableState.pivotTableRedux.selectedAggregations.map(
+ state.nonSerializableState.pivotTable.selectedAggregations =
+ state.nonSerializableState.pivotTable.selectedAggregations.map(
(agg, index) => ({
column: agg.column,
aggregationFunction: agg.aggregationFunction,
@@ -1114,26 +1111,24 @@
setPivotTableArgumentNames(
state: StateDraft, args: {argumentNames: string[]}) {
- state.nonSerializableState.pivotTableRedux.argumentNames =
- args.argumentNames;
+ state.nonSerializableState.pivotTable.argumentNames = args.argumentNames;
},
changePivotTablePivotOrder(
state: StateDraft,
args: {from: number, to: number, direction: DropDirection}) {
- const pivots = state.nonSerializableState.pivotTableRedux.selectedPivots;
- state.nonSerializableState.pivotTableRedux.selectedPivots =
- performReordering(
- computeIntervals(pivots.length, args.from, args.to, args.direction),
- pivots);
+ const pivots = state.nonSerializableState.pivotTable.selectedPivots;
+ state.nonSerializableState.pivotTable.selectedPivots = performReordering(
+ computeIntervals(pivots.length, args.from, args.to, args.direction),
+ pivots);
},
changePivotTableAggregationOrder(
state: StateDraft,
args: {from: number, to: number, direction: DropDirection}) {
const aggregations =
- state.nonSerializableState.pivotTableRedux.selectedAggregations;
- state.nonSerializableState.pivotTableRedux.selectedAggregations =
+ state.nonSerializableState.pivotTable.selectedAggregations;
+ state.nonSerializableState.pivotTable.selectedAggregations =
performReordering(
computeIntervals(
aggregations.length, args.from, args.to, args.direction),
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index 72b7f8b..4267be2 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -15,7 +15,7 @@
import {createEmptyRecordConfig} from '../controller/record_config_types';
import {
Aggregation,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_types';
import {
autosaveConfigStore,
recordTargetStore,
@@ -57,7 +57,7 @@
export function createEmptyNonSerializableState(): NonSerializableState {
return {
- pivotTableRedux: {
+ pivotTable: {
queryResult: null,
selectedPivots: [{kind: 'regular', table: 'slice', column: 'name'}],
selectedAggregations: [
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index b3b9aba..3b978b9 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -18,7 +18,7 @@
PivotTree,
RegularColumn,
TableColumn,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_types';
/**
* A plain js object, holding objects of type |Class| keyed by string id.
@@ -403,41 +403,41 @@
// Auxiliary metadata needed to parse the query result, as well as to render it
// correctly. Generated together with the text of query and passed without the
// change to the query response.
-export interface PivotTableReduxQueryMetadata {
+export interface PivotTableQueryMetadata {
pivotColumns: TableColumn[];
aggregationColumns: Aggregation[];
countIndex: number;
}
// Everything that's necessary to run the query for pivot table
-export interface PivotTableReduxQuery {
+export interface PivotTableQuery {
text: string;
- metadata: PivotTableReduxQueryMetadata;
+ metadata: PivotTableQueryMetadata;
}
// Pivot table query result
-export interface PivotTableReduxResult {
+export interface PivotTableResult {
// Hierarchical pivot structure on top of rows
tree: PivotTree;
// Copy of the query metadata from the request, bundled up with the query
// result to ensure the correct rendering.
- metadata: PivotTableReduxQueryMetadata;
+ metadata: PivotTableQueryMetadata;
}
// Input parameters to check whether the pivot table needs to be re-queried.
-export interface PivotTableReduxAreaState {
+export interface PivotTableAreaState {
areaId: string;
tracks: string[];
}
export type SortDirection = 'DESC'|'ASC';
-export interface PivotTableReduxState {
+export interface PivotTableState {
// Currently selected area, if null, pivot table is not going to be visible.
- selectionArea?: PivotTableReduxAreaState;
+ selectionArea?: PivotTableAreaState;
// Query response
- queryResult: PivotTableReduxResult|null;
+ queryResult: PivotTableResult|null;
// Selected pivots for tables other than slice.
// Because of the query generation, pivoting happens first on non-slice
@@ -477,7 +477,7 @@
LoadedConfigNone|LoadedConfigAutomatic|LoadedConfigNamed;
export interface NonSerializableState {
- pivotTableRedux: PivotTableReduxState;
+ pivotTable: PivotTableState;
}
export interface LogFilteringCriteria {
diff --git a/ui/src/controller/pivot_table_redux_controller.ts b/ui/src/controller/pivot_table_controller.ts
similarity index 90%
rename from ui/src/controller/pivot_table_redux_controller.ts
rename to ui/src/controller/pivot_table_controller.ts
index 6329e81..cc33e3e 100644
--- a/ui/src/controller/pivot_table_redux_controller.ts
+++ b/ui/src/controller/pivot_table_controller.ts
@@ -21,22 +21,22 @@
import {ColumnType, STR} from '../common/query_result';
import {
AreaSelection,
- PivotTableReduxQuery,
- PivotTableReduxQueryMetadata,
- PivotTableReduxResult,
- PivotTableReduxState,
+ PivotTableQuery,
+ PivotTableQueryMetadata,
+ PivotTableResult,
+ PivotTableState,
} from '../common/state';
import {globals} from '../frontend/globals';
import {
aggregationIndex,
generateQueryFromState,
-} from '../frontend/pivot_table_redux_query_generator';
-import {Aggregation, PivotTree} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_query_generator';
+import {Aggregation, PivotTree} from '../frontend/pivot_table_types';
import {Controller} from './controller';
export const PIVOT_TABLE_REDUX_FLAG = featureFlags.register({
- id: 'pivotTableRedux',
+ id: 'pivotTable',
name: 'Pivot tables V2',
description: 'Second version of pivot table',
// Enabled in canary and autopush by default.
@@ -53,7 +53,7 @@
// Auxiliary class to build the tree from query response.
export class PivotTableTreeBuilder {
private readonly root: PivotTree;
- queryMetadata: PivotTableReduxQueryMetadata;
+ queryMetadata: PivotTableQueryMetadata;
get pivotColumnsCount(): number {
return this.queryMetadata.pivotColumns.length;
@@ -63,8 +63,7 @@
return this.queryMetadata.aggregationColumns;
}
- constructor(
- queryMetadata: PivotTableReduxQueryMetadata, firstRow: ColumnType[]) {
+ constructor(queryMetadata: PivotTableQueryMetadata, firstRow: ColumnType[]) {
this.queryMetadata = queryMetadata;
this.root = this.createNode(firstRow);
let tree = this.root;
@@ -163,8 +162,8 @@
}
}
-function createEmptyQueryResult(metadata: PivotTableReduxQueryMetadata):
- PivotTableReduxResult {
+function createEmptyQueryResult(metadata: PivotTableQueryMetadata):
+ PivotTableResult {
return {
tree: {
aggregates: [],
@@ -178,7 +177,7 @@
// Controller responsible for showing the panel with pivot table, as well as
// executing its queries and post-processing query results.
-export class PivotTableReduxController extends Controller<{}> {
+export class PivotTableController extends Controller<{}> {
static detailsCount = 0;
engine: Engine;
lastQueryAreaId = '';
@@ -205,7 +204,7 @@
return true;
}
- shouldRerun(state: PivotTableReduxState, selection: AreaSelection) {
+ shouldRerun(state: PivotTableState, selection: AreaSelection) {
if (state.selectionArea === undefined) {
return false;
}
@@ -220,7 +219,7 @@
return false;
}
- async processQuery(query: PivotTableReduxQuery) {
+ async processQuery(query: PivotTableQuery) {
const result = await this.engine.query(query.text);
try {
await result.waitAllRows();
@@ -256,7 +255,7 @@
globals.dispatch(Actions.setPivotStateQueryResult(
{queryResult: {tree: treeBuilder.build(), metadata: query.metadata}}));
- globals.dispatch(Actions.setCurrentTab({tab: 'pivot_table_redux'}));
+ globals.dispatch(Actions.setCurrentTab({tab: 'pivot_table'}));
}
async requestArgumentNames() {
@@ -285,7 +284,7 @@
this.requestArgumentNames();
}
- const pivotTableState = globals.state.nonSerializableState.pivotTableRedux;
+ const pivotTableState = globals.state.nonSerializableState.pivotTable;
const selection = globals.state.currentSelection;
if (pivotTableState.queryRequested ||
@@ -301,8 +300,7 @@
if (selection !== null && selection.kind === 'AREA' &&
(pivotTableState.selectionArea === undefined ||
pivotTableState.selectionArea.areaId !== selection.areaId)) {
- globals.dispatch(
- Actions.togglePivotTableRedux({areaId: selection.areaId}));
+ globals.dispatch(Actions.togglePivotTable({areaId: selection.areaId}));
}
}
}
diff --git a/ui/src/controller/pivot_table_tree_builder_unittest.ts b/ui/src/controller/pivot_table_tree_builder_unittest.ts
index b2e5c7e..5ab93f9 100644
--- a/ui/src/controller/pivot_table_tree_builder_unittest.ts
+++ b/ui/src/controller/pivot_table_tree_builder_unittest.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {PivotTableTreeBuilder} from './pivot_table_redux_controller';
+import {PivotTableTreeBuilder} from './pivot_table_controller';
describe('Pivot Table tree builder', () => {
test('aggregates averages correctly', () => {
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 0e42c8f..7fd41ed 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -82,8 +82,8 @@
import {MetricsController} from './metrics_controller';
import {
PIVOT_TABLE_REDUX_FLAG,
- PivotTableReduxController,
-} from './pivot_table_redux_controller';
+ PivotTableController,
+} from './pivot_table_controller';
import {QueryController, QueryControllerArgs} from './query_controller';
import {SearchController} from './search_controller';
import {
@@ -311,7 +311,7 @@
app: globals,
}));
childControllers.push(
- Child('pivot_table_redux', PivotTableReduxController, {engine}));
+ Child('pivot_table', PivotTableController, {engine}));
childControllers.push(Child('logs', LogsController, {
engine,
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index d195b7e..2c73385 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -36,7 +36,7 @@
import {LogPanel} from './logs_panel';
import {NotesEditorTab} from './notes_panel';
import {AnyAttrsVnode, PanelContainer} from './panel_container';
-import {PivotTableRedux} from './pivot_table_redux';
+import {PivotTable} from './pivot_table';
import {QueryTable} from './query_table';
import {SliceDetailsPanel} from './slice_details_panel';
import {ThreadStateTab} from './thread_state_tab';
@@ -355,15 +355,15 @@
}
- if (globals.state.nonSerializableState.pivotTableRedux.selectionArea !==
+ if (globals.state.nonSerializableState.pivotTable.selectionArea !==
undefined) {
detailsPanels.push({
- key: 'pivot_table_redux',
+ key: 'pivot_table',
name: 'Pivot Table',
- vnode: m(PivotTableRedux, {
- key: 'pivot_table_redux',
+ vnode: m(PivotTable, {
+ key: 'pivot_table',
selectionArea:
- globals.state.nonSerializableState.pivotTableRedux.selectionArea,
+ globals.state.nonSerializableState.pivotTable.selectionArea,
}),
});
}
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index d406835..b24f69a 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -74,7 +74,6 @@
scrollToTrackId?: string|number;
httpRpcState: HttpRpcState = {connected: false};
newVersionAvailable = false;
- showPivotTable = false;
// This is used to calculate the tracks within a Y range for area selection.
areaY: Range = {};
@@ -126,11 +125,6 @@
}
}
- togglePivotTable() {
- this.showPivotTable = !this.showPivotTable;
- globals.rafScheduler.scheduleFullRedraw();
- }
-
mergeState(state: FrontendState): void {
// This is unfortunately subtle. This class mutates this._visibleState.
// Since we may not mutate |state| (in order to make immer's immutable
diff --git a/ui/src/frontend/pivot_table_redux.ts b/ui/src/frontend/pivot_table.ts
similarity index 90%
rename from ui/src/frontend/pivot_table_redux.ts
rename to ui/src/frontend/pivot_table.ts
index 20f1b48..a408b22 100644
--- a/ui/src/frontend/pivot_table_redux.ts
+++ b/ui/src/frontend/pivot_table.ts
@@ -23,34 +23,34 @@
import {ColumnType} from '../common/query_result';
import {
Area,
- PivotTableReduxAreaState,
- PivotTableReduxResult,
+ PivotTableAreaState,
+ PivotTableResult,
SortDirection,
} from '../common/state';
import {fromNs, timeToCode} from '../common/time';
import {
- PivotTableReduxController,
-} from '../controller/pivot_table_redux_controller';
+ PivotTableController,
+} from '../controller/pivot_table_controller';
import {globals} from './globals';
import {fullscreenModalContainer, ModalDefinition} from './modal';
import {Panel} from './panel';
import {AnyAttrsVnode} from './panel_container';
-import {ArgumentPopup} from './pivot_table_redux_argument_popup';
+import {ArgumentPopup} from './pivot_table_argument_popup';
import {
aggregationIndex,
areaFilter,
extractArgumentExpression,
sliceAggregationColumns,
tables,
-} from './pivot_table_redux_query_generator';
+} from './pivot_table_query_generator';
import {
Aggregation,
AggregationFunction,
columnKey,
PivotTree,
TableColumn,
-} from './pivot_table_redux_types';
+} from './pivot_table_types';
import {PopupMenuButton, PopupMenuItem} from './popup_menu';
import {ReorderableCell, ReorderableCellGroup} from './reorderable_cells';
@@ -60,8 +60,8 @@
nextKey: ColumnType;
}
-interface PivotTableReduxAttrs {
- selectionArea: PivotTableReduxAreaState;
+interface PivotTableAttrs {
+ selectionArea: PivotTableAreaState;
}
interface DrillFilter {
@@ -107,12 +107,12 @@
return '';
}
-export class PivotTableRedux extends Panel<PivotTableReduxAttrs> {
+export class PivotTable extends Panel<PivotTableAttrs> {
get pivotState() {
- return globals.state.nonSerializableState.pivotTableRedux;
+ return globals.state.nonSerializableState.pivotTable;
}
get constrainToArea() {
- return globals.state.nonSerializableState.pivotTableRedux.constrainToArea;
+ return globals.state.nonSerializableState.pivotTable.constrainToArea;
}
renderCanvas(): void {}
@@ -139,7 +139,7 @@
// custom query is a temporary one, replace with a proper UI.
globals.dispatch(Actions.executeQuery({
queryId: `pivot_table_details_${
- PivotTableReduxController.detailsCount++}`,
+ PivotTableController.detailsCount++}`,
query,
}));
},
@@ -149,7 +149,7 @@
renderSectionRow(
area: Area, path: PathItem[], tree: PivotTree,
- result: PivotTableReduxResult): m.Vnode {
+ result: PivotTableResult): m.Vnode {
const renderedCells = [];
for (let j = 0; j + 1 < path.length; j++) {
renderedCells.push(m('td', m('span.indent', ' '), `${path[j].nextKey}`));
@@ -200,8 +200,8 @@
}
renderTree(
- area: Area, path: PathItem[], tree: PivotTree,
- result: PivotTableReduxResult, sink: m.Vnode[]) {
+ area: Area, path: PathItem[], tree: PivotTree, result: PivotTableResult,
+ sink: m.Vnode[]) {
if (tree.isCollapsed) {
sink.push(this.renderSectionRow(area, path, tree, result));
return;
@@ -251,7 +251,7 @@
}
}
- renderTotalsRow(queryResult: PivotTableReduxResult) {
+ renderTotalsRow(queryResult: PivotTableResult) {
const overallValuesRow =
[m('td.total-values',
{'colspan': queryResult.metadata.pivotColumns.length},
@@ -328,7 +328,7 @@
aggregation: Aggregation, index: number,
removeItem: boolean): ReorderableCell {
const popupItems: PopupMenuItem[] = [];
- const state = globals.state.nonSerializableState.pivotTableRedux;
+ const state = globals.state.nonSerializableState.pivotTable;
let icon = 'more_horiz';
if (aggregation.sortDirection === undefined) {
popupItems.push(
@@ -409,13 +409,14 @@
renderModal(): ModalDefinition {
return {
title: 'Enter argument name',
- content: m(ArgumentPopup, {
- knownArguments: globals.state.nonSerializableState
- .pivotTableRedux.argumentNames,
- onArgumentChange: (arg) => {
- this.typedArgument = arg;
- },
- }) as AnyAttrsVnode,
+ content:
+ m(ArgumentPopup, {
+ knownArguments:
+ globals.state.nonSerializableState.pivotTable.argumentNames,
+ onArgumentChange: (arg) => {
+ this.typedArgument = arg;
+ },
+ }) as AnyAttrsVnode,
buttons: [
{
text: 'Add',
@@ -433,7 +434,7 @@
}
renderPivotColumnHeader(
- queryResult: PivotTableReduxResult, pivot: TableColumn,
+ queryResult: PivotTableResult, pivot: TableColumn,
selectedPivots: Set<string>): ReorderableCell {
const items: PopupMenuItem[] = [{
itemType: 'regular',
@@ -496,12 +497,12 @@
};
}
- renderResultsTable(attrs: PivotTableReduxAttrs) {
- const state = globals.state.nonSerializableState.pivotTableRedux;
+ renderResultsTable(attrs: PivotTableAttrs) {
+ const state = globals.state.nonSerializableState.pivotTable;
if (state.queryResult === null) {
return m('div', 'Loading...');
}
- const queryResult: PivotTableReduxResult = state.queryResult;
+ const queryResult: PivotTableResult = state.queryResult;
const renderedRows: m.Vnode[] = [];
const tree = state.queryResult.tree;
@@ -566,7 +567,7 @@
'Query data for the whole timeline' :
'Constrain to selected area',
callback: () => {
- globals.dispatch(Actions.setPivotTableReduxConstrainToArea(
+ globals.dispatch(Actions.setPivotTableConstrainToArea(
{constrain: !state.constrainToArea}));
globals.dispatch(Actions.setPivotTableQueryRequested(
{queryRequested: true}));
@@ -576,11 +577,11 @@
m('tbody', this.renderTotalsRow(state.queryResult), renderedRows));
}
- view({attrs}: m.Vnode<PivotTableReduxAttrs>): m.Children {
+ view({attrs}: m.Vnode<PivotTableAttrs>): m.Children {
if (this.showModal) {
fullscreenModalContainer.updateVdom(this.renderModal());
}
- return m('.pivot-table-redux', this.renderResultsTable(attrs));
+ return m('.pivot-table', this.renderResultsTable(attrs));
}
}
diff --git a/ui/src/frontend/pivot_table_redux_argument_popup.ts b/ui/src/frontend/pivot_table_argument_popup.ts
similarity index 100%
rename from ui/src/frontend/pivot_table_redux_argument_popup.ts
rename to ui/src/frontend/pivot_table_argument_popup.ts
diff --git a/ui/src/frontend/pivot_table_redux_query_generator.ts b/ui/src/frontend/pivot_table_query_generator.ts
similarity index 96%
rename from ui/src/frontend/pivot_table_redux_query_generator.ts
rename to ui/src/frontend/pivot_table_query_generator.ts
index 8e819cb..50df919 100644
--- a/ui/src/frontend/pivot_table_redux_query_generator.ts
+++ b/ui/src/frontend/pivot_table_query_generator.ts
@@ -17,8 +17,8 @@
import {sqliteString} from '../base/string_utils';
import {
Area,
- PivotTableReduxQuery,
- PivotTableReduxState,
+ PivotTableQuery,
+ PivotTableState,
} from '../common/state';
import {toNs} from '../common/time';
import {
@@ -29,7 +29,7 @@
import {
Aggregation,
TableColumn,
-} from './pivot_table_redux_types';
+} from './pivot_table_types';
export interface Table {
name: string;
@@ -132,9 +132,8 @@
return pivotColumns + aggregationNo;
}
-export function generateQueryFromState(
- state: PivotTableReduxState,
- ): PivotTableReduxQuery {
+export function generateQueryFromState(state: PivotTableState):
+ PivotTableQuery {
if (state.selectionArea === undefined) {
throw new QueryGeneratorError('Should not be called without area');
}
diff --git a/ui/src/frontend/pivot_table_redux_types.ts b/ui/src/frontend/pivot_table_types.ts
similarity index 100%
rename from ui/src/frontend/pivot_table_redux_types.ts
rename to ui/src/frontend/pivot_table_types.ts
diff --git a/ui/src/frontend/sql_utils.ts b/ui/src/frontend/sql_utils.ts
index ff6e200..422e70f 100644
--- a/ui/src/frontend/sql_utils.ts
+++ b/ui/src/frontend/sql_utils.ts
@@ -21,7 +21,7 @@
// Interface for defining constraints which can be passed to a SQL query.
export interface SQLConstraints {
- where?: string[];
+ filters?: string[];
orderBy?: OrderClause[];
limit?: number;
}
@@ -30,8 +30,8 @@
// SQL query.
export function constraintsToQueryFragment(c: SQLConstraints): string {
const result: string[] = [];
- if (c.where && c.where.length > 0) {
- result.push(`WHERE ${c.where.join(' and ')}`);
+ if (c.filters && c.filters.length > 0) {
+ result.push(`WHERE ${c.filters.join(' and ')}`);
}
if (c.orderBy && c.orderBy.length > 0) {
const orderBys = c.orderBy.map((clause) => {
diff --git a/ui/src/frontend/sql_utils_unittest.ts b/ui/src/frontend/sql_utils_unittest.ts
index d8e35ba..0e5dc76 100644
--- a/ui/src/frontend/sql_utils_unittest.ts
+++ b/ui/src/frontend/sql_utils_unittest.ts
@@ -21,7 +21,7 @@
test('constraintsToQueryFragment: where', () => {
expect(normalize(constraintsToQueryFragment({
- where: ['ts > 1000', 'dur != 0'],
+ filters: ['ts > 1000', 'dur != 0'],
}))).toEqual('WHERE ts > 1000 and dur != 0');
});
@@ -37,7 +37,7 @@
test('constraintsToQueryFragment: all', () => {
expect(normalize(constraintsToQueryFragment({
- where: ['id != 1'],
+ filters: ['id != 1'],
orderBy: [{fieldName: 'ts'}],
limit: 1,
}))).toEqual('WHERE id != 1 ORDER BY ts LIMIT 1');
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index 4513c55..1a4f558 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -125,7 +125,7 @@
export async function getThreadState(
engine: EngineProxy, id: number): Promise<ThreadState|undefined> {
const result = await getThreadStateFromConstraints(engine, {
- where: [`id=${id}`],
+ filters: [`id=${id}`],
});
if (result.length > 1) {
throw new Error(`thread_state table has more than one row with id ${id}`);
diff --git a/ui/src/frontend/widgets/empty_state.ts b/ui/src/frontend/widgets/empty_state.ts
new file mode 100644
index 0000000..255e9ff
--- /dev/null
+++ b/ui/src/frontend/widgets/empty_state.ts
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 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 * as m from 'mithril';
+
+export interface EmptyStateAttrs {
+ // Which material icon to show.
+ // Defaults to 'search'.
+ icon?: string;
+ // Some text to show under the icon. No text shown if omitted.
+ header?: string;
+}
+
+// Something to show when there's nothing else to show!
+// Features a large icon, followed by some text explaining what went wrong, and
+// some optional content passed as children elements, usually containing common
+// actions for things you might want to do next (e.g. clear a search box).
+export class EmptyState implements m.ClassComponent<EmptyStateAttrs> {
+ view({attrs, children}: m.Vnode<EmptyStateAttrs, this>): void|m.Children {
+ const {
+ icon = 'search', // Icon defaults to the search symbol
+ header,
+ } = attrs;
+ return m(
+ '.pf-empty-state',
+ m('i.material-icons', icon),
+ header && m('span.pf-empty-state-header', header),
+ m('div.pf-empty-state-content', children),
+ );
+ }
+}
diff --git a/ui/src/frontend/widgets_page.ts b/ui/src/frontend/widgets_page.ts
index 02e8398..6f667df 100644
--- a/ui/src/frontend/widgets_page.ts
+++ b/ui/src/frontend/widgets_page.ts
@@ -13,11 +13,12 @@
// limitations under the License.
import * as m from 'mithril';
-import {globals} from './globals';
+import {globals} from './globals';
import {createPage} from './pages';
import {Button} from './widgets/button';
import {Checkbox} from './widgets/checkbox';
+import {EmptyState} from './widgets/empty_state';
import {TextInput} from './widgets/text_input';
interface WidgetShowcaseAttrs {
@@ -114,6 +115,19 @@
disabled: false,
},
}),
+ m('h2', 'Empty State'),
+ m(WidgetShowcase, {
+ renderWidget: ({header, content}) =>
+ m(EmptyState,
+ {
+ header: header && 'No search results found...',
+ },
+ content && m(Button, {label: 'Try again'})),
+ initialOpts: {
+ header: true,
+ content: true,
+ },
+ }),
);
},
});