aboutsummaryrefslogtreecommitdiff
path: root/ui/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/common')
-rw-r--r--ui/src/common/actions.ts123
-rw-r--r--ui/src/common/actions_unittest.ts79
-rw-r--r--ui/src/common/arg_types.ts2
-rw-r--r--ui/src/common/empty_state.ts4
-rw-r--r--ui/src/common/flamegraph_unittest.ts3
-rw-r--r--ui/src/common/flamegraph_util.ts79
-rw-r--r--ui/src/common/metatracing.ts47
-rw-r--r--ui/src/common/plugins.ts33
-rw-r--r--ui/src/common/queries.ts77
-rw-r--r--ui/src/common/state.ts83
-rw-r--r--ui/src/common/state_unittest.ts8
11 files changed, 199 insertions, 339 deletions
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 4cebecb05..c8ba1e621 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -15,7 +15,7 @@
import {Draft} from 'immer';
import {assertExists, assertTrue} from '../base/logging';
-import {duration, Time, time} from '../base/time';
+import {duration, time} from '../base/time';
import {RecordConfig} from '../controller/record_config_types';
import {
GenericSliceDetailsTabConfig,
@@ -37,7 +37,6 @@ import {
performReordering,
} from './dragndrop_logic';
import {createEmptyState} from './empty_state';
-import {defaultViewingOption} from './flamegraph_util';
import {
MetatraceTrackId,
traceEventBegin,
@@ -47,9 +46,7 @@ import {
import {
AdbRecordingTarget,
Area,
- CallsiteInfo,
EngineMode,
- FlamegraphStateViewingOption,
LoadedConfig,
NewEngineMode,
OmniboxMode,
@@ -253,15 +250,15 @@ export const StateActions = {
// the reducer.
args: {
name: string;
- id: string;
+ key: string;
summaryTrackKey?: string;
collapsed: boolean;
fixedOrdering?: boolean;
},
): void {
- state.trackGroups[args.id] = {
+ state.trackGroups[args.key] = {
name: args.name,
- id: args.id,
+ key: args.key,
collapsed: args.collapsed,
tracks: [],
summaryTrack: args.summaryTrackKey,
@@ -394,12 +391,8 @@ export const StateActions = {
}
},
- toggleTrackGroupCollapsed(
- state: StateDraft,
- args: {trackGroupId: string},
- ): void {
- const id = args.trackGroupId;
- const trackGroup = assertExists(state.trackGroups[id]);
+ toggleTrackGroupCollapsed(state: StateDraft, args: {groupKey: string}): void {
+ const trackGroup = assertExists(state.trackGroups[args.groupKey]);
trackGroup.collapsed = !trackGroup.collapsed;
},
@@ -448,31 +441,6 @@ export const StateActions = {
}
},
- createPermalink(state: StateDraft, args: {isRecordingConfig: boolean}): void {
- state.permalink = {
- requestId: generateNextId(state),
- hash: undefined,
- isRecordingConfig: args.isRecordingConfig,
- };
- },
-
- setPermalink(
- state: StateDraft,
- args: {requestId: string; hash: string},
- ): void {
- // Drop any links for old requests.
- if (state.permalink.requestId !== args.requestId) return;
- state.permalink = args;
- },
-
- loadPermalink(state: StateDraft, args: {hash: string}): void {
- state.permalink = {requestId: generateNextId(state), hash: args.hash};
- },
-
- clearPermalink(state: StateDraft, _: {}): void {
- state.permalink = {};
- },
-
updateStatus(state: StateDraft, args: Status): void {
if (statusTraceEvent) {
traceEventEnd(statusTraceEvent);
@@ -666,13 +634,6 @@ export const StateActions = {
type: args.type,
},
};
- this.openFlamegraph(state, {
- type: args.type,
- start: Time.ZERO,
- end: args.ts,
- upids: [args.upid],
- viewingOption: defaultViewingOption(args.type),
- });
},
selectPerfSamples(
@@ -696,35 +657,6 @@ export const StateActions = {
type: args.type,
},
};
- this.openFlamegraph(state, {
- type: args.type,
- start: args.leftTs,
- end: args.rightTs,
- upids: [args.upid],
- viewingOption: defaultViewingOption(args.type),
- });
- },
-
- openFlamegraph(
- state: StateDraft,
- args: {
- upids: number[];
- start: time;
- end: time;
- type: ProfileType;
- viewingOption: FlamegraphStateViewingOption;
- },
- ): void {
- state.currentFlamegraphState = {
- kind: 'FLAMEGRAPH_STATE',
- upids: args.upids,
- start: args.start,
- end: args.end,
- type: args.type,
- viewingOption: args.viewingOption,
- focusRegex: '',
- expandedCallsiteByViewingOption: {},
- };
},
selectCpuProfileSample(
@@ -742,43 +674,14 @@ export const StateActions = {
};
},
- expandFlamegraphState(
- state: StateDraft,
- args: {
- expandedCallsite?: CallsiteInfo;
- viewingOption: FlamegraphStateViewingOption;
- },
- ): void {
- if (state.currentFlamegraphState === null) return;
- state.currentFlamegraphState.expandedCallsiteByViewingOption[
- args.viewingOption
- ] = args.expandedCallsite;
- },
-
- changeViewFlamegraphState(
- state: StateDraft,
- args: {viewingOption: FlamegraphStateViewingOption},
- ): void {
- if (state.currentFlamegraphState === null) return;
- state.currentFlamegraphState.viewingOption = args.viewingOption;
- },
-
- changeFocusFlamegraphState(
- state: StateDraft,
- args: {focusRegex: string},
- ): void {
- if (state.currentFlamegraphState === null) return;
- state.currentFlamegraphState.focusRegex = args.focusRegex;
- },
-
- selectChromeSlice(
+ selectSlice(
state: StateDraft,
args: {id: number; trackKey: string; table?: string; scroll?: boolean},
): void {
state.selection = {
kind: 'legacy',
legacySelection: {
- kind: 'CHROME_SLICE',
+ kind: 'SLICE',
id: args.id,
trackKey: args.trackKey,
table: args.table,
@@ -920,7 +823,7 @@ export const StateActions = {
toggleTrackSelection(
state: StateDraft,
- args: {id: string; isTrackGroup: boolean},
+ args: {key: string; isTrackGroup: boolean},
) {
const selection = state.selection;
if (
@@ -930,12 +833,12 @@ export const StateActions = {
return;
}
const areaId = selection.legacySelection.areaId;
- const index = state.areas[areaId].tracks.indexOf(args.id);
+ const index = state.areas[areaId].tracks.indexOf(args.key);
if (index > -1) {
state.areas[areaId].tracks.splice(index, 1);
if (args.isTrackGroup) {
// Also remove all child tracks.
- for (const childTrack of state.trackGroups[args.id].tracks) {
+ for (const childTrack of state.trackGroups[args.key].tracks) {
const childIndex = state.areas[areaId].tracks.indexOf(childTrack);
if (childIndex > -1) {
state.areas[areaId].tracks.splice(childIndex, 1);
@@ -943,10 +846,10 @@ export const StateActions = {
}
}
} else {
- state.areas[areaId].tracks.push(args.id);
+ state.areas[areaId].tracks.push(args.key);
if (args.isTrackGroup) {
// Also add all child tracks.
- for (const childTrack of state.trackGroups[args.id].tracks) {
+ for (const childTrack of state.trackGroups[args.key].tracks) {
if (!state.areas[areaId].tracks.includes(childTrack)) {
state.areas[areaId].tracks.push(childTrack);
}
diff --git a/ui/src/common/actions_unittest.ts b/ui/src/common/actions_unittest.ts
index de497d7f6..468755e34 100644
--- a/ui/src/common/actions_unittest.ts
+++ b/ui/src/common/actions_unittest.ts
@@ -15,7 +15,6 @@
import {produce} from 'immer';
import {assertExists} from '../base/logging';
-import {Time} from '../base/time';
import {PrimaryTrackSortKey} from '../public';
import {HEAP_PROFILE_TRACK_KIND} from '../core_plugins/heap_profile';
import {PROCESS_SCHEDULING_TRACK_KIND} from '../core_plugins/process_summary/process_scheduling_track';
@@ -25,13 +24,12 @@ import {StateActions} from './actions';
import {createEmptyState} from './empty_state';
import {
InThreadTrackSortKey,
- ProfileType,
SCROLLING_TRACK_GROUP,
State,
TraceUrlSource,
TrackSortKey,
} from './state';
-import {SLICE_TRACK_KIND} from '../core_plugins/chrome_slices/chrome_slice_track';
+import {THREAD_SLICE_TRACK_KIND} from '../core_plugins/thread_slice/thread_slice_track';
function fakeTrack(
state: State,
@@ -60,14 +58,14 @@ function fakeTrack(
function fakeTrackGroup(
state: State,
- args: {id: string; summaryTrackId: string},
+ args: {key: string; summaryTrackKey: string},
): State {
return produce(state, (draft) => {
StateActions.addTrackGroup(draft, {
name: 'A group',
- id: args.id,
+ key: args.key,
collapsed: false,
- summaryTrackKey: args.summaryTrackId,
+ summaryTrackKey: args.summaryTrackKey,
});
});
}
@@ -117,7 +115,7 @@ test('add track to track group', () => {
const afterGroup = produce(state, (draft) => {
StateActions.addTrackGroup(draft, {
name: 'A track group',
- id: '123-123-123',
+ key: '123-123-123',
summaryTrackKey: 's',
collapsed: false,
});
@@ -151,19 +149,19 @@ test('reorder tracks', () => {
});
});
- const firstTrackId = once.scrollingTracks[0];
- const secondTrackId = once.scrollingTracks[1];
+ const firstTrackKey = once.scrollingTracks[0];
+ const secondTrackKey = once.scrollingTracks[1];
const twice = produce(once, (draft) => {
StateActions.moveTrack(draft, {
- srcId: `${firstTrackId}`,
+ srcId: `${firstTrackKey}`,
op: 'after',
- dstId: `${secondTrackId}`,
+ dstId: `${secondTrackKey}`,
});
});
- expect(twice.scrollingTracks[0]).toBe(secondTrackId);
- expect(twice.scrollingTracks[1]).toBe(firstTrackId);
+ expect(twice.scrollingTracks[0]).toBe(secondTrackKey);
+ expect(twice.scrollingTracks[1]).toBe(firstTrackKey);
});
test('reorder pinned to scrolling', () => {
@@ -326,7 +324,7 @@ test('setEngineReady', () => {
test('sortTracksByPriority', () => {
let state = createEmptyState();
- state = fakeTrackGroup(state, {id: 'g', summaryTrackId: 'b'});
+ state = fakeTrackGroup(state, {key: 'g', summaryTrackKey: 'b'});
state = fakeTrack(state, {
key: 'b',
uri: HEAP_PROFILE_TRACK_KIND,
@@ -351,7 +349,7 @@ test('sortTracksByPriority', () => {
test('sortTracksByPriorityAndKindAndName', () => {
let state = createEmptyState();
- state = fakeTrackGroup(state, {id: 'g', summaryTrackId: 'b'});
+ state = fakeTrackGroup(state, {key: 'g', summaryTrackKey: 'b'});
state = fakeTrack(state, {
key: 'a',
uri: PROCESS_SCHEDULING_TRACK_KIND,
@@ -360,19 +358,19 @@ test('sortTracksByPriorityAndKindAndName', () => {
});
state = fakeTrack(state, {
key: 'b',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackGroup: 'g',
trackSortKey: PrimaryTrackSortKey.MAIN_THREAD,
});
state = fakeTrack(state, {
key: 'c',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackGroup: 'g',
trackSortKey: PrimaryTrackSortKey.RENDER_THREAD,
});
state = fakeTrack(state, {
key: 'd',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackGroup: 'g',
trackSortKey: PrimaryTrackSortKey.GPU_COMPLETION_THREAD,
});
@@ -383,13 +381,13 @@ test('sortTracksByPriorityAndKindAndName', () => {
});
state = fakeTrack(state, {
key: 'f',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackGroup: 'g',
name: 'T2',
});
state = fakeTrack(state, {
key: 'g',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackGroup: 'g',
name: 'T10',
});
@@ -416,10 +414,10 @@ test('sortTracksByPriorityAndKindAndName', () => {
test('sortTracksByTidThenName', () => {
let state = createEmptyState();
- state = fakeTrackGroup(state, {id: 'g', summaryTrackId: 'a'});
+ state = fakeTrackGroup(state, {key: 'g', summaryTrackKey: 'a'});
state = fakeTrack(state, {
key: 'a',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackSortKey: {
utid: 1,
priority: InThreadTrackSortKey.ORDINARY,
@@ -430,7 +428,7 @@ test('sortTracksByTidThenName', () => {
});
state = fakeTrack(state, {
key: 'b',
- uri: SLICE_TRACK_KIND,
+ uri: THREAD_SLICE_TRACK_KIND,
trackSortKey: {
utid: 2,
priority: InThreadTrackSortKey.ORDINARY,
@@ -457,38 +455,3 @@ test('sortTracksByTidThenName', () => {
expect(after.trackGroups['g'].tracks).toEqual(['a', 'c', 'b']);
});
-
-test('perf samples open flamegraph', () => {
- const state = createEmptyState();
-
- const afterSelectingPerf = produce(state, (draft) => {
- StateActions.selectPerfSamples(draft, {
- id: 0,
- upid: 0,
- leftTs: Time.fromRaw(0n),
- rightTs: Time.fromRaw(0n),
- type: ProfileType.PERF_SAMPLE,
- });
- });
-
- expect(assertExists(afterSelectingPerf.currentFlamegraphState).type).toBe(
- ProfileType.PERF_SAMPLE,
- );
-});
-
-test('heap profile opens flamegraph', () => {
- const state = createEmptyState();
-
- const afterSelectingPerf = produce(state, (draft) => {
- StateActions.selectHeapProfile(draft, {
- id: 0,
- upid: 0,
- ts: Time.fromRaw(0n),
- type: ProfileType.JAVA_HEAP_GRAPH,
- });
- });
-
- expect(assertExists(afterSelectingPerf.currentFlamegraphState).type).toBe(
- ProfileType.JAVA_HEAP_GRAPH,
- );
-});
diff --git a/ui/src/common/arg_types.ts b/ui/src/common/arg_types.ts
index 853cf7972..551333b22 100644
--- a/ui/src/common/arg_types.ts
+++ b/ui/src/common/arg_types.ts
@@ -14,5 +14,5 @@
export type ArgValue =
| string
- | {kind: 'SLICE'; trackId: string; sliceId: number; rawValue: string};
+ | {kind: 'SCHED_SLICE'; trackId: string; sliceId: number; rawValue: string};
export type Args = Map<string, ArgValue>;
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index e3ed2b75b..4c945f631 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -95,7 +95,6 @@ export function createEmptyState(): State {
scrollingTracks: [],
areas: {},
queries: {},
- permalink: {},
notes: {},
recordConfig: AUTOLOAD_STARTED_CONFIG_FLAG.get()
@@ -107,7 +106,7 @@ export function createEmptyState(): State {
frontendLocalState: {
visibleState: {
start: Time.ZERO,
- end: Time.ZERO,
+ end: Time.fromSeconds(10),
lastUpdate: 0,
resolution: 0n,
},
@@ -122,7 +121,6 @@ export function createEmptyState(): State {
selection: {
kind: 'empty',
},
- currentFlamegraphState: null,
traceConversionInProgress: false,
perfDebug: false,
diff --git a/ui/src/common/flamegraph_unittest.ts b/ui/src/common/flamegraph_unittest.ts
index a9034bff7..e0b99bf52 100644
--- a/ui/src/common/flamegraph_unittest.ts
+++ b/ui/src/common/flamegraph_unittest.ts
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {mergeCallsites} from './flamegraph_util';
-import {CallsiteInfo} from './state';
+import {CallsiteInfo, mergeCallsites} from './flamegraph_util';
test('zeroCallsitesMerged', () => {
const callsites: CallsiteInfo[] = [
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index acf2ee827..7e87e1c3e 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -13,13 +13,36 @@
// limitations under the License.
import {featureFlags} from '../core/feature_flags';
-import {CallsiteInfo, FlamegraphStateViewingOption, ProfileType} from './state';
+import {ProfileType} from './state';
+
+export enum FlamegraphViewingOption {
+ SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY = 'SPACE',
+ ALLOC_SPACE_MEMORY_ALLOCATED_KEY = 'ALLOC_SPACE',
+ OBJECTS_ALLOCATED_NOT_FREED_KEY = 'OBJECTS',
+ OBJECTS_ALLOCATED_KEY = 'ALLOC_OBJECTS',
+ PERF_SAMPLES_KEY = 'PERF_SAMPLES',
+ DOMINATOR_TREE_OBJ_SIZE_KEY = 'DOMINATED_OBJ_SIZE',
+ DOMINATOR_TREE_OBJ_COUNT_KEY = 'DOMINATED_OBJ_COUNT',
+}
interface ViewingOption {
- option: FlamegraphStateViewingOption;
+ option: FlamegraphViewingOption;
name: string;
}
+export interface CallsiteInfo {
+ id: number;
+ parentId: number;
+ depth: number;
+ name?: string;
+ totalSize: number;
+ selfSize: number;
+ mapping: string;
+ merged: boolean;
+ highlighted: boolean;
+ location?: string;
+}
+
const SHOW_HEAP_GRAPH_DOMINATOR_TREE_FLAG = featureFlags.register({
id: 'showHeapGraphDominatorTree',
name: 'Show heap graph dominator tree',
@@ -32,32 +55,29 @@ export function viewingOptions(profileType: ProfileType): Array<ViewingOption> {
case ProfileType.PERF_SAMPLE:
return [
{
- option: FlamegraphStateViewingOption.PERF_SAMPLES_KEY,
+ option: FlamegraphViewingOption.PERF_SAMPLES_KEY,
name: 'Samples',
},
];
case ProfileType.JAVA_HEAP_GRAPH:
return [
{
- option:
- FlamegraphStateViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
+ option: FlamegraphViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
name: 'Size',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
name: 'Objects',
},
].concat(
SHOW_HEAP_GRAPH_DOMINATOR_TREE_FLAG.get()
? [
{
- option:
- FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY,
+ option: FlamegraphViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY,
name: 'Dominated size',
},
{
- option:
- FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY,
+ option: FlamegraphViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY,
name: 'Dominated objects',
},
]
@@ -66,62 +86,60 @@ export function viewingOptions(profileType: ProfileType): Array<ViewingOption> {
case ProfileType.HEAP_PROFILE:
return [
{
- option:
- FlamegraphStateViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
+ option: FlamegraphViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
name: 'Unreleased size',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
name: 'Unreleased count',
},
{
- option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
name: 'Total size',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_KEY,
name: 'Total count',
},
];
case ProfileType.NATIVE_HEAP_PROFILE:
return [
{
- option:
- FlamegraphStateViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
+ option: FlamegraphViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
name: 'Unreleased malloc size',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
name: 'Unreleased malloc count',
},
{
- option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
name: 'Total malloc size',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_KEY,
name: 'Total malloc count',
},
];
case ProfileType.JAVA_HEAP_SAMPLES:
return [
{
- option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
name: 'Total allocation size',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_KEY,
name: 'Total allocation count',
},
];
case ProfileType.MIXED_HEAP_PROFILE:
return [
{
- option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
name: 'Total allocation size (malloc + java)',
},
{
- option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
+ option: FlamegraphViewingOption.OBJECTS_ALLOCATED_KEY,
name: 'Total allocation count (malloc + java)',
},
];
@@ -133,14 +151,14 @@ export function viewingOptions(profileType: ProfileType): Array<ViewingOption> {
export function defaultViewingOption(
profileType: ProfileType,
-): FlamegraphStateViewingOption {
+): FlamegraphViewingOption {
return viewingOptions(profileType)[0].option;
}
export function expandCallsites(
- data: CallsiteInfo[],
+ data: ReadonlyArray<CallsiteInfo>,
clickedCallsiteIndex: number,
-): CallsiteInfo[] {
+): ReadonlyArray<CallsiteInfo> {
if (clickedCallsiteIndex === -1) return data;
const expandedCallsites: CallsiteInfo[] = [];
if (clickedCallsiteIndex >= data.length || clickedCallsiteIndex < -1) {
@@ -170,7 +188,10 @@ export function expandCallsites(
// Merge callsites that have approximately width less than
// MIN_PIXEL_DISPLAYED. All small callsites in the same depth and with same
// parent will be merged to one with total size of all merged callsites.
-export function mergeCallsites(data: CallsiteInfo[], minSizeDisplayed: number) {
+export function mergeCallsites(
+ data: ReadonlyArray<CallsiteInfo>,
+ minSizeDisplayed: number,
+) {
const mergedData: CallsiteInfo[] = [];
const mergedCallsites: Map<number, number> = new Map();
for (let i = 0; i < data.length; i++) {
@@ -238,7 +259,7 @@ function getCallsitesParentHash(
? +map.get(callsite.parentId)!
: callsite.parentId;
}
-export function findRootSize(data: CallsiteInfo[]) {
+export function findRootSize(data: ReadonlyArray<CallsiteInfo>) {
let totalSize = 0;
let i = 0;
while (i < data.length && data[i].depth === 0) {
diff --git a/ui/src/common/metatracing.ts b/ui/src/common/metatracing.ts
index 756d68dd8..cdd43b494 100644
--- a/ui/src/common/metatracing.ts
+++ b/ui/src/common/metatracing.ts
@@ -13,12 +13,8 @@
// limitations under the License.
import {featureFlags} from '../core/feature_flags';
-import {
- MetatraceCategories,
- PerfettoMetatrace,
- Trace,
- TracePacket,
-} from '../protos';
+import {MetatraceCategories, PerfettoMetatrace} from '../protos';
+import protobuf from 'protobufjs/minimal';
const METATRACING_BUFFER_SIZE = 100000;
@@ -83,7 +79,7 @@ interface TraceEvent {
const traceEvents: TraceEvent[] = [];
function readMetatrace(): Uint8Array {
- const eventToPacket = (e: TraceEvent): TracePacket => {
+ const eventToPacket = (e: TraceEvent): Uint8Array => {
const metatraceEvent = PerfettoMetatrace.create({
eventName: e.eventName,
threadId: e.track,
@@ -97,20 +93,37 @@ function readMetatrace(): Uint8Array {
}),
);
}
- return TracePacket.create({
- timestamp: e.startNs,
- timestampClockId: 1,
- perfettoMetatrace: metatraceEvent,
- });
+ const PROTO_VARINT_TYPE = 0;
+ const PROTO_LEN_DELIMITED_WIRE_TYPE = 2;
+ const TRACE_PACKET_PROTO_TAG = (1 << 3) | PROTO_LEN_DELIMITED_WIRE_TYPE;
+ const TRACE_PACKET_TIMESTAMP_TAG = (8 << 3) | PROTO_VARINT_TYPE;
+ const TRACE_PACKET_CLOCK_ID_TAG = (58 << 3) | PROTO_VARINT_TYPE;
+ const TRACE_PACKET_METATRACE_TAG =
+ (49 << 3) | PROTO_LEN_DELIMITED_WIRE_TYPE;
+
+ const wri = protobuf.Writer.create();
+ wri.uint32(TRACE_PACKET_PROTO_TAG);
+ wri.fork(); // Start of Trace Packet.
+ wri.uint32(TRACE_PACKET_TIMESTAMP_TAG).int64(e.startNs);
+ wri.uint32(TRACE_PACKET_CLOCK_ID_TAG).int32(1);
+ wri
+ .uint32(TRACE_PACKET_METATRACE_TAG)
+ .bytes(PerfettoMetatrace.encode(metatraceEvent).finish());
+ wri.ldelim();
+ return wri.finish();
};
- const packets: TracePacket[] = [];
+ const packets: Uint8Array[] = [];
for (const event of traceEvents) {
packets.push(eventToPacket(event));
}
- const trace = Trace.create({
- packet: packets,
- });
- return Trace.encode(trace).finish();
+ const totalLength = packets.reduce((acc, arr) => acc + arr.length, 0);
+ const trace = new Uint8Array(totalLength);
+ let offset = 0;
+ for (const packet of packets) {
+ trace.set(packet, offset);
+ offset += packet.length;
+ }
+ return trace;
}
interface TraceEventParams {
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index 3a5899ed7..145545815 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -17,7 +17,7 @@ import {v4 as uuidv4} from 'uuid';
import {Disposable, Trash} from '../base/disposable';
import {Registry} from '../base/registry';
import {Span, duration, time} from '../base/time';
-import {globals} from '../frontend/globals';
+import {TraceContext, globals} from '../frontend/globals';
import {
Command,
DetailsPanel,
@@ -45,6 +45,7 @@ import {assertExists} from '../base/logging';
import {raf} from '../core/raf_scheduler';
import {defaultPlugins} from '../core/default_plugins';
import {HighPrecisionTimeSpan} from './high_precision_time';
+import {PromptOption} from '../frontend/omnibox_manager';
// Every plugin gets its own PluginContext. This is how we keep track
// what each plugin is doing and how we can blame issues on particular
@@ -221,9 +222,12 @@ class PluginContextTraceImpl implements PluginContextTrace, Disposable {
pinTracksByPredicate(predicate: TrackPredicate) {
const tracks = Object.values(globals.state.tracks);
+ const groups = globals.state.trackGroups;
for (const track of tracks) {
const tags = {
name: track.name,
+ groupName: (track.trackGroup ? groups[track.trackGroup] : undefined)
+ ?.name,
};
if (predicate(tags) && !isPinned(track.key)) {
globals.dispatch(
@@ -275,10 +279,10 @@ class PluginContextTraceImpl implements PluginContextTrace, Disposable {
};
return predicate(ref);
})
- .map((group) => group.id);
+ .map((group) => group.key);
- for (const trackGroupId of groupsToExpand) {
- globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
+ for (const groupKey of groupsToExpand) {
+ globals.dispatch(Actions.toggleTrackGroupCollapsed({groupKey}));
}
},
@@ -293,10 +297,10 @@ class PluginContextTraceImpl implements PluginContextTrace, Disposable {
};
return predicate(ref);
})
- .map((group) => group.id);
+ .map((group) => group.key);
- for (const trackGroupId of groupsToCollapse) {
- globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
+ for (const groupKey of groupsToCollapse) {
+ globals.dispatch(Actions.toggleTrackGroupCollapsed({groupKey}));
}
},
@@ -342,11 +346,16 @@ class PluginContextTraceImpl implements PluginContextTrace, Disposable {
return globals.store.createSubStore(['plugins', this.pluginId], migrate);
}
- readonly trace = {
- get span(): Span<time, duration> {
- return globals.stateTraceTimeTP();
- },
- };
+ get trace(): TraceContext {
+ return globals.traceContext;
+ }
+
+ async prompt(
+ text: string,
+ options?: PromptOption[] | undefined,
+ ): Promise<string> {
+ return globals.omnibox.prompt(text, options);
+ }
}
function isPinned(trackId: string): boolean {
diff --git a/ui/src/common/queries.ts b/ui/src/common/queries.ts
index a6c461b8d..227b9cd60 100644
--- a/ui/src/common/queries.ts
+++ b/ui/src/common/queries.ts
@@ -40,48 +40,59 @@ export async function runQuery(
params?: QueryRunParams,
): Promise<QueryResponse> {
const startMs = performance.now();
- const queryRes = engine.execute(sqlQuery);
// TODO(primiano): once the controller thread is gone we should pass down
// the result objects directly to the frontend, iterate over the result
// and deal with pagination there. For now we keep the old behavior and
// truncate to 10k rows.
- try {
- await queryRes.waitAllRows();
- } catch {
+ const maybeResult = await engine.tryQuery(sqlQuery);
+
+ if (maybeResult.success) {
+ const queryRes = maybeResult.result;
+ const convertNullsToString = params?.convertNullsToString ?? true;
+
+ const durationMs = performance.now() - startMs;
+ const rows: Row[] = [];
+ const columns = queryRes.columns();
+ let numRows = 0;
+ for (const iter = queryRes.iter({}); iter.valid(); iter.next()) {
+ const row: Row = {};
+ for (const colName of columns) {
+ const value = iter.get(colName);
+ row[colName] = value === null && convertNullsToString ? 'NULL' : value;
+ }
+ rows.push(row);
+ if (++numRows >= MAX_DISPLAY_ROWS) break;
+ }
+
+ const result: QueryResponse = {
+ query: sqlQuery,
+ durationMs,
+ error: queryRes.error(),
+ totalRowCount: queryRes.numRows(),
+ columns,
+ rows,
+ statementCount: queryRes.statementCount(),
+ statementWithOutputCount: queryRes.statementWithOutputCount(),
+ lastStatementSql: queryRes.lastStatementSql(),
+ };
+ return result;
+ } else {
// In the case of a query error we don't want the exception to bubble up
// as a crash. The |queryRes| object will be populated anyways.
// queryRes.error() is used to tell if the query errored or not. If it
// errored, the frontend will show a graceful message instead.
+ return {
+ query: sqlQuery,
+ durationMs: performance.now() - startMs,
+ error: maybeResult.error.message,
+ totalRowCount: 0,
+ columns: [],
+ rows: [],
+ statementCount: 0,
+ statementWithOutputCount: 0,
+ lastStatementSql: '',
+ };
}
-
- const convertNullsToString = params?.convertNullsToString ?? true;
-
- const durationMs = performance.now() - startMs;
- const rows: Row[] = [];
- const columns = queryRes.columns();
- let numRows = 0;
- for (const iter = queryRes.iter({}); iter.valid(); iter.next()) {
- const row: Row = {};
- for (const colName of columns) {
- const value = iter.get(colName);
- row[colName] = value === null && convertNullsToString ? 'NULL' : value;
- }
- rows.push(row);
- if (++numRows >= MAX_DISPLAY_ROWS) break;
- }
-
- const result: QueryResponse = {
- query: sqlQuery,
- durationMs,
- error: queryRes.error(),
- totalRowCount: queryRes.numRows(),
- columns,
- rows,
- statementCount: queryRes.statementCount(),
- statementWithOutputCount: queryRes.statementWithOutputCount(),
- lastStatementSql: queryRes.lastStatementSql(),
- };
- return result;
}
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index fc3018e83..884c598ab 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -28,7 +28,6 @@ import {
selectionToLegacySelection,
Selection,
LegacySelection,
- ProfileType,
} from '../core/selection_manager';
export {
@@ -42,7 +41,7 @@ export {
LegacySelection,
AreaSelection,
ProfileType,
- ChromeSliceSelection,
+ ThreadSliceSelection,
CpuProfileSampleSelection,
} from '../core/selection_manager';
@@ -151,7 +150,10 @@ export const MAX_TIME = 180;
// 52. Update track group state - don't make the summary track the first track.
// 53. Remove android log state.
// 54. Remove traceTime.
-export const STATE_VERSION = 54;
+// 55. Rename TrackGroupState.id -> TrackGroupState.key.
+// 56. Renamed chrome slice to thread slice everywhere.
+// 57. Remove flamegraph related code from state.
+export const STATE_VERSION = 57;
export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
@@ -187,56 +189,6 @@ export type UtidToTrackSortKey = {
};
};
-export enum FlamegraphStateViewingOption {
- SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY = 'SPACE',
- ALLOC_SPACE_MEMORY_ALLOCATED_KEY = 'ALLOC_SPACE',
- OBJECTS_ALLOCATED_NOT_FREED_KEY = 'OBJECTS',
- OBJECTS_ALLOCATED_KEY = 'ALLOC_OBJECTS',
- PERF_SAMPLES_KEY = 'PERF_SAMPLES',
- DOMINATOR_TREE_OBJ_SIZE_KEY = 'DOMINATED_OBJ_SIZE',
- DOMINATOR_TREE_OBJ_COUNT_KEY = 'DOMINATED_OBJ_COUNT',
-}
-
-const HEAP_GRAPH_DOMINATOR_TREE_VIEWING_OPTIONS = [
- FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY,
- FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY,
-] as const;
-
-export type HeapGraphDominatorTreeViewingOption =
- (typeof HEAP_GRAPH_DOMINATOR_TREE_VIEWING_OPTIONS)[number];
-
-export function isHeapGraphDominatorTreeViewingOption(
- option: FlamegraphStateViewingOption,
-): option is HeapGraphDominatorTreeViewingOption {
- return (
- HEAP_GRAPH_DOMINATOR_TREE_VIEWING_OPTIONS as readonly FlamegraphStateViewingOption[]
- ).includes(option);
-}
-
-export interface FlamegraphState {
- kind: 'FLAMEGRAPH_STATE';
- upids: number[];
- start: time;
- end: time;
- type: ProfileType;
- viewingOption: FlamegraphStateViewingOption;
- focusRegex: string;
- expandedCallsiteByViewingOption: {[key: string]: CallsiteInfo | undefined};
-}
-
-export interface CallsiteInfo {
- id: number;
- parentId: number;
- depth: number;
- name?: string;
- totalSize: number;
- selfSize: number;
- mapping: string;
- merged: boolean;
- highlighted: boolean;
- location?: string;
-}
-
export interface TraceFileSource {
type: 'FILE';
file: File;
@@ -288,7 +240,7 @@ export interface TrackState {
}
export interface TrackGroupState {
- id: string;
+ key: string;
name: string;
collapsed: boolean;
tracks: string[]; // Child track ids.
@@ -310,12 +262,6 @@ export interface QueryConfig {
query: string;
}
-export interface PermalinkConfig {
- requestId?: string; // Set by the frontend to request a new permalink.
- hash?: string; // Set by the controller when the link has been created.
- isRecordingConfig?: boolean; // this permalink request is for a recording config only
-}
-
export interface FrontendLocalState {
visibleState: VisibleState;
}
@@ -476,7 +422,7 @@ export interface State {
newEngineMode: NewEngineMode;
engine?: EngineConfig;
traceUuid?: string;
- trackGroups: ObjectById<TrackGroupState>;
+ trackGroups: ObjectByKey<TrackGroupState>;
tracks: ObjectByKey<TrackState>;
utidToThreadSortKey: UtidToTrackSortKey;
areas: ObjectById<AreaById>;
@@ -486,12 +432,11 @@ export interface State {
debugTrackId?: string;
lastTrackReloadRequest?: number;
queries: ObjectById<QueryConfig>;
- permalink: PermalinkConfig;
notes: ObjectById<Note | AreaNote>;
status: Status;
selection: Selection;
- currentFlamegraphState: FlamegraphState | null;
traceConversionInProgress: boolean;
+ flamegraphModalDismissed: boolean;
/**
* This state is updated on the frontend at 60Hz and eventually syncronised to
@@ -527,7 +472,6 @@ export interface State {
recordingInProgress: boolean;
recordingCancelled: boolean;
extensionInstalled: boolean;
- flamegraphModalDismissed: boolean;
recordingTarget: RecordingTarget;
availableAdbDevices: AdbRecordingTarget[];
lastRecordingError?: string;
@@ -915,20 +859,19 @@ export function getBuiltinChromeCategoryList(): string[] {
];
}
-export function getContainingTrackId(
+export function getContainingGroupKey(
state: State,
trackKey: string,
): null | string {
const track = state.tracks[trackKey];
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
- if (!track) {
+ if (track === undefined) {
return null;
}
- const parentId = track.trackGroup;
- if (!parentId) {
+ const parentGroupKey = track.trackGroup;
+ if (!parentGroupKey) {
return null;
}
- return parentId;
+ return parentGroupKey;
}
export function getLegacySelection(state: State): LegacySelection | null {
diff --git a/ui/src/common/state_unittest.ts b/ui/src/common/state_unittest.ts
index be66340d9..ff111de6b 100644
--- a/ui/src/common/state_unittest.ts
+++ b/ui/src/common/state_unittest.ts
@@ -15,7 +15,7 @@
import {PrimaryTrackSortKey} from '../public';
import {createEmptyState} from './empty_state';
-import {getContainingTrackId, State} from './state';
+import {getContainingGroupKey, State} from './state';
import {deserializeStateObject, serializeStateObject} from './upload_utils';
test('createEmptyState', () => {
@@ -40,9 +40,9 @@ test('getContainingTrackId', () => {
trackGroup: 'containsB',
};
- expect(getContainingTrackId(state, 'z')).toEqual(null);
- expect(getContainingTrackId(state, 'a')).toEqual(null);
- expect(getContainingTrackId(state, 'b')).toEqual('containsB');
+ expect(getContainingGroupKey(state, 'z')).toEqual(null);
+ expect(getContainingGroupKey(state, 'a')).toEqual(null);
+ expect(getContainingGroupKey(state, 'b')).toEqual('containsB');
});
test('state is serializable', () => {