• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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