• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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 java.io.PrintWriter
20 
21 /**
22  * Utility for logging nice table data to be parsed (and pretty printed) in bugreports. The general
23  * idea here is to feed your nice, table-like data to this class, which embeds the schema and rows
24  * into the dumpsys, wrapped in a known start and stop tags. Later, one can build a simple parser
25  * and pretty-print this data in a table
26  *
27  * Note: Something should be said here about silently eating errors by filtering out malformed
28  * lines. Because this class is expected to be utilized only during a dumpsys, it doesn't feel
29  * most correct to throw an exception here (since an exception can often be the reason that this
30  * class is created). Because of this, [DumpsysTableLogger] will simply filter out invalid lines
31  * based solely on line length. This behavior might need to be revisited in the future.
32  *
33  * USAGE:
34  * Assuming we have some data that would be logged to dumpsys like so:
35  *
36  * ```
37  *      1: field1=val1, field2=val2..., fieldN=valN
38  *      //...
39  *      M: field1M=val1M, ..., fieldNM
40  * ```
41  *
42  * You can break the `field<n>` values out into a columns spec:
43  * ```
44  *      val cols = [field1, field2,...,fieldN]
45  * ```
46  * And then take all of the historical data lines (1 through M), and break them out into their own
47  * lists:
48  * ```
49  *      val rows = [
50  *          [field10, field20,..., fieldN0],
51  *          //...
52  *          [field1M, field2M,..., fieldNM]
53  *      ]
54  * ```
55  *
56  * Lastly, create a bugreport-unique section name, and use the table logger to write the data to
57  * dumpsys:
58  * ```
59  *      val logger = DumpsysTableLogger(uniqueName, cols, rows)
60  *      logger.printTableData(pw)
61  * ```
62  *
63  * The expected output in the dumpsys would be:
64  * ```
65  *      SystemUI TableSection START: <SectionName>
66  *      version 1
67  *      col1|col2|...|colN
68  *      field10|field20|...|fieldN0
69  *      //...
70  *      field1M|field2M|...|fieldNM
71  *      SystemUI TableSection END: <SectionName>
72  * ```
73  *
74  *  @param sectionName A name for the table data section. Should be unique in the bugreport
75  *  @param columns Definition for the columns of the table. This should be the same length as all
76  *      data rows
77  *  @param rows List of rows to be displayed in the table
78  */
79 class DumpsysTableLogger(
80     private val sectionName: String,
81     private val columns: List<String>,
82     private val rows: List<Row>
83 ) {
84 
85     fun printTableData(pw: PrintWriter) {
86         printSectionStart(pw)
87         printSchema(pw)
88         printData(pw)
89         printSectionEnd(pw)
90     }
91 
92     private fun printSectionStart(pw: PrintWriter) {
93         pw.println(HEADER_PREFIX + sectionName)
94         pw.println("version $VERSION")
95     }
96 
97     private fun printSectionEnd(pw: PrintWriter) {
98         pw.println(FOOTER_PREFIX + sectionName)
99     }
100 
101     private fun printSchema(pw: PrintWriter) {
102         pw.println(columns.joinToString(separator = SEPARATOR))
103     }
104 
105     private fun printData(pw: PrintWriter) {
106         val count = columns.size
107         rows
108             .filter { it.size == count }
109             .forEach { dataLine ->
110                 pw.println(dataLine.joinToString(separator = SEPARATOR))
111         }
112     }
113 }
114 
115 typealias Row = List<String>
116 
117 /**
118  * DO NOT CHANGE! (but if you must...)
119  *  1. Update the version number
120  *  2. Update any consumers to parse the new version
121  */
122 private const val HEADER_PREFIX = "SystemUI TableSection START: "
123 private const val FOOTER_PREFIX = "SystemUI TableSection END: "
124 private const val SEPARATOR = "|" // TBD
125 private const val VERSION = "1"