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