1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import m from 'mithril'; 16import {copyToClipboard} from '../../base/clipboard'; 17import {Icons} from '../../base/semantic_icons'; 18import {exists} from '../../base/utils'; 19import {addEphemeralTab} from '../details/add_ephemeral_tab'; 20import {getThreadInfo, getThreadName, ThreadInfo} from '../sql_utils/thread'; 21import {Anchor} from '../../widgets/anchor'; 22import {MenuItem, PopupMenu2} from '../../widgets/menu'; 23import {ThreadDetailsTab} from '../details/thread_details_tab'; 24import { 25 createSqlIdRefRenderer, 26 sqlIdRegistry, 27} from './sql/details/sql_ref_renderer_registry'; 28import {asUtid} from '../sql_utils/core_types'; 29import {Utid} from '../sql_utils/core_types'; 30import {AppImpl} from '../../core/app_impl'; 31 32export function showThreadDetailsMenuItem( 33 utid: Utid, 34 tid?: number, 35): m.Children { 36 return m(MenuItem, { 37 icon: Icons.ExternalLink, 38 label: 'Show thread details', 39 onclick: () => { 40 // TODO(primiano): `trace` should be injected, but doing so would require 41 // an invasive refactoring of most classes in frontend/widgets/sql/*. 42 const trace = AppImpl.instance.trace; 43 if (trace === undefined) return; 44 addEphemeralTab( 45 'threadDetails', 46 new ThreadDetailsTab({ 47 trace, 48 utid, 49 tid, 50 }), 51 ); 52 }, 53 }); 54} 55 56export function threadRefMenuItems(info: { 57 utid: Utid; 58 name?: string; 59 tid?: number; 60}): m.Children { 61 // We capture a copy to be able to pass it across async boundary to `onclick`. 62 const name = info.name; 63 return [ 64 exists(name) && 65 m(MenuItem, { 66 icon: Icons.Copy, 67 label: 'Copy thread name', 68 onclick: () => copyToClipboard(name), 69 }), 70 exists(info.tid) && 71 m(MenuItem, { 72 icon: Icons.Copy, 73 label: 'Copy tid', 74 onclick: () => copyToClipboard(`${info.tid}`), 75 }), 76 m(MenuItem, { 77 icon: Icons.Copy, 78 label: 'Copy utid', 79 onclick: () => copyToClipboard(`${info.utid}`), 80 }), 81 showThreadDetailsMenuItem(info.utid, info.tid), 82 ]; 83} 84 85export function renderThreadRef(info: { 86 utid: Utid; 87 name?: string; 88 tid?: number; 89}): m.Children { 90 return m( 91 PopupMenu2, 92 { 93 trigger: m(Anchor, getThreadName(info)), 94 }, 95 threadRefMenuItems(info), 96 ); 97} 98 99sqlIdRegistry['thread'] = createSqlIdRefRenderer<ThreadInfo>( 100 async (engine, id) => await getThreadInfo(engine, asUtid(Number(id))), 101 (data: ThreadInfo) => ({ 102 value: renderThreadRef(data), 103 }), 104); 105