1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.dump 18 19 import android.util.ArrayMap 20 import com.android.systemui.Dumpable 21 import com.android.systemui.ProtoDumpable 22 import com.android.systemui.dump.nano.SystemUIProtoDump 23 import com.android.systemui.plugins.log.LogBuffer 24 import java.io.PrintWriter 25 import javax.inject.Inject 26 import javax.inject.Singleton 27 28 /** 29 * Maintains a registry of things that should be dumped when a bug report is taken 30 * 31 * When a bug report is taken, SystemUI dumps various diagnostic information that we hope will be 32 * useful for the eventual readers of the bug report. Code that wishes to participate in this dump 33 * should register itself here. 34 * 35 * See [DumpHandler] for more information on how and when this information is dumped. 36 */ 37 @Singleton 38 open class DumpManager @Inject constructor() { 39 private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap() 40 private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap() 41 42 /** 43 * Registers a dumpable to be called during the CRITICAL section of the bug report. 44 * 45 * The CRITICAL section gets very high priority during a dump, but also a very limited amount of 46 * time to do the dumping. So, please don't dump an excessive amount of stuff using CRITICAL. 47 * 48 * See [registerDumpable]. 49 */ registerCriticalDumpablenull50 fun registerCriticalDumpable(name: String, module: Dumpable) { 51 registerDumpable(name, module, DumpPriority.CRITICAL) 52 } 53 54 /** See [registerNormalDumpable]. */ registerNormalDumpablenull55 fun registerNormalDumpable(module: Dumpable) { 56 registerNormalDumpable(module::class.java.simpleName, module) 57 } 58 59 /** 60 * Registers a dumpable to be called during the NORMAL section of the bug report. 61 * 62 * The NORMAL section gets a lower priority during a dump, but also more time. This should be 63 * used by [LogBuffer] instances, [ProtoDumpable] instances, and any [Dumpable] instances that 64 * dump a lot of information. 65 */ registerNormalDumpablenull66 fun registerNormalDumpable(name: String, module: Dumpable) { 67 registerDumpable(name, module, DumpPriority.NORMAL) 68 } 69 70 /** 71 * Register a dumpable to be called during a bug report. 72 * 73 * @param name The name to register the dumpable under. This is typically the qualified class 74 * name of the thing being dumped (getClass().getName()), but can be anything as long as it 75 * doesn't clash with an existing registration. 76 * @param priority the priority level of this dumpable, which affects at what point in the bug 77 * report this gets dump. By default, the dumpable will be called during the CRITICAL section of 78 * the bug report, so don't dump an excessive amount of stuff here. 79 * 80 * TODO(b/259973758): Replace all calls to this method with calls to [registerCriticalDumpable] 81 * or [registerNormalDumpable] instead. 82 */ 83 @Synchronized 84 @JvmOverloads 85 @Deprecated("Use registerCriticalDumpable or registerNormalDumpable instead") registerDumpablenull86 fun registerDumpable( 87 name: String, 88 module: Dumpable, 89 priority: DumpPriority = DumpPriority.CRITICAL, 90 ) { 91 if (!canAssignToNameLocked(name, module)) { 92 throw IllegalArgumentException("'$name' is already registered") 93 } 94 95 dumpables[name] = RegisteredDumpable(name, module, priority) 96 } 97 98 /** 99 * Same as the above override, but automatically uses the simple class name as the dumpable 100 * name. 101 */ 102 @Synchronized registerDumpablenull103 fun registerDumpable(module: Dumpable) { 104 registerDumpable(module::class.java.simpleName, module) 105 } 106 107 /** 108 * Unregisters a previously-registered dumpable. 109 */ 110 @Synchronized unregisterDumpablenull111 fun unregisterDumpable(name: String) { 112 dumpables.remove(name) 113 } 114 115 /** 116 * Register a [LogBuffer] to be dumped during a bug report. 117 */ 118 @Synchronized registerBuffernull119 fun registerBuffer(name: String, buffer: LogBuffer) { 120 if (!canAssignToNameLocked(name, buffer)) { 121 throw IllegalArgumentException("'$name' is already registered") 122 } 123 124 // All buffers must be priority NORMAL, not CRITICAL, because they often contain a lot of 125 // data. 126 buffers[name] = RegisteredDumpable(name, buffer, DumpPriority.NORMAL) 127 } 128 129 /** 130 * Dumps the first dumpable or buffer whose registered name ends with [target] 131 */ 132 @Synchronized dumpTargetnull133 fun dumpTarget( 134 target: String, 135 pw: PrintWriter, 136 args: Array<String>, 137 tailLength: Int, 138 ) { 139 for (dumpable in dumpables.values) { 140 if (dumpable.name.endsWith(target)) { 141 dumpDumpable(dumpable, pw, args) 142 return 143 } 144 } 145 146 for (buffer in buffers.values) { 147 if (buffer.name.endsWith(target)) { 148 dumpBuffer(buffer, pw, tailLength) 149 return 150 } 151 } 152 } 153 154 @Synchronized dumpProtoTargetnull155 fun dumpProtoTarget( 156 target: String, 157 protoDump: SystemUIProtoDump, 158 args: Array<String> 159 ) { 160 for (dumpable in dumpables.values) { 161 if (dumpable.dumpable is ProtoDumpable && dumpable.name.endsWith(target)) { 162 dumpProtoDumpable(dumpable.dumpable, protoDump, args) 163 return 164 } 165 } 166 } 167 168 @Synchronized dumpProtoDumpablesnull169 fun dumpProtoDumpables( 170 systemUIProtoDump: SystemUIProtoDump, 171 args: Array<String> 172 ) { 173 for (dumpable in dumpables.values) { 174 if (dumpable.dumpable is ProtoDumpable) { 175 dumpProtoDumpable( 176 dumpable.dumpable, 177 systemUIProtoDump, 178 args 179 ) 180 } 181 } 182 } 183 184 /** 185 * Dumps all registered dumpables with critical priority to [pw] 186 */ 187 @Synchronized dumpCriticalnull188 fun dumpCritical(pw: PrintWriter, args: Array<String>) { 189 for (dumpable in dumpables.values) { 190 if (dumpable.priority == DumpPriority.CRITICAL) { 191 dumpDumpable(dumpable, pw, args) 192 } 193 } 194 } 195 196 /** 197 * To [pw], dumps (1) all registered dumpables with normal priority; and (2) all [LogBuffer]s. 198 */ 199 @Synchronized dumpNormalnull200 fun dumpNormal(pw: PrintWriter, args: Array<String>, tailLength: Int = 0) { 201 for (dumpable in dumpables.values) { 202 if (dumpable.priority == DumpPriority.NORMAL) { 203 dumpDumpable(dumpable, pw, args) 204 } 205 } 206 207 for (buffer in buffers.values) { 208 dumpBuffer(buffer, pw, tailLength) 209 } 210 } 211 212 /** 213 * Dump all the instances of [Dumpable]. 214 */ 215 @Synchronized dumpDumpablesnull216 fun dumpDumpables(pw: PrintWriter, args: Array<String>) { 217 for (module in dumpables.values) { 218 dumpDumpable(module, pw, args) 219 } 220 } 221 222 /** 223 * Dumps the names of all registered dumpables (one per line) 224 */ 225 @Synchronized listDumpablesnull226 fun listDumpables(pw: PrintWriter) { 227 for (module in dumpables.values) { 228 pw.println(module.name) 229 } 230 } 231 232 /** 233 * Dumps all registered [LogBuffer]s to [pw] 234 */ 235 @Synchronized dumpBuffersnull236 fun dumpBuffers(pw: PrintWriter, tailLength: Int) { 237 for (buffer in buffers.values) { 238 dumpBuffer(buffer, pw, tailLength) 239 } 240 } 241 242 /** 243 * Dumps the names of all registered buffers (one per line) 244 */ 245 @Synchronized listBuffersnull246 fun listBuffers(pw: PrintWriter) { 247 for (buffer in buffers.values) { 248 pw.println(buffer.name) 249 } 250 } 251 252 @Synchronized freezeBuffersnull253 fun freezeBuffers() { 254 for (buffer in buffers.values) { 255 buffer.dumpable.freeze() 256 } 257 } 258 259 @Synchronized unfreezeBuffersnull260 fun unfreezeBuffers() { 261 for (buffer in buffers.values) { 262 buffer.dumpable.unfreeze() 263 } 264 } 265 dumpDumpablenull266 private fun dumpDumpable( 267 dumpable: RegisteredDumpable<Dumpable>, 268 pw: PrintWriter, 269 args: Array<String> 270 ) { 271 pw.println() 272 pw.println("${dumpable.name}:") 273 pw.println("----------------------------------------------------------------------------") 274 dumpable.dumpable.dump(pw, args) 275 } 276 dumpBuffernull277 private fun dumpBuffer( 278 buffer: RegisteredDumpable<LogBuffer>, 279 pw: PrintWriter, 280 tailLength: Int 281 ) { 282 pw.println() 283 pw.println() 284 pw.println("BUFFER ${buffer.name}:") 285 pw.println("============================================================================") 286 buffer.dumpable.dump(pw, tailLength) 287 } 288 dumpProtoDumpablenull289 private fun dumpProtoDumpable( 290 protoDumpable: ProtoDumpable, 291 systemUIProtoDump: SystemUIProtoDump, 292 args: Array<String> 293 ) { 294 protoDumpable.dumpProto(systemUIProtoDump, args) 295 } 296 canAssignToNameLockednull297 private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean { 298 val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable 299 return existingDumpable == null || newDumpable == existingDumpable 300 } 301 } 302 303 private data class RegisteredDumpable<T>( 304 val name: String, 305 val dumpable: T, 306 val priority: DumpPriority, 307 ) 308 309 /** 310 * The priority level for a given dumpable, which affects at what point in the bug report this gets 311 * dumped. 312 */ 313 enum class DumpPriority { 314 CRITICAL, 315 NORMAL, 316 } 317