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