1// Copyright (C) 2022 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'; 16 17import {MeminfoCounters, VmstatCounters} from '../../protos'; 18import {globals} from '../globals'; 19import { 20 Dropdown, 21 DropdownAttrs, 22 Probe, 23 ProbeAttrs, 24 Slider, 25 SliderAttrs, 26 Textarea, 27 TextareaAttrs, 28 Toggle, 29 ToggleAttrs, 30} from '../record_widgets'; 31 32import {POLL_INTERVAL_MS, RecordingSectionAttrs} from './recording_sections'; 33 34class HeapSettings implements m.ClassComponent<RecordingSectionAttrs> { 35 view({attrs}: m.CVnode<RecordingSectionAttrs>) { 36 const valuesForMS = [ 37 0, 38 1000, 39 10 * 1000, 40 30 * 1000, 41 60 * 1000, 42 5 * 60 * 1000, 43 10 * 60 * 1000, 44 30 * 60 * 1000, 45 60 * 60 * 1000, 46 ]; 47 const valuesForShMemBuff = [ 48 0, 49 512, 50 1024, 51 2 * 1024, 52 4 * 1024, 53 8 * 1024, 54 16 * 1024, 55 32 * 1024, 56 64 * 1024, 57 128 * 1024, 58 256 * 1024, 59 512 * 1024, 60 1024 * 1024, 61 64 * 1024 * 1024, 62 128 * 1024 * 1024, 63 256 * 1024 * 1024, 64 512 * 1024 * 1024, 65 ]; 66 67 return m( 68 `.${attrs.cssClass}`, 69 m(Textarea, { 70 title: 'Names or pids of the processes to track (required)', 71 docsLink: 72 'https://perfetto.dev/docs/data-sources/native-heap-profiler#heapprofd-targets', 73 placeholder: 74 'One per line, e.g.:\n' + 75 'system_server\n' + 76 'com.google.android.apps.photos\n' + 77 '1503', 78 set: (cfg, val) => (cfg.hpProcesses = val), 79 get: (cfg) => cfg.hpProcesses, 80 } as TextareaAttrs), 81 m(Slider, { 82 title: 'Sampling interval', 83 cssClass: '.thin', 84 values: [ 85 /* eslint-disable no-multi-spaces */ 86 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 87 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 88 /* eslint-enable no-multi-spaces */ 89 ], 90 unit: 'B', 91 min: 0, 92 set: (cfg, val) => (cfg.hpSamplingIntervalBytes = val), 93 get: (cfg) => cfg.hpSamplingIntervalBytes, 94 } as SliderAttrs), 95 m(Slider, { 96 title: 'Continuous dumps interval ', 97 description: 'Time between following dumps (0 = disabled)', 98 cssClass: '.thin', 99 values: valuesForMS, 100 unit: 'ms', 101 min: 0, 102 set: (cfg, val) => { 103 cfg.hpContinuousDumpsInterval = val; 104 }, 105 get: (cfg) => cfg.hpContinuousDumpsInterval, 106 } as SliderAttrs), 107 m(Slider, { 108 title: 'Continuous dumps phase', 109 description: 'Time before first dump', 110 cssClass: `.thin${ 111 globals.state.recordConfig.hpContinuousDumpsInterval === 0 112 ? '.greyed-out' 113 : '' 114 }`, 115 values: valuesForMS, 116 unit: 'ms', 117 min: 0, 118 disabled: globals.state.recordConfig.hpContinuousDumpsInterval === 0, 119 set: (cfg, val) => (cfg.hpContinuousDumpsPhase = val), 120 get: (cfg) => cfg.hpContinuousDumpsPhase, 121 } as SliderAttrs), 122 m(Slider, { 123 title: `Shared memory buffer`, 124 cssClass: '.thin', 125 values: valuesForShMemBuff.filter( 126 (value) => value === 0 || (value >= 8192 && value % 4096 === 0), 127 ), 128 unit: 'B', 129 min: 0, 130 set: (cfg, val) => (cfg.hpSharedMemoryBuffer = val), 131 get: (cfg) => cfg.hpSharedMemoryBuffer, 132 } as SliderAttrs), 133 m(Toggle, { 134 title: 'Block client', 135 cssClass: '.thin', 136 descr: `Slow down target application if profiler cannot keep up.`, 137 setEnabled: (cfg, val) => (cfg.hpBlockClient = val), 138 isEnabled: (cfg) => cfg.hpBlockClient, 139 } as ToggleAttrs), 140 m(Toggle, { 141 title: 'All custom allocators (Q+)', 142 cssClass: '.thin', 143 descr: `If the target application exposes custom allocators, also 144sample from those.`, 145 setEnabled: (cfg, val) => (cfg.hpAllHeaps = val), 146 isEnabled: (cfg) => cfg.hpAllHeaps, 147 } as ToggleAttrs), 148 // TODO(hjd): Add advanced options. 149 ); 150 } 151} 152 153class JavaHeapDumpSettings implements m.ClassComponent<RecordingSectionAttrs> { 154 view({attrs}: m.CVnode<RecordingSectionAttrs>) { 155 const valuesForMS = [ 156 0, 157 1000, 158 10 * 1000, 159 30 * 1000, 160 60 * 1000, 161 5 * 60 * 1000, 162 10 * 60 * 1000, 163 30 * 60 * 1000, 164 60 * 60 * 1000, 165 ]; 166 167 return m( 168 `.${attrs.cssClass}`, 169 m(Textarea, { 170 title: 'Names or pids of the processes to track (required)', 171 placeholder: 'One per line, e.g.:\n' + 'com.android.vending\n' + '1503', 172 set: (cfg, val) => (cfg.jpProcesses = val), 173 get: (cfg) => cfg.jpProcesses, 174 } as TextareaAttrs), 175 m(Slider, { 176 title: 'Continuous dumps interval ', 177 description: 'Time between following dumps (0 = disabled)', 178 cssClass: '.thin', 179 values: valuesForMS, 180 unit: 'ms', 181 min: 0, 182 set: (cfg, val) => { 183 cfg.jpContinuousDumpsInterval = val; 184 }, 185 get: (cfg) => cfg.jpContinuousDumpsInterval, 186 } as SliderAttrs), 187 m(Slider, { 188 title: 'Continuous dumps phase', 189 description: 'Time before first dump', 190 cssClass: `.thin${ 191 globals.state.recordConfig.jpContinuousDumpsInterval === 0 192 ? '.greyed-out' 193 : '' 194 }`, 195 values: valuesForMS, 196 unit: 'ms', 197 min: 0, 198 disabled: globals.state.recordConfig.jpContinuousDumpsInterval === 0, 199 set: (cfg, val) => (cfg.jpContinuousDumpsPhase = val), 200 get: (cfg) => cfg.jpContinuousDumpsPhase, 201 } as SliderAttrs), 202 ); 203 } 204} 205 206export class MemorySettings implements m.ClassComponent<RecordingSectionAttrs> { 207 view({attrs}: m.CVnode<RecordingSectionAttrs>) { 208 const meminfoOpts = new Map<string, string>(); 209 for (const x in MeminfoCounters) { 210 if ( 211 typeof MeminfoCounters[x] === 'number' && 212 !`${x}`.endsWith('_UNSPECIFIED') 213 ) { 214 meminfoOpts.set(x, x.replace('MEMINFO_', '').toLowerCase()); 215 } 216 } 217 const vmstatOpts = new Map<string, string>(); 218 for (const x in VmstatCounters) { 219 if ( 220 typeof VmstatCounters[x] === 'number' && 221 !`${x}`.endsWith('_UNSPECIFIED') 222 ) { 223 vmstatOpts.set(x, x.replace('VMSTAT_', '').toLowerCase()); 224 } 225 } 226 return m( 227 `.record-section${attrs.cssClass}`, 228 m( 229 Probe, 230 { 231 title: 'Native heap profiling', 232 img: 'rec_native_heap_profiler.png', 233 descr: `Track native heap allocations & deallocations of an Android 234 process. (Available on Android 10+)`, 235 setEnabled: (cfg, val) => (cfg.heapProfiling = val), 236 isEnabled: (cfg) => cfg.heapProfiling, 237 } as ProbeAttrs, 238 m(HeapSettings, attrs), 239 ), 240 m( 241 Probe, 242 { 243 title: 'Java heap dumps', 244 img: 'rec_java_heap_dump.png', 245 descr: `Dump information about the Java object graph of an 246 Android app. (Available on Android 11+)`, 247 setEnabled: (cfg, val) => (cfg.javaHeapDump = val), 248 isEnabled: (cfg) => cfg.javaHeapDump, 249 } as ProbeAttrs, 250 m(JavaHeapDumpSettings, attrs), 251 ), 252 m( 253 Probe, 254 { 255 title: 'Kernel meminfo', 256 img: 'rec_meminfo.png', 257 descr: 'Polling of /proc/meminfo', 258 setEnabled: (cfg, val) => (cfg.meminfo = val), 259 isEnabled: (cfg) => cfg.meminfo, 260 } as ProbeAttrs, 261 m(Slider, { 262 title: 'Poll interval', 263 cssClass: '.thin', 264 values: POLL_INTERVAL_MS, 265 unit: 'ms', 266 set: (cfg, val) => (cfg.meminfoPeriodMs = val), 267 get: (cfg) => cfg.meminfoPeriodMs, 268 } as SliderAttrs), 269 m(Dropdown, { 270 title: 'Select counters', 271 cssClass: '.multicolumn', 272 options: meminfoOpts, 273 set: (cfg, val) => (cfg.meminfoCounters = val), 274 get: (cfg) => cfg.meminfoCounters, 275 } as DropdownAttrs), 276 ), 277 m(Probe, { 278 title: 'High-frequency memory events', 279 img: 'rec_mem_hifreq.png', 280 descr: `Allows to track short memory spikes and transitories through 281 ftrace's mm_event, rss_stat and ion events. Available only 282 on recent Android Q+ kernels`, 283 setEnabled: (cfg, val) => (cfg.memHiFreq = val), 284 isEnabled: (cfg) => cfg.memHiFreq, 285 } as ProbeAttrs), 286 m(Probe, { 287 title: 'Low memory killer', 288 img: 'rec_lmk.png', 289 descr: `Record LMK events. Works both with the old in-kernel LMK 290 and the newer userspace lmkd. It also tracks OOM score 291 adjustments.`, 292 setEnabled: (cfg, val) => (cfg.memLmk = val), 293 isEnabled: (cfg) => cfg.memLmk, 294 } as ProbeAttrs), 295 m( 296 Probe, 297 { 298 title: 'Per process stats', 299 img: 'rec_ps_stats.png', 300 descr: `Periodically samples all processes in the system tracking: 301 their thread list, memory counters (RSS, swap and other 302 /proc/status counters) and oom_score_adj.`, 303 setEnabled: (cfg, val) => (cfg.procStats = val), 304 isEnabled: (cfg) => cfg.procStats, 305 } as ProbeAttrs, 306 m(Slider, { 307 title: 'Poll interval', 308 cssClass: '.thin', 309 values: POLL_INTERVAL_MS, 310 unit: 'ms', 311 set: (cfg, val) => (cfg.procStatsPeriodMs = val), 312 get: (cfg) => cfg.procStatsPeriodMs, 313 } as SliderAttrs), 314 ), 315 m( 316 Probe, 317 { 318 title: 'Virtual memory stats', 319 img: 'rec_vmstat.png', 320 descr: `Periodically polls virtual memory stats from /proc/vmstat. 321 Allows to gather statistics about swap, eviction, 322 compression and pagecache efficiency`, 323 setEnabled: (cfg, val) => (cfg.vmstat = val), 324 isEnabled: (cfg) => cfg.vmstat, 325 } as ProbeAttrs, 326 m(Slider, { 327 title: 'Poll interval', 328 cssClass: '.thin', 329 values: POLL_INTERVAL_MS, 330 unit: 'ms', 331 set: (cfg, val) => (cfg.vmstatPeriodMs = val), 332 get: (cfg) => cfg.vmstatPeriodMs, 333 } as SliderAttrs), 334 m(Dropdown, { 335 title: 'Select counters', 336 cssClass: '.multicolumn', 337 options: vmstatOpts, 338 set: (cfg, val) => (cfg.vmstatCounters = val), 339 get: (cfg) => cfg.vmstatCounters, 340 } as DropdownAttrs), 341 ), 342 ); 343 } 344} 345