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 '../../common/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: 'One per line, e.g.:\n' + 74 'system_server\n' + 75 'com.google.android.apps.photos\n' + 76 '1503', 77 set: (cfg, val) => cfg.hpProcesses = val, 78 get: (cfg) => cfg.hpProcesses, 79 } as TextareaAttrs), 80 m(Slider, { 81 title: 'Sampling interval', 82 cssClass: '.thin', 83 values: [ 84 /* eslint-disable no-multi-spaces */ 85 0, 1, 2, 4, 8, 16, 32, 64, 86 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 87 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 values: valuesForMS, 115 unit: 'ms', 116 min: 0, 117 disabled: globals.state.recordConfig.hpContinuousDumpsInterval === 0, 118 set: (cfg, val) => cfg.hpContinuousDumpsPhase = val, 119 get: (cfg) => cfg.hpContinuousDumpsPhase, 120 } as SliderAttrs), 121 m(Slider, { 122 title: `Shared memory buffer`, 123 cssClass: '.thin', 124 values: valuesForShMemBuff.filter( 125 (value) => value === 0 || value >= 8192 && value % 4096 === 0), 126 unit: 'B', 127 min: 0, 128 set: (cfg, val) => cfg.hpSharedMemoryBuffer = val, 129 get: (cfg) => cfg.hpSharedMemoryBuffer, 130 } as SliderAttrs), 131 m(Toggle, { 132 title: 'Block client', 133 cssClass: '.thin', 134 descr: `Slow down target application if profiler cannot keep up.`, 135 setEnabled: (cfg, val) => cfg.hpBlockClient = val, 136 isEnabled: (cfg) => cfg.hpBlockClient, 137 } as ToggleAttrs), 138 m(Toggle, { 139 title: 'All custom allocators (Q+)', 140 cssClass: '.thin', 141 descr: `If the target application exposes custom allocators, also 142sample from those.`, 143 setEnabled: (cfg, val) => cfg.hpAllHeaps = val, 144 isEnabled: (cfg) => cfg.hpAllHeaps, 145 } as ToggleAttrs), 146 // TODO(hjd): Add advanced options. 147 ); 148 } 149} 150 151class JavaHeapDumpSettings implements m.ClassComponent<RecordingSectionAttrs> { 152 view({attrs}: m.CVnode<RecordingSectionAttrs>) { 153 const valuesForMS = [ 154 0, 155 1000, 156 10 * 1000, 157 30 * 1000, 158 60 * 1000, 159 5 * 60 * 1000, 160 10 * 60 * 1000, 161 30 * 60 * 1000, 162 60 * 60 * 1000, 163 ]; 164 165 return m( 166 `.${attrs.cssClass}`, 167 m(Textarea, { 168 title: 'Names or pids of the processes to track (required)', 169 placeholder: 'One per line, e.g.:\n' + 170 'com.android.vending\n' + 171 '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 values: valuesForMS, 195 unit: 'ms', 196 min: 0, 197 disabled: globals.state.recordConfig.jpContinuousDumpsInterval === 0, 198 set: (cfg, val) => cfg.jpContinuousDumpsPhase = val, 199 get: (cfg) => cfg.jpContinuousDumpsPhase, 200 } as SliderAttrs), 201 ); 202 } 203} 204 205export class MemorySettings implements m.ClassComponent<RecordingSectionAttrs> { 206 view({attrs}: m.CVnode<RecordingSectionAttrs>) { 207 const meminfoOpts = new Map<string, string>(); 208 for (const x in MeminfoCounters) { 209 if (typeof MeminfoCounters[x] === 'number' && 210 !`${x}`.endsWith('_UNSPECIFIED')) { 211 meminfoOpts.set(x, x.replace('MEMINFO_', '').toLowerCase()); 212 } 213 } 214 const vmstatOpts = new Map<string, string>(); 215 for (const x in VmstatCounters) { 216 if (typeof VmstatCounters[x] === 'number' && 217 !`${x}`.endsWith('_UNSPECIFIED')) { 218 vmstatOpts.set(x, x.replace('VMSTAT_', '').toLowerCase()); 219 } 220 } 221 return m( 222 `.record-section${attrs.cssClass}`, 223 m(Probe, 224 { 225 title: 'Native heap profiling', 226 img: 'rec_native_heap_profiler.png', 227 descr: `Track native heap allocations & deallocations of an Android 228 process. (Available on Android 10+)`, 229 setEnabled: (cfg, val) => cfg.heapProfiling = val, 230 isEnabled: (cfg) => cfg.heapProfiling, 231 } as ProbeAttrs, 232 m(HeapSettings, attrs)), 233 m(Probe, 234 { 235 title: 'Java heap dumps', 236 img: 'rec_java_heap_dump.png', 237 descr: `Dump information about the Java object graph of an 238 Android app. (Available on Android 11+)`, 239 setEnabled: (cfg, val) => cfg.javaHeapDump = val, 240 isEnabled: (cfg) => cfg.javaHeapDump, 241 } as ProbeAttrs, 242 m(JavaHeapDumpSettings, attrs)), 243 m(Probe, 244 { 245 title: 'Kernel meminfo', 246 img: 'rec_meminfo.png', 247 descr: 'Polling of /proc/meminfo', 248 setEnabled: (cfg, val) => cfg.meminfo = val, 249 isEnabled: (cfg) => cfg.meminfo, 250 } as ProbeAttrs, 251 m(Slider, { 252 title: 'Poll interval', 253 cssClass: '.thin', 254 values: POLL_INTERVAL_MS, 255 unit: 'ms', 256 set: (cfg, val) => cfg.meminfoPeriodMs = val, 257 get: (cfg) => cfg.meminfoPeriodMs, 258 } as SliderAttrs), 259 m(Dropdown, { 260 title: 'Select counters', 261 cssClass: '.multicolumn', 262 options: meminfoOpts, 263 set: (cfg, val) => cfg.meminfoCounters = val, 264 get: (cfg) => cfg.meminfoCounters, 265 } as DropdownAttrs)), 266 m(Probe, { 267 title: 'High-frequency memory events', 268 img: 'rec_mem_hifreq.png', 269 descr: `Allows to track short memory spikes and transitories through 270 ftrace's mm_event, rss_stat and ion events. Available only 271 on recent Android Q+ kernels`, 272 setEnabled: (cfg, val) => cfg.memHiFreq = val, 273 isEnabled: (cfg) => cfg.memHiFreq, 274 } as ProbeAttrs), 275 m(Probe, { 276 title: 'Low memory killer', 277 img: 'rec_lmk.png', 278 descr: `Record LMK events. Works both with the old in-kernel LMK 279 and the newer userspace lmkd. It also tracks OOM score 280 adjustments.`, 281 setEnabled: (cfg, val) => cfg.memLmk = val, 282 isEnabled: (cfg) => cfg.memLmk, 283 } as ProbeAttrs), 284 m(Probe, 285 { 286 title: 'Per process stats', 287 img: 'rec_ps_stats.png', 288 descr: `Periodically samples all processes in the system tracking: 289 their thread list, memory counters (RSS, swap and other 290 /proc/status counters) and oom_score_adj.`, 291 setEnabled: (cfg, val) => cfg.procStats = val, 292 isEnabled: (cfg) => cfg.procStats, 293 } as ProbeAttrs, 294 m(Slider, { 295 title: 'Poll interval', 296 cssClass: '.thin', 297 values: POLL_INTERVAL_MS, 298 unit: 'ms', 299 set: (cfg, val) => cfg.procStatsPeriodMs = val, 300 get: (cfg) => cfg.procStatsPeriodMs, 301 } as SliderAttrs)), 302 m(Probe, 303 { 304 title: 'Virtual memory stats', 305 img: 'rec_vmstat.png', 306 descr: `Periodically polls virtual memory stats from /proc/vmstat. 307 Allows to gather statistics about swap, eviction, 308 compression and pagecache efficiency`, 309 setEnabled: (cfg, val) => cfg.vmstat = val, 310 isEnabled: (cfg) => cfg.vmstat, 311 } as ProbeAttrs, 312 m(Slider, { 313 title: 'Poll interval', 314 cssClass: '.thin', 315 values: POLL_INTERVAL_MS, 316 unit: 'ms', 317 set: (cfg, val) => cfg.vmstatPeriodMs = val, 318 get: (cfg) => cfg.vmstatPeriodMs, 319 } as SliderAttrs), 320 m(Dropdown, { 321 title: 'Select counters', 322 cssClass: '.multicolumn', 323 options: vmstatOpts, 324 set: (cfg, val) => cfg.vmstatCounters = val, 325 get: (cfg) => cfg.vmstatCounters, 326 } as DropdownAttrs))); 327 } 328} 329