diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts index 9988967b59..869cfbd173 100644 --- a/ui/src/common/empty_state.ts +++ b/ui/src/common/empty_state.ts @@ -20,7 +20,7 @@ import { autosaveConfigStore, recordTargetStore, } from '../frontend/record_config'; -import {SqlTables} from '../frontend/well_known_sql_tables'; +import {SqlTables} from '../frontend/widgets/sql/table/well_known_sql_tables'; import {NonSerializableState, State, STATE_VERSION} from './state'; diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts index 65b0e48033..b2e3d18c96 100644 --- a/ui/src/controller/flow_events_controller.ts +++ b/ui/src/controller/flow_events_controller.ts @@ -17,7 +17,7 @@ import {AreaSelection, getLegacySelection} from '../common/state'; import {featureFlags} from '../core/feature_flags'; import {Flow, globals} from '../frontend/globals'; import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish'; -import {asSliceSqlId} from '../frontend/sql_types'; +import {asSliceSqlId} from '../trace_processor/sql_utils/core_types'; import {Engine} from '../trace_processor/engine'; import {LONG, NUM, STR_NULL} from '../trace_processor/query_result'; diff --git a/ui/src/core_plugins/chrome_critical_user_interactions/startup_details_panel.ts b/ui/src/core_plugins/chrome_critical_user_interactions/startup_details_panel.ts index 4312079966..19ae82d473 100644 --- a/ui/src/core_plugins/chrome_critical_user_interactions/startup_details_panel.ts +++ b/ui/src/core_plugins/chrome_critical_user_interactions/startup_details_panel.ts @@ -25,7 +25,7 @@ import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout'; import {Section} from '../../widgets/section'; import {SqlRef} from '../../widgets/sql_ref'; import {dictToTreeNodes, Tree} from '../../widgets/tree'; -import {asUpid, Upid} from '../../frontend/sql_types'; +import {asUpid, Upid} from '../../trace_processor/sql_utils/core_types'; interface Data { startupId: number; diff --git a/ui/src/core_plugins/chrome_critical_user_interactions/web_content_interaction_details_panel.ts b/ui/src/core_plugins/chrome_critical_user_interactions/web_content_interaction_details_panel.ts index 7cf7b48f26..77e1b9e950 100644 --- a/ui/src/core_plugins/chrome_critical_user_interactions/web_content_interaction_details_panel.ts +++ b/ui/src/core_plugins/chrome_critical_user_interactions/web_content_interaction_details_panel.ts @@ -31,7 +31,7 @@ import m from 'mithril'; import {duration, Time, time} from '../../base/time'; import {BottomTab, NewBottomTabArgs} from '../../frontend/bottom_tab'; import {GenericSliceDetailsTabConfig} from '../../frontend/generic_slice_details_tab'; -import {asUpid, Upid} from '../../frontend/sql_types'; +import {asUpid, Upid} from '../../trace_processor/sql_utils/core_types'; import {DurationWidget} from '../../frontend/widgets/duration'; import {Timestamp} from '../../frontend/widgets/timestamp'; import {LONG, NUM, STR} from '../../trace_processor/query_result'; diff --git a/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts b/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts index c081259aba..080d34cfe5 100644 --- a/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts +++ b/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts @@ -25,10 +25,12 @@ import { getSlice, getSliceFromConstraints, SliceDetails, - sliceRef, SliceTreeNode, -} from '../../frontend/sql/slice'; -import {asSliceSqlId, SliceSqlId} from '../../frontend/sql_types'; +} from '../../trace_processor/sql_utils/slice'; +import { + asSliceSqlId, + SliceSqlId, +} from '../../trace_processor/sql_utils/core_types'; import { ColumnDescriptor, Table, @@ -57,6 +59,7 @@ import { ScrollJankSlice, } from './scroll_jank_slice'; import {ScrollJankV3TrackKind} from './common'; +import {sliceRef} from '../../frontend/widgets/slice'; // Given a node in the slice tree, return a path from root to it. function getPath(slice: SliceTreeNode): string[] { diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts index e6111161c3..6b57e461d8 100644 --- a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts +++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts @@ -23,7 +23,7 @@ import { focusHorizontalRange, verticalScrollToTrack, } from '../../frontend/scroll_helper'; -import {SliceSqlId} from '../../frontend/sql_types'; +import {SliceSqlId} from '../../trace_processor/sql_utils/core_types'; import {Engine} from '../../trace_processor/engine'; import {LONG, NUM, STR} from '../../trace_processor/query_result'; import {Anchor} from '../../widgets/anchor'; diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts index e8674bb77b..c628ec47c4 100644 --- a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts +++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts @@ -19,7 +19,7 @@ import {duration, time, Time} from '../../base/time'; import {Actions} from '../../common/actions'; import {globals} from '../../frontend/globals'; import {scrollToTrackAndTs} from '../../frontend/scroll_helper'; -import {SliceSqlId} from '../../frontend/sql_types'; +import {SliceSqlId} from '../../trace_processor/sql_utils/core_types'; import {Engine} from '../../trace_processor/engine'; import {LONG, NUM} from '../../trace_processor/query_result'; import { diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts index ae8622b62a..90035a4a2f 100644 --- a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts +++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts @@ -19,8 +19,8 @@ import {exists} from '../../base/utils'; import {raf} from '../../core/raf_scheduler'; import {BottomTab, NewBottomTabArgs} from '../../frontend/bottom_tab'; import {GenericSliceDetailsTabConfig} from '../../frontend/generic_slice_details_tab'; -import {getSlice, SliceDetails} from '../../frontend/sql/slice'; -import {asSliceSqlId} from '../../frontend/sql_types'; +import {getSlice, SliceDetails} from '../../trace_processor/sql_utils/slice'; +import {asSliceSqlId} from '../../trace_processor/sql_utils/core_types'; import {DurationWidget} from '../../frontend/widgets/duration'; import {Timestamp} from '../../frontend/widgets/timestamp'; import {Engine} from '../../trace_processor/engine'; diff --git a/ui/src/core_plugins/chrome_tasks/index.ts b/ui/src/core_plugins/chrome_tasks/index.ts index 9faeefcb0c..4024c98966 100644 --- a/ui/src/core_plugins/chrome_tasks/index.ts +++ b/ui/src/core_plugins/chrome_tasks/index.ts @@ -15,7 +15,7 @@ import {uuidv4} from '../../base/uuid'; import {GenericSliceDetailsTabConfig} from '../../frontend/generic_slice_details_tab'; import {addSqlTableTab} from '../../frontend/sql_table_tab'; -import {asUtid} from '../../frontend/sql_types'; +import {asUtid} from '../../trace_processor/sql_utils/core_types'; import { BottomTabToSCSAdapter, NUM, diff --git a/ui/src/core_plugins/chrome_tasks/track.ts b/ui/src/core_plugins/chrome_tasks/track.ts index 85e6179b4b..6e25b03a27 100644 --- a/ui/src/core_plugins/chrome_tasks/track.ts +++ b/ui/src/core_plugins/chrome_tasks/track.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Utid} from '../../frontend/sql_types'; +import {Utid} from '../../trace_processor/sql_utils/core_types'; import { CustomSqlDetailsPanelConfig, CustomSqlTableDefConfig, diff --git a/ui/src/core_plugins/screenshots/screenshot_panel.ts b/ui/src/core_plugins/screenshots/screenshot_panel.ts index 387ebf6899..8f8d469459 100644 --- a/ui/src/core_plugins/screenshots/screenshot_panel.ts +++ b/ui/src/core_plugins/screenshots/screenshot_panel.ts @@ -18,8 +18,8 @@ import {assertTrue} from '../../base/logging'; import {exists} from '../../base/utils'; import {BottomTab, NewBottomTabArgs} from '../../frontend/bottom_tab'; import {GenericSliceDetailsTabConfig} from '../../frontend/generic_slice_details_tab'; -import {getSlice, SliceDetails} from '../../frontend/sql/slice'; -import {asSliceSqlId} from '../../frontend/sql_types'; +import {getSlice, SliceDetails} from '../../trace_processor/sql_utils/slice'; +import {asSliceSqlId} from '../../trace_processor/sql_utils/core_types'; import {Engine} from '../../trace_processor/engine'; async function getSliceDetails( diff --git a/ui/src/core_plugins/thread_state/index.ts b/ui/src/core_plugins/thread_state/index.ts index cdd6171865..97047f30eb 100644 --- a/ui/src/core_plugins/thread_state/index.ts +++ b/ui/src/core_plugins/thread_state/index.ts @@ -14,7 +14,7 @@ import {uuidv4} from '../../base/uuid'; import {THREAD_STATE_TRACK_KIND} from '../../public'; -import {asThreadStateSqlId} from '../../frontend/sql_types'; +import {asThreadStateSqlId} from '../../trace_processor/sql_utils/core_types'; import {ThreadStateTab} from '../../frontend/thread_state_tab'; import { BottomTabToSCSAdapter, diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts index da6ddca489..e9c91a74aa 100644 --- a/ui/src/frontend/app.ts +++ b/ui/src/frontend/app.ts @@ -52,12 +52,12 @@ import { } from './keyboard_event_handler'; import {publishPermalinkHash} from './publish'; import {OmniboxMode, PromptOption} from './omnibox_manager'; -import {Utid} from './sql_types'; -import {getThreadInfo} from './thread_and_process_info'; +import {Utid} from '../trace_processor/sql_utils/core_types'; import {THREAD_STATE_TRACK_KIND} from '../core/track_kinds'; import {DisposableStack} from '../base/disposable_stack'; import {addSqlTableTab} from './sql_table_tab'; -import {SqlTables} from './well_known_sql_tables'; +import {SqlTables} from './widgets/sql/table/well_known_sql_tables'; +import {getThreadInfo} from '../trace_processor/sql_utils/thread'; function renderPermalink(): m.Children { const hash = globals.permalinkHash; diff --git a/ui/src/frontend/debug_tracks/details_tab.ts b/ui/src/frontend/debug_tracks/details_tab.ts index 5cc0f6f444..b8b8ce9c98 100644 --- a/ui/src/frontend/debug_tracks/details_tab.ts +++ b/ui/src/frontend/debug_tracks/details_tab.ts @@ -19,10 +19,12 @@ import {raf} from '../../core/raf_scheduler'; import {BottomTab, NewBottomTabArgs} from '../bottom_tab'; import {GenericSliceDetailsTabConfig} from '../generic_slice_details_tab'; import {hasArgs, renderArguments} from '../slice_args'; -import {getSlice, SliceDetails, sliceRef} from '../sql/slice'; -import {asSliceSqlId, Utid} from '../sql_types'; -import {getProcessName, getThreadName} from '../thread_and_process_info'; -import {getThreadState, ThreadState, threadStateRef} from '../thread_state'; +import {getSlice, SliceDetails} from '../../trace_processor/sql_utils/slice'; +import {asSliceSqlId, Utid} from '../../trace_processor/sql_utils/core_types'; +import { + getThreadState, + ThreadState, +} from '../../trace_processor/sql_utils/thread_state'; import {DurationWidget} from '../widgets/duration'; import {Timestamp} from '../widgets/timestamp'; import { @@ -37,6 +39,10 @@ import {DetailsShell} from '../../widgets/details_shell'; import {GridLayout} from '../../widgets/grid_layout'; import {Section} from '../../widgets/section'; import {dictToTree, dictToTreeNodes, Tree, TreeNode} from '../../widgets/tree'; +import {threadStateRef} from '../widgets/thread_state'; +import {getThreadName} from '../../trace_processor/sql_utils/thread'; +import {getProcessName} from '../../trace_processor/sql_utils/process'; +import {sliceRef} from '../widgets/slice'; export const ARG_PREFIX = 'arg_'; diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts index 02cd2f8a9d..ce10df2e58 100644 --- a/ui/src/frontend/globals.ts +++ b/ui/src/frontend/globals.ts @@ -37,7 +37,7 @@ import {Engine, EngineBase} from '../trace_processor/engine'; import {HttpRpcState} from '../trace_processor/http_rpc_engine'; import {Analytics, initAnalytics} from './analytics'; import {Timeline} from './timeline'; -import {SliceSqlId} from './sql_types'; +import {SliceSqlId} from '../trace_processor/sql_utils/core_types'; import {SelectionManager, LegacySelection} from '../core/selection_manager'; import {Optional, exists} from '../base/utils'; import {OmniboxManager} from './omnibox_manager'; diff --git a/ui/src/frontend/pivot_table.ts b/ui/src/frontend/pivot_table.ts index 9c7051ae30..f083fae97a 100644 --- a/ui/src/frontend/pivot_table.ts +++ b/ui/src/frontend/pivot_table.ts @@ -44,8 +44,8 @@ import {PopupMenuButton, popupMenuIcon, PopupMenuItem} from './popup_menu'; import {ReorderableCell, ReorderableCellGroup} from './reorderable_cells'; import {AttributeModalHolder} from './tables/attribute_modal_holder'; import {DurationWidget} from './widgets/duration'; -import {SqlTables} from './well_known_sql_tables'; import {addSqlTableTab} from './sql_table_tab'; +import {SqlTables} from './widgets/sql/table/well_known_sql_tables'; interface PathItem { tree: PivotTree; diff --git a/ui/src/frontend/pivot_table_query_generator.ts b/ui/src/frontend/pivot_table_query_generator.ts index 681b5f76bb..f7211651a3 100644 --- a/ui/src/frontend/pivot_table_query_generator.ts +++ b/ui/src/frontend/pivot_table_query_generator.ts @@ -19,7 +19,7 @@ import {Area, PivotTableQuery, PivotTableState} from '../common/state'; import {getSelectedTrackKeys} from '../controller/aggregation/slice_aggregation_controller'; import {Aggregation, TableColumn} from './pivot_table_types'; -import {SqlTables} from './well_known_sql_tables'; +import {SqlTables} from './widgets/sql/table/well_known_sql_tables'; export interface Table { name: string; diff --git a/ui/src/frontend/slice_args.ts b/ui/src/frontend/slice_args.ts index 3104259529..86f6999a43 100644 --- a/ui/src/frontend/slice_args.ts +++ b/ui/src/frontend/slice_args.ts @@ -25,10 +25,10 @@ import {Anchor} from '../widgets/anchor'; import {MenuItem, PopupMenu2} from '../widgets/menu'; import {TreeNode} from '../widgets/tree'; -import {Arg} from './sql/args'; +import {Arg} from '../trace_processor/sql_utils/args'; import {globals} from './globals'; import {addSqlTableTab} from './sql_table_tab'; -import {SqlTables} from './well_known_sql_tables'; +import {SqlTables} from './widgets/sql/table/well_known_sql_tables'; // Renders slice arguments (key/value pairs) as a subtree. export function renderArguments(engine: Engine, args: Arg[]): m.Children { diff --git a/ui/src/frontend/slice_details.ts b/ui/src/frontend/slice_details.ts index 1d368e7ca5..89dccc8a33 100644 --- a/ui/src/frontend/slice_details.ts +++ b/ui/src/frontend/slice_details.ts @@ -23,16 +23,17 @@ import {Section} from '../widgets/section'; import {SqlRef} from '../widgets/sql_ref'; import {Tree, TreeNode} from '../widgets/tree'; -import {SliceDetails} from './sql/slice'; +import {SliceDetails} from '../trace_processor/sql_utils/slice'; import { BreakdownByThreadState, BreakdownByThreadStateTreeNode, } from './sql/thread_state'; -import {getProcessName, getThreadName} from './thread_and_process_info'; import {DurationWidget} from './widgets/duration'; import {Timestamp} from './widgets/timestamp'; import {addSqlTableTab} from './sql_table_tab'; -import {SqlTables} from './well_known_sql_tables'; +import {SqlTables} from './widgets/sql/table/well_known_sql_tables'; +import {getThreadName} from '../trace_processor/sql_utils/thread'; +import {getProcessName} from '../trace_processor/sql_utils/process'; // Renders a widget storing all of the generic details for a slice from the // slice table. diff --git a/ui/src/frontend/sql/thread_state.ts b/ui/src/frontend/sql/thread_state.ts index 0e3aa3d664..d078832d20 100644 --- a/ui/src/frontend/sql/thread_state.ts +++ b/ui/src/frontend/sql/thread_state.ts @@ -23,7 +23,7 @@ import { STR_NULL, } from '../../trace_processor/query_result'; import {TreeNode} from '../../widgets/tree'; -import {Utid} from '../sql_types'; +import {Utid} from '../../trace_processor/sql_utils/core_types'; import {DurationWidget} from '../widgets/duration'; // An individual node of the thread state breakdown tree. diff --git a/ui/src/frontend/thread_and_process_info.ts b/ui/src/frontend/thread_and_process_info.ts deleted file mode 100644 index 48c9aa25e6..0000000000 --- a/ui/src/frontend/thread_and_process_info.ts +++ /dev/null @@ -1,190 +0,0 @@ -// 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 m from 'mithril'; - -import {copyToClipboard} from '../base/clipboard'; -import {Icons} from '../base/semantic_icons'; -import {exists} from '../base/utils'; -import {Engine} from '../trace_processor/engine'; -import {NUM, NUM_NULL, STR_NULL} from '../trace_processor/query_result'; -import {fromNumNull} from '../trace_processor/sql_utils'; -import {Anchor} from '../widgets/anchor'; -import {MenuItem, PopupMenu2} from '../widgets/menu'; - -import {Upid, Utid} from './sql_types'; - -// Interface definitions for process and thread-related information -// and functions to extract them from SQL. - -// TODO(altimin): Current implementation ends up querying process and thread -// information separately for each thread. Given that there is a limited -// numer of threads and processes, it might be easier to fetch this information -// once when loading the trace and then just look it up synchronously. - -export interface ProcessInfo { - upid: Upid; - pid?: number; - name?: string; - uid?: number; - packageName?: string; - versionCode?: number; -} - -export async function getProcessInfo( - engine: Engine, - upid: Upid, -): Promise { - const res = await engine.query(` - include perfetto module android.process_metadata; - select - p.upid, - p.pid, - p.name, - p.uid, - m.package_name as packageName, - m.version_code as versionCode - from process p - left join android_process_metadata m using (upid) - where upid = ${upid}; - `); - const row = res.firstRow({ - upid: NUM, - pid: NUM, - name: STR_NULL, - uid: NUM_NULL, - packageName: STR_NULL, - versionCode: NUM_NULL, - }); - return { - upid, - pid: row.pid, - name: row.name ?? undefined, - uid: fromNumNull(row.uid), - packageName: row.packageName ?? undefined, - versionCode: fromNumNull(row.versionCode), - }; -} - -function getDisplayName( - name: string | undefined, - id: number | undefined, -): string | undefined { - if (name === undefined) { - return id === undefined ? undefined : `${id}`; - } - return id === undefined ? name : `${name} [${id}]`; -} - -export function renderProcessRef(info: ProcessInfo): m.Children { - const name = info.name; - return m( - PopupMenu2, - { - trigger: m(Anchor, getProcessName(info)), - }, - exists(name) && - m(MenuItem, { - icon: Icons.Copy, - label: 'Copy process name', - onclick: () => copyToClipboard(name), - }), - exists(info.pid) && - m(MenuItem, { - icon: Icons.Copy, - label: 'Copy pid', - onclick: () => copyToClipboard(`${info.pid}`), - }), - m(MenuItem, { - icon: Icons.Copy, - label: 'Copy upid', - onclick: () => copyToClipboard(`${info.upid}`), - }), - ); -} - -export function getProcessName(info?: ProcessInfo): string | undefined { - return getDisplayName(info?.name, info?.pid); -} - -export interface ThreadInfo { - utid: Utid; - tid?: number; - name?: string; - process?: ProcessInfo; -} - -export async function getThreadInfo( - engine: Engine, - utid: Utid, -): Promise { - const it = ( - await engine.query(` - SELECT tid, name, upid - FROM thread - WHERE utid = ${utid}; - `) - ).iter({tid: NUM, name: STR_NULL, upid: NUM_NULL}); - if (!it.valid()) { - return { - utid, - }; - } - const upid = fromNumNull(it.upid) as Upid | undefined; - return { - utid, - tid: it.tid, - name: it.name ?? undefined, - process: upid ? await getProcessInfo(engine, upid) : undefined, - }; -} - -export function renderThreadRef(info: ThreadInfo): m.Children { - const name = info.name; - return m( - PopupMenu2, - { - trigger: m(Anchor, getThreadName(info)), - }, - exists(name) && - m(MenuItem, { - icon: Icons.Copy, - label: 'Copy thread name', - onclick: () => copyToClipboard(name), - }), - exists(info.tid) && - m(MenuItem, { - icon: Icons.Copy, - label: 'Copy tid', - onclick: () => copyToClipboard(`${info.tid}`), - }), - m(MenuItem, { - icon: Icons.Copy, - label: 'Copy utid', - onclick: () => copyToClipboard(`${info.utid}`), - }), - ); -} - -export function getThreadName(info?: ThreadInfo): string | undefined { - return getDisplayName(info?.name, info?.tid); -} - -// Return the full thread name, including the process name. -export function getFullThreadName(info?: ThreadInfo): string | undefined { - if (info?.process === undefined) { - return getThreadName(info); - } - return `${getThreadName(info)} ${getProcessName(info.process)}`; -} diff --git a/ui/src/frontend/thread_slice_details_tab.ts b/ui/src/frontend/thread_slice_details_tab.ts index c43c9fd51c..86340ce622 100644 --- a/ui/src/frontend/thread_slice_details_tab.ts +++ b/ui/src/frontend/thread_slice_details_tab.ts @@ -33,15 +33,16 @@ import {FlowPoint, globals} from './globals'; import {addQueryResultsTab} from './query_result_tab'; import {hasArgs, renderArguments} from './slice_args'; import {renderDetails} from './slice_details'; -import {getSlice, SliceDetails, SliceRef} from './sql/slice'; +import {getSlice, SliceDetails} from '../trace_processor/sql_utils/slice'; import { BreakdownByThreadState, breakDownIntervalByThreadState, } from './sql/thread_state'; -import {asSliceSqlId} from './sql_types'; +import {asSliceSqlId} from '../trace_processor/sql_utils/core_types'; import {DurationWidget} from './widgets/duration'; import {addSqlTableTab} from './sql_table_tab'; -import {SqlTables} from './well_known_sql_tables'; +import {SqlTables} from './widgets/sql/table/well_known_sql_tables'; +import {SliceRef} from './widgets/slice'; interface ContextMenuItem { name: string; diff --git a/ui/src/frontend/thread_state_tab.ts b/ui/src/frontend/thread_state_tab.ts index 8ce6eb7a41..ed20d994ee 100644 --- a/ui/src/frontend/thread_state_tab.ts +++ b/ui/src/frontend/thread_state_tab.ts @@ -26,24 +26,27 @@ import {Tree, TreeNode} from '../widgets/tree'; import {Intent} from '../widgets/common'; import {BottomTab, NewBottomTabArgs} from './bottom_tab'; -import {SchedSqlId, ThreadStateSqlId} from './sql_types'; import { - getFullThreadName, - getProcessName, - getThreadName, - ThreadInfo, -} from './thread_and_process_info'; + SchedSqlId, + ThreadStateSqlId, +} from '../trace_processor/sql_utils/core_types'; import { getThreadState, getThreadStateFromConstraints, goToSchedSlice, ThreadState, - ThreadStateRef, -} from './thread_state'; +} from '../trace_processor/sql_utils/thread_state'; import {DurationWidget, renderDuration} from './widgets/duration'; import {Timestamp} from './widgets/timestamp'; import {addDebugSliceTrack} from './debug_tracks/debug_tracks'; import {globals} from './globals'; +import {getProcessName} from '../trace_processor/sql_utils/process'; +import { + ThreadInfo, + getFullThreadName, + getThreadName, +} from '../trace_processor/sql_utils/thread'; +import {ThreadStateRef} from './widgets/thread_state'; interface ThreadStateTabConfig { // Id into |thread_state| sql table. diff --git a/ui/src/frontend/widgets/process.ts b/ui/src/frontend/widgets/process.ts new file mode 100644 index 0000000000..f7bcb93be0 --- /dev/null +++ b/ui/src/frontend/widgets/process.ts @@ -0,0 +1,51 @@ +// 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 m from 'mithril'; +import { + ProcessInfo, + getProcessName, +} from '../../trace_processor/sql_utils/process'; +import {MenuItem, PopupMenu2} from '../../widgets/menu'; +import {Anchor} from '../../widgets/anchor'; +import {exists} from '../../base/utils'; +import {Icons} from '../../base/semantic_icons'; +import {copyToClipboard} from '../../base/clipboard'; + +export function renderProcessRef(info: ProcessInfo): m.Children { + const name = info.name; + return m( + PopupMenu2, + { + trigger: m(Anchor, getProcessName(info)), + }, + exists(name) && + m(MenuItem, { + icon: Icons.Copy, + label: 'Copy process name', + onclick: () => copyToClipboard(name), + }), + exists(info.pid) && + m(MenuItem, { + icon: Icons.Copy, + label: 'Copy pid', + onclick: () => copyToClipboard(`${info.pid}`), + }), + m(MenuItem, { + icon: Icons.Copy, + label: 'Copy upid', + onclick: () => copyToClipboard(`${info.upid}`), + }), + ); +} diff --git a/ui/src/frontend/widgets/slice.ts b/ui/src/frontend/widgets/slice.ts new file mode 100644 index 0000000000..059074e2e8 --- /dev/null +++ b/ui/src/frontend/widgets/slice.ts @@ -0,0 +1,86 @@ +// 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 m from 'mithril'; + +import {Time, duration, time} from '../../base/time'; +import {SliceSqlId} from '../../trace_processor/sql_utils/core_types'; +import {Anchor} from '../../widgets/anchor'; +import {Icons} from '../../base/semantic_icons'; +import {globals} from '../globals'; +import {focusHorizontalRange, verticalScrollToTrack} from '../scroll_helper'; +import {BigintMath} from '../../base/bigint_math'; +import {SliceDetails} from '../../trace_processor/sql_utils/slice'; + +interface SliceRefAttrs { + readonly id: SliceSqlId; + readonly name: string; + readonly ts: time; + readonly dur: duration; + readonly sqlTrackId: number; + + // Whether clicking on the reference should change the current tab + // to "current selection" tab in addition to updating the selection + // and changing the viewport. True by default. + readonly switchToCurrentSelectionTab?: boolean; +} + +export class SliceRef implements m.ClassComponent { + view(vnode: m.Vnode) { + const switchTab = vnode.attrs.switchToCurrentSelectionTab ?? true; + return m( + Anchor, + { + icon: Icons.UpdateSelection, + onclick: () => { + const trackKeyByTrackId = globals.trackManager.trackKeyByTrackId; + const trackKey = trackKeyByTrackId.get(vnode.attrs.sqlTrackId); + if (trackKey === undefined) return; + verticalScrollToTrack(trackKey, true); + // Clamp duration to 1 - i.e. for instant events + const dur = BigintMath.max(1n, vnode.attrs.dur); + focusHorizontalRange( + vnode.attrs.ts, + Time.fromRaw(vnode.attrs.ts + dur), + ); + + globals.setLegacySelection( + { + kind: 'SLICE', + id: vnode.attrs.id, + trackKey, + table: 'slice', + }, + { + clearSearch: true, + pendingScrollId: undefined, + switchToCurrentSelectionTab: switchTab, + }, + ); + }, + }, + vnode.attrs.name, + ); + } +} + +export function sliceRef(slice: SliceDetails, name?: string): m.Child { + return m(SliceRef, { + id: slice.id, + name: name ?? slice.name, + ts: slice.ts, + dur: slice.dur, + sqlTrackId: slice.trackId, + }); +} diff --git a/ui/src/frontend/widgets/sql/details/details.ts b/ui/src/frontend/widgets/sql/details/details.ts index ece8e6e803..09b7f4c526 100644 --- a/ui/src/frontend/widgets/sql/details/details.ts +++ b/ui/src/frontend/widgets/sql/details/details.ts @@ -29,10 +29,10 @@ import {renderError} from '../../../../widgets/error'; import {SqlRef} from '../../../../widgets/sql_ref'; import {Tree, TreeNode} from '../../../../widgets/tree'; import {hasArgs, renderArguments} from '../../../slice_args'; -import {asArgSetId} from '../../../sql_types'; +import {asArgSetId} from '../../../../trace_processor/sql_utils/core_types'; import {DurationWidget} from '../../../widgets/duration'; import {Timestamp as TimestampWidget} from '../../../widgets/timestamp'; -import {Arg, getArgs} from '../../../sql/args'; +import {Arg, getArgs} from '../../../../trace_processor/sql_utils/args'; // This file contains the helper to render the details tree (based on Tree // widget) for an object represented by a SQL row in some table. The user passes diff --git a/ui/src/frontend/widgets/sql/details/well_known_types.ts b/ui/src/frontend/widgets/sql/details/well_known_types.ts index 0b1588c145..37940017e1 100644 --- a/ui/src/frontend/widgets/sql/details/well_known_types.ts +++ b/ui/src/frontend/widgets/sql/details/well_known_types.ts @@ -12,16 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {SliceDetails, getSlice, sliceRef} from '../../../sql/slice'; -import {asSliceSqlId, asUpid, asUtid} from '../../../sql_types'; import { - getProcessInfo, - getThreadInfo, ProcessInfo, - renderProcessRef, - renderThreadRef, + getProcessInfo, +} from '../../../../trace_processor/sql_utils/process'; +import { + SliceDetails, + getSlice, +} from '../../../../trace_processor/sql_utils/slice'; +import { + asSliceSqlId, + asUpid, + asUtid, +} from '../../../../trace_processor/sql_utils/core_types'; +import { ThreadInfo, -} from '../../../thread_and_process_info'; + getThreadInfo, +} from '../../../../trace_processor/sql_utils/thread'; +import {renderProcessRef} from '../../process'; +import {sliceRef} from '../../slice'; +import {renderThreadRef} from '../../thread'; import {createSqlIdRefRenderer, SqlIdRefRenderer} from './details'; export const wellKnownTypes: {[key: string]: SqlIdRefRenderer} = { diff --git a/ui/src/frontend/widgets/sql/table/render_cell.ts b/ui/src/frontend/widgets/sql/table/render_cell.ts index 73f8e73113..78748529e4 100644 --- a/ui/src/frontend/widgets/sql/table/render_cell.ts +++ b/ui/src/frontend/widgets/sql/table/render_cell.ts @@ -33,8 +33,8 @@ import {SqlTableState} from './state'; import {SliceIdDisplayConfig} from './table_description'; import {Timestamp} from '../../timestamp'; import {DurationWidget} from '../../duration'; -import {SliceRef} from '../../../sql/slice'; -import {asSliceSqlId} from '../../../sql_types'; +import {asSliceSqlId} from '../../../../trace_processor/sql_utils/core_types'; +import {SliceRef} from '../../slice'; // This file is responsible for rendering a value in a given sell based on the // column type. diff --git a/ui/src/frontend/well_known_sql_tables.ts b/ui/src/frontend/widgets/sql/table/well_known_sql_tables.ts similarity index 97% rename from ui/src/frontend/well_known_sql_tables.ts rename to ui/src/frontend/widgets/sql/table/well_known_sql_tables.ts index 1d7fd77129..6bc8c811b1 100644 --- a/ui/src/frontend/well_known_sql_tables.ts +++ b/ui/src/frontend/widgets/sql/table/well_known_sql_tables.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {SqlTableDescription} from './widgets/sql/table/table'; +import {SqlTableDescription} from './table_description'; const sliceTable: SqlTableDescription = { imports: ['slices.slices'], diff --git a/ui/src/frontend/widgets/thread.ts b/ui/src/frontend/widgets/thread.ts new file mode 100644 index 0000000000..b668d1ccc1 --- /dev/null +++ b/ui/src/frontend/widgets/thread.ts @@ -0,0 +1,52 @@ +// 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 m from 'mithril'; + +import { + ThreadInfo, + getThreadName, +} from '../../trace_processor/sql_utils/thread'; +import {MenuItem, PopupMenu2} from '../../widgets/menu'; +import {Anchor} from '../../widgets/anchor'; +import {exists} from '../../base/utils'; +import {Icons} from '../../base/semantic_icons'; +import {copyToClipboard} from '../../base/clipboard'; + +export function renderThreadRef(info: ThreadInfo): m.Children { + const name = info.name; + return m( + PopupMenu2, + { + trigger: m(Anchor, getThreadName(info)), + }, + exists(name) && + m(MenuItem, { + icon: Icons.Copy, + label: 'Copy thread name', + onclick: () => copyToClipboard(name), + }), + exists(info.tid) && + m(MenuItem, { + icon: Icons.Copy, + label: 'Copy tid', + onclick: () => copyToClipboard(`${info.tid}`), + }), + m(MenuItem, { + icon: Icons.Copy, + label: 'Copy utid', + onclick: () => copyToClipboard(`${info.utid}`), + }), + ); +} diff --git a/ui/src/frontend/widgets/thread_state.ts b/ui/src/frontend/widgets/thread_state.ts new file mode 100644 index 0000000000..581f0b7a31 --- /dev/null +++ b/ui/src/frontend/widgets/thread_state.ts @@ -0,0 +1,85 @@ +// 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 m from 'mithril'; +import { + ThreadStateSqlId, + Utid, +} from '../../trace_processor/sql_utils/core_types'; +import {duration, time} from '../../base/time'; +import {Anchor} from '../../widgets/anchor'; +import {Icons} from '../../base/semantic_icons'; +import {globals} from '../globals'; +import {THREAD_STATE_TRACK_KIND} from '../../core/track_kinds'; +import {Actions} from '../../common/actions'; +import {scrollToTrackAndTs} from '../scroll_helper'; +import {ThreadState} from '../../trace_processor/sql_utils/thread_state'; + +interface ThreadStateRefAttrs { + id: ThreadStateSqlId; + ts: time; + dur: duration; + utid: Utid; + // If not present, a placeholder name will be used. + name?: string; +} + +export class ThreadStateRef implements m.ClassComponent { + view(vnode: m.Vnode) { + return m( + Anchor, + { + icon: Icons.UpdateSelection, + onclick: () => { + let trackKey: string | number | undefined; + for (const track of Object.values(globals.state.tracks)) { + const trackDesc = globals.trackManager.resolveTrackInfo(track.uri); + if ( + trackDesc && + trackDesc.tags?.kind === THREAD_STATE_TRACK_KIND && + trackDesc.tags?.utid === vnode.attrs.utid + ) { + trackKey = track.key; + } + } + + /* eslint-disable @typescript-eslint/strict-boolean-expressions */ + if (trackKey) { + /* eslint-enable */ + globals.makeSelection( + Actions.selectThreadState({ + id: vnode.attrs.id, + trackKey: trackKey.toString(), + }), + ); + + scrollToTrackAndTs(trackKey, vnode.attrs.ts, true); + } + }, + }, + vnode.attrs.name ?? `Thread State ${vnode.attrs.id}`, + ); + } +} + +export function threadStateRef(state: ThreadState): m.Child { + if (state.thread === undefined) return null; + + return m(ThreadStateRef, { + id: state.threadStateSqlId, + ts: state.ts, + dur: state.dur, + utid: state.thread?.utid, + }); +} diff --git a/ui/src/trace_processor/sql_utils/README b/ui/src/trace_processor/sql_utils/README new file mode 100644 index 0000000000..d24c27df8b --- /dev/null +++ b/ui/src/trace_processor/sql_utils/README @@ -0,0 +1,5 @@ +# sql_utils + +This directory contains the definitions of the core concepts present in +trace processor (slice / thread / process) and helper functions to fetch +related data. diff --git a/ui/src/frontend/sql/args.ts b/ui/src/trace_processor/sql_utils/args.ts similarity index 92% rename from ui/src/frontend/sql/args.ts rename to ui/src/trace_processor/sql_utils/args.ts index b8e19d6424..51aa6f8ddc 100644 --- a/ui/src/frontend/sql/args.ts +++ b/ui/src/trace_processor/sql_utils/args.ts @@ -12,15 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Engine} from '../../trace_processor/engine'; -import { - LONG_NULL, - NUM, - NUM_NULL, - STR, - STR_NULL, -} from '../../trace_processor/query_result'; -import {ArgSetId, ArgsId, asArgId} from '../sql_types'; +import {Engine} from '../engine'; +import {LONG_NULL, NUM, NUM_NULL, STR, STR_NULL} from '../query_result'; +import {ArgSetId, ArgsId, asArgId} from './core_types'; export type ArgValue = bigint | string | number | boolean | null; type ArgValueType = diff --git a/ui/src/frontend/sql_types.ts b/ui/src/trace_processor/sql_utils/core_types.ts similarity index 98% rename from ui/src/frontend/sql_types.ts rename to ui/src/trace_processor/sql_utils/core_types.ts index 33278548a1..336f4057e0 100644 --- a/ui/src/frontend/sql_types.ts +++ b/ui/src/trace_processor/sql_utils/core_types.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Brand} from '../base/brand'; +import {Brand} from '../../base/brand'; // Type-safe aliases for various flavours of ints Trace Processor exposes // (e.g. timestamp or ids into a given SQL table) and functions to work with diff --git a/ui/src/trace_processor/sql_utils/process.ts b/ui/src/trace_processor/sql_utils/process.ts new file mode 100644 index 0000000000..e1a7f38025 --- /dev/null +++ b/ui/src/trace_processor/sql_utils/process.ts @@ -0,0 +1,80 @@ +// 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 {Engine} from '../engine'; +import {NUM, NUM_NULL, STR_NULL} from '../query_result'; +import {fromNumNull} from '../sql_utils'; + +import {Upid} from './core_types'; + +// TODO(altimin): We should consider implementing some form of cache rather than querying +// the data from trace processor each time. + +export interface ProcessInfo { + upid: Upid; + pid?: number; + name?: string; + uid?: number; + packageName?: string; + versionCode?: number; +} + +export async function getProcessInfo( + engine: Engine, + upid: Upid, +): Promise { + const res = await engine.query(` + include perfetto module android.process_metadata; + select + p.upid, + p.pid, + p.name, + p.uid, + m.package_name as packageName, + m.version_code as versionCode + from process p + left join android_process_metadata m using (upid) + where upid = ${upid}; + `); + const row = res.firstRow({ + upid: NUM, + pid: NUM, + name: STR_NULL, + uid: NUM_NULL, + packageName: STR_NULL, + versionCode: NUM_NULL, + }); + return { + upid, + pid: row.pid, + name: row.name ?? undefined, + uid: fromNumNull(row.uid), + packageName: row.packageName ?? undefined, + versionCode: fromNumNull(row.versionCode), + }; +} + +function getDisplayName( + name: string | undefined, + id: number | undefined, +): string | undefined { + if (name === undefined) { + return id === undefined ? undefined : `${id}`; + } + return id === undefined ? name : `${name} [${id}]`; +} + +export function getProcessName(info?: ProcessInfo): string | undefined { + return getDisplayName(info?.name, info?.pid); +} diff --git a/ui/src/frontend/sql/slice.ts b/ui/src/trace_processor/sql_utils/slice.ts similarity index 70% rename from ui/src/frontend/sql/slice.ts rename to ui/src/trace_processor/sql_utils/slice.ts index e2648afd0e..c53cb73467 100644 --- a/ui/src/frontend/sql/slice.ts +++ b/ui/src/trace_processor/sql_utils/slice.ts @@ -12,28 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import m from 'mithril'; - -import {BigintMath} from '../../base/bigint_math'; -import {Icons} from '../../base/semantic_icons'; import {duration, Time, time} from '../../base/time'; import {exists} from '../../base/utils'; -import {Engine} from '../../trace_processor/engine'; -import { - LONG, - LONG_NULL, - NUM, - NUM_NULL, - STR, - STR_NULL, -} from '../../trace_processor/query_result'; -import { - constraintsToQuerySuffix, - SQLConstraints, -} from '../../trace_processor/sql_utils'; -import {Anchor} from '../../widgets/anchor'; -import {globals} from '../globals'; -import {focusHorizontalRange, verticalScrollToTrack} from '../scroll_helper'; +import {Engine} from '../engine'; +import {LONG, LONG_NULL, NUM, NUM_NULL, STR, STR_NULL} from '../query_result'; +import {constraintsToQuerySuffix, SQLConstraints} from '../sql_utils'; import { asArgSetId, asSliceSqlId, @@ -42,15 +25,11 @@ import { SliceSqlId, Upid, Utid, -} from '../sql_types'; -import { - getProcessInfo, - getThreadInfo, - ProcessInfo, - ThreadInfo, -} from '../thread_and_process_info'; +} from './core_types'; import {Arg, getArgs} from './args'; +import {getThreadInfo, ThreadInfo} from './thread'; +import {getProcessInfo, ProcessInfo} from './process'; // Basic information about a slice. export interface SliceDetails { @@ -204,68 +183,6 @@ export async function getSlice( return result[0]; } -interface SliceRefAttrs { - readonly id: SliceSqlId; - readonly name: string; - readonly ts: time; - readonly dur: duration; - readonly sqlTrackId: number; - - // Whether clicking on the reference should change the current tab - // to "current selection" tab in addition to updating the selection - // and changing the viewport. True by default. - readonly switchToCurrentSelectionTab?: boolean; -} - -export class SliceRef implements m.ClassComponent { - view(vnode: m.Vnode) { - const switchTab = vnode.attrs.switchToCurrentSelectionTab ?? true; - return m( - Anchor, - { - icon: Icons.UpdateSelection, - onclick: () => { - const trackKeyByTrackId = globals.trackManager.trackKeyByTrackId; - const trackKey = trackKeyByTrackId.get(vnode.attrs.sqlTrackId); - if (trackKey === undefined) return; - verticalScrollToTrack(trackKey, true); - // Clamp duration to 1 - i.e. for instant events - const dur = BigintMath.max(1n, vnode.attrs.dur); - focusHorizontalRange( - vnode.attrs.ts, - Time.fromRaw(vnode.attrs.ts + dur), - ); - - globals.setLegacySelection( - { - kind: 'SLICE', - id: vnode.attrs.id, - trackKey, - table: 'slice', - }, - { - clearSearch: true, - pendingScrollId: undefined, - switchToCurrentSelectionTab: switchTab, - }, - ); - }, - }, - vnode.attrs.name, - ); - } -} - -export function sliceRef(slice: SliceDetails, name?: string): m.Child { - return m(SliceRef, { - id: slice.id, - name: name ?? slice.name, - ts: slice.ts, - dur: slice.dur, - sqlTrackId: slice.trackId, - }); -} - // A slice tree node, combining the information about the given slice with // information about its descendants. export interface SliceTreeNode extends SliceDetails { diff --git a/ui/src/trace_processor/sql_utils/thread.ts b/ui/src/trace_processor/sql_utils/thread.ts new file mode 100644 index 0000000000..df70bf5edf --- /dev/null +++ b/ui/src/trace_processor/sql_utils/thread.ts @@ -0,0 +1,77 @@ +// 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 {Engine} from '../engine'; +import {NUM, NUM_NULL, STR_NULL} from '../query_result'; +import {fromNumNull} from '../sql_utils'; +import {ProcessInfo, getProcessInfo, getProcessName} from './process'; + +import {Upid, Utid} from './core_types'; + +// TODO(altimin): We should consider implementing some form of cache rather than querying +// the data from trace processor each time. + +export interface ThreadInfo { + utid: Utid; + tid?: number; + name?: string; + process?: ProcessInfo; +} + +export async function getThreadInfo( + engine: Engine, + utid: Utid, +): Promise { + const it = ( + await engine.query(` + SELECT tid, name, upid + FROM thread + WHERE utid = ${utid}; + `) + ).iter({tid: NUM, name: STR_NULL, upid: NUM_NULL}); + if (!it.valid()) { + return { + utid, + }; + } + const upid = fromNumNull(it.upid) as Upid | undefined; + return { + utid, + tid: it.tid, + name: it.name ?? undefined, + process: upid ? await getProcessInfo(engine, upid) : undefined, + }; +} + +function getDisplayName( + name: string | undefined, + id: number | undefined, +): string | undefined { + if (name === undefined) { + return id === undefined ? undefined : `${id}`; + } + return id === undefined ? name : `${name} [${id}]`; +} + +export function getThreadName(info?: ThreadInfo): string | undefined { + return getDisplayName(info?.name, info?.tid); +} + +// Return the full thread name, including the process name. +export function getFullThreadName(info?: ThreadInfo): string | undefined { + if (info?.process === undefined) { + return getThreadName(info); + } + return `${getThreadName(info)} ${getProcessName(info.process)}`; +} diff --git a/ui/src/frontend/thread_state.ts b/ui/src/trace_processor/sql_utils/thread_state.ts similarity index 64% rename from ui/src/frontend/thread_state.ts rename to ui/src/trace_processor/sql_utils/thread_state.ts index 7c7b8adf62..4836ddf26e 100644 --- a/ui/src/frontend/thread_state.ts +++ b/ui/src/trace_processor/sql_utils/thread_state.ts @@ -12,30 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -import m from 'mithril'; - -import {Icons} from '../base/semantic_icons'; -import {duration, Time, time} from '../base/time'; -import {exists} from '../base/utils'; -import {Actions} from '../common/actions'; -import {translateState} from '../common/thread_state'; -import {Engine} from '../trace_processor/engine'; -import {LONG, NUM, NUM_NULL, STR_NULL} from '../trace_processor/query_result'; +import {duration, Time, time} from '../../base/time'; +import {exists} from '../../base/utils'; +import {translateState} from '../../common/thread_state'; +import {Engine} from '../engine'; +import {LONG, NUM, NUM_NULL, STR_NULL} from '../query_result'; import { constraintsToQuerySuffix, fromNumNull, SQLConstraints, -} from '../trace_processor/sql_utils'; -import {Anchor} from '../widgets/anchor'; +} from '../sql_utils'; -import {globals} from './globals'; -import {scrollToTrackAndTs} from './scroll_helper'; -import {asUtid, SchedSqlId, ThreadStateSqlId, Utid} from './sql_types'; -import {getThreadInfo, ThreadInfo} from './thread_and_process_info'; -import { - CPU_SLICE_TRACK_KIND, - THREAD_STATE_TRACK_KIND, -} from '../core/track_kinds'; +import {globals} from '../../frontend/globals'; +import {scrollToTrackAndTs} from '../../frontend/scroll_helper'; +import {asUtid, SchedSqlId, ThreadStateSqlId} from './core_types'; +import {CPU_SLICE_TRACK_KIND} from '../../core/track_kinds'; +import {getThreadInfo, ThreadInfo} from './thread'; // Representation of a single thread state object, corresponding to // a row for the |thread_slice| table. @@ -167,61 +159,3 @@ export function goToSchedSlice(cpu: number, id: SchedSqlId, ts: time) { scrollToTrackAndTs(trackId, ts); } - -interface ThreadStateRefAttrs { - id: ThreadStateSqlId; - ts: time; - dur: duration; - utid: Utid; - // If not present, a placeholder name will be used. - name?: string; -} - -export class ThreadStateRef implements m.ClassComponent { - view(vnode: m.Vnode) { - return m( - Anchor, - { - icon: Icons.UpdateSelection, - onclick: () => { - let trackKey: string | number | undefined; - for (const track of Object.values(globals.state.tracks)) { - const trackDesc = globals.trackManager.resolveTrackInfo(track.uri); - if ( - trackDesc && - trackDesc.tags?.kind === THREAD_STATE_TRACK_KIND && - trackDesc.tags?.utid === vnode.attrs.utid - ) { - trackKey = track.key; - } - } - - /* eslint-disable @typescript-eslint/strict-boolean-expressions */ - if (trackKey) { - /* eslint-enable */ - globals.makeSelection( - Actions.selectThreadState({ - id: vnode.attrs.id, - trackKey: trackKey.toString(), - }), - ); - - scrollToTrackAndTs(trackKey, vnode.attrs.ts, true); - } - }, - }, - vnode.attrs.name ?? `Thread State ${vnode.attrs.id}`, - ); - } -} - -export function threadStateRef(state: ThreadState): m.Child { - if (state.thread === undefined) return null; - - return m(ThreadStateRef, { - id: state.threadStateSqlId, - ts: state.ts, - dur: state.dur, - utid: state.thread?.utid, - }); -} diff --git a/ui/src/frontend/sql_utils_unittest.ts b/ui/src/trace_processor/sql_utils_unittest.ts similarity index 96% rename from ui/src/frontend/sql_utils_unittest.ts rename to ui/src/trace_processor/sql_utils_unittest.ts index 0c056bc018..6c3581cf56 100644 --- a/ui/src/frontend/sql_utils_unittest.ts +++ b/ui/src/trace_processor/sql_utils_unittest.ts @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - constraintsToQueryPrefix, - constraintsToQuerySuffix, -} from '../trace_processor/sql_utils'; +import {constraintsToQueryPrefix, constraintsToQuerySuffix} from './sql_utils'; // Clean up repeated whitespaces to allow for easier testing. function normalize(s: string): string {