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 #define TRACE_TAG ADB 18 19 #include "bugreport.h" 20 21 #include <string> 22 #include <vector> 23 24 #include <android-base/file.h> 25 #include <android-base/strings.h> 26 27 #include "sysdeps.h" 28 #include "adb_utils.h" 29 #include "file_sync_service.h" 30 31 static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:"; 32 static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:"; 33 static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/"; 34 static constexpr char BUGZ_OK_PREFIX[] = "OK:"; 35 static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:"; 36 37 // Custom callback used to handle the output of zipped bugreports. 38 class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface { 39 public: 40 BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file, 41 bool show_progress, Bugreport* br) 42 : br_(br), 43 src_file_(), 44 dest_dir_(dest_dir), 45 dest_file_(dest_file), 46 line_message_(), 47 invalid_lines_(), 48 show_progress_(show_progress), 49 status_(0), 50 line_(), 51 last_progress_percentage_(0) { 52 SetLineMessage("generating"); 53 } 54 55 void OnStdout(const char* buffer, int length) { 56 for (int i = 0; i < length; i++) { 57 char c = buffer[i]; 58 if (c == '\n') { 59 ProcessLine(line_); 60 line_.clear(); 61 } else { 62 line_.append(1, c); 63 } 64 } 65 } 66 67 void OnStderr(const char* buffer, int length) { 68 OnStream(nullptr, stderr, buffer, length); 69 } 70 71 int Done(int unused_) { 72 // Process remaining line, if any. 73 ProcessLine(line_); 74 75 // Warn about invalid lines, if any. 76 if (!invalid_lines_.empty()) { 77 fprintf(stderr, 78 "WARNING: bugreportz generated %zu line(s) with unknown commands, " 79 "device might not support zipped bugreports:\n", 80 invalid_lines_.size()); 81 for (const auto& line : invalid_lines_) { 82 fprintf(stderr, "\t%s\n", line.c_str()); 83 } 84 fprintf(stderr, 85 "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n"); 86 } 87 88 // Pull the generated bug report. 89 if (status_ == 0) { 90 if (src_file_.empty()) { 91 fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX, 92 BUGZ_FAIL_PREFIX); 93 return -1; 94 } 95 std::string destination; 96 if (dest_dir_.empty()) { 97 destination = dest_file_; 98 } else { 99 destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(), 100 OS_PATH_SEPARATOR, dest_file_.c_str()); 101 } 102 std::vector<const char*> srcs{src_file_.c_str()}; 103 SetLineMessage("pulling"); 104 status_ = 105 br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1; 106 if (status_ != 0) { 107 fprintf(stderr, 108 "Bug report finished but could not be copied to '%s'.\n" 109 "Try to run 'adb pull %s <directory>'\n" 110 "to copy it to a directory that can be written.\n", 111 destination.c_str(), src_file_.c_str()); 112 } 113 } 114 return status_; 115 } 116 117 private: 118 void SetLineMessage(const std::string& action) { 119 line_message_ = action + " " + android::base::Basename(dest_file_); 120 } 121 122 void SetSrcFile(const std::string path) { 123 src_file_ = path; 124 if (!dest_dir_.empty()) { 125 // Only uses device-provided name when user passed a directory. 126 dest_file_ = android::base::Basename(path); 127 SetLineMessage("generating"); 128 } 129 } 130 131 void ProcessLine(const std::string& line) { 132 if (line.empty()) return; 133 134 if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) { 135 SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]); 136 } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) { 137 SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]); 138 } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) { 139 const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)]; 140 fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message); 141 status_ = -1; 142 } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) { 143 // progress_line should have the following format: 144 // 145 // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL 146 // 147 size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX); 148 size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR); 149 int progress = std::stoi(line.substr(idx1, (idx2 - idx1))); 150 int total = std::stoi(line.substr(idx2 + 1)); 151 int progress_percentage = (progress * 100 / total); 152 if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) { 153 // Ignore. 154 return; 155 } 156 last_progress_percentage_ = progress_percentage; 157 br_->UpdateProgress(line_message_, progress_percentage); 158 } else { 159 invalid_lines_.push_back(line); 160 } 161 } 162 163 Bugreport* br_; 164 165 // Path of bugreport on device. 166 std::string src_file_; 167 168 // Bugreport destination on host, depending on argument passed on constructor: 169 // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name 170 // of the bugreport reported by the device. 171 // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the 172 // name of the bugreport reported by the device. 173 // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor. 174 std::string dest_dir_, dest_file_; 175 176 // Message displayed on LinePrinter, it's updated every time the destination above change. 177 std::string line_message_; 178 179 // Lines sent by bugreportz that contain invalid commands; will be displayed at the end. 180 std::vector<std::string> invalid_lines_; 181 182 // Whether PROGRESS_LINES should be interpreted as progress. 183 bool show_progress_; 184 185 // Overall process of the operation, as returned by Done(). 186 int status_; 187 188 // Temporary buffer containing the characters read since the last newline (\n). 189 std::string line_; 190 191 // Last displayed progress. 192 // Since dumpstate progress can recede, only forward progress should be displayed 193 int last_progress_percentage_; 194 195 DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback); 196 }; 197 198 int Bugreport::DoIt(int argc, const char** argv) { 199 if (argc > 2) return syntax_error("adb bugreport [PATH]"); 200 201 // Gets bugreportz version. 202 std::string bugz_stdout, bugz_stderr; 203 DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr); 204 int status = SendShellCommand("bugreportz -v", false, &version_callback); 205 std::string bugz_version = android::base::Trim(bugz_stderr); 206 std::string bugz_output = android::base::Trim(bugz_stdout); 207 208 if (status != 0 || bugz_version.empty()) { 209 D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status, 210 bugz_output.c_str(), bugz_version.c_str()); 211 if (argc == 1) { 212 // Device does not support bugreportz: if called as 'adb bugreport', just falls out to 213 // the flat-file version. 214 fprintf(stderr, 215 "Failed to get bugreportz version, which is only available on devices " 216 "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n"); 217 return SendShellCommand("bugreport", false); 218 } 219 220 // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling 221 // 'bugreport' would generate a lot of output the user might not be prepared to handle). 222 fprintf(stderr, 223 "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n" 224 "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n", 225 bugz_output.c_str(), status); 226 return status != 0 ? status : -1; 227 } 228 229 std::string dest_file, dest_dir; 230 231 if (argc == 1) { 232 // No args - use current directory 233 if (!getcwd(&dest_dir)) { 234 perror("adb: getcwd failed"); 235 return 1; 236 } 237 } else { 238 // Check whether argument is a directory or file 239 if (directory_exists(argv[1])) { 240 dest_dir = argv[1]; 241 } else { 242 dest_file = argv[1]; 243 } 244 } 245 246 if (dest_file.empty()) { 247 // Uses a default value until device provides the proper name 248 dest_file = "bugreport.zip"; 249 } else { 250 if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) { 251 dest_file += ".zip"; 252 } 253 } 254 255 bool show_progress = true; 256 std::string bugz_command = "bugreportz -p"; 257 if (bugz_version == "1.0") { 258 // 1.0 does not support progress notifications, so print a disclaimer 259 // message instead. 260 fprintf(stderr, 261 "Bugreport is in progress and it could take minutes to complete.\n" 262 "Please be patient and do not cancel or disconnect your device " 263 "until it completes.\n"); 264 show_progress = false; 265 bugz_command = "bugreportz"; 266 } 267 BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this); 268 return SendShellCommand(bugz_command, false, &bugz_callback); 269 } 270 271 void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) { 272 line_printer_.Print( 273 android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()), 274 LinePrinter::INFO); 275 } 276 277 int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol, 278 StandardStreamsCallbackInterface* callback) { 279 return send_shell_command(command, disable_shell_protocol, callback); 280 } 281 282 bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs, 283 const char* name) { 284 return do_sync_pull(srcs, dst, copy_attrs, name); 285 } 286