diff options
Diffstat (limited to 'cmds/dumpstate/dumpstate.cpp')
-rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 877 |
1 files changed, 578 insertions, 299 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 581d3ded89..2d11b908c2 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mount.h> #include <sys/poll.h> #include <sys/prctl.h> #include <sys/resource.h> @@ -78,6 +79,7 @@ #include <hardware_legacy/power.h> #include <hidl/ServiceManagement.h> #include <log/log.h> +#include <log/log_read.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> @@ -94,6 +96,7 @@ using ::android::hardware::dumpstate::V1_1::DumpstateStatus; using ::android::hardware::dumpstate::V1_1::toString; using ::std::literals::chrono_literals::operator""ms; using ::std::literals::chrono_literals::operator""s; +using ::std::placeholders::_1; // TODO: remove once moved to namespace using android::defaultServiceManager; @@ -112,7 +115,9 @@ using android::base::StringPrintf; using android::os::IDumpstateListener; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; +using android::os::dumpstate::DumpPool; using android::os::dumpstate::PropertiesHelper; +using android::os::dumpstate::TaskQueue; // Keep in sync with // frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java @@ -125,8 +130,8 @@ static const int32_t WEIGHT_FILE = 5; static Dumpstate& ds = Dumpstate::GetInstance(); static int RunCommand(const std::string& title, const std::vector<std::string>& full_command, const CommandOptions& options = CommandOptions::DEFAULT, - bool verbose_duration = false) { - return ds.RunCommand(title, full_command, options, verbose_duration); + bool verbose_duration = false, int out_fd = STDOUT_FILENO) { + return ds.RunCommand(title, full_command, options, verbose_duration, out_fd); } // Reasonable value for max stats. @@ -170,6 +175,8 @@ void add_mountinfo(); #define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log" #define LINKERCONFIG_DIR "/linkerconfig" #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list" +#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace" +#define CGROUPFS_DIR "/sys/fs/cgroup" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -195,8 +202,35 @@ static const std::string ANR_FILE_PREFIX = "anr_"; func_ptr(__VA_ARGS__); \ RETURN_IF_USER_DENIED_CONSENT(); +// Runs func_ptr, and logs a duration report after it's finished. +#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...) \ + { \ + DurationReporter duration_reporter_in_macro(log_title); \ + func_ptr(__VA_ARGS__); \ + } + +// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report +// is output after a slow function is finished. +#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \ + RETURN_IF_USER_DENIED_CONSENT(); \ + RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__); \ + RETURN_IF_USER_DENIED_CONSENT(); + +#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \ + RETURN_IF_USER_DENIED_CONSENT(); \ + pool_ptr->waitForTask(task_name); \ + RETURN_IF_USER_DENIED_CONSENT(); + static const char* WAKE_LOCK_NAME = "dumpstate_wakelock"; +// Names of parallel tasks, they are used for the DumpPool to identify the dump +// task and the log title of the duration report. +static const std::string DUMP_TRACES_TASK = "DUMP TRACES"; +static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT"; +static const std::string DUMP_HALS_TASK = "DUMP HALS"; +static const std::string DUMP_BOARD_TASK = "dumpstate_board()"; +static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS"; + namespace android { namespace os { namespace { @@ -305,8 +339,12 @@ static bool CopyFileToFile(const std::string& input_file, const std::string& out static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS, - long dumpsysTimeoutMs = 0) { - return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs); + long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) { + return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd); +} +static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, + int out_fd) { + return ds.RunDumpsys(title, dumpsysArgs, Dumpstate::DEFAULT_DUMPSYS, 0, out_fd); } static int DumpFile(const std::string& title, const std::string& path) { return ds.DumpFile(title, path); @@ -661,18 +699,31 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int f static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000; -/* timeout in ms to read a list of buffers */ +// Returns the actual readable size of the given buffer or -1 on error. +static long logcat_buffer_readable_size(const std::string& buffer) { + std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{ + android_logger_list_alloc(0, 0, 0), &android_logger_list_free}; + auto logger = android_logger_open(logger_list.get(), android_name_to_log_id(buffer.c_str())); + + return android_logger_get_log_readable_size(logger); +} + +// Returns timeout in ms to read a list of buffers. static unsigned long logcat_timeout(const std::vector<std::string>& buffers) { unsigned long timeout_ms = 0; for (const auto& buffer : buffers) { - log_id_t id = android_name_to_log_id(buffer.c_str()); - unsigned long property_size = __android_logger_get_buffer_size(id); - /* Engineering margin is ten-fold our guess */ - timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf; + long readable_size = logcat_buffer_readable_size(buffer); + if (readable_size > 0) { + // Engineering margin is ten-fold our guess. + timeout_ms += 10 * (readable_size + worst_write_perf) / worst_write_perf; + } } return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } +// Opens a socket and returns its file descriptor. +static int open_socket(const char* service); + Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) { } @@ -743,6 +794,9 @@ void Dumpstate::PrintHeader() const { if (module_metadata_version != 0) { printf("Module Metadata version: %" PRId64 "\n", module_metadata_version); } + printf("SDK extension versions [r=%s s=%s]\n", + android::base::GetProperty("build.version.extensions.r", "-").c_str(), + android::base::GetProperty("build.version.extensions.s", "-").c_str()); printf("Kernel: "); DumpFileToFd(STDOUT_FILENO, "", "/proc/version"); @@ -751,8 +805,9 @@ void Dumpstate::PrintHeader() const { RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"}, CommandOptions::WithTimeout(1).Always().Build()); printf("Bugreport format version: %s\n", version_.c_str()); - printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_, - PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str()); + printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n", + id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(), + options_->args.c_str(), options_->bugreport_mode.c_str()); printf("\n"); } @@ -982,7 +1037,6 @@ static void DumpIncidentReport() { MYLOGD("Not dumping incident report because it's not a zipped bugreport\n"); return; } - DurationReporter duration_reporter("INCIDENT REPORT"); const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report"; auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, @@ -997,9 +1051,29 @@ static void DumpIncidentReport() { // Use a different name from "incident.proto" // /proto/incident.proto is reserved for incident service dump // i.e. metadata for debugging. - ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path); + ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt, + path); + } else { + unlink(path.c_str()); } - unlink(path.c_str()); +} + +static void MaybeAddSystemTraceToZip() { + // This function copies into the .zip the system trace that was snapshotted + // by the early call to MaybeSnapshotSystemTrace(), if any background + // tracing was happening. + if (!ds.IsZipping()) { + MYLOGD("Not dumping system trace because it's not a zipped bugreport\n"); + return; + } + if (!ds.has_system_trace_) { + // No background trace was happening at the time dumpstate was invoked. + return; + } + ds.AddZipEntry( + ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT, + SYSTEM_TRACE_SNAPSHOT); + android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT); } static void DumpVisibleWindowViews() { @@ -1138,10 +1212,6 @@ static void DumpBlockStatFiles() { static void DumpPacketStats() { DumpFile("NETWORK DEV INFO", "/proc/net/dev"); - DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all"); - DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt"); - DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl"); - DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats"); } static void DumpIpAddrAndRules() { @@ -1171,8 +1241,15 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i if (status == OK) { dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); std::chrono::duration<double> elapsed_seconds; - status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, - /* as_proto = */ false, elapsed_seconds, bytes_written); + if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH && + service == String16("meminfo")) { + // Use a longer timeout for meminfo, since 30s is not always enough. + status = dumpsys.writeDump(STDOUT_FILENO, service, 60s, + /* as_proto = */ false, elapsed_seconds, bytes_written); + } else { + status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, + /* as_proto = */ false, elapsed_seconds, bytes_written); + } dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); bool dump_complete = (status == OK); dumpsys.stopDumpThread(dump_complete); @@ -1294,15 +1371,21 @@ static Dumpstate::RunStatus RunDumpsysNormal() { /* timeout= */ 90s, /* service_timeout= */ 10s); } -static void DumpHals() { +/* + * |out_fd| A fd to support the DumpPool to output results to a temporary file. + * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO + * if it's not running in the parallel task. + */ +static void DumpHals(int out_fd = STDOUT_FILENO) { if (!ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"}, + CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(), + false, out_fd); return; } - DurationReporter duration_reporter("DUMP HALS"); - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(), + false, out_fd); using android::hidl::manager::V1_0::IServiceManager; using android::hardware::defaultServiceManager; @@ -1324,6 +1407,7 @@ static void DumpHals() { }, '_'); const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; + bool empty = false; { auto fd = android::base::unique_fd( TEMP_FAILURE_RETRY(open(path.c_str(), @@ -1338,13 +1422,14 @@ static void DumpHals() { {"lshal", "debug", "-E", interface}, CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - bool empty = 0 == lseek(fd, 0, SEEK_END); - if (!empty) { - ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path); - } + empty = 0 == lseek(fd, 0, SEEK_END); + } + if (!empty) { + ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt", + path); + } else { + unlink(path.c_str()); } - - unlink(path.c_str()); } }); @@ -1416,6 +1501,8 @@ static void DumpstateLimitedOnly() { RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS CONNECTIVITY REQUESTS", {"connectivity", "requests"}, + CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); printf("========================================================\n"); printf("== Dropbox crashes\n"); @@ -1432,6 +1519,73 @@ static void DumpstateLimitedOnly() { printf("========================================================\n"); } +/* + * |out_fd| A fd to support the DumpPool to output results to a temporary file. + * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO + * if it's not running in the parallel task. + */ +static void DumpCheckins(int out_fd = STDOUT_FILENO) { + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Checkins\n"); + dprintf(out_fd, "========================================================\n"); + + RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd); + RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd); + RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd); + RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd); + RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd); + RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}, out_fd); +} + +/* + * Runs dumpsys on activity service to dump all application activities, services + * and providers in the device. + * + * |out_fd| A fd to support the DumpPool to output results to a temporary file. + * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO + * if it's not running in the parallel task. + */ +static void DumpAppInfos(int out_fd = STDOUT_FILENO) { + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Running Application Activities\n"); + dprintf(out_fd, "========================================================\n"); + + // The following dumpsys internally collects output from running apps, so it can take a long + // time. So let's extend the timeout. + + const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); + + RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd); + + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Running Application Services (platform)\n"); + dprintf(out_fd, "========================================================\n"); + + RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"}, + DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd); + + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Running Application Services (non-platform)\n"); + dprintf(out_fd, "========================================================\n"); + + RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd); + + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Running Application Providers (platform)\n"); + dprintf(out_fd, "========================================================\n"); + + RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"}, + DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd); + + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Running Application Providers (non-platform)\n"); + dprintf(out_fd, "========================================================\n"); + + RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd); +} + // Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent // via the consent they are shown. Ignores other errors that occur while running various // commands. The consent checking is currently done around long running tasks, which happen to @@ -1439,6 +1593,18 @@ static void DumpstateLimitedOnly() { static Dumpstate::RunStatus dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); + // Enqueue slow functions into the thread pool, if the parallel run is enabled. + if (ds.dump_pool_) { + // Pool was shutdown in DumpstateDefaultAfterCritical method in order to + // drop root user. Restarts it with two threads for the parallel run. + ds.dump_pool_->start(/* thread_counts = */2); + + ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1); + ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport); + ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1); + ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1); + } + // Dump various things. Note that anything that takes "long" (i.e. several seconds) should // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK). @@ -1470,7 +1636,11 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, CommandOptions::AS_ROOT); - DumpHals(); + if (ds.dump_pool_) { + WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_); + } else { + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals); + } RunCommand("PRINTENV", {"printenv"}); RunCommand("NETSTAT", {"netstat", "-nW"}); @@ -1479,10 +1649,13 @@ static Dumpstate::RunStatus dumpstate() { MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n"); } else { RunCommand("LSMOD", {"lsmod"}); + RunCommand("MODULES INFO", + {"sh", "-c", "cat /proc/modules | cut -d' ' -f1 | " + " while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; " + "done"}, CommandOptions::AS_ROOT); } - if (__android_logger_property_get_bool( - "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) { + if (android::base::GetBoolProperty("ro.logd.kernel", false)) { DoKernelLogcat(); } else { do_dmesg(); @@ -1495,8 +1668,8 @@ static Dumpstate::RunStatus dumpstate() { for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); - /* Dump Bluetooth HCI logs */ - ds.AddDir("/data/misc/bluetooth/logs", true); + /* Dump Nfc NCI logs */ + ds.AddDir("/data/misc/nfc/logs", true); if (ds.options_->do_screenshot && !ds.do_early_screenshot_) { MYLOGI("taking late screenshot\n"); @@ -1505,6 +1678,8 @@ static Dumpstate::RunStatus dumpstate() { AddAnrTraceFiles(); + MaybeAddSystemTraceToZip(); + // NOTE: tombstones are always added as separate entries in the zip archive // and are not interspersed with the main report. const bool tombstones_dumped = AddDumps(ds.tombstone_data_.begin(), ds.tombstone_data_.end(), @@ -1529,6 +1704,12 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh); + // The dump mechanism in connectivity is refactored due to modularization work. Connectivity can + // only register with a default priority(NORMAL priority). Dumpstate has to call connectivity + // dump with priority parameters to dump high priority information. + RunDumpsys("SERVICE HIGH connectivity", {"connectivity", "--dump-priority", "HIGH"}, + CommandOptions::WithTimeout(10).Build()); + RunCommand("SYSTEM PROPERTIES", {"getprop"}); RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); @@ -1552,7 +1733,11 @@ static Dumpstate::RunStatus dumpstate() { ds.AddDir(SNAPSHOTCTL_LOG_DIR, false); - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard); + if (ds.dump_pool_) { + WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_); + } else { + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard); + } /* Migrate the ril_dumpstate to a device specific dumpstate? */ int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0); @@ -1574,57 +1759,16 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal); - printf("========================================================\n"); - printf("== Checkins\n"); - printf("========================================================\n"); - - RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); - - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"}); - - RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}); - RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}); - RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}); - RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}); - - printf("========================================================\n"); - printf("== Running Application Activities\n"); - printf("========================================================\n"); - - // The following dumpsys internally collects output from running apps, so it can take a long - // time. So let's extend the timeout. - - const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); - - RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS); - - printf("========================================================\n"); - printf("== Running Application Services (platform)\n"); - printf("========================================================\n"); - - RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"}, - DUMPSYS_COMPONENTS_OPTIONS); - - printf("========================================================\n"); - printf("== Running Application Services (non-platform)\n"); - printf("========================================================\n"); - - RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, - DUMPSYS_COMPONENTS_OPTIONS); - - printf("========================================================\n"); - printf("== Running Application Providers (platform)\n"); - printf("========================================================\n"); - - RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"}, - DUMPSYS_COMPONENTS_OPTIONS); + /* Dump Bluetooth HCI logs after getting bluetooth_manager dumpsys */ + ds.AddDir("/data/misc/bluetooth/logs", true); - printf("========================================================\n"); - printf("== Running Application Providers (non-platform)\n"); - printf("========================================================\n"); + if (ds.dump_pool_) { + WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_); + } else { + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins); + } - RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"}, - DUMPSYS_COMPONENTS_OPTIONS); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpAppInfos); printf("========================================================\n"); printf("== Dropbox crashes\n"); @@ -1649,7 +1793,15 @@ static Dumpstate::RunStatus dumpstate() { // Add linker configuration directory ds.AddDir(LINKERCONFIG_DIR, true); - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport); + /* Dump cgroupfs */ + ds.AddDir(CGROUPFS_DIR, true); + + if (ds.dump_pool_) { + WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_); + } else { + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK, + DumpIncidentReport); + } return Dumpstate::RunStatus::OK; } @@ -1670,7 +1822,18 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { time_t logcat_ts = time(nullptr); /* collect stack traces from Dalvik and native processes (needs root) */ - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path); + if (dump_pool_) { + RETURN_IF_USER_DENIED_CONSENT(); + // One thread is enough since we only need to enqueue DumpTraces here. + dump_pool_->start(/* thread_counts = */1); + + // DumpTraces takes long time, post it to the another thread in the + // pool, if pool is available + dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path); + } else { + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces, + &dump_traces_path); + } /* Run some operations that require root. */ ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); @@ -1704,15 +1867,22 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"}); // Gather shared memory buffer info if the product implements it - struct stat st; - if (!stat("/product/bin/dmabuf_dump", &st)) { - RunCommand("Dmabuf dump", {"/product/bin/dmabuf_dump"}); - } + RunCommand("Dmabuf dump", {"dmabuf_dump"}); + RunCommand("Dmabuf per-buffer/per-exporter/per-device stats", {"dmabuf_dump", "-b"}); DumpFile("PSI cpu", "/proc/pressure/cpu"); DumpFile("PSI memory", "/proc/pressure/memory"); DumpFile("PSI io", "/proc/pressure/io"); + if (dump_pool_) { + RETURN_IF_USER_DENIED_CONSENT(); + dump_pool_->waitForTask(DUMP_TRACES_TASK); + + // Current running thread in the pool is the root user also. Shutdown + // the pool and restart later to ensure all threads in the pool could + // drop the root user. + dump_pool_->shutdown(); + } if (!DropRootUser()) { return Dumpstate::RunStatus::ERROR; } @@ -1724,31 +1894,39 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { return status; } +// Common states for telephony and wifi which are needed to be collected before +// dumpstate drop the root user. +static void DumpstateRadioAsRoot() { + DumpIpTablesAsRoot(); + ds.AddDir(LOGPERSIST_DATA_DIR, false); +} + // This method collects common dumpsys for telephony and wifi. Typically, wifi // reports are fine to include all information, but telephony reports on user // builds need to strip some content (see DumpstateTelephonyOnly). static void DumpstateRadioCommon(bool include_sensitive_info = true) { - DumpIpTablesAsRoot(); - - ds.AddDir(LOGPERSIST_DATA_DIR, false); - - if (!DropRootUser()) { - return; - } - // We need to be picky about some stuff for telephony reports on user builds. if (!include_sensitive_info) { // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info). DoRadioLogcat(); } else { + // DumpHals takes long time, post it to the another thread in the pool, + // if pool is available. + if (ds.dump_pool_) { + ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1); + } // Contains various system properties and process startup info. do_dmesg(); // Logs other than the radio buffer may contain package/component names and potential PII. DoLogcat(); // Too broad for connectivity problems. DoKmsg(); - // Contains unrelated hardware info (camera, NFC, biometrics, ...). - DumpHals(); + // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...). + if (ds.dump_pool_) { + ds.dump_pool_->waitForTask(DUMP_HALS_TASK); + } else { + RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals); + } } DumpPacketStats(); @@ -1772,6 +1950,21 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { const bool include_sensitive_info = !PropertiesHelper::IsUserBuild(); + DumpstateRadioAsRoot(); + if (!DropRootUser()) { + return; + } + + // Starts thread pool after the root user is dropped, and two additional threads + // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard. + if (ds.dump_pool_) { + ds.dump_pool_->start(/*thread_counts =*/2); + + // DumpstateBoard takes long time, post it to the another thread in the pool, + // if pool is available. + ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1); + } + DumpstateRadioCommon(include_sensitive_info); if (include_sensitive_info) { @@ -1788,6 +1981,8 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"vcn_management"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); if (include_sensitive_info) { // Carrier apps' services will be dumped below in dumpsys activity service all-non-platform. RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), @@ -1848,12 +2043,29 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); + + if (ds.dump_pool_) { + ds.dump_pool_->waitForTask(DUMP_BOARD_TASK); + } else { + RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard); + } } // This method collects dumpsys for wifi debugging only static void DumpstateWifiOnly() { DurationReporter duration_reporter("DUMPSTATE"); + DumpstateRadioAsRoot(); + if (!DropRootUser()) { + return; + } + + // Starts thread pool after the root user is dropped. Only one additional + // thread is needed for DumpHals in the DumpstateRadioCommon. + if (ds.dump_pool_) { + ds.dump_pool_->start(/*thread_counts =*/1); + } + DumpstateRadioCommon(); printf("========================================================\n"); @@ -1871,9 +2083,7 @@ static void DumpstateWifiOnly() { } Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { - DurationReporter duration_reporter("DUMP TRACES"); - - const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX"; + const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX"; const size_t buf_size = temp_file_pattern.length() + 1; std::unique_ptr<char[]> file_name_buf(new char[buf_size]); memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size); @@ -1980,17 +2190,31 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { return RunStatus::OK; } -void Dumpstate::DumpstateBoard() { - DurationReporter duration_reporter("dumpstate_board()"); - printf("========================================================\n"); - printf("== Board\n"); - printf("========================================================\n"); +void Dumpstate::DumpstateBoard(int out_fd) { + dprintf(out_fd, "========================================================\n"); + dprintf(out_fd, "== Board\n"); + dprintf(out_fd, "========================================================\n"); if (!IsZipping()) { MYLOGD("Not dumping board info because it's not a zipped bugreport\n"); return; } + /* + * mount debugfs for non-user builds with ro.product.debugfs_restrictions.enabled + * set to true and unmount it after invoking dumpstateBoard_* methods. + * This is to enable debug builds to not have debugfs mounted during runtime. + * It will also ensure that debugfs is only accessed by the dumpstate HAL. + */ + auto mount_debugfs = + android::base::GetBoolProperty("ro.product.debugfs_restrictions.enabled", false); + if (mount_debugfs) { + RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"}, + AS_ROOT_20); + RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, + AS_ROOT_20); + } + std::vector<std::string> paths; std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { @@ -2090,6 +2314,13 @@ void Dumpstate::DumpstateBoard() { "there might be racing in content\n", killing_timeout_sec); } + if (mount_debugfs) { + auto keep_debugfs_mounted = + android::base::GetProperty("persist.dbg.keep_debugfs_mounted", ""); + if (keep_debugfs_mounted.empty()) + RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20); + } + auto file_sizes = std::make_unique<ssize_t[]>(paths.size()); for (size_t i = 0; i < paths.size(); i++) { struct stat s; @@ -2110,27 +2341,26 @@ void Dumpstate::DumpstateBoard() { MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str()); continue; } - AddZipEntry(kDumpstateBoardFiles[i], paths[i]); - printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str()); + remover[i].Disable(); + EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]); + dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str()); } } static void ShowUsage() { fprintf(stderr, - "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] " - "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" + "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] " + "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" " -h: display this help message\n" " -b: play sound file instead of vibrate, at beginning of job\n" " -e: play sound file instead of vibrate, at end of job\n" " -o: write to custom directory (only in limited mode)\n" - " -d: append date to filename\n" " -p: capture screenshot to filename.png\n" - " -z: generate zipped file\n" - " -s: write output to control socket (for init)\n" - " -S: write file location to control socket (for init; requires -z)\n" + " -s: write zipped file to control socket (for init)\n" + " -S: write file location to control socket (for init)\n" " -q: disable vibrate\n" " -P: send broadcast when started and do progress updates\n" - " -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n" + " -R: take bugreport in remote mode (shouldn't be used with -P)\n" " -w: start binder service and make it wait for a call to startBugreport\n" " -L: output limited information that is safe for submission in feedback reports\n" " -v: prints the dumpstate header and exit\n"); @@ -2141,6 +2371,11 @@ static void register_sig_handler() { } bool Dumpstate::FinishZipFile() { + // Runs all enqueued adding zip entry and cleanup tasks before finishing the zip file. + if (zip_entry_tasks_) { + zip_entry_tasks_->run(/* do_cancel = */false); + } + std::string entry_name = base_name_ + "-" + name_ + ".txt"; MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(), tmp_path_.c_str()); @@ -2207,7 +2442,9 @@ static void SendBroadcast(const std::string& action, const std::vector<std::stri static void Vibrate(int duration_ms) { // clang-format off - RunCommand("", {"cmd", "vibrator", "vibrate", "-f", std::to_string(duration_ms), "dumpstate"}, + std::vector<std::string> args = {"cmd", "vibrator_manager", "synced", "-f", "-d", "dumpstate", + "oneshot", std::to_string(duration_ms)}; + RunCommand("", args, CommandOptions::WithTimeout(10) .Log("Vibrate: '%s'\n") .Always() @@ -2224,21 +2461,17 @@ static void MaybeResolveSymlink(std::string* path) { /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter - * if we are writing zip files and adds the version file. + * and adds the version file. Return false if zip_file could not be open to write. */ -static void PrepareToWriteToFile() { +static bool PrepareToWriteToFile() { MaybeResolveSymlink(&ds.bugreport_internal_dir_); std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str()); - if (ds.options_->do_add_date) { - char date[80]; - strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); - ds.name_ = date; - } else { - ds.name_ = "undated"; - } + char date[80]; + strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); + ds.name_ = date; if (ds.options_->telephony_only) { ds.base_name_ += "-telephony"; @@ -2265,18 +2498,17 @@ static void PrepareToWriteToFile() { destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); - if (ds.options_->do_zip_file) { - ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip"); - MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); - create_parent_dirs(ds.path_.c_str()); - ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); - if (ds.zip_file == nullptr) { - MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); - } else { - ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); - } - ds.AddTextZipEntry("version.txt", ds.version_); + ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip"); + MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); + create_parent_dirs(ds.path_.c_str()); + ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); + if (ds.zip_file == nullptr) { + MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); + return false; } + ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); + ds.AddTextZipEntry("version.txt", ds.version_); + return true; } /* @@ -2284,14 +2516,9 @@ static void PrepareToWriteToFile() { * printing zipped file status, etc. */ static void FinalizeFile() { - bool do_text_file = true; - if (ds.options_->do_zip_file) { - if (!ds.FinishZipFile()) { - MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); - do_text_file = true; - } else { - do_text_file = false; - } + bool do_text_file = !ds.FinishZipFile(); + if (do_text_file) { + MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); } std::string final_path = ds.path_; @@ -2300,7 +2527,9 @@ static void FinalizeFile() { android::os::CopyFileToFile(ds.path_, final_path); } - if (ds.options_->use_control_socket) { + if (ds.options_->stream_to_socket) { + android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_); + } else if (ds.options_->progress_updates_to_socket) { if (do_text_file) { dprintf(ds.control_socket_fd_, "FAIL:could not create zip file, check %s " @@ -2344,7 +2573,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: // Currently, the dumpstate binder is only used by Shell to update progress. - options->do_start_service = true; options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE; @@ -2356,9 +2584,7 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt options->dumpstate_hal_mode = DumpstateMode::REMOTE; break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: - options->do_start_service = true; options->do_progress_updates = true; - options->do_zip_file = true; options->do_screenshot = is_screenshot_requested; options->dumpstate_hal_mode = DumpstateMode::WEAR; break; @@ -2371,7 +2597,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; - options->do_zip_file = true; options->do_screenshot = false; options->dumpstate_hal_mode = DumpstateMode::WIFI; break; @@ -2382,13 +2607,13 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( - "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d " - "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d " + "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d " + "is_remote_mode: %d show_header_only: %d telephony_only: %d " "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " "limited_only: %d args: %s\n", - options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket, + options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, - options.do_start_service, options.telephony_only, options.wifi_only, + options.telephony_only, options.wifi_only, options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(), toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str()); } @@ -2397,11 +2622,6 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd_in, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { - // In the new API world, date is always added; output is always a zip file. - // TODO(111441001): remove these options once they are obsolete. - do_add_date = true; - do_zip_file = true; - // Duplicate the fds because the passed in fds don't outlive the binder transaction. bugreport_fd.reset(dup(bugreport_fd_in.get())); screenshot_fd.reset(dup(screenshot_fd_in.get())); @@ -2415,18 +2635,20 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) { switch (c) { // clang-format off - case 'd': do_add_date = true; break; - case 'z': do_zip_file = true; break; case 'o': out_dir = optarg; break; - case 's': use_socket = true; break; - case 'S': use_control_socket = true; break; + case 's': stream_to_socket = true; break; + case 'S': progress_updates_to_socket = true; break; case 'v': show_header_only = true; break; case 'q': do_vibrate = false; break; case 'p': do_screenshot = true; break; case 'P': do_progress_updates = true; break; case 'R': is_remote_mode = true; break; case 'L': limited_only = true; break; - case 'V': break; // compatibility no-op + case 'V': + case 'd': + case 'z': + // compatibility no-op + break; case 'w': // This was already processed break; @@ -2455,19 +2677,15 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { - if (bugreport_fd.get() != -1 && !do_zip_file) { - return false; - } - - if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) { + if (bugreport_fd.get() != -1 && stream_to_socket) { return false; } - if (use_control_socket && !do_zip_file) { + if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) { return false; } - if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) { + if (is_remote_mode && (do_progress_updates || stream_to_socket)) { return false; } return true; @@ -2519,6 +2737,15 @@ void Dumpstate::Cancel() { } tombstone_data_.clear(); anr_data_.clear(); + + // Instead of shutdown the pool, we delete temporary files directly since + // shutdown blocking the call. + if (dump_pool_) { + dump_pool_->deleteTempFiles(); + } + if (zip_entry_tasks_) { + zip_entry_tasks_->run(/*do_cancel =*/ true); + } } /* @@ -2533,17 +2760,16 @@ void Dumpstate::Cancel() { * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * - * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final - * text file. + * A bunch of other files and dumps are added to the zip archive. * - * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also - * gets added to the archive. + * The temporary bugreport file and the log file also get added to the archive. * * Bugreports are first generated in a local directory and later copied to the caller's fd * or directory if supplied. */ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, const std::string& calling_package) { + DurationReporter duration_reporter("RUN INTERNAL", /* logcat_only = */true); LogDumpOptions(*options_); if (!options_->ValidateOptions()) { MYLOGE("Invalid options specified\n"); @@ -2584,14 +2810,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n", calling_uid, calling_package.c_str()); - // Redirect output if needed - bool is_redirecting = options_->OutputToFile(); - // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = - is_redirecting - ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) - : ""; + android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()); progress_.reset(new Progress(stats_path)); if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { @@ -2603,15 +2824,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, register_sig_handler(); - // TODO(b/111441001): maybe skip if already started? - if (options_->do_start_service) { - MYLOGI("Starting 'dumpstate' service\n"); - android::status_t ret; - if ((ret = android::os::DumpstateService::Start()) != android::OK) { - MYLOGE("Unable to start DumpstateService: %d\n", ret); - } - } - if (PropertiesHelper::IsDryRun()) { MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n"); } @@ -2623,35 +2835,33 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. - if (options_->use_socket) { - if (!redirect_to_socket(stdout, "dumpstate")) { - return ERROR; - } - } - - if (options_->use_control_socket) { + if (options_->stream_to_socket || options_->progress_updates_to_socket) { MYLOGD("Opening control socket\n"); - control_socket_fd_ = open_socket("dumpstate"); + control_socket_fd_ = open_socket_fn_("dumpstate"); if (control_socket_fd_ == -1) { return ERROR; } - options_->do_progress_updates = 1; + if (options_->progress_updates_to_socket) { + options_->do_progress_updates = 1; + } } - if (is_redirecting) { - PrepareToWriteToFile(); + if (!PrepareToWriteToFile()) { + return ERROR; + } - if (options_->do_progress_updates) { - // clang-format off - std::vector<std::string> am_args = { - "--receiver-permission", "android.permission.DUMP", - }; - // clang-format on - // Send STARTED broadcast for apps that listen to bugreport generation events - SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); - if (options_->use_control_socket) { - dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); - } + // Interactive, wear & telephony modes are default to true. + // and may enable from cli option or when using control socket + if (options_->do_progress_updates) { + // clang-format off + std::vector<std::string> am_args = { + "--receiver-permission", "android.permission.DUMP", + }; + // clang-format on + // Send STARTED broadcast for apps that listen to bugreport generation events + SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); + if (options_->progress_updates_to_socket) { + dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); } } @@ -2666,76 +2876,83 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, Vibrate(150); } - if (options_->do_zip_file && zip_file != nullptr) { + if (zip_file != nullptr) { if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(), - strerror(errno)); + strerror(errno)); } } int dup_stdout_fd; int dup_stderr_fd; - if (is_redirecting) { - // Redirect stderr to log_path_ for debugging. - TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); - if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) { - return ERROR; - } - if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { - MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), - strerror(errno)); - } + // Redirect stderr to log_path_ for debugging. + TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); + if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) { + return ERROR; + } + if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), + strerror(errno)); + } - // Redirect stdout to tmp_path_. This is the main bugreport entry and will be - // moved into zip file later, if zipping. - TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout))); - // TODO: why not write to a file instead of stdout to overcome this problem? - /* TODO: rather than generating a text file now and zipping it later, - it would be more efficient to redirect stdout to the zip entry - directly, but the libziparchive doesn't support that option yet. */ - if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) { - return ERROR; - } - if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { - MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", - tmp_path_.c_str(), strerror(errno)); - } + // Redirect stdout to tmp_path_. This is the main bugreport entry and will be + // moved into zip file later, if zipping. + TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout))); + // TODO: why not write to a file instead of stdout to overcome this problem? + /* TODO: rather than generating a text file now and zipping it later, + it would be more efficient to redirect stdout to the zip entry + directly, but the libziparchive doesn't support that option yet. */ + if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) { + return ERROR; + } + if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", + tmp_path_.c_str(), strerror(errno)); } // Don't buffer stdout setvbuf(stdout, nullptr, _IONBF, 0); + // Enable the parallel run if the client requests to output to a file. + EnableParallelRunIfNeeded(); + // Using scope guard to make sure the dump pool can be shut down correctly. + auto scope_guard_to_shutdown_pool = android::base::make_scope_guard([=]() { + ShutdownDumpPool(); + }); + // NOTE: there should be no stdout output until now, otherwise it would break the header. // In particular, DurationReport objects should be created passing 'title, NULL', so their // duration is logged into MYLOG instead. PrintHeader(); - // TODO(b/158737089) reduce code repetition in if branches + bool is_dumpstate_restricted = options_->telephony_only + || options_->wifi_only + || options_->limited_only; + if (!is_dumpstate_restricted) { + // Invoke critical dumpsys first to preserve system state, before doing anything else. + RunDumpsysCritical(); + } + MaybeTakeEarlyScreenshot(); + + if (!is_dumpstate_restricted) { + // Snapshot the system trace now (if running) to avoid that dumpstate's + // own activity pushes out interesting data from the trace ring buffer. + // The trace file is added to the zip by MaybeAddSystemTraceToZip(). + MaybeSnapshotSystemTrace(); + + // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later + // from WMTRACE_DATA_DIR. + MaybeSnapshotWinTrace(); + } + onUiIntensiveBugreportDumpsFinished(calling_uid); + MaybeCheckUserConsent(calling_uid, calling_package); if (options_->telephony_only) { - MaybeTakeEarlyScreenshot(); - onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); - MaybeCheckUserConsent(calling_uid, calling_package); DumpstateTelephonyOnly(calling_package); - DumpstateBoard(); } else if (options_->wifi_only) { - MaybeTakeEarlyScreenshot(); - onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); - MaybeCheckUserConsent(calling_uid, calling_package); DumpstateWifiOnly(); } else if (options_->limited_only) { - MaybeTakeEarlyScreenshot(); - onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); - MaybeCheckUserConsent(calling_uid, calling_package); DumpstateLimitedOnly(); } else { - // Invoke critical dumpsys first to preserve system state, before doing anything else. - RunDumpsysCritical(); - - // Take screenshot and get consent only after critical dumpsys has finished. - MaybeTakeEarlyScreenshot(); - onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); - MaybeCheckUserConsent(calling_uid, calling_package); - // Dump state for the default case. This also drops root. RunStatus s = DumpstateDefaultAfterCritical(); if (s != RunStatus::OK) { @@ -2747,14 +2964,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } /* close output if needed */ - if (is_redirecting) { - TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); - } + TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); // Zip the (now complete) .tmp file within the internal directory. - if (options_->OutputToFile()) { - FinalizeFile(); - } + FinalizeFile(); // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; @@ -2797,11 +3010,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, progress_->Save(); MYLOGI("done (id %d)\n", id_); - if (is_redirecting) { - TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); - } + TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); - if (options_->use_control_socket && control_socket_fd_ != -1) { + if (control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); close(control_socket_fd_); } @@ -2823,16 +3034,45 @@ void Dumpstate::MaybeTakeEarlyScreenshot() { TakeScreenshot(); } -void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid, - const std::string& calling_package) { +void Dumpstate::MaybeSnapshotSystemTrace() { + // If a background system trace is happening and is marked as "suitable for + // bugreport" (i.e. bugreport_score > 0 in the trace config), this command + // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) + // case that no trace is ongoing, this command is a no-op. + // Note: this should not be enqueued as we need to freeze the trace before + // dumpstate starts. Otherwise the trace ring buffers will contain mostly + // the dumpstate's own activity which is irrelevant. + int res = RunCommand( + "SERIALIZE PERFETTO TRACE", + {"perfetto", "--save-for-bugreport"}, + CommandOptions::WithTimeout(10) + .DropRoot() + .CloseAllFileDescriptorsOnExec() + .Build()); + has_system_trace_ = res == 0; + // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip + // file in the later stages. +} + +void Dumpstate::MaybeSnapshotWinTrace() { + // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol. + for (const auto& service : {"window", "input_method"}) { + RunCommand( + // Empty name because it's not intended to be classified as a bugreport section. + // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport. + "", {"cmd", service, "tracing", "save-for-bugreport"}, + CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); + } +} + +void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) { if (calling_uid == AID_SHELL || !CalledByApi()) { return; } if (listener_ != nullptr) { // Let listener know ui intensive bugreport dumps are finished, then it can do event // handling if required. - android::String16 package(calling_package.c_str()); - listener_->onUiIntensiveBugreportDumpsFinished(package); + listener_->onUiIntensiveBugreportDumpsFinished(); } } @@ -2869,6 +3109,45 @@ void Dumpstate::CleanupTmpFiles() { android::os::UnlinkAndLogOnError(tmp_path_); android::os::UnlinkAndLogOnError(screenshot_path_); android::os::UnlinkAndLogOnError(path_); + if (dump_traces_path != nullptr) { + android::os::UnlinkAndLogOnError(dump_traces_path); + } +} + +void Dumpstate::EnableParallelRunIfNeeded() { + if (!PropertiesHelper::IsParallelRun()) { + return; + } + dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_); + zip_entry_tasks_ = std::make_unique<TaskQueue>(); +} + +void Dumpstate::ShutdownDumpPool() { + if (dump_pool_) { + dump_pool_->shutdown(); + dump_pool_ = nullptr; + } + if (zip_entry_tasks_) { + zip_entry_tasks_->run(/* do_cancel = */true); + zip_entry_tasks_ = nullptr; + } +} + +void Dumpstate::EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name, + const std::string& entry_path) { + auto func_add_zip_entry_and_cleanup = [=](bool task_cancelled) { + if (!task_cancelled) { + AddZipEntry(entry_name, entry_path); + } + android::os::UnlinkAndLogOnError(entry_path); + }; + if (zip_entry_tasks_) { + // Enqueues AddZipEntryAndCleanup function if the parallel run is enabled. + zip_entry_tasks_->add(func_add_zip_entry_and_cleanup, _1); + } else { + // Invokes AddZipEntryAndCleanup immediately + std::invoke(func_add_zip_entry_and_cleanup, /* task_cancelled = */false); + } } Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() { @@ -2927,6 +3206,11 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented(int32_t calling_uid // Since we do not have user consent to share the bugreport it does not get // copied over to the calling app but remains in the internal directory from // where the user can manually pull it. + std::string final_path = GetPath(".zip"); + bool copy_succeeded = android::os::CopyFileToFile(path_, final_path); + if (copy_succeeded) { + android::os::UnlinkAndLogOnError(path_); + } return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT; } // Unknown result; must be a programming error. @@ -2981,7 +3265,8 @@ Dumpstate::Dumpstate(const std::string& version) options_(new Dumpstate::DumpOptions()), last_reported_percent_progress_(0), version_(version), - now_(time(nullptr)) { + now_(time(nullptr)), + open_socket_fn_(open_socket) { } Dumpstate& Dumpstate::GetInstance() { @@ -2989,8 +3274,9 @@ Dumpstate& Dumpstate::GetInstance() { return singleton_; } -DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose) - : title_(title), logcat_only_(logcat_only), verbose_(verbose) { +DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose, + int duration_fd) : title_(title), logcat_only_(logcat_only), verbose_(verbose), + duration_fd_(duration_fd) { if (!title_.empty()) { started_ = Nanotime(); } @@ -3004,7 +3290,8 @@ DurationReporter::~DurationReporter() { } if (!logcat_only_) { // Use "Yoda grammar" to make it easier to grep|sort sections. - printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); + dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n", + elapsed, title_.c_str()); } } } @@ -3590,10 +3877,11 @@ int dump_file_from_fd(const char *title, const char *path, int fd) { } int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command, - const CommandOptions& options, bool verbose_duration) { - DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration); + const CommandOptions& options, bool verbose_duration, int out_fd) { + DurationReporter duration_reporter(title, false /* logcat_only */, + verbose_duration, out_fd); - int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options); + int status = RunCommandToFd(out_fd, title, full_command, options); /* TODO: for now we're simplifying the progress calculation by using the * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys, @@ -3605,14 +3893,14 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin } void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, - const CommandOptions& options, long dumpsysTimeoutMs) { + const CommandOptions& options, long dumpsysTimeoutMs, int out_fd) { long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); - RunCommand(title, dumpsys, options); + RunCommand(title, dumpsys, options, false, out_fd); } -int open_socket(const char *service) { +static int open_socket(const char* service) { int s = android_get_control_socket(service); if (s < 0) { MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); @@ -3647,19 +3935,6 @@ int open_socket(const char *service) { return fd; } -/* redirect output to a service control socket */ -bool redirect_to_socket(FILE* redirect, const char* service) { - int fd = open_socket(service); - if (fd == -1) { - return false; - } - fflush(redirect); - // TODO: handle dup2 failure - TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); - close(fd); - return true; -} - // TODO: should call is_valid_output_file and/or be merged into it. void create_parent_dirs(const char *path) { char *chp = const_cast<char *> (path); @@ -3732,12 +4007,16 @@ void dump_route_tables() { fclose(fp); } -// TODO: make this function thread safe if sections are generated in parallel. void Dumpstate::UpdateProgress(int32_t delta_sec) { if (progress_ == nullptr) { MYLOGE("UpdateProgress: progress_ not set\n"); return; } + // This function updates progress related members of the dumpstate and reports + // progress percentage to the bugreport client. Since it could be called by + // different dump tasks at the same time if the parallel run is enabled, a + // mutex lock is necessary here to synchronize the call. + std::lock_guard<std::recursive_mutex> lock(mutex_); // Always update progess so stats can be tuned... progress_->Inc(delta_sec); |