1 /* 2 * Copyright (C) 2017 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 #ifndef INCIDENT_HELPER_UTIL_H 18 #define INCIDENT_HELPER_UTIL_H 19 20 #include <map> 21 #include <stack> 22 #include <string> 23 #include <vector> 24 25 #include <android/util/ProtoOutputStream.h> 26 27 using namespace android::util; 28 29 typedef std::vector<std::string> header_t; 30 typedef std::vector<std::string> record_t; 31 typedef std::string (*trans_func) (const std::string&); 32 33 const std::string DEFAULT_WHITESPACE = " \t"; 34 const std::string DEFAULT_NEWLINE = "\r\n"; 35 const std::string TAB_DELIMITER = "\t"; 36 const std::string COMMA_DELIMITER = ","; 37 const std::string PIPE_DELIMITER = "|"; 38 const std::string PARENTHESES_DELIMITER = "()"; 39 40 // returns true if c is a-zA-Z0-9 or underscore 41 bool isValidChar(char c); 42 43 // trim the string with the given charset 44 std::string trim(const std::string& s, const std::string& charset); 45 46 /** 47 * When a text has a table format like this 48 * line 1: HeadA HeadB HeadC 49 * line 2: v1 v2 v3 50 * line 3: v11 v12 v13 51 * 52 * We want to parse the line in structure given the delimiter. 53 * parseHeader is used to parse the firse line of the table and returns a list of strings in lower case 54 * parseRecord is used to parse other lines and returns a list of strings 55 * empty strings are skipped 56 */ 57 header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); 58 record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); 59 60 /** 61 * Gets the list of end indices of each word in the line and places it in the given vector, 62 * clearing out the vector beforehand. These indices can be used with parseRecordByColumns. 63 * Will return false if there was a problem getting the indices. headerNames 64 * must be NULL terminated. 65 */ 66 bool getColumnIndices(std::vector<int>& indices, const char* headerNames[], const std::string& line); 67 68 /** 69 * When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters. 70 * This function allows to parse record by its header's column position' indices, must in ascending order. 71 * At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters. 72 */ 73 record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE); 74 75 /** Prints record_t to stderr */ 76 void printRecord(const record_t& record); 77 78 /** 79 * When the line starts/ends with the given key, the function returns true 80 * as well as the line argument is changed to the rest trimmed part of the original. 81 * e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes 82 * "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:", 83 * otherwise the line is not changed. 84 * 85 * In order to prevent two values have same prefix which cause entering to incorrect conditions, 86 * stripPrefix and stripSuffix can turn on a flag that requires the ending char in the line must not be a valid 87 * character or digits, this feature is off by default. 88 * i.e. ABC%some value, ABCD%other value 89 */ 90 bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false); 91 bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false); 92 93 /** 94 * behead the given line by the cut, return the head and reassign the line to be the rest. 95 */ 96 std::string behead(std::string* line, const char cut); 97 98 /** 99 * Converts string to the desired type 100 */ 101 int toInt(const std::string& s); 102 long long toLongLong(const std::string& s); 103 double toDouble(const std::string& s); 104 105 /** 106 * Reader class reads data from given fd in streaming fashion. 107 * The buffer size is controlled by capacity parameter. 108 */ 109 class Reader 110 { 111 public: 112 explicit Reader(const int fd); 113 ~Reader(); 114 115 bool readLine(std::string* line); 116 bool ok(std::string* error); 117 118 private: 119 FILE* mFile; 120 std::string mStatus; 121 }; 122 123 /** 124 * The Table class is constructed from two arrays generated by the given message with 125 * option (stream_proto.stream_msg).enable_fields_mapping = true. 126 * The names are each field's names in the message and must corresponding to the header/name of 127 * the text to be parsed, and the ids are the streaming proto encoded field ids. 128 * 129 * This class then allows users to insert the table values to proto based on its header. 130 * 131 * Advance feature: if some fields in the message are enums, user must explicitly add the 132 * mapping from enum name string to its enum values. 133 */ 134 class Message; 135 class Table 136 { 137 friend class Message; 138 public: 139 Table(const char* names[], const uint64_t ids[], const int count); 140 ~Table(); 141 142 // Add enum names to values for parsing purpose. 143 void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize); 144 145 // Manually add enum names to values mapping, useful when an Enum type is used by 146 // a number of fields, there must not be any enum name conflicts. 147 void addEnumNameToValue(const char* enumName, const int enumValue); 148 149 // Based on given name, find the right field id, parse the text value and insert to proto. 150 // Return false if the given name can't be found. 151 bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value); 152 private: 153 std::map<std::string, uint64_t> mFields; 154 std::map<std::string, std::map<std::string, int>> mEnums; 155 std::map<std::string, int> mEnumValuesByName; 156 }; 157 158 /** 159 * Reconstructs a typical proto message given its message Table, adds submessage fields explicitly. 160 * It allows user to insert nested proto values purely by the names. See insertField for detail. 161 */ 162 class Message 163 { 164 public: 165 explicit Message(Table* table); 166 ~Message(); 167 168 // Reconstructs the typical proto message by adding its message fields. 169 void addSubMessage(uint64_t fieldId, Message* fieldMsg); 170 171 // Inserts value if the given name has the corresponding field in its message and return true. 172 // It will recursively search the name in submessages and find the correct field to insert. 173 // For example, when the name is dalvik_vm_heapsize, and the message's corresponding proto is: 174 // message Properties { 175 // message DalvikVm { 176 // int32 heapsize = 1; 177 // bool usejit = 2; 178 // } 179 // DalvikVm dalvik_vm = 1; 180 // string hack_in = 2; 181 // } 182 // The value will be inserted into field heapsize in dalvik_vm submessage. 183 // 184 // Also value belongs to same submessage MUST be inserted contiguously. 185 // For example, dalvik_vm_usejit must be inserted directly after dalvik_vm_heapsize, otherwise 186 // if hack_in attempts to be inserted before dalvik_vm_usejit, value of usejit isn't added as expected. 187 bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value); 188 189 // Starts a new message field proto session. 190 void startSession(ProtoOutputStream* proto, const std::string& name); 191 192 // Ends the previous message field proto session. 193 void endSession(ProtoOutputStream* proto); 194 private: 195 Table* mTable; 196 std::string mPreviousField; 197 std::stack<uint64_t> mTokens; 198 std::map<std::string, Message*> mSubMessages; 199 }; 200 201 #endif // INCIDENT_HELPER_UTIL_H 202