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