main.cpp revision ee6495982fb7b99dc90a52dc751ce4f7cae3fb9d
1/* 2 * Copyright (C) 2016 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#include "generic_message.h" 18#include "printer.h" 19 20#include <frameworks/base/core/proto/android/os/incident.pb.h> 21#include <google/protobuf/wire_format.h> 22#include <google/protobuf/io/coded_stream.h> 23#include <google/protobuf/io/zero_copy_stream_impl.h> 24 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <sys/wait.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <stdio.h> 31#include <string.h> 32#include <unistd.h> 33 34using namespace android::os; 35using namespace google::protobuf; 36using namespace google::protobuf::io; 37using namespace google::protobuf::internal; 38 39static bool read_message(CodedInputStream* in, Descriptor const* descriptor, 40 GenericMessage* message); 41static void print_message(Out* out, Descriptor const* descriptor, GenericMessage const* message); 42 43// ================================================================================ 44static bool 45read_length_delimited(CodedInputStream* in, uint32 fieldId, Descriptor const* descriptor, 46 GenericMessage* message) 47{ 48 uint32 size; 49 if (!in->ReadVarint32(&size)) { 50 return false; 51 } 52 53 FieldDescriptor const* field = descriptor->FindFieldByNumber(fieldId); 54 if (field != NULL) { 55 int type = field->type(); 56 if (type == FieldDescriptor::TYPE_MESSAGE) { 57 GenericMessage* child = message->addMessage(fieldId); 58 59 CodedInputStream::Limit limit = in->PushLimit(size); 60 bool rv = read_message(in, field->message_type(), child); 61 in->PopLimit(limit); 62 return rv; 63 } else if (type == FieldDescriptor::TYPE_STRING) { 64 // TODO: do a version of readstring that just pumps the data 65 // rather than allocating a string which we don't care about. 66 string str; 67 if (in->ReadString(&str, size)) { 68 message->addString(fieldId, str); 69 return true; 70 } else { 71 return false; 72 } 73 } else if (type == FieldDescriptor::TYPE_BYTES) { 74 // TODO: Save bytes field. 75 return in->Skip(size); 76 } 77 } 78 return in->Skip(size); 79} 80 81// ================================================================================ 82static bool 83read_message(CodedInputStream* in, Descriptor const* descriptor, GenericMessage* message) 84{ 85 uint32 value32; 86 uint64 value64; 87 88 while (true) { 89 uint32 tag = in->ReadTag(); 90 if (tag == 0) { 91 return true; 92 } 93 int fieldId = WireFormatLite::GetTagFieldNumber(tag); 94 switch (WireFormatLite::GetTagWireType(tag)) { 95 case WireFormatLite::WIRETYPE_VARINT: 96 if (in->ReadVarint64(&value64)) { 97 message->addInt64(fieldId, value64); 98 break; 99 } else { 100 fprintf(stderr, "bad VARINT: 0x%x (%d) at index %d\n", tag, tag, 101 in->CurrentPosition()); 102 return false; 103 } 104 case WireFormatLite::WIRETYPE_FIXED64: 105 if (in->ReadLittleEndian64(&value64)) { 106 message->addInt64(fieldId, value64); 107 break; 108 } else { 109 fprintf(stderr, "bad VARINT: 0x%x (%d) at index %d\n", tag, tag, 110 in->CurrentPosition()); 111 return false; 112 } 113 case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: 114 if (!read_length_delimited(in, fieldId, descriptor, message)) { 115 fprintf(stderr, "bad LENGTH_DELIMITED: 0x%x (%d) at index %d\n", 116 tag, tag, in->CurrentPosition()); 117 return false; 118 } 119 break; 120 case WireFormatLite::WIRETYPE_FIXED32: 121 if (in->ReadLittleEndian32(&value32)) { 122 message->addInt32(fieldId, value32); 123 break; 124 } else { 125 fprintf(stderr, "bad FIXED32: 0x%x (%d) at index %d\n", tag, tag, 126 in->CurrentPosition()); 127 return false; 128 } 129 default: 130 fprintf(stderr, "bad tag: 0x%x (%d) at index %d\n", tag, tag, 131 in->CurrentPosition()); 132 return false; 133 } 134 } 135} 136 137// ================================================================================ 138static void 139print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const& node) 140{ 141 FieldDescriptor::Type type = field->type(); 142 143 switch (node.type) { 144 case GenericMessage::TYPE_VALUE32: 145 switch (type) { 146 case FieldDescriptor::TYPE_FIXED32: 147 out->printf("%u", node.value32); 148 break; 149 case FieldDescriptor::TYPE_SFIXED32: 150 out->printf("%d", node.value32); 151 break; 152 case FieldDescriptor::TYPE_FLOAT: 153 out->printf("%f", *(float*)&node.value32); 154 break; 155 default: 156 out->printf("(unexpected value32 %d (0x%x)", node.value32, node.value32); 157 break; 158 } 159 break; 160 case GenericMessage::TYPE_VALUE64: 161 switch (type) { 162 case FieldDescriptor::TYPE_DOUBLE: 163 out->printf("%f", *(double*)&node.value64); 164 break; 165 // Int32s here were added with addInt64 from a WIRETYPE_VARINT, 166 // even if the definition is for a 32 bit int. 167 case FieldDescriptor::TYPE_SINT32: 168 case FieldDescriptor::TYPE_INT32: 169 out->printf("%d", node.value64); 170 break; 171 case FieldDescriptor::TYPE_INT64: 172 case FieldDescriptor::TYPE_SINT64: 173 case FieldDescriptor::TYPE_SFIXED64: 174 out->printf("%lld", node.value64); 175 break; 176 case FieldDescriptor::TYPE_UINT32: 177 case FieldDescriptor::TYPE_UINT64: 178 case FieldDescriptor::TYPE_FIXED64: 179 out->printf("%u", node.value64); 180 break; 181 case FieldDescriptor::TYPE_BOOL: 182 if (node.value64) { 183 out->printf("true"); 184 } else { 185 out->printf("false"); 186 } 187 break; 188 case FieldDescriptor::TYPE_ENUM: 189 if (field->enum_type()->FindValueByNumber((int)node.value64) == NULL) { 190 out->printf("%lld", (int) node.value64); 191 } else { 192 out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64) 193 ->name().c_str()); 194 } 195 break; 196 default: 197 out->printf("(unexpected value64 %lld (0x%x))", node.value64, node.value64); 198 break; 199 } 200 break; 201 case GenericMessage::TYPE_MESSAGE: 202 print_message(out, field->message_type(), node.message); 203 break; 204 case GenericMessage::TYPE_STRING: 205 // TODO: custom format for multi-line strings. 206 out->printf("%s", node.str->c_str()); 207 break; 208 case GenericMessage::TYPE_DATA: 209 out->printf("<bytes>"); 210 break; 211 } 212} 213 214static void 215print_message(Out* out, Descriptor const* descriptor, GenericMessage const* message) 216{ 217 out->printf("%s {\n", descriptor->name().c_str()); 218 out->indent(); 219 220 int const N = descriptor->field_count(); 221 for (int i=0; i<N; i++) { 222 FieldDescriptor const* field = descriptor->field(i); 223 224 int fieldId = field->number(); 225 bool repeated = field->label() == FieldDescriptor::LABEL_REPEATED; 226 FieldDescriptor::Type type = field->type(); 227 GenericMessage::const_iterator_pair it = message->find(fieldId); 228 229 out->printf("%s=", field->name().c_str()); 230 if (repeated) { 231 if (it.first != it.second) { 232 out->printf("[\n"); 233 out->indent(); 234 235 for (GenericMessage::const_iterator_pair it = message->find(fieldId); 236 it.first != it.second; it.first++) { 237 print_value(out, field, it.first->second); 238 out->printf("\n"); 239 } 240 241 out->dedent(); 242 out->printf("]"); 243 } else { 244 out->printf("[]"); 245 } 246 } else { 247 if (it.first != it.second) { 248 print_value(out, field, it.first->second); 249 } else { 250 switch (type) { 251 case FieldDescriptor::TYPE_BOOL: 252 out->printf("false"); 253 break; 254 case FieldDescriptor::TYPE_STRING: 255 case FieldDescriptor::TYPE_MESSAGE: 256 out->printf(""); 257 break; 258 case FieldDescriptor::TYPE_ENUM: 259 out->printf("%s", field->default_value_enum()->name().c_str()); 260 break; 261 default: 262 out->printf("0"); 263 break; 264 } 265 } 266 } 267 out->printf("\n"); 268 } 269 out->dedent(); 270 out->printf("}"); 271} 272 273// ================================================================================ 274static uint8_t* 275write_raw_varint(uint8_t* buf, uint32_t val) 276{ 277 uint8_t* p = buf; 278 while (true) { 279 if ((val & ~0x7F) == 0) { 280 *p++ = (uint8_t)val; 281 return p; 282 } else { 283 *p++ = (uint8_t)((val & 0x7F) | 0x80); 284 val >>= 7; 285 } 286 } 287} 288 289static int 290write_all(int fd, uint8_t const* buf, size_t size) 291{ 292 while (size > 0) { 293 ssize_t amt = ::write(fd, buf, size); 294 if (amt < 0) { 295 return errno; 296 } 297 size -= amt; 298 buf += amt; 299 } 300 return 0; 301} 302 303static int 304adb_incident_workaround(const char* adbSerial, const vector<string>& sections) 305{ 306 const int maxAllowedSize = 20 * 1024 * 1024; // 20MB 307 unique_ptr<uint8_t[]> buffer(new uint8_t[maxAllowedSize]); 308 309 for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) { 310 Descriptor const* descriptor = IncidentProto::descriptor(); 311 FieldDescriptor const* field; 312 313 // Get the name and field id. 314 string name = *it; 315 char* end; 316 int id = strtol(name.c_str(), &end, 0); 317 if (*end == '\0') { 318 // If it's an id, find out the string. 319 field = descriptor->FindFieldByNumber(id); 320 if (field == NULL) { 321 fprintf(stderr, "Unable to find field number: %d\n", id); 322 return 1; 323 } 324 name = field->name(); 325 } else { 326 // If it's a string, find out the id. 327 field = descriptor->FindFieldByName(name); 328 if (field == NULL) { 329 fprintf(stderr, "Unable to find field: %s\n", name.c_str()); 330 return 1; 331 } 332 id = field->number(); 333 } 334 335 int pfd[2]; 336 if (pipe(pfd) != 0) { 337 fprintf(stderr, "pipe failed: %s\n", strerror(errno)); 338 return 1; 339 } 340 341 pid_t pid = fork(); 342 if (pid == -1) { 343 fprintf(stderr, "fork failed: %s\n", strerror(errno)); 344 return 1; 345 } else if (pid == 0) { 346 // child 347 dup2(pfd[1], STDOUT_FILENO); 348 close(pfd[0]); 349 close(pfd[1]); 350 351 char const** args = (char const**)malloc(sizeof(char*) * 8); 352 int argpos = 0; 353 args[argpos++] = "adb"; 354 if (adbSerial != NULL) { 355 args[argpos++] = "-s"; 356 args[argpos++] = adbSerial; 357 } 358 args[argpos++] = "shell"; 359 args[argpos++] = "dumpsys"; 360 args[argpos++] = name.c_str(); 361 args[argpos++] = "--proto"; 362 args[argpos++] = NULL; 363 execvp(args[0], (char*const*)args); 364 fprintf(stderr, "execvp failed: %s\n", strerror(errno)); 365 free(args); 366 return 1; 367 } else { 368 // parent 369 close(pfd[1]); 370 371 size_t size = 0; 372 while (size < maxAllowedSize) { 373 ssize_t amt = read(pfd[0], buffer.get() + size, maxAllowedSize - size); 374 if (amt == 0) { 375 break; 376 } else if (amt == -1) { 377 fprintf(stderr, "read error: %s\n", strerror(errno)); 378 return 1; 379 } 380 size += amt; 381 } 382 383 int status; 384 do { 385 waitpid(pid, &status, 0); 386 } while (!WIFEXITED(status)); 387 if (WEXITSTATUS(status) != 0) { 388 return WEXITSTATUS(status); 389 } 390 391 if (size > 0) { 392 uint8_t header[20]; 393 uint8_t* p = write_raw_varint(header, (id << 3) | 2); 394 p = write_raw_varint(p, size); 395 int err = write_all(STDOUT_FILENO, header, p-header); 396 if (err != 0) { 397 fprintf(stderr, "write error: %s\n", strerror(err)); 398 return 1; 399 } 400 err = write_all(STDOUT_FILENO, buffer.get(), size); 401 if (err != 0) { 402 fprintf(stderr, "write error: %s\n", strerror(err)); 403 return 1; 404 } 405 } 406 407 close(pfd[0]); 408 } 409 } 410 411 return 0; 412} 413 414// ================================================================================ 415static void 416usage(FILE* out) 417{ 418 fprintf(out, "usage: incident_report -i INPUT [-o OUTPUT]\n"); 419 fprintf(out, "\n"); 420 fprintf(out, "Pretty-prints an incident report protobuf file.\n"); 421 fprintf(out, " -i INPUT the input file. INPUT may be '-' to use stdin\n"); 422 fprintf(out, " -o OUTPUT the output file. OUTPUT may be '-' or omitted to use stdout\n"); 423 fprintf(out, "\n"); 424 fprintf(out, "\n"); 425 fprintf(out, "usage: incident_report [-o OUTPUT] [-t|b] [-s SERIAL] [SECTION...]\n"); 426 fprintf(out, "\n"); 427 fprintf(out, "Take an incident report over adb (which must be in the PATH).\n"); 428 fprintf(out, " -b output the incident report raw protobuf format\n"); 429 fprintf(out, " -o OUTPUT the output file. OUTPUT may be '-' or omitted to use stdout\n"); 430 fprintf(out, " -s SERIAL sent to adb to choose which device, instead of $ANDROID_SERIAL\n"); 431 fprintf(out, " -t output the incident report in pretty-printed text format\n"); 432 fprintf(out, "\n"); 433 fprintf(out, " SECTION which bugreport sections to print, either the int code of the\n"); 434 fprintf(out, " section in the Incident proto or the field name. If ommited,\n"); 435 fprintf(out, " the report will contain all fields\n"); 436 fprintf(out, "\n"); 437} 438 439int 440main(int argc, char** argv) 441{ 442 enum { OUTPUT_TEXT, OUTPUT_PROTO } outputFormat = OUTPUT_TEXT; 443 const char* inFilename = NULL; 444 const char* outFilename = NULL; 445 const char* adbSerial = NULL; 446 bool adbIncidentWorkaround = true; 447 pid_t childPid = -1; 448 vector<string> sections; 449 450 int opt; 451 while ((opt = getopt(argc, argv, "bhi:o:s:tw")) != -1) { 452 switch (opt) { 453 case 'b': 454 outputFormat = OUTPUT_PROTO; 455 break; 456 case 'i': 457 inFilename = optarg; 458 break; 459 case 'o': 460 outFilename = optarg; 461 break; 462 case 's': 463 adbSerial = optarg; 464 break; 465 case 't': 466 outputFormat = OUTPUT_TEXT; 467 break; 468 case 'h': 469 usage(stdout); 470 return 0; 471 case 'w': 472 adbIncidentWorkaround = false; 473 break; 474 default: 475 usage(stderr); 476 return 1; 477 } 478 } 479 480 while (optind < argc) { 481 sections.push_back(argv[optind++]); 482 } 483 484 int inFd; 485 if (inFilename != NULL) { 486 // translate-only mode - oepn the file or use stdin. 487 if (strcmp("-", inFilename) == 0) { 488 inFd = STDIN_FILENO; 489 } else { 490 inFd = open(inFilename, O_RDONLY | O_CLOEXEC); 491 if (inFd < 0) { 492 fprintf(stderr, "unable to open file for read (%s): %s\n", strerror(errno), 493 inFilename); 494 return 1; 495 } 496 } 497 } else { 498 // pipe mode - run adb shell incident ... 499 int pfd[2]; 500 if (pipe(pfd) != 0) { 501 fprintf(stderr, "pipe failed: %s\n", strerror(errno)); 502 return 1; 503 } 504 505 childPid = fork(); 506 if (childPid == -1) { 507 fprintf(stderr, "fork failed: %s\n", strerror(errno)); 508 return 1; 509 } else if (childPid == 0) { 510 dup2(pfd[1], STDOUT_FILENO); 511 close(pfd[0]); 512 close(pfd[1]); 513 // child 514 if (adbIncidentWorkaround) { 515 // TODO: Until the device side incident command is checked in, 516 // the incident_report builds the outer Incident proto by hand 517 // from individual adb shell dumpsys <service> --proto calls, 518 // with a maximum allowed output size. 519 return adb_incident_workaround(adbSerial, sections); 520 } 521 522 // TODO: This is what the real implementation will be... 523 char const** args = (char const**)malloc(sizeof(char*) * (6 + sections.size())); 524 int argpos = 0; 525 args[argpos++] = "adb"; 526 if (adbSerial != NULL) { 527 args[argpos++] = "-s"; 528 args[argpos++] = adbSerial; 529 } 530 args[argpos++] = "shell"; 531 args[argpos++] = "incident"; 532 for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) { 533 args[argpos++] = it->c_str(); 534 } 535 args[argpos++] = NULL; 536 execvp(args[0], (char*const*)args); 537 fprintf(stderr, "execvp failed: %s\n", strerror(errno)); 538 return 0; 539 } else { 540 // parent 541 inFd = pfd[0]; 542 close(pfd[1]); 543 } 544 } 545 546 int outFd; 547 if (outFilename == NULL || strcmp("-", outFilename) == 0) { 548 outFd = STDOUT_FILENO; 549 } else { 550 outFd = open(outFilename, O_CREAT | O_RDWR, 0666); 551 if (outFd < 0) { 552 fprintf(stderr, "unable to open file for write: %s\n", outFilename); 553 return 1; 554 } 555 } 556 557 GenericMessage message; 558 559 Descriptor const* descriptor = IncidentProto::descriptor(); 560 FileInputStream infile(inFd); 561 CodedInputStream in(&infile); 562 563 if (!read_message(&in, descriptor, &message)) { 564 fprintf(stderr, "unable to read incident\n"); 565 return 1; 566 } 567 568 Out out(outFd); 569 570 print_message(&out, descriptor, &message); 571 out.printf("\n"); 572 573 if (childPid != -1) { 574 int status; 575 do { 576 waitpid(childPid, &status, 0); 577 } while (!WIFEXITED(status)); 578 if (WEXITSTATUS(status) != 0) { 579 return WEXITSTATUS(status); 580 } 581 } 582 583 return 0; 584} 585