dumpstate.cpp revision 32af8c2aefd9a31e851c8f17168f19afcb5efb18
1/* 2 * Copyright (C) 2008 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 <dirent.h> 18#include <errno.h> 19#include <fcntl.h> 20#include <libgen.h> 21#include <limits.h> 22#include <memory> 23#include <regex> 24#include <set> 25#include <signal.h> 26#include <stdbool.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string> 30#include <string.h> 31#include <sys/prctl.h> 32#include <sys/resource.h> 33#include <sys/stat.h> 34#include <sys/time.h> 35#include <sys/wait.h> 36#include <unistd.h> 37 38#include <android-base/file.h> 39#include <android-base/properties.h> 40#include <android-base/stringprintf.h> 41#include <android-base/unique_fd.h> 42#include <cutils/properties.h> 43#include <hardware_legacy/power.h> 44 45#include "private/android_filesystem_config.h" 46 47#define LOG_TAG "dumpstate" 48#include <cutils/log.h> 49 50#include "dumpstate.h" 51#include "ziparchive/zip_writer.h" 52 53#include <openssl/sha.h> 54 55using android::base::StringPrintf; 56 57/* read before root is shed */ 58static char cmdline_buf[16384] = "(unknown)"; 59static const char *dump_traces_path = NULL; 60 61// Command-line arguments as string 62static std::string args; 63 64// TODO: variables below should be part of dumpstate object 65static std::string buildType; 66static time_t now; 67static std::unique_ptr<ZipWriter> zip_writer; 68static std::set<std::string> mount_points; 69void add_mountinfo(); 70/* suffix of the bugreport files - it's typically the date (when invoked with -d), 71 * although it could be changed by the user using a system property */ 72static std::string suffix; 73static std::string extraOptions; 74 75#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops" 76#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0" 77 78#define RAFT_DIR "/data/misc/raft" 79#define RECOVERY_DIR "/cache/recovery" 80#define RECOVERY_DATA_DIR "/data/misc/recovery" 81#define LOGPERSIST_DATA_DIR "/data/misc/logd" 82#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" 83#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" 84#define TOMBSTONE_DIR "/data/tombstones" 85#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_" 86/* Can accomodate a tombstone number up to 9999. */ 87#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4) 88#define NUM_TOMBSTONES 10 89#define WLUTIL "/vendor/xbin/wlutil" 90#define WAKE_LOCK_NAME "dumpstate_wakelock" 91 92typedef struct { 93 char name[TOMBSTONE_MAX_LEN]; 94 int fd; 95} tombstone_data_t; 96 97static tombstone_data_t tombstone_data[NUM_TOMBSTONES]; 98 99// TODO: temporary variables and functions used during C++ refactoring 100static Dumpstate& ds = Dumpstate::GetInstance(); 101static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, 102 const CommandOptions& options = CommandOptions::DEFAULT) { 103 return ds.RunCommand(title, fullCommand, options); 104} 105static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, 106 const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS, 107 long dumpsysTimeout = 0) { 108 return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout); 109} 110static int DumpFile(const std::string& title, const std::string& path) { 111 return ds.DumpFile(title, path); 112} 113bool IsUserBuild() { 114 return ds.IsUserBuild(); 115} 116 117/* 118 * List of supported zip format versions. 119 * 120 * See bugreport-format.md for more info. 121 */ 122static std::string VERSION_DEFAULT = "1.0"; 123 124// Relative directory (inside the zip) for all files copied as-is into the bugreport. 125static const std::string ZIP_ROOT_DIR = "FS"; 126 127static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options"; 128static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id"; 129 130bool Dumpstate::IsUserBuild() { 131 return "user" == buildType; 132} 133 134bool Dumpstate::IsDryRun() { 135 return dryRun_; 136} 137 138/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones; 139 * otherwise, gets just those modified in the last half an hour. */ 140static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) { 141 time_t thirty_minutes_ago = now - 60*30; 142 for (size_t i = 0; i < NUM_TOMBSTONES; i++) { 143 snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i); 144 int fd = TEMP_FAILURE_RETRY(open(data[i].name, 145 O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)); 146 struct stat st; 147 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 && 148 (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) { 149 data[i].fd = fd; 150 } else { 151 close(fd); 152 data[i].fd = -1; 153 } 154 } 155} 156 157// for_each_pid() callback to get mount info about a process. 158void do_mountinfo(int pid, const char *name) { 159 char path[PATH_MAX]; 160 161 // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points 162 // are added. 163 snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid); 164 char linkname[PATH_MAX]; 165 ssize_t r = readlink(path, linkname, PATH_MAX); 166 if (r == -1) { 167 MYLOGE("Unable to read link for %s: %s\n", path, strerror(errno)); 168 return; 169 } 170 linkname[r] = '\0'; 171 172 if (mount_points.find(linkname) == mount_points.end()) { 173 // First time this mount point was found: add it 174 snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid); 175 if (add_zip_entry(ZIP_ROOT_DIR + path, path)) { 176 mount_points.insert(linkname); 177 } else { 178 MYLOGE("Unable to add mountinfo %s to zip file\n", path); 179 } 180 } 181} 182 183void add_mountinfo() { 184 if (!zip_writer) return; 185 std::string title = "MOUNT INFO"; 186 mount_points.clear(); 187 DurationReporter durationReporter(title, nullptr); 188 for_each_pid(do_mountinfo, nullptr); 189 MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size()); 190} 191 192static void dump_dev_files(const char *title, const char *driverpath, const char *filename) 193{ 194 DIR *d; 195 struct dirent *de; 196 char path[PATH_MAX]; 197 198 d = opendir(driverpath); 199 if (d == NULL) { 200 return; 201 } 202 203 while ((de = readdir(d))) { 204 if (de->d_type != DT_LNK) { 205 continue; 206 } 207 snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename); 208 DumpFile(title, path); 209 } 210 211 closedir(d); 212} 213 214// return pid of a userspace process. If not found or error, return 0. 215static unsigned int pid_of_process(const char* ps_name) { 216 DIR *proc_dir; 217 struct dirent *ps; 218 unsigned int pid; 219 std::string cmdline; 220 221 if (!(proc_dir = opendir("/proc"))) { 222 MYLOGE("Can't open /proc\n"); 223 return 0; 224 } 225 226 while ((ps = readdir(proc_dir))) { 227 if (!(pid = atoi(ps->d_name))) { 228 continue; 229 } 230 android::base::ReadFileToString("/proc/" 231 + std::string(ps->d_name) + "/cmdline", &cmdline); 232 if (cmdline.find(ps_name) == std::string::npos) { 233 continue; 234 } else { 235 closedir(proc_dir); 236 return pid; 237 } 238 } 239 closedir(proc_dir); 240 return 0; 241} 242 243// dump anrd's trace and add to the zip file. 244// 1. check if anrd is running on this device. 245// 2. send a SIGUSR1 to its pid which will dump anrd's trace. 246// 3. wait until the trace generation completes and add to the zip file. 247static bool dump_anrd_trace() { 248 unsigned int pid; 249 char buf[50], path[PATH_MAX]; 250 struct dirent *trace; 251 struct stat st; 252 DIR *trace_dir; 253 int retry = 5; 254 long max_ctime = 0, old_mtime; 255 long long cur_size = 0; 256 const char *trace_path = "/data/misc/anrd/"; 257 258 if (!zip_writer) { 259 MYLOGE("Not dumping anrd trace because zip_writer is not set\n"); 260 return false; 261 } 262 263 // find anrd's pid if it is running. 264 pid = pid_of_process("/system/xbin/anrd"); 265 266 if (pid > 0) { 267 if (stat(trace_path, &st) == 0) { 268 old_mtime = st.st_mtime; 269 } else { 270 MYLOGE("Failed to find: %s\n", trace_path); 271 return false; 272 } 273 274 // send SIGUSR1 to the anrd to generate a trace. 275 sprintf(buf, "%u", pid); 276 if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf}, 277 CommandOptions::WithTimeout(1).Build())) { 278 MYLOGE("anrd signal timed out. Please manually collect trace\n"); 279 return false; 280 } 281 282 while (retry-- > 0 && old_mtime == st.st_mtime) { 283 sleep(1); 284 stat(trace_path, &st); 285 } 286 287 if (retry < 0 && old_mtime == st.st_mtime) { 288 MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path); 289 return false; 290 } 291 292 // identify the trace file by its creation time. 293 if (!(trace_dir = opendir(trace_path))) { 294 MYLOGE("Can't open trace file under %s\n", trace_path); 295 } 296 while ((trace = readdir(trace_dir))) { 297 if (strcmp(trace->d_name, ".") == 0 298 || strcmp(trace->d_name, "..") == 0) { 299 continue; 300 } 301 sprintf(path, "%s%s", trace_path, trace->d_name); 302 if (stat(path, &st) == 0) { 303 if (st.st_ctime > max_ctime) { 304 max_ctime = st.st_ctime; 305 sprintf(buf, "%s", trace->d_name); 306 } 307 } 308 } 309 closedir(trace_dir); 310 311 // Wait until the dump completes by checking the size of the trace. 312 if (max_ctime > 0) { 313 sprintf(path, "%s%s", trace_path, buf); 314 while(true) { 315 sleep(1); 316 if (stat(path, &st) == 0) { 317 if (st.st_size == cur_size) { 318 break; 319 } else if (st.st_size > cur_size) { 320 cur_size = st.st_size; 321 } else { 322 return false; 323 } 324 } else { 325 MYLOGE("Cant stat() %s anymore\n", path); 326 return false; 327 } 328 } 329 // Add to the zip file. 330 if (!add_zip_entry("anrd_trace.txt", path)) { 331 MYLOGE("Unable to add anrd_trace file %s to zip file\n", path); 332 } else { 333 if (remove(path)) { 334 MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno)); 335 } 336 return true; 337 } 338 } else { 339 MYLOGE("Can't stats any trace file under %s\n", trace_path); 340 } 341 } 342 return false; 343} 344 345static void dump_systrace() { 346 if (!zip_writer) { 347 MYLOGD("Not dumping systrace because zip_writer is not set\n"); 348 return; 349 } 350 std::string systrace_path = ds.bugreportDir_ + "/systrace-" + suffix + ".txt"; 351 if (systrace_path.empty()) { 352 MYLOGE("Not dumping systrace because path is empty\n"); 353 return; 354 } 355 const char* path = "/sys/kernel/debug/tracing/tracing_on"; 356 long int is_tracing; 357 if (read_file_as_long(path, &is_tracing)) { 358 return; // error already logged 359 } 360 if (is_tracing <= 0) { 361 MYLOGD("Skipping systrace because '%s' content is '%ld'\n", path, is_tracing); 362 return; 363 } 364 365 MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes", 366 systrace_path.c_str()); 367 if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path}, 368 CommandOptions::WithTimeout(120).Build())) { 369 MYLOGE("systrace timed out, its zip entry will be incomplete\n"); 370 // TODO: RunCommand tries to kill the process, but atrace doesn't die 371 // peacefully; ideally, we should call strace to stop itself, but there is no such option 372 // yet (just a --async_stop, which stops and dump 373 // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) { 374 // MYLOGE("could not stop systrace "); 375 // } 376 } 377 if (!add_zip_entry("systrace.txt", systrace_path)) { 378 MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str()); 379 } else { 380 if (remove(systrace_path.c_str())) { 381 MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno)); 382 } 383 } 384} 385 386static void dump_raft() { 387 if (IsUserBuild()) { 388 return; 389 } 390 391 std::string raft_log_path = ds.bugreportDir_ + "/raft_log.txt"; 392 if (raft_log_path.empty()) { 393 MYLOGD("raft_log_path is empty\n"); 394 return; 395 } 396 397 struct stat s; 398 if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) { 399 MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR); 400 return; 401 } 402 403 CommandOptions options = CommandOptions::WithTimeout(600).Build(); 404 if (!zip_writer) { 405 // Write compressed and encoded raft logs to stdout if not zip_writer. 406 RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options); 407 return; 408 } 409 410 RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_log_path}, options); 411 if (!add_zip_entry("raft_log.txt", raft_log_path)) { 412 MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str()); 413 } else { 414 if (remove(raft_log_path.c_str())) { 415 MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno)); 416 } 417 } 418} 419 420static bool skip_not_stat(const char *path) { 421 static const char stat[] = "/stat"; 422 size_t len = strlen(path); 423 if (path[len - 1] == '/') { /* Directory? */ 424 return false; 425 } 426 return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */ 427} 428 429static bool skip_none(const char *path) { 430 return false; 431} 432 433static const char mmcblk0[] = "/sys/block/mmcblk0/"; 434unsigned long worst_write_perf = 20000; /* in KB/s */ 435 436// 437// stat offsets 438// Name units description 439// ---- ----- ----------- 440// read I/Os requests number of read I/Os processed 441#define __STAT_READ_IOS 0 442// read merges requests number of read I/Os merged with in-queue I/O 443#define __STAT_READ_MERGES 1 444// read sectors sectors number of sectors read 445#define __STAT_READ_SECTORS 2 446// read ticks milliseconds total wait time for read requests 447#define __STAT_READ_TICKS 3 448// write I/Os requests number of write I/Os processed 449#define __STAT_WRITE_IOS 4 450// write merges requests number of write I/Os merged with in-queue I/O 451#define __STAT_WRITE_MERGES 5 452// write sectors sectors number of sectors written 453#define __STAT_WRITE_SECTORS 6 454// write ticks milliseconds total wait time for write requests 455#define __STAT_WRITE_TICKS 7 456// in_flight requests number of I/Os currently in flight 457#define __STAT_IN_FLIGHT 8 458// io_ticks milliseconds total time this block device has been active 459#define __STAT_IO_TICKS 9 460// time_in_queue milliseconds total wait time for all requests 461#define __STAT_IN_QUEUE 10 462#define __STAT_NUMBER_FIELD 11 463// 464// read I/Os, write I/Os 465// ===================== 466// 467// These values increment when an I/O request completes. 468// 469// read merges, write merges 470// ========================= 471// 472// These values increment when an I/O request is merged with an 473// already-queued I/O request. 474// 475// read sectors, write sectors 476// =========================== 477// 478// These values count the number of sectors read from or written to this 479// block device. The "sectors" in question are the standard UNIX 512-byte 480// sectors, not any device- or filesystem-specific block size. The 481// counters are incremented when the I/O completes. 482#define SECTOR_SIZE 512 483// 484// read ticks, write ticks 485// ======================= 486// 487// These values count the number of milliseconds that I/O requests have 488// waited on this block device. If there are multiple I/O requests waiting, 489// these values will increase at a rate greater than 1000/second; for 490// example, if 60 read requests wait for an average of 30 ms, the read_ticks 491// field will increase by 60*30 = 1800. 492// 493// in_flight 494// ========= 495// 496// This value counts the number of I/O requests that have been issued to 497// the device driver but have not yet completed. It does not include I/O 498// requests that are in the queue but not yet issued to the device driver. 499// 500// io_ticks 501// ======== 502// 503// This value counts the number of milliseconds during which the device has 504// had I/O requests queued. 505// 506// time_in_queue 507// ============= 508// 509// This value counts the number of milliseconds that I/O requests have waited 510// on this block device. If there are multiple I/O requests waiting, this 511// value will increase as the product of the number of milliseconds times the 512// number of requests waiting (see "read ticks" above for an example). 513#define S_TO_MS 1000 514// 515 516static int dump_stat_from_fd(const char *title __unused, const char *path, int fd) { 517 unsigned long long fields[__STAT_NUMBER_FIELD]; 518 bool z; 519 char *cp, *buffer = NULL; 520 size_t i = 0; 521 FILE *fp = fdopen(fd, "rb"); 522 getline(&buffer, &i, fp); 523 fclose(fp); 524 if (!buffer) { 525 return -errno; 526 } 527 i = strlen(buffer); 528 while ((i > 0) && (buffer[i - 1] == '\n')) { 529 buffer[--i] = '\0'; 530 } 531 if (!*buffer) { 532 free(buffer); 533 return 0; 534 } 535 z = true; 536 for (cp = buffer, i = 0; i < (sizeof(fields) / sizeof(fields[0])); ++i) { 537 fields[i] = strtoull(cp, &cp, 10); 538 if (fields[i] != 0) { 539 z = false; 540 } 541 } 542 if (z) { /* never accessed */ 543 free(buffer); 544 return 0; 545 } 546 547 if (!strncmp(path, mmcblk0, sizeof(mmcblk0) - 1)) { 548 path += sizeof(mmcblk0) - 1; 549 } 550 551 printf("%s: %s\n", path, buffer); 552 free(buffer); 553 554 if (fields[__STAT_IO_TICKS]) { 555 unsigned long read_perf = 0; 556 unsigned long read_ios = 0; 557 if (fields[__STAT_READ_TICKS]) { 558 unsigned long long divisor = fields[__STAT_READ_TICKS] 559 * fields[__STAT_IO_TICKS]; 560 read_perf = ((unsigned long long)SECTOR_SIZE 561 * fields[__STAT_READ_SECTORS] 562 * fields[__STAT_IN_QUEUE] + (divisor >> 1)) 563 / divisor; 564 read_ios = ((unsigned long long)S_TO_MS * fields[__STAT_READ_IOS] 565 * fields[__STAT_IN_QUEUE] + (divisor >> 1)) 566 / divisor; 567 } 568 569 unsigned long write_perf = 0; 570 unsigned long write_ios = 0; 571 if (fields[__STAT_WRITE_TICKS]) { 572 unsigned long long divisor = fields[__STAT_WRITE_TICKS] 573 * fields[__STAT_IO_TICKS]; 574 write_perf = ((unsigned long long)SECTOR_SIZE 575 * fields[__STAT_WRITE_SECTORS] 576 * fields[__STAT_IN_QUEUE] + (divisor >> 1)) 577 / divisor; 578 write_ios = ((unsigned long long)S_TO_MS * fields[__STAT_WRITE_IOS] 579 * fields[__STAT_IN_QUEUE] + (divisor >> 1)) 580 / divisor; 581 } 582 583 unsigned queue = (fields[__STAT_IN_QUEUE] 584 + (fields[__STAT_IO_TICKS] >> 1)) 585 / fields[__STAT_IO_TICKS]; 586 587 if (!write_perf && !write_ios) { 588 printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n", 589 path, read_perf, read_ios, queue); 590 } else { 591 printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n", 592 path, read_perf, read_ios, write_perf, write_ios, queue); 593 } 594 595 /* bugreport timeout factor adjustment */ 596 if ((write_perf > 1) && (write_perf < worst_write_perf)) { 597 worst_write_perf = write_perf; 598 } 599 } 600 return 0; 601} 602 603/* Copied policy from system/core/logd/LogBuffer.cpp */ 604 605#define LOG_BUFFER_SIZE (256 * 1024) 606#define LOG_BUFFER_MIN_SIZE (64 * 1024UL) 607#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL) 608 609static bool valid_size(unsigned long value) { 610 if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) { 611 return false; 612 } 613 614 long pages = sysconf(_SC_PHYS_PAGES); 615 if (pages < 1) { 616 return true; 617 } 618 619 long pagesize = sysconf(_SC_PAGESIZE); 620 if (pagesize <= 1) { 621 pagesize = PAGE_SIZE; 622 } 623 624 // maximum memory impact a somewhat arbitrary ~3% 625 pages = (pages + 31) / 32; 626 unsigned long maximum = pages * pagesize; 627 628 if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) { 629 return true; 630 } 631 632 return value <= maximum; 633} 634 635// TODO: migrate to logd/LogBuffer.cpp or use android::base::GetProperty 636static unsigned long property_get_size(const char *key) { 637 unsigned long value; 638 char *cp, property[PROPERTY_VALUE_MAX]; 639 640 property_get(key, property, ""); 641 value = strtoul(property, &cp, 10); 642 643 switch(*cp) { 644 case 'm': 645 case 'M': 646 value *= 1024; 647 /* FALLTHRU */ 648 case 'k': 649 case 'K': 650 value *= 1024; 651 /* FALLTHRU */ 652 case '\0': 653 break; 654 655 default: 656 value = 0; 657 } 658 659 if (!valid_size(value)) { 660 value = 0; 661 } 662 663 return value; 664} 665 666/* timeout in ms */ 667static unsigned long logcat_timeout(const char *name) { 668 static const char global_tuneable[] = "persist.logd.size"; // Settings App 669 static const char global_default[] = "ro.logd.size"; // BoardConfig.mk 670 char key[PROP_NAME_MAX]; 671 unsigned long property_size, default_size; 672 673 default_size = property_get_size(global_tuneable); 674 if (!default_size) { 675 default_size = property_get_size(global_default); 676 } 677 678 snprintf(key, sizeof(key), "%s.%s", global_tuneable, name); 679 property_size = property_get_size(key); 680 681 if (!property_size) { 682 snprintf(key, sizeof(key), "%s.%s", global_default, name); 683 property_size = property_get_size(key); 684 } 685 686 if (!property_size) { 687 property_size = default_size; 688 } 689 690 if (!property_size) { 691 property_size = LOG_BUFFER_SIZE; 692 } 693 694 /* Engineering margin is ten-fold our guess */ 695 return 10 * (property_size + worst_write_perf) / worst_write_perf; 696} 697 698/* End copy from system/core/logd/LogBuffer.cpp */ 699 700/* dumps the current system state to stdout */ 701void print_header(const std::string& version) { 702 std::string build, fingerprint, radio, bootloader, network; 703 char date[80]; 704 705 build = android::base::GetProperty("ro.build.display.id", "(unknown)"); 706 fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)"); 707 buildType = android::base::GetProperty("ro.build.type", "(unknown)"); 708 radio = android::base::GetProperty("gsm.version.baseband", "(unknown)"); 709 bootloader = android::base::GetProperty("ro.bootloader", "(unknown)"); 710 network = android::base::GetProperty("gsm.operator.alpha", "(unknown)"); 711 strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now)); 712 713 printf("========================================================\n"); 714 printf("== dumpstate: %s\n", date); 715 printf("========================================================\n"); 716 717 printf("\n"); 718 printf("Build: %s\n", build.c_str()); 719 // NOTE: fingerprint entry format is important for other tools. 720 printf("Build fingerprint: '%s'\n", fingerprint.c_str()); 721 printf("Bootloader: %s\n", bootloader.c_str()); 722 printf("Radio: %s\n", radio.c_str()); 723 printf("Network: %s\n", network.c_str()); 724 725 printf("Kernel: "); 726 DumpFile("", "/proc/version"); 727 printf("Command line: %s\n", strtok(cmdline_buf, "\n")); 728 printf("Bugreport format version: %s\n", version.c_str()); 729 printf("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", ds.id_, getpid(), 730 ds.dryRun_, args.c_str(), extraOptions.c_str()); 731 printf("\n"); 732} 733 734// List of file extensions that can cause a zip file attachment to be rejected by some email 735// service providers. 736static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = { 737 ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp", 738 ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct", 739 ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh" 740}; 741 742bool add_zip_entry_from_fd(const std::string& entry_name, int fd) { 743 if (!zip_writer) { 744 MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n", 745 entry_name.c_str()); 746 return false; 747 } 748 std::string valid_name = entry_name; 749 750 // Rename extension if necessary. 751 size_t idx = entry_name.rfind("."); 752 if (idx != std::string::npos) { 753 std::string extension = entry_name.substr(idx); 754 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); 755 if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) { 756 valid_name = entry_name + ".renamed"; 757 MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str()); 758 } 759 } 760 761 // Logging statement below is useful to time how long each entry takes, but it's too verbose. 762 // MYLOGD("Adding zip entry %s\n", entry_name.c_str()); 763 int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(), 764 ZipWriter::kCompress, get_mtime(fd, now)); 765 if (err) { 766 MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(), 767 ZipWriter::ErrorCodeString(err)); 768 return false; 769 } 770 771 std::vector<uint8_t> buffer(65536); 772 while (1) { 773 ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size())); 774 if (bytes_read == 0) { 775 break; 776 } else if (bytes_read == -1) { 777 MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno)); 778 return false; 779 } 780 err = zip_writer->WriteBytes(buffer.data(), bytes_read); 781 if (err) { 782 MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err)); 783 return false; 784 } 785 } 786 787 err = zip_writer->FinishEntry(); 788 if (err) { 789 MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); 790 return false; 791 } 792 793 return true; 794} 795 796bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) { 797 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK 798 | O_CLOEXEC))); 799 if (fd == -1) { 800 MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno)); 801 return false; 802 } 803 804 return add_zip_entry_from_fd(entry_name, fd.get()); 805} 806 807/* adds a file to the existing zipped bugreport */ 808static int _add_file_from_fd(const char *title, const char *path, int fd) { 809 return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1; 810} 811 812// TODO: move to util.cpp 813void add_dir(const std::string& dir, bool recursive) { 814 if (!zip_writer) { 815 MYLOGD("Not adding dir %s because zip_writer is not set\n", dir.c_str()); 816 return; 817 } 818 MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive); 819 DurationReporter durationReporter(dir, nullptr); 820 dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd); 821} 822 823/* adds a text entry entry to the existing zip file. */ 824static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) { 825 if (!zip_writer) { 826 MYLOGD("Not adding text zip entry %s because zip_writer is not set\n", entry_name.c_str()); 827 return false; 828 } 829 MYLOGD("Adding zip text entry %s\n", entry_name.c_str()); 830 int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now); 831 if (err) { 832 MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(), 833 ZipWriter::ErrorCodeString(err)); 834 return false; 835 } 836 837 err = zip_writer->WriteBytes(content.c_str(), content.length()); 838 if (err) { 839 MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(), 840 ZipWriter::ErrorCodeString(err)); 841 return false; 842 } 843 844 err = zip_writer->FinishEntry(); 845 if (err) { 846 MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); 847 return false; 848 } 849 850 return true; 851} 852 853static void dump_iptables() { 854 RunCommand("IPTABLES", {"iptables", "-L", "-nvx"}); 855 RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"}); 856 RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"}); 857 /* no ip6 nat */ 858 RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"}); 859 RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"}); 860 RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"}); 861 RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"}); 862} 863 864static void dumpstate(const std::string& screenshot_path, const std::string& version) { 865 DurationReporter durationReporter("DUMPSTATE"); 866 unsigned long timeout; 867 868 dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); 869 RunCommand("UPTIME", {"uptime"}); 870 dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd); 871 dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd"); 872 DumpFile("MEMORY INFO", "/proc/meminfo"); 873 RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", 874 "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); 875 RunCommand("PROCRANK", {"procrank"}, CommandOptions::AS_ROOT_20); 876 DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat"); 877 DumpFile("VMALLOC INFO", "/proc/vmallocinfo"); 878 DumpFile("SLAB INFO", "/proc/slabinfo"); 879 DumpFile("ZONEINFO", "/proc/zoneinfo"); 880 DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo"); 881 DumpFile("BUDDYINFO", "/proc/buddyinfo"); 882 DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index"); 883 884 DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources"); 885 DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); 886 DumpFile("KERNEL SYNC", "/d/sync"); 887 888 RunCommand("PROCESSES AND THREADS", 889 {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"}); 890 RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10); 891 892 RunCommand("PRINTENV", {"printenv"}); 893 RunCommand("NETSTAT", {"netstat", "-n"}); 894 struct stat s; 895 if (stat("/proc/modules", &s) != 0) { 896 MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n"); 897 } else { 898 RunCommand("LSMOD", {"lsmod"}); 899 } 900 901 do_dmesg(); 902 903 RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT_10); 904 for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); 905 for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); 906 for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); 907 908 /* Dump Bluetooth HCI logs */ 909 add_dir("/data/misc/bluetooth/logs", true); 910 911 if (!screenshot_path.empty()) { 912 MYLOGI("taking late screenshot\n"); 913 take_screenshot(screenshot_path); 914 MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str()); 915 } 916 917 // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); 918 // calculate timeout 919 timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash"); 920 if (timeout < 20000) { 921 timeout = 20000; 922 } 923 RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-d", "*:v"}, 924 CommandOptions::WithTimeout(timeout / 1000).Build()); 925 timeout = logcat_timeout("events"); 926 if (timeout < 20000) { 927 timeout = 20000; 928 } 929 RunCommand("EVENT LOG", 930 {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-d", "*:v"}, 931 CommandOptions::WithTimeout(timeout / 1000).Build()); 932 timeout = logcat_timeout("radio"); 933 if (timeout < 20000) { 934 timeout = 20000; 935 } 936 RunCommand("RADIO LOG", 937 {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-d", "*:v"}, 938 CommandOptions::WithTimeout(timeout / 1000).Build()); 939 940 RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); 941 942 /* show the traces we collected in main(), if that was done */ 943 if (dump_traces_path != NULL) { 944 DumpFile("VM TRACES JUST NOW", dump_traces_path); 945 } 946 947 /* only show ANR traces if they're less than 15 minutes old */ 948 struct stat st; 949 std::string anrTracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", ""); 950 if (anrTracesPath.empty()) { 951 printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n"); 952 } else { 953 int fd = TEMP_FAILURE_RETRY( 954 open(anrTracesPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)); 955 if (fd < 0) { 956 printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anrTracesPath.c_str(), strerror(errno)); 957 } else { 958 dump_file_from_fd("VM TRACES AT LAST ANR", anrTracesPath.c_str(), fd); 959 } 960 } 961 962 /* slow traces for slow operations */ 963 if (!anrTracesPath.empty()) { 964 int tail = anrTracesPath.size() - 1; 965 while (tail > 0 && anrTracesPath.at(tail) != '/') { 966 tail--; 967 } 968 int i = 0; 969 while (1) { 970 anrTracesPath = 971 anrTracesPath.substr(0, tail + 1) + android::base::StringPrintf("slow%02d.txt", i); 972 if (stat(anrTracesPath.c_str(), &st)) { 973 // No traces file at this index, done with the files. 974 break; 975 } 976 DumpFile("VM TRACES WHEN SLOW", anrTracesPath.c_str()); 977 i++; 978 } 979 } 980 981 int dumped = 0; 982 for (size_t i = 0; i < NUM_TOMBSTONES; i++) { 983 if (tombstone_data[i].fd != -1) { 984 const char *name = tombstone_data[i].name; 985 int fd = tombstone_data[i].fd; 986 dumped = 1; 987 if (zip_writer) { 988 if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) { 989 MYLOGE("Unable to add tombstone %s to zip file\n", name); 990 } 991 } else { 992 dump_file_from_fd("TOMBSTONE", name, fd); 993 } 994 close(fd); 995 tombstone_data[i].fd = -1; 996 } 997 } 998 if (!dumped) { 999 printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR); 1000 } 1001 1002 DumpFile("NETWORK DEV INFO", "/proc/net/dev"); 1003 DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all"); 1004 DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt"); 1005 DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl"); 1006 DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats"); 1007 1008 if (!stat(PSTORE_LAST_KMSG, &st)) { 1009 /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */ 1010 DumpFile("LAST KMSG", PSTORE_LAST_KMSG); 1011 } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) { 1012 DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG); 1013 } else { 1014 /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */ 1015 DumpFile("LAST KMSG", "/proc/last_kmsg"); 1016 } 1017 1018 /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */ 1019 RunCommand("LAST LOGCAT", 1020 {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-d", "*:v"}); 1021 1022 /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */ 1023 1024 RunCommand("NETWORK INTERFACES", {"ip", "link"}); 1025 1026 RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"}); 1027 RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"}); 1028 1029 RunCommand("IP RULES", {"ip", "rule", "show"}); 1030 RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); 1031 1032 dump_route_tables(); 1033 1034 RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"}); 1035 RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"}); 1036 RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"}); 1037 RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"}, 1038 CommandOptions::WithTimeout(20).Build()); 1039 1040#ifdef FWDUMP_bcmdhd 1041 RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5); 1042 1043 RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20); 1044 1045 RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5); 1046 1047#endif 1048 DumpFile("INTERRUPTS (1)", "/proc/interrupts"); 1049 1050 RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, 1051 CommandOptions::WithTimeout(10).Build()); 1052 1053#ifdef FWDUMP_bcmdhd 1054 RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, CommandOptions::AS_ROOT_20); 1055 1056 RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20); 1057 1058 RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5); 1059#endif 1060 DumpFile("INTERRUPTS (2)", "/proc/interrupts"); 1061 1062 print_properties(); 1063 1064 RunCommand("VOLD DUMP", {"vdc", "dump"}); 1065 RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"}); 1066 1067 RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); 1068 1069 RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"}); 1070 1071 printf("------ BACKLIGHTS ------\n"); 1072 printf("LCD brightness="); 1073 DumpFile("", "/sys/class/leds/lcd-backlight/brightness"); 1074 printf("Button brightness="); 1075 DumpFile("", "/sys/class/leds/button-backlight/brightness"); 1076 printf("Keyboard brightness="); 1077 DumpFile("", "/sys/class/leds/keyboard-backlight/brightness"); 1078 printf("ALS mode="); 1079 DumpFile("", "/sys/class/leds/lcd-backlight/als"); 1080 printf("LCD driver registers:\n"); 1081 DumpFile("", "/sys/class/leds/lcd-backlight/registers"); 1082 printf("\n"); 1083 1084 /* Binder state is expensive to look at as it uses a lot of memory. */ 1085 DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log"); 1086 DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log"); 1087 DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions"); 1088 DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); 1089 DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); 1090 1091 printf("========================================================\n"); 1092 printf("== Board\n"); 1093 printf("========================================================\n"); 1094 1095 { 1096 DurationReporter tmpDr("dumpstate_board()"); 1097 dumpstate_board(); 1098 printf("\n"); 1099 } 1100 1101 /* Migrate the ril_dumpstate to a dumpstate_board()? */ 1102 int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0); 1103 if (rilDumpstateTimeout > 0) { 1104 // su does not exist on user builds, so try running without it. 1105 // This way any implementations of vril-dump that do not require 1106 // root can run on user builds. 1107 CommandOptions::CommandOptionsBuilder options = 1108 CommandOptions::WithTimeout(rilDumpstateTimeout); 1109 if (!IsUserBuild()) { 1110 options.AsRoot(); 1111 } 1112 RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build()); 1113 } 1114 1115 printf("========================================================\n"); 1116 printf("== Android Framework Services\n"); 1117 printf("========================================================\n"); 1118 1119 RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(), 1120 10); 1121 1122 printf("========================================================\n"); 1123 printf("== Checkins\n"); 1124 printf("========================================================\n"); 1125 1126 RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); 1127 RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}); 1128 RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}); 1129 RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}); 1130 RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}); 1131 RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}); 1132 1133 printf("========================================================\n"); 1134 printf("== Running Application Activities\n"); 1135 printf("========================================================\n"); 1136 1137 RunDumpsys("APP ACTIVITIES", {"activity", "all"}); 1138 1139 printf("========================================================\n"); 1140 printf("== Running Application Services\n"); 1141 printf("========================================================\n"); 1142 1143 RunDumpsys("APP SERVICES", {"activity", "service", "all"}); 1144 1145 printf("========================================================\n"); 1146 printf("== Running Application Providers\n"); 1147 printf("========================================================\n"); 1148 1149 RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"}); 1150 1151 printf("========================================================\n"); 1152 printf("== Final progress (pid %d): %d/%d (originally %d)\n", getpid(), ds.progress_, 1153 ds.weightTotal_, WEIGHT_TOTAL); 1154 printf("========================================================\n"); 1155 printf("== dumpstate: done (id %lu)\n", ds.id_); 1156 printf("========================================================\n"); 1157} 1158 1159static void ShowUsageAndExit(int exitCode = 1) { 1160 fprintf(stderr, 1161 "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] " 1162 "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n" 1163 " -h: display this help message\n" 1164 " -b: play sound file instead of vibrate, at beginning of job\n" 1165 " -e: play sound file instead of vibrate, at end of job\n" 1166 " -o: write to file (instead of stdout)\n" 1167 " -d: append date to filename (requires -o)\n" 1168 " -p: capture screenshot to filename.png (requires -o)\n" 1169 " -z: generate zipped file (requires -o)\n" 1170 " -s: write output to control socket (for init)\n" 1171 " -S: write file location to control socket (for init; requires -o and -z)" 1172 " -q: disable vibrate\n" 1173 " -B: send broadcast when finished (requires -o)\n" 1174 " -P: send broadcast when started and update system properties on " 1175 "progress (requires -o and -B)\n" 1176 " -R: take bugreport in remote mode (requires -o, -z, -d and -B, " 1177 "shouldn't be used with -P)\n" 1178 " -V: sets the bugreport format version (valid values: %s)\n", 1179 VERSION_DEFAULT.c_str()); 1180 exit(exitCode); 1181} 1182 1183static void ExitOnInvalidArgs() { 1184 fprintf(stderr, "invalid combination of args\n"); 1185 ShowUsageAndExit(); 1186} 1187 1188static void wake_lock_releaser() { 1189 if (release_wake_lock(WAKE_LOCK_NAME) < 0) { 1190 MYLOGE("Failed to release wake lock: %s \n", strerror(errno)); 1191 } else { 1192 MYLOGD("Wake lock released.\n"); 1193 } 1194} 1195 1196static void sig_handler(int signo) { 1197 wake_lock_releaser(); 1198 _exit(EXIT_FAILURE); 1199} 1200 1201static void register_sig_handler() { 1202 struct sigaction sa; 1203 sigemptyset(&sa.sa_mask); 1204 sa.sa_flags = 0; 1205 sa.sa_handler = sig_handler; 1206 sigaction(SIGPIPE, &sa, NULL); // broken pipe 1207 sigaction(SIGSEGV, &sa, NULL); // segment fault 1208 sigaction(SIGINT, &sa, NULL); // ctrl-c 1209 sigaction(SIGTERM, &sa, NULL); // killed 1210 sigaction(SIGQUIT, &sa, NULL); // quit 1211} 1212 1213/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the 1214 temporary file. 1215 */ 1216static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path, 1217 const std::string& log_path, time_t now) { 1218 // Final timestamp 1219 char date[80]; 1220 time_t the_real_now_please_stand_up = time(nullptr); 1221 strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up)); 1222 MYLOGD("dumpstate id %lu finished around %s (%ld s)\n", ds.id_, date, 1223 the_real_now_please_stand_up - now); 1224 1225 if (!add_zip_entry(bugreport_name, bugreport_path)) { 1226 MYLOGE("Failed to add text entry to .zip file\n"); 1227 return false; 1228 } 1229 if (!add_text_zip_entry("main_entry.txt", bugreport_name)) { 1230 MYLOGE("Failed to add main_entry.txt to .zip file\n"); 1231 return false; 1232 } 1233 1234 // Add log file (which contains stderr output) to zip... 1235 fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n"); 1236 if (!add_zip_entry("dumpstate_log.txt", log_path.c_str())) { 1237 MYLOGE("Failed to add dumpstate log to .zip file\n"); 1238 return false; 1239 } 1240 // ... and re-opens it for further logging. 1241 redirect_to_existing_file(stderr, const_cast<char*>(log_path.c_str())); 1242 fprintf(stderr, "\n"); 1243 1244 int32_t err = zip_writer->Finish(); 1245 if (err) { 1246 MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err)); 1247 return false; 1248 } 1249 1250 if (IsUserBuild()) { 1251 MYLOGD("Removing temporary file %s\n", bugreport_path.c_str()) 1252 if (remove(bugreport_path.c_str())) { 1253 ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno)); 1254 } 1255 } else { 1256 MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str()) 1257 } 1258 1259 return true; 1260} 1261 1262static std::string SHA256_file_hash(std::string filepath) { 1263 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK 1264 | O_CLOEXEC | O_NOFOLLOW))); 1265 if (fd == -1) { 1266 MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno)); 1267 return NULL; 1268 } 1269 1270 SHA256_CTX ctx; 1271 SHA256_Init(&ctx); 1272 1273 std::vector<uint8_t> buffer(65536); 1274 while (1) { 1275 ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd.get(), buffer.data(), buffer.size())); 1276 if (bytes_read == 0) { 1277 break; 1278 } else if (bytes_read == -1) { 1279 MYLOGE("read(%s): %s\n", filepath.c_str(), strerror(errno)); 1280 return NULL; 1281 } 1282 1283 SHA256_Update(&ctx, buffer.data(), bytes_read); 1284 } 1285 1286 uint8_t hash[SHA256_DIGEST_LENGTH]; 1287 SHA256_Final(hash, &ctx); 1288 1289 char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1]; 1290 for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) { 1291 sprintf(hash_buffer + (i * 2), "%02x", hash[i]); 1292 } 1293 hash_buffer[sizeof(hash_buffer) - 1] = 0; 1294 return std::string(hash_buffer); 1295} 1296 1297int main(int argc, char *argv[]) { 1298 int do_add_date = 0; 1299 int do_zip_file = 0; 1300 int do_vibrate = 1; 1301 char* use_outfile = 0; 1302 int use_socket = 0; 1303 int use_control_socket = 0; 1304 int do_fb = 0; 1305 int do_broadcast = 0; 1306 int do_early_screenshot = 0; 1307 int is_remote_mode = 0; 1308 std::string version = VERSION_DEFAULT; 1309 1310 now = time(nullptr); 1311 1312 MYLOGI("begin\n"); 1313 1314 if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { 1315 MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno)); 1316 } else { 1317 MYLOGD("Wake lock acquired.\n"); 1318 atexit(wake_lock_releaser); 1319 register_sig_handler(); 1320 } 1321 1322 ds.dryRun_ = android::base::GetBoolProperty("dumpstate.dry_run", false); 1323 if (ds.dryRun_) { 1324 MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n"); 1325 } 1326 1327 // TODO: use helper function to convert argv into a string 1328 for (int i = 0; i < argc; i++) { 1329 args += argv[i]; 1330 if (i < argc - 1) { 1331 args += " "; 1332 } 1333 } 1334 1335 extraOptions = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, ""); 1336 MYLOGI("Dumpstate args: %s (extra options: %s)\n", args.c_str(), extraOptions.c_str()); 1337 1338 /* gets the sequential id */ 1339 int lastId = android::base::GetIntProperty(PROPERTY_LAST_ID, 0); 1340 ds.id_ = ++lastId; 1341 android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(lastId)); 1342 MYLOGI("dumpstate id: %lu\n", ds.id_); 1343 1344 /* set as high priority, and protect from OOM killer */ 1345 setpriority(PRIO_PROCESS, 0, -20); 1346 1347 FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we"); 1348 if (oom_adj) { 1349 fputs("-1000", oom_adj); 1350 fclose(oom_adj); 1351 } else { 1352 /* fallback to kernels <= 2.6.35 */ 1353 oom_adj = fopen("/proc/self/oom_adj", "we"); 1354 if (oom_adj) { 1355 fputs("-17", oom_adj); 1356 fclose(oom_adj); 1357 } 1358 } 1359 1360 /* parse arguments */ 1361 int c; 1362 while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) { 1363 switch (c) { 1364 // clang-format off 1365 case 'd': do_add_date = 1; break; 1366 case 'z': do_zip_file = 1; break; 1367 case 'o': use_outfile = optarg; break; 1368 case 's': use_socket = 1; break; 1369 case 'S': use_control_socket = 1; break; 1370 case 'v': break; // compatibility no-op 1371 case 'q': do_vibrate = 0; break; 1372 case 'p': do_fb = 1; break; 1373 case 'P': ds.updateProgress_ = 1; break; 1374 case 'R': is_remote_mode = 1; break; 1375 case 'B': do_broadcast = 1; break; 1376 case 'V': version = optarg; break; 1377 case 'h': 1378 ShowUsageAndExit(0); 1379 break; 1380 default: 1381 fprintf(stderr, "Invalid option: %c\n", c); 1382 ShowUsageAndExit(); 1383 // clang-format on 1384 } 1385 } 1386 1387 if (!extraOptions.empty()) { 1388 // Framework uses a system property to override some command-line args. 1389 // Currently, it contains the type of the requested bugreport. 1390 if (extraOptions == "bugreportplus") { 1391 MYLOGD("Running as bugreportplus: add -P, remove -p\n"); 1392 ds.updateProgress_ = 1; 1393 do_fb = 0; 1394 } else if (extraOptions == "bugreportremote") { 1395 MYLOGD("Running as bugreportremote: add -q -R, remove -p\n"); 1396 do_vibrate = 0; 1397 is_remote_mode = 1; 1398 do_fb = 0; 1399 } else if (extraOptions == "bugreportwear") { 1400 MYLOGD("Running as bugreportwear: add -P\n"); 1401 ds.updateProgress_ = 1; 1402 } else { 1403 MYLOGE("Unknown extra option: %s\n", extraOptions.c_str()); 1404 } 1405 // Reset the property 1406 android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, ""); 1407 } 1408 1409 if ((do_zip_file || do_add_date || ds.updateProgress_ || do_broadcast) && !use_outfile) { 1410 ExitOnInvalidArgs(); 1411 } 1412 1413 if (use_control_socket && !do_zip_file) { 1414 ExitOnInvalidArgs(); 1415 } 1416 1417 if (ds.updateProgress_ && !do_broadcast) { 1418 ExitOnInvalidArgs(); 1419 } 1420 1421 if (is_remote_mode && (ds.updateProgress_ || !do_broadcast || !do_zip_file || !do_add_date)) { 1422 ExitOnInvalidArgs(); 1423 } 1424 1425 if (version != VERSION_DEFAULT) { 1426 ShowUsageAndExit(); 1427 } 1428 1429 MYLOGI("bugreport format version: %s\n", version.c_str()); 1430 1431 do_early_screenshot = ds.updateProgress_; 1432 1433 // If we are going to use a socket, do it as early as possible 1434 // to avoid timeouts from bugreport. 1435 if (use_socket) { 1436 redirect_to_socket(stdout, "dumpstate"); 1437 } 1438 1439 if (use_control_socket) { 1440 MYLOGD("Opening control socket\n"); 1441 ds.controlSocketFd_ = open_socket("dumpstate"); 1442 ds.updateProgress_ = 1; 1443 } 1444 1445 /* full path of the temporary file containing the bugreport */ 1446 std::string tmp_path; 1447 1448 /* full path of the file containing the dumpstate logs */ 1449 std::string log_path; 1450 1451 /* full path of the systrace file, when enabled */ 1452 std::string systrace_path; 1453 1454 /* full path of the temporary file containing the screenshot (when requested) */ 1455 std::string screenshot_path; 1456 1457 /* base name (without suffix or extensions) of the bugreport files */ 1458 std::string base_name; 1459 1460 /* pointer to the actual path, be it zip or text */ 1461 std::string path; 1462 1463 /* pointer to the zipped file */ 1464 std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose); 1465 1466 /* redirect output if needed */ 1467 bool is_redirecting = !use_socket && use_outfile; 1468 1469 if (is_redirecting) { 1470 ds.bugreportDir_ = dirname(use_outfile); 1471 base_name = basename(use_outfile); 1472 if (do_add_date) { 1473 char date[80]; 1474 strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&now)); 1475 suffix = date; 1476 } else { 1477 suffix = "undated"; 1478 } 1479 std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); 1480 base_name = base_name + "-" + buildId; 1481 if (do_fb) { 1482 // TODO: if dumpstate was an object, the paths could be internal variables and then 1483 // we could have a function to calculate the derived values, such as: 1484 // screenshot_path = GetPath(".png"); 1485 screenshot_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".png"; 1486 } 1487 tmp_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".tmp"; 1488 log_path = 1489 ds.bugreportDir_ + "/dumpstate_log-" + suffix + "-" + std::to_string(getpid()) + ".txt"; 1490 1491 MYLOGD( 1492 "Bugreport dir: %s\n" 1493 "Base name: %s\n" 1494 "Suffix: %s\n" 1495 "Log path: %s\n" 1496 "Temporary path: %s\n" 1497 "Screenshot path: %s\n", 1498 ds.bugreportDir_.c_str(), base_name.c_str(), suffix.c_str(), log_path.c_str(), 1499 tmp_path.c_str(), screenshot_path.c_str()); 1500 1501 if (do_zip_file) { 1502 path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".zip"; 1503 MYLOGD("Creating initial .zip file (%s)\n", path.c_str()); 1504 create_parent_dirs(path.c_str()); 1505 zip_file.reset(fopen(path.c_str(), "wb")); 1506 if (!zip_file) { 1507 MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno)); 1508 do_zip_file = 0; 1509 } else { 1510 zip_writer.reset(new ZipWriter(zip_file.get())); 1511 } 1512 add_text_zip_entry("version.txt", version); 1513 } 1514 1515 if (ds.updateProgress_) { 1516 if (do_broadcast) { 1517 // clang-format off 1518 std::vector<std::string> am_args = { 1519 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", 1520 "--es", "android.intent.extra.NAME", suffix, 1521 "--ei", "android.intent.extra.ID", std::to_string(ds.id_), 1522 "--ei", "android.intent.extra.PID", std::to_string(getpid()), 1523 "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL), 1524 }; 1525 // clang-format on 1526 send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args); 1527 } 1528 if (use_control_socket) { 1529 dprintf(ds.controlSocketFd_, "BEGIN:%s\n", path.c_str()); 1530 } 1531 } 1532 } 1533 1534 /* read /proc/cmdline before dropping root */ 1535 FILE *cmdline = fopen("/proc/cmdline", "re"); 1536 if (cmdline) { 1537 fgets(cmdline_buf, sizeof(cmdline_buf), cmdline); 1538 fclose(cmdline); 1539 } 1540 1541 /* open the vibrator before dropping root */ 1542 std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose); 1543 if (do_vibrate) { 1544 vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we")); 1545 if (vibrator) { 1546 vibrate(vibrator.get(), 150); 1547 } 1548 } 1549 1550 if (do_fb && do_early_screenshot) { 1551 if (screenshot_path.empty()) { 1552 // should not have happened 1553 MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n"); 1554 } else { 1555 MYLOGI("taking early screenshot\n"); 1556 take_screenshot(screenshot_path); 1557 MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str()); 1558 if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) { 1559 MYLOGE("Unable to change ownership of screenshot file %s: %s\n", 1560 screenshot_path.c_str(), strerror(errno)); 1561 } 1562 } 1563 } 1564 1565 if (do_zip_file) { 1566 if (chown(path.c_str(), AID_SHELL, AID_SHELL)) { 1567 MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno)); 1568 } 1569 } 1570 1571 if (is_redirecting) { 1572 redirect_to_file(stderr, const_cast<char*>(log_path.c_str())); 1573 if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) { 1574 MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", 1575 log_path.c_str(), strerror(errno)); 1576 } 1577 /* TODO: rather than generating a text file now and zipping it later, 1578 it would be more efficient to redirect stdout to the zip entry 1579 directly, but the libziparchive doesn't support that option yet. */ 1580 redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str())); 1581 if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) { 1582 MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", 1583 tmp_path.c_str(), strerror(errno)); 1584 } 1585 } 1586 // NOTE: there should be no stdout output until now, otherwise it would break the header. 1587 // In particular, DurationReport objects should be created passing 'title, NULL', so their 1588 // duration is logged into MYLOG instead. 1589 print_header(version); 1590 1591 // Dumps systrace right away, otherwise it will be filled with unnecessary events. 1592 // First try to dump anrd trace if the daemon is running. Otherwise, dump 1593 // the raw trace. 1594 if (!dump_anrd_trace()) { 1595 dump_systrace(); 1596 } 1597 1598 // Invoking the following dumpsys calls before dump_traces() to try and 1599 // keep the system stats as close to its initial state as possible. 1600 RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"}, 1601 CommandOptions::WithTimeout(90).DropRoot().Build()); 1602 RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"}, 1603 CommandOptions::WithTimeout(10).DropRoot().Build()); 1604 1605 // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed. 1606 dump_raft(); 1607 1608 /* collect stack traces from Dalvik and native processes (needs root) */ 1609 dump_traces_path = dump_traces(); 1610 1611 /* Run some operations that require root. */ 1612 get_tombstone_fds(tombstone_data); 1613 add_dir(RECOVERY_DIR, true); 1614 add_dir(RECOVERY_DATA_DIR, true); 1615 add_dir(LOGPERSIST_DATA_DIR, false); 1616 if (!IsUserBuild()) { 1617 add_dir(PROFILE_DATA_DIR_CUR, true); 1618 add_dir(PROFILE_DATA_DIR_REF, true); 1619 } 1620 add_mountinfo(); 1621 dump_iptables(); 1622 1623 // Capture any IPSec policies in play. No keys are exposed here. 1624 RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, 1625 CommandOptions::WithTimeout(10).Build()); 1626 1627 // Run ss as root so we can see socket marks. 1628 RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build()); 1629 1630 if (!drop_root_user()) { 1631 return -1; 1632 } 1633 1634 dumpstate(do_early_screenshot ? "": screenshot_path, version); 1635 1636 /* close output if needed */ 1637 if (is_redirecting) { 1638 fclose(stdout); 1639 } 1640 1641 /* rename or zip the (now complete) .tmp file to its final location */ 1642 if (use_outfile) { 1643 1644 /* check if user changed the suffix using system properties */ 1645 std::string name = android::base::GetProperty( 1646 android::base::StringPrintf("dumpstate.%d.name", getpid()), ""); 1647 bool change_suffix= false; 1648 if (!name.empty()) { 1649 /* must whitelist which characters are allowed, otherwise it could cross directories */ 1650 std::regex valid_regex("^[-_a-zA-Z0-9]+$"); 1651 if (std::regex_match(name.c_str(), valid_regex)) { 1652 change_suffix = true; 1653 } else { 1654 MYLOGE("invalid suffix provided by user: %s\n", name.c_str()); 1655 } 1656 } 1657 if (change_suffix) { 1658 MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), name.c_str()); 1659 suffix = name; 1660 if (!screenshot_path.empty()) { 1661 std::string new_screenshot_path = 1662 ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".png"; 1663 if (rename(screenshot_path.c_str(), new_screenshot_path.c_str())) { 1664 MYLOGE("rename(%s, %s): %s\n", screenshot_path.c_str(), 1665 new_screenshot_path.c_str(), strerror(errno)); 1666 } else { 1667 screenshot_path = new_screenshot_path; 1668 } 1669 } 1670 } 1671 1672 bool do_text_file = true; 1673 if (do_zip_file) { 1674 std::string entry_name = base_name + "-" + suffix + ".txt"; 1675 MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str()); 1676 if (!finish_zip_file(entry_name, tmp_path, log_path, now)) { 1677 MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); 1678 do_text_file = true; 1679 } else { 1680 do_text_file = false; 1681 // Since zip file is already created, it needs to be renamed. 1682 std::string new_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".zip"; 1683 if (path != new_path) { 1684 MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str()); 1685 if (rename(path.c_str(), new_path.c_str())) { 1686 MYLOGE("rename(%s, %s): %s\n", path.c_str(), 1687 new_path.c_str(), strerror(errno)); 1688 } else { 1689 path = new_path; 1690 } 1691 } 1692 } 1693 } 1694 if (do_text_file) { 1695 path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".txt"; 1696 MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str()); 1697 if (rename(tmp_path.c_str(), path.c_str())) { 1698 MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno)); 1699 path.clear(); 1700 } 1701 } 1702 if (use_control_socket) { 1703 if (do_text_file) { 1704 dprintf(ds.controlSocketFd_, 1705 "FAIL:could not create zip file, check %s " 1706 "for more details\n", 1707 log_path.c_str()); 1708 } else { 1709 dprintf(ds.controlSocketFd_, "OK:%s\n", path.c_str()); 1710 } 1711 } 1712 } 1713 1714 /* vibrate a few but shortly times to let user know it's finished */ 1715 if (vibrator) { 1716 for (int i = 0; i < 3; i++) { 1717 vibrate(vibrator.get(), 75); 1718 usleep((75 + 50) * 1000); 1719 } 1720 } 1721 1722 /* tell activity manager we're done */ 1723 if (do_broadcast) { 1724 if (!path.empty()) { 1725 MYLOGI("Final bugreport path: %s\n", path.c_str()); 1726 // clang-format off 1727 std::vector<std::string> am_args = { 1728 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", 1729 "--ei", "android.intent.extra.ID", std::to_string(ds.id_), 1730 "--ei", "android.intent.extra.PID", std::to_string(getpid()), 1731 "--ei", "android.intent.extra.MAX", std::to_string(ds.weightTotal_), 1732 "--es", "android.intent.extra.BUGREPORT", path, 1733 "--es", "android.intent.extra.DUMPSTATE_LOG", log_path 1734 }; 1735 // clang-format on 1736 if (do_fb) { 1737 am_args.push_back("--es"); 1738 am_args.push_back("android.intent.extra.SCREENSHOT"); 1739 am_args.push_back(screenshot_path); 1740 } 1741 if (is_remote_mode) { 1742 am_args.push_back("--es"); 1743 am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH"); 1744 am_args.push_back(SHA256_file_hash(path)); 1745 send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); 1746 } else { 1747 send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args); 1748 } 1749 } else { 1750 MYLOGE("Skipping finished broadcast because bugreport could not be generated\n"); 1751 } 1752 } 1753 1754 MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weightTotal_, WEIGHT_TOTAL); 1755 MYLOGI("done (id %lu)\n", ds.id_); 1756 1757 if (is_redirecting) { 1758 fclose(stderr); 1759 } 1760 1761 if (use_control_socket && ds.controlSocketFd_ != -1) { 1762 MYLOGD("Closing control socket\n"); 1763 close(ds.controlSocketFd_); 1764 } 1765 1766 return 0; 1767} 1768