Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 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 | #define LOG_TAG "incident_helper" |
| 18 | |
| 19 | #include "IncidentHelper.h" |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 20 | #include "ih_util.h" |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 21 | |
| 22 | #include "frameworks/base/core/proto/android/os/kernelwake.pb.h" |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 23 | #include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 24 | #include "frameworks/base/core/proto/android/os/procrank.pb.h" |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 25 | |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 26 | #include <android-base/file.h> |
| 27 | #include <unistd.h> |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 28 | #include <string> |
| 29 | #include <vector> |
| 30 | |
| 31 | using namespace android::base; |
| 32 | using namespace android::os; |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 33 | using namespace google::protobuf; |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 34 | using namespace std; |
| 35 | |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 36 | |
| 37 | static const string TAB_DELIMITER = "\t"; |
| 38 | static const string COMMA_DELIMITER = ","; |
| 39 | |
| 40 | static inline int toInt(const string& s) { |
| 41 | return atoi(s.c_str()); |
| 42 | } |
| 43 | |
| 44 | static inline long toLong(const string& s) { |
| 45 | return atol(s.c_str()); |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * Sets the given protobuf message when the field name matches one of the |
| 50 | * fields. It is useful to set values to proto from table-like plain texts. |
| 51 | */ |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 52 | static bool |
| 53 | SetTableField(::google::protobuf::Message* message, string field_name, string field_value) { |
| 54 | const Descriptor* descriptor = message->GetDescriptor(); |
| 55 | const Reflection* reflection = message->GetReflection(); |
| 56 | |
| 57 | const FieldDescriptor* field = descriptor->FindFieldByName(field_name); |
| 58 | switch (field->type()) { |
| 59 | case FieldDescriptor::TYPE_STRING: |
| 60 | reflection->SetString(message, field, field_value); |
| 61 | return true; |
| 62 | case FieldDescriptor::TYPE_INT64: |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 63 | reflection->SetInt64(message, field, toLong(field_value)); |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 64 | return true; |
| 65 | case FieldDescriptor::TYPE_UINT64: |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 66 | reflection->SetUInt64(message, field, toLong(field_value)); |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 67 | return true; |
| 68 | case FieldDescriptor::TYPE_INT32: |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 69 | reflection->SetInt32(message, field, toInt(field_value)); |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 70 | return true; |
| 71 | case FieldDescriptor::TYPE_UINT32: |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 72 | reflection->SetUInt32(message, field, toInt(field_value)); |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 73 | return true; |
| 74 | default: |
| 75 | // Add new scalar types |
| 76 | return false; |
| 77 | } |
| 78 | } |
| 79 | |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 80 | // ================================================================================ |
Yi Jin | 99c248f | 2017-08-25 18:11:58 -0700 | [diff] [blame] | 81 | status_t NoopParser::Parse(const int in, const int out) const |
| 82 | { |
| 83 | string content; |
| 84 | if (!ReadFdToString(in, &content)) { |
| 85 | fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); |
| 86 | return -1; |
| 87 | } |
| 88 | if (!WriteStringToFd(content, out)) { |
| 89 | fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); |
| 90 | return -1; |
| 91 | } |
| 92 | return NO_ERROR; |
| 93 | } |
| 94 | |
| 95 | // ================================================================================ |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 96 | status_t ReverseParser::Parse(const int in, const int out) const |
| 97 | { |
| 98 | string content; |
| 99 | if (!ReadFdToString(in, &content)) { |
| 100 | fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); |
| 101 | return -1; |
| 102 | } |
| 103 | // reverse the content |
| 104 | reverse(content.begin(), content.end()); |
| 105 | if (!WriteStringToFd(content, out)) { |
| 106 | fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); |
| 107 | return -1; |
| 108 | } |
| 109 | return NO_ERROR; |
| 110 | } |
| 111 | |
| 112 | // ================================================================================ |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 113 | status_t KernelWakesParser::Parse(const int in, const int out) const { |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 114 | Reader reader(in); |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 115 | string line; |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 116 | header_t header; // the header of /d/wakeup_sources |
| 117 | record_t record; // retain each record |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 118 | int nline = 0; |
| 119 | |
| 120 | KernelWakeSources proto; |
| 121 | |
| 122 | // parse line by line |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 123 | while (reader.readLine(&line)) { |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 124 | if (line.empty()) continue; |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 125 | // parse head line |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 126 | if (nline++ == 0) { |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 127 | header = parseHeader(line, TAB_DELIMITER); |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 128 | continue; |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | // parse for each record, the line delimiter is \t only! |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 132 | record = parseRecord(line, TAB_DELIMITER); |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 133 | |
| 134 | if (record.size() != header.size()) { |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 135 | // TODO: log this to incident report! |
| 136 | fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); |
| 137 | continue; |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | WakeupSourceProto* source = proto.add_wakeup_sources(); |
Yi Jin | 603f3b3 | 2017-08-03 18:50:48 -0700 | [diff] [blame] | 141 | for (int i=0; i<(int)record.size(); i++) { |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 142 | if (!SetTableField(source, header[i], record[i])) { |
| 143 | fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", |
| 144 | this->name.string(), nline, header[i].c_str(), record[i].c_str()); |
| 145 | } |
Yi Jin | 603f3b3 | 2017-08-03 18:50:48 -0700 | [diff] [blame] | 146 | } |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 147 | } |
| 148 | |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 149 | if (!reader.ok(&line)) { |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 150 | fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); |
| 151 | return -1; |
| 152 | } |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 153 | |
| 154 | if (!proto.SerializeToFileDescriptor(out)) { |
| 155 | fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); |
| 156 | return -1; |
| 157 | } |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 158 | fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); |
Yi Jin | 0a3406f | 2017-06-22 19:23:11 -0700 | [diff] [blame] | 159 | return NO_ERROR; |
| 160 | } |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 161 | |
| 162 | // ================================================================================ |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 163 | status_t ProcrankParser::Parse(const int in, const int out) const { |
| 164 | Reader reader(in); |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 165 | string line; |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 166 | header_t header; // the header of /d/wakeup_sources |
| 167 | record_t record; // retain each record |
| 168 | int nline = 0; |
| 169 | |
| 170 | Procrank proto; |
| 171 | |
| 172 | // parse line by line |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 173 | while (reader.readLine(&line)) { |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 174 | if (line.empty()) continue; |
| 175 | |
| 176 | // parse head line |
| 177 | if (nline++ == 0) { |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 178 | header = parseHeader(line); |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 179 | continue; |
| 180 | } |
| 181 | |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 182 | if (hasPrefix(&line, "ZRAM:")) { |
| 183 | proto.mutable_summary()->mutable_zram()->set_raw_text(line); |
| 184 | continue; |
| 185 | } |
| 186 | if (hasPrefix(&line, "RAM:")) { |
| 187 | proto.mutable_summary()->mutable_ram()->set_raw_text(line); |
| 188 | continue; |
| 189 | } |
| 190 | |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 191 | record = parseRecord(line); |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 192 | if (record.size() != header.size()) { |
| 193 | if (record[record.size() - 1] == "TOTAL") { // TOTAL record |
| 194 | ProcessProto* total = proto.mutable_summary()->mutable_total(); |
Yi Jin | 603f3b3 | 2017-08-03 18:50:48 -0700 | [diff] [blame] | 195 | for (int i=1; i<=(int)record.size(); i++) { |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 196 | SetTableField(total, header[header.size() - i], record[record.size() - i]); |
Yi Jin | 603f3b3 | 2017-08-03 18:50:48 -0700 | [diff] [blame] | 197 | } |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 198 | } else { |
| 199 | fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, |
| 200 | line.c_str()); |
| 201 | } |
| 202 | continue; |
| 203 | } |
| 204 | |
| 205 | ProcessProto* process = proto.add_processes(); |
Yi Jin | 603f3b3 | 2017-08-03 18:50:48 -0700 | [diff] [blame] | 206 | for (int i=0; i<(int)record.size(); i++) { |
Yi Jin | 4ef28b7 | 2017-08-14 14:45:28 -0700 | [diff] [blame] | 207 | if (!SetTableField(process, header[i], record[i])) { |
| 208 | fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", |
| 209 | this->name.string(), nline, header[i].c_str(), record[i].c_str()); |
| 210 | } |
Yi Jin | 603f3b3 | 2017-08-03 18:50:48 -0700 | [diff] [blame] | 211 | } |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 212 | } |
| 213 | |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 214 | if (!reader.ok(&line)) { |
Yi Jin | b44f7d4 | 2017-07-21 12:12:59 -0700 | [diff] [blame] | 215 | fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); |
| 216 | return -1; |
| 217 | } |
| 218 | |
| 219 | if (!proto.SerializeToFileDescriptor(out)) { |
| 220 | fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); |
| 221 | return -1; |
| 222 | } |
| 223 | fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); |
| 224 | return NO_ERROR; |
Yi Jin | 99c248f | 2017-08-25 18:11:58 -0700 | [diff] [blame] | 225 | } |
Yi Jin | 810b14f | 2017-09-11 19:01:08 -0700 | [diff] [blame] | 226 | |
| 227 | // ================================================================================ |
| 228 | status_t PageTypeInfoParser::Parse(const int in, const int out) const { |
| 229 | Reader reader(in); |
| 230 | string line; |
| 231 | bool migrateTypeSession = false; |
| 232 | int pageBlockOrder; |
| 233 | header_t blockHeader; |
| 234 | |
| 235 | PageTypeInfo pageTypeInfo; |
| 236 | |
| 237 | while (reader.readLine(&line)) { |
| 238 | if (line.empty()) { |
| 239 | migrateTypeSession = false; |
| 240 | blockHeader.clear(); |
| 241 | continue; |
| 242 | } |
| 243 | |
| 244 | if (hasPrefix(&line, "Page block order:")) { |
| 245 | pageBlockOrder = toInt(line); |
| 246 | pageTypeInfo.set_page_block_order(pageBlockOrder); |
| 247 | continue; |
| 248 | } |
| 249 | if (hasPrefix(&line, "Pages per block:")) { |
| 250 | pageTypeInfo.set_pages_per_block(toInt(line)); |
| 251 | continue; |
| 252 | } |
| 253 | if (hasPrefix(&line, "Free pages count per migrate type at order")) { |
| 254 | migrateTypeSession = true; |
| 255 | continue; |
| 256 | } |
| 257 | if (hasPrefix(&line, "Number of blocks type")) { |
| 258 | blockHeader = parseHeader(line); |
| 259 | continue; |
| 260 | } |
| 261 | |
| 262 | record_t record = parseRecord(line, COMMA_DELIMITER); |
| 263 | if (migrateTypeSession && record.size() == 3) { |
| 264 | MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types(); |
| 265 | // expect part 0 starts with "Node" |
| 266 | if (hasPrefix(&record[0], "Node")) { |
| 267 | migrateType->set_node(toInt(record[0])); |
| 268 | } else goto ERROR; |
| 269 | // expect part 1 starts with "zone" |
| 270 | if (hasPrefix(&record[1], "zone")) { |
| 271 | migrateType->set_zone(record[1]); |
| 272 | } else goto ERROR; |
| 273 | // expect part 2 starts with "type" |
| 274 | if (hasPrefix(&record[2], "type")) { |
| 275 | // expect the rest of part 2 has number of (pageBlockOrder + 2) parts |
| 276 | // An example looks like: |
| 277 | // header line: type 0 1 2 3 4 5 6 7 8 9 10 |
| 278 | // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 |
| 279 | // The pageBlockOrder = 10 and it's zero-indexed. so total parts |
| 280 | // are 10 + 1(zero-indexed) + 1(the type part) = 12. |
| 281 | record_t pageCounts = parseRecord(record[2]); |
| 282 | int pageCountsSize = pageBlockOrder + 2; |
| 283 | if ((int)pageCounts.size() != pageCountsSize) goto ERROR; |
| 284 | |
| 285 | migrateType->set_type(pageCounts[0]); |
| 286 | for (auto i=1; i<pageCountsSize; i++) { |
| 287 | migrateType->add_free_pages_count(toInt(pageCounts[i])); |
| 288 | } |
| 289 | } else goto ERROR; |
| 290 | continue; |
| 291 | } |
| 292 | |
| 293 | if (!blockHeader.empty() && record.size() == 2) { |
| 294 | BlockProto* block = pageTypeInfo.add_blocks(); |
| 295 | |
| 296 | if (hasPrefix(&record[0], "Node")) { |
| 297 | block->set_node(toInt(record[0])); |
| 298 | } else goto ERROR; |
| 299 | |
| 300 | if (hasPrefix(&record[1], "zone")) { |
| 301 | record_t blockCounts = parseRecord(record[1]); |
| 302 | block->set_zone(blockCounts[0]); |
| 303 | for (size_t i=0; i<blockHeader.size(); i++) { |
| 304 | if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR; |
| 305 | } |
| 306 | } else goto ERROR; |
| 307 | |
| 308 | continue; |
| 309 | } |
| 310 | |
| 311 | ERROR: // print out error for this single line and continue parsing |
| 312 | fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str()); |
| 313 | } |
| 314 | |
| 315 | if (!reader.ok(&line)) { |
| 316 | fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); |
| 317 | return -1; |
| 318 | } |
| 319 | |
| 320 | if (!pageTypeInfo.SerializeToFileDescriptor(out)) { |
| 321 | fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); |
| 322 | return -1; |
| 323 | } |
| 324 | |
| 325 | fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize()); |
| 326 | return NO_ERROR; |
| 327 | } |