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 {splitLinesNonEmpty} from '../../../base/string_utils'; 16import protos from '../../../protos'; 17import {RecordSubpage, RecordProbe} from '../config/config_interfaces'; 18import {TraceConfigBuilder} from '../config/trace_config_builder'; 19import {TypedMultiselect} from './widgets/multiselect'; 20import {POLL_INTERVAL_SLIDER, Slider} from './widgets/slider'; 21import {Textarea} from './widgets/textarea'; 22import {Toggle} from './widgets/toggle'; 23 24export function androidRecordSection(): RecordSubpage { 25 return { 26 kind: 'PROBES_PAGE', 27 id: 'android', 28 title: 'Android apps & svcs', 29 subtitle: 'Android-specific data sources', 30 icon: 'android', 31 probes: [ 32 atrace(), 33 logcat(), 34 frameTimeline(), 35 gameInterventions(), 36 netTracing(), 37 statsdAtoms(), 38 ], 39 }; 40} 41 42function atrace(): RecordProbe { 43 const settings = { 44 categories: new TypedMultiselect<string>({ 45 options: new Map( 46 Object.entries(ATRACE_CATEGORIES).map(([id, name]) => [ 47 `${id}: ${name}`, 48 id, 49 ]), 50 ), 51 }), 52 apps: new Textarea({ 53 title: 'Process / package names to trace', 54 placeholder: 'e.g. system_server\ncom.android.settings', 55 }), 56 allApps: new Toggle({ 57 title: 'Record events from all Android apps and services', 58 cssClass: '.thin', 59 onChange(allAppsEnabled: boolean) { 60 settings.apps.attrs.disabled = allAppsEnabled; 61 }, 62 }), 63 }; 64 return { 65 id: 'atrace', 66 title: 'Atrace userspace annotations', 67 image: 'rec_atrace.png', 68 description: 69 'Enables C++ / Java codebase annotations (ATRACE_BEGIN() / os.Trace())', 70 supportedPlatforms: ['ANDROID'], 71 settings, 72 genConfig: function (tc: TraceConfigBuilder) { 73 tc.addAtraceCategories(...settings.categories.selectedValues()); 74 if (settings.allApps.enabled) { 75 tc.addAtraceApps('*'); 76 } else { 77 for (const line of splitLinesNonEmpty(settings.apps.text)) { 78 tc.addAtraceApps(line); 79 } 80 } 81 if ( 82 settings.categories.selectedKeys().length > 0 || 83 settings.allApps.enabled 84 ) { 85 tc.addFtraceEvents('ftrace/print'); 86 } 87 }, 88 }; 89} 90 91function logcat(): RecordProbe { 92 const settings = { 93 buffers: new TypedMultiselect<protos.AndroidLogId>({ 94 options: new Map( 95 Object.entries({ 96 'Crash': protos.AndroidLogId.LID_CRASH, 97 'Main': protos.AndroidLogId.LID_DEFAULT, 98 'Binary events': protos.AndroidLogId.LID_EVENTS, 99 'Kernel': protos.AndroidLogId.LID_KERNEL, 100 'Radio': protos.AndroidLogId.LID_RADIO, 101 'Security': protos.AndroidLogId.LID_SECURITY, 102 'Stats': protos.AndroidLogId.LID_STATS, 103 'System': protos.AndroidLogId.LID_SYSTEM, 104 }), 105 ), 106 }), 107 }; 108 return { 109 id: 'logcat', 110 title: 'Event log (logcat)', 111 image: 'rec_logcat.png', 112 description: 113 'Streams the event log into the trace. If no buffer filter is ' + 114 'specified, all buffers are selected.', 115 supportedPlatforms: ['ANDROID'], 116 settings, 117 genConfig: function (tc: TraceConfigBuilder) { 118 const logIds = settings.buffers.selectedValues(); 119 tc.addDataSource('android.log').androidLogConfig = { 120 logIds: logIds.length > 0 ? logIds : undefined, 121 }; 122 }, 123 }; 124} 125 126function frameTimeline(): RecordProbe { 127 return { 128 id: 'android_frame_timeline', 129 title: 'Frame timeline', 130 description: 131 'Records expected/actual frame timings from surface_flinger.' + 132 'Requires Android 12 (S) or above.', 133 supportedPlatforms: ['ANDROID'], 134 docsLink: 'https://perfetto.dev/docs/data-sources/frametimeline', 135 genConfig: function (tc: TraceConfigBuilder) { 136 tc.addDataSource('android.surfaceflinger.frametimeline'); 137 }, 138 }; 139} 140 141function gameInterventions(): RecordProbe { 142 return { 143 id: 'android_game_interventions', 144 title: 'Game intervention list', 145 description: 146 'List game modes and interventions. Requires Android 13 (T) or above.', 147 supportedPlatforms: ['ANDROID'], 148 docsLink: 149 'https://perfetto.dev/docs/data-sources/android-game-intervention-list', 150 genConfig: function (tc: TraceConfigBuilder) { 151 tc.addDataSource('android.game_interventions'); 152 }, 153 }; 154} 155 156function netTracing(): RecordProbe { 157 const settings = {pollMs: new Slider(POLL_INTERVAL_SLIDER)}; 158 return { 159 id: 'network_tracing', 160 title: 'Network Tracing', 161 description: 162 'Records detailed information on network packets. ' + 163 'Requires Android 14 (U) or above', 164 supportedPlatforms: ['ANDROID'], 165 settings, 166 genConfig: function (tc: TraceConfigBuilder) { 167 tc.addDataSource('android.network_packets').networkPacketTraceConfig = { 168 pollMs: settings.pollMs.value, 169 }; 170 }, 171 }; 172} 173 174function statsdAtoms(): RecordProbe { 175 const settings = { 176 pushAtoms: new TypedMultiselect<protos.AtomId>({ 177 title: 'Push atoms', 178 options: new Map( 179 Object.entries(protos.AtomId) 180 .filter(([_, v]) => typeof v === 'number' && v > 2 && v < 9999) 181 .map(([k, v]) => [k, v as protos.AtomId]), 182 ), 183 }), 184 rawPushIds: new Textarea({ 185 placeholder: 186 'Add raw pushed atoms IDs, one per line, e.g.:\n' + '818\n' + '819', 187 }), 188 pullAtoms: new TypedMultiselect<protos.AtomId>({ 189 title: 'Pull atoms', 190 options: new Map( 191 Object.entries(protos.AtomId) 192 .filter(([_, v]) => typeof v === 'number' && v > 10000 && v < 99999) 193 .map(([k, v]) => [k, v as protos.AtomId]), 194 ), 195 }), 196 rawPullIds: new Textarea({ 197 placeholder: 198 'Add raw pulled atom IDs, one per line, e.g.:\n10063\n10064\n', 199 }), 200 pullInterval: new Slider({...POLL_INTERVAL_SLIDER, default: 5000}), 201 pullPkg: new Textarea({ 202 placeholder: 203 'Add pulled atom packages, one per line, e.g.:\n' + 204 'com.android.providers.telephony', 205 }), 206 }; 207 return { 208 id: 'statsd', 209 title: 'Statsd atoms', 210 description: 'Record instances of statsd atoms to the Statsd Atoms track.', 211 supportedPlatforms: ['ANDROID'], 212 docsLink: 213 'https://cs.android.com/android/platform/superproject/main/+/main:frameworks/proto_logging/stats/atoms.proto', 214 settings, 215 genConfig: function (tc: TraceConfigBuilder) { 216 const pkg = splitLinesNonEmpty(settings.pullPkg.text); 217 const pullIds = settings.pullAtoms.selectedValues(); 218 const rawPullIds = splitLinesNonEmpty(settings.rawPullIds.text).map((l) => 219 parseInt(l.trim()), 220 ); 221 const hasPull = pullIds.length > 0 || rawPullIds.length > 0; 222 tc.addDataSource('android.statsd').statsdTracingConfig = { 223 pushAtomId: settings.pushAtoms.selectedValues(), 224 rawPushAtomId: splitLinesNonEmpty(settings.rawPushIds.text).map((l) => 225 parseInt(l.trim()), 226 ), 227 pullConfig: hasPull 228 ? [ 229 { 230 pullAtomId: pullIds, 231 rawPullAtomId: rawPullIds, 232 pullFrequencyMs: settings.pullInterval.value, 233 packages: pkg.length > 0 ? pkg : undefined, 234 }, 235 ] 236 : undefined, 237 }; 238 }, 239 }; 240} 241 242const ATRACE_CATEGORIES = { 243 adb: 'ADB', 244 aidl: 'AIDL calls', 245 am: 'Activity Manager', 246 audio: 'Audio', 247 binder_driver: 'Binder Kernel driver', 248 binder_lock: 'Binder global lock trace', 249 bionic: 'Bionic C library', 250 camera: 'Camera', 251 dalvik: 'ART & Dalvik', 252 database: 'Database', 253 gfx: 'Graphics', 254 hal: 'Hardware Modules', 255 input: 'Input', 256 network: 'Network', 257 nnapi: 'Neural Network API', 258 pm: 'Package Manager', 259 power: 'Power Management', 260 res: 'Resource Loading', 261 rro: 'Resource Overlay', 262 rs: 'RenderScript', 263 sm: 'Sync Manager', 264 ss: 'System Server', 265 vibrator: 'Vibrator', 266 video: 'Video', 267 view: 'View System', 268 webview: 'WebView', 269 wm: 'Window Manager', 270}; 271