blob: 91aa44b1b03606b41ee5f67c98ed37d96b75269b [file] [log] [blame]
Felipe Leme218e1ff2016-07-19 17:07:22 -07001/*
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 <string>
Felipe Leme5f6eaca2016-07-29 15:47:00 -070018#include <vector>
Felipe Leme218e1ff2016-07-19 17:07:22 -070019
20#include <android-base/strings.h>
21
22#include "bugreport.h"
Felipe Leme218e1ff2016-07-19 17:07:22 -070023#include "file_sync_service.h"
24
25static constexpr char BUGZ_OK_PREFIX[] = "OK:";
26static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
Felipe Leme97b73a02016-07-26 12:23:40 -070027static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
28static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
Felipe Leme218e1ff2016-07-19 17:07:22 -070029
Felipe Lemed1885422016-07-26 12:14:39 -070030// Custom callback used to handle the output of zipped bugreports.
31class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
32 public:
Felipe Leme97b73a02016-07-26 12:23:40 -070033 BugreportStandardStreamsCallback(const std::string& dest_file, bool show_progress, Bugreport* br)
Felipe Leme33ae8492016-07-27 19:23:09 -070034 : br_(br),
Felipe Leme5f6eaca2016-07-29 15:47:00 -070035 src_file_(),
Felipe Leme33ae8492016-07-27 19:23:09 -070036 dest_file_(dest_file),
37 line_message_(android::base::StringPrintf("generating %s", dest_file_.c_str())),
Felipe Leme5f6eaca2016-07-29 15:47:00 -070038 invalid_lines_(),
Felipe Leme33ae8492016-07-27 19:23:09 -070039 show_progress_(show_progress),
Felipe Leme5f6eaca2016-07-29 15:47:00 -070040 status_(0),
Felipe Leme33ae8492016-07-27 19:23:09 -070041 line_() {
Felipe Lemed1885422016-07-26 12:14:39 -070042 }
43
44 void OnStdout(const char* buffer, int length) {
Felipe Leme97b73a02016-07-26 12:23:40 -070045 for (int i = 0; i < length; i++) {
46 char c = buffer[i];
47 if (c == '\n') {
48 ProcessLine(line_);
49 line_.clear();
50 } else {
51 line_.append(1, c);
52 }
53 }
Felipe Lemed1885422016-07-26 12:14:39 -070054 }
55
56 void OnStderr(const char* buffer, int length) {
57 OnStream(nullptr, stderr, buffer, length);
58 }
Felipe Lemed1885422016-07-26 12:14:39 -070059 int Done(int unused_) {
Felipe Leme5f6eaca2016-07-29 15:47:00 -070060 // Process remaining line, if any.
Felipe Leme97b73a02016-07-26 12:23:40 -070061 ProcessLine(line_);
Felipe Leme5f6eaca2016-07-29 15:47:00 -070062
63 // Warn about invalid lines, if any.
64 if (!invalid_lines_.empty()) {
65 fprintf(stderr,
66 "WARNING: bugreportz generated %zu line(s) with unknown commands, "
67 "device might not support zipped bugreports:\n",
68 invalid_lines_.size());
69 for (const auto& line : invalid_lines_) {
70 fprintf(stderr, "\t%s\n", line.c_str());
71 }
72 fprintf(stderr,
73 "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
74 }
75
76 // Pull the generated bug report.
77 if (status_ == 0) {
78 if (src_file_.empty()) {
79 fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
80 BUGZ_FAIL_PREFIX);
81 return -1;
82 }
83 std::vector<const char*> srcs{src_file_.c_str()};
84 status_ = br_->DoSyncPull(srcs, dest_file_.c_str(), true, line_message_.c_str()) ? 0 : 1;
85 if (status_ != 0) {
86 fprintf(stderr,
87 "Bug report finished but could not be copied to '%s'.\n"
88 "Try to run 'adb pull %s <directory>'\n"
89 "to copy it to a directory that can be written.\n",
90 dest_file_.c_str(), src_file_.c_str());
91 }
92 }
Felipe Leme97b73a02016-07-26 12:23:40 -070093 return status_;
Felipe Lemed1885422016-07-26 12:14:39 -070094 }
95
96 private:
Felipe Leme97b73a02016-07-26 12:23:40 -070097 void ProcessLine(const std::string& line) {
98 if (line.empty()) return;
99
100 if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
Felipe Leme5f6eaca2016-07-29 15:47:00 -0700101 src_file_ = &line[strlen(BUGZ_OK_PREFIX)];
Felipe Leme97b73a02016-07-26 12:23:40 -0700102 } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
103 const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
104 fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
105 status_ = -1;
106 } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
107 // progress_line should have the following format:
108 //
109 // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
110 //
111 size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
112 size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
113 int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
114 int total = std::stoi(line.substr(idx2 + 1));
115 br_->UpdateProgress(dest_file_, progress, total);
116 } else {
Felipe Leme5f6eaca2016-07-29 15:47:00 -0700117 invalid_lines_.push_back(line);
Felipe Leme97b73a02016-07-26 12:23:40 -0700118 }
119 }
120
Felipe Lemed1885422016-07-26 12:14:39 -0700121 Bugreport* br_;
Felipe Leme5f6eaca2016-07-29 15:47:00 -0700122 std::string src_file_;
Felipe Lemed1885422016-07-26 12:14:39 -0700123 const std::string dest_file_;
Felipe Leme33ae8492016-07-27 19:23:09 -0700124 const std::string line_message_;
Felipe Leme5f6eaca2016-07-29 15:47:00 -0700125 std::vector<std::string> invalid_lines_;
Felipe Leme97b73a02016-07-26 12:23:40 -0700126 bool show_progress_;
127 int status_;
128
129 // Temporary buffer containing the characters read since the last newline
130 // (\n).
131 std::string line_;
Felipe Lemed1885422016-07-26 12:14:39 -0700132
133 DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
134};
135
Felipe Leme97b73a02016-07-26 12:23:40 -0700136// Implemented in commandline.cpp
137int usage();
138
Felipe Leme218e1ff2016-07-19 17:07:22 -0700139int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
140 if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
141 if (argc != 2) return usage();
142
143 // Zipped bugreport option - will call 'bugreportz', which prints the location
144 // of the generated
145 // file, then pull it to the destination file provided by the user.
146 std::string dest_file = argv[1];
147 if (!android::base::EndsWith(argv[1], ".zip")) {
148 // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
149 dest_file += ".zip";
150 }
Felipe Leme97b73a02016-07-26 12:23:40 -0700151
152 // Gets bugreportz version.
153 std::string bugz_stderr;
154 DefaultStandardStreamsCallback version_callback(nullptr, &bugz_stderr);
155 int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
Felipe Leme97b73a02016-07-26 12:23:40 -0700156 std::string bugz_version = android::base::Trim(bugz_stderr);
157
Felipe Leme5f6eaca2016-07-29 15:47:00 -0700158 if (status != 0 || bugz_version.empty()) {
159 fprintf(stderr,
160 "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
161 "If the device runs Android M or below, try 'adb bugreport' instead.\n",
162 bugz_stderr.c_str(), status);
163 return status != 0 ? status : -1;
164 }
165
Felipe Leme97b73a02016-07-26 12:23:40 -0700166 bool show_progress = true;
167 std::string bugz_command = "bugreportz -p";
168 if (bugz_version == "1.0") {
169 // 1.0 does not support progress notifications, so print a disclaimer
170 // message instead.
171 fprintf(stderr,
172 "Bugreport is in progress and it could take minutes to complete.\n"
173 "Please be patient and do not cancel or disconnect your device "
Felipe Leme5f6eaca2016-07-29 15:47:00 -0700174 "until it completes.\n");
Felipe Leme97b73a02016-07-26 12:23:40 -0700175 show_progress = false;
176 bugz_command = "bugreportz";
177 }
178 BugreportStandardStreamsCallback bugz_callback(dest_file, show_progress, this);
179 return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
180}
181
Felipe Leme33ae8492016-07-27 19:23:09 -0700182void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
Felipe Leme97b73a02016-07-26 12:23:40 -0700183 int progress_percentage = (progress * 100 / total);
Felipe Leme33ae8492016-07-27 19:23:09 -0700184 line_printer_.Print(
185 android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
186 LinePrinter::INFO);
Felipe Leme218e1ff2016-07-19 17:07:22 -0700187}
188
189int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
190 const std::string& command, bool disable_shell_protocol,
Felipe Lemed1885422016-07-26 12:14:39 -0700191 StandardStreamsCallbackInterface* callback) {
192 return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
Felipe Leme218e1ff2016-07-19 17:07:22 -0700193}
194
195bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
196 const char* name) {
197 return do_sync_pull(srcs, dst, copy_attrs, name);
198}