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"