diff options
Diffstat (limited to 'cmds/dumpstate/dumpstate.cpp')
-rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 877 |
1 files changed, 299 insertions, 578 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 2d11b908c2..581d3ded89 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -28,7 +28,6 @@ #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> @@ -79,7 +78,6 @@ #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> @@ -96,7 +94,6 @@ 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; @@ -115,9 +112,7 @@ 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 @@ -130,8 +125,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, int out_fd = STDOUT_FILENO) { - return ds.RunCommand(title, full_command, options, verbose_duration, out_fd); + bool verbose_duration = false) { + return ds.RunCommand(title, full_command, options, verbose_duration); } // Reasonable value for max stats. @@ -175,8 +170,6 @@ 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. @@ -202,35 +195,8 @@ 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 { @@ -339,12 +305,8 @@ 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, 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); + long dumpsysTimeoutMs = 0) { + return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs); } static int DumpFile(const std::string& title, const std::string& path) { return ds.DumpFile(title, path); @@ -699,31 +661,18 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int f static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000; -// 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. +/* 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) { - 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; - } + 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; } 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()) { } @@ -794,9 +743,6 @@ 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"); @@ -805,9 +751,8 @@ 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 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("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("\n"); } @@ -1037,6 +982,7 @@ 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, @@ -1051,29 +997,9 @@ 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.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt, - path); - } else { - unlink(path.c_str()); + ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path); } -} - -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); + unlink(path.c_str()); } static void DumpVisibleWindowViews() { @@ -1212,6 +1138,10 @@ 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() { @@ -1241,15 +1171,8 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i if (status == OK) { dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); std::chrono::duration<double> elapsed_seconds; - 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); - } + 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); @@ -1371,21 +1294,15 @@ static Dumpstate::RunStatus RunDumpsysNormal() { /* timeout= */ 90s, /* service_timeout= */ 10s); } -/* - * |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) { +static void DumpHals() { if (!ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"}, - CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(), - false, out_fd); + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); return; } - RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(), - false, out_fd); + DurationReporter duration_reporter("DUMP HALS"); + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); using android::hidl::manager::V1_0::IServiceManager; using android::hardware::defaultServiceManager; @@ -1407,7 +1324,6 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { }, '_'); 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(), @@ -1422,14 +1338,13 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { {"lshal", "debug", "-E", interface}, CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - empty = 0 == lseek(fd, 0, SEEK_END); - } - if (!empty) { - ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt", - path); - } else { - unlink(path.c_str()); + bool empty = 0 == lseek(fd, 0, SEEK_END); + if (!empty) { + ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path); + } } + + unlink(path.c_str()); } }); @@ -1501,8 +1416,6 @@ 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"); @@ -1519,73 +1432,6 @@ 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 @@ -1593,18 +1439,6 @@ static void DumpAppInfos(int out_fd = STDOUT_FILENO) { 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). @@ -1636,11 +1470,7 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, CommandOptions::AS_ROOT); - 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); - } + DumpHals(); RunCommand("PRINTENV", {"printenv"}); RunCommand("NETSTAT", {"netstat", "-nW"}); @@ -1649,13 +1479,10 @@ 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::base::GetBoolProperty("ro.logd.kernel", false)) { + if (__android_logger_property_get_bool( + "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) { DoKernelLogcat(); } else { do_dmesg(); @@ -1668,8 +1495,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 Nfc NCI logs */ - ds.AddDir("/data/misc/nfc/logs", true); + /* Dump Bluetooth HCI logs */ + ds.AddDir("/data/misc/bluetooth/logs", true); if (ds.options_->do_screenshot && !ds.do_early_screenshot_) { MYLOGI("taking late screenshot\n"); @@ -1678,8 +1505,6 @@ 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(), @@ -1704,12 +1529,6 @@ 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"}); @@ -1733,11 +1552,7 @@ static Dumpstate::RunStatus dumpstate() { ds.AddDir(SNAPSHOTCTL_LOG_DIR, false); - 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); - } + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard); /* Migrate the ril_dumpstate to a device specific dumpstate? */ int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0); @@ -1759,16 +1574,57 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal); - /* Dump Bluetooth HCI logs after getting bluetooth_manager dumpsys */ - ds.AddDir("/data/misc/bluetooth/logs", true); + printf("========================================================\n"); + printf("== Checkins\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("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); - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpAppInfos); + 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); + + printf("========================================================\n"); + printf("== Running Application Providers (non-platform)\n"); + printf("========================================================\n"); + + RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Dropbox crashes\n"); @@ -1793,15 +1649,7 @@ static Dumpstate::RunStatus dumpstate() { // Add linker configuration directory ds.AddDir(LINKERCONFIG_DIR, true); - /* 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); - } + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport); return Dumpstate::RunStatus::OK; } @@ -1822,18 +1670,7 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { time_t logcat_ts = time(nullptr); /* collect stack traces from Dalvik and native processes (needs root) */ - 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_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path); /* Run some operations that require root. */ ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); @@ -1867,22 +1704,15 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"}); // Gather shared memory buffer info if the product implements it - RunCommand("Dmabuf dump", {"dmabuf_dump"}); - RunCommand("Dmabuf per-buffer/per-exporter/per-device stats", {"dmabuf_dump", "-b"}); + struct stat st; + if (!stat("/product/bin/dmabuf_dump", &st)) { + RunCommand("Dmabuf dump", {"/product/bin/dmabuf_dump"}); + } 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; } @@ -1894,39 +1724,31 @@ 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(); - // 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); - } + // Contains unrelated hardware info (camera, NFC, biometrics, ...). + DumpHals(); } DumpPacketStats(); @@ -1950,21 +1772,6 @@ 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) { @@ -1981,8 +1788,6 @@ 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(), @@ -2043,29 +1848,12 @@ 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"); @@ -2083,7 +1871,9 @@ static void DumpstateWifiOnly() { } Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { - const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX"; + DurationReporter duration_reporter("DUMP TRACES"); + + const std::string temp_file_pattern = "/data/anr/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); @@ -2190,31 +1980,17 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { return RunStatus::OK; } -void Dumpstate::DumpstateBoard(int out_fd) { - dprintf(out_fd, "========================================================\n"); - dprintf(out_fd, "== Board\n"); - dprintf(out_fd, "========================================================\n"); +void Dumpstate::DumpstateBoard() { + DurationReporter duration_reporter("dumpstate_board()"); + printf("========================================================\n"); + printf("== Board\n"); + printf("========================================================\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++) { @@ -2314,13 +2090,6 @@ void Dumpstate::DumpstateBoard(int out_fd) { "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; @@ -2341,26 +2110,27 @@ void Dumpstate::DumpstateBoard(int out_fd) { MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str()); continue; } - remover[i].Disable(); - EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]); - dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str()); + AddZipEntry(kDumpstateBoardFiles[i], paths[i]); + printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str()); } } static void ShowUsage() { fprintf(stderr, - "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] " - "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" + "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] " + "[-z] [-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" - " -s: write zipped file to control socket (for init)\n" - " -S: write file location to control socket (for init)\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" " -q: disable vibrate\n" " -P: send broadcast when started and do progress updates\n" - " -R: take bugreport in remote mode (shouldn't be used with -P)\n" + " -R: take bugreport in remote mode (requires -z and -d, 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"); @@ -2371,11 +2141,6 @@ 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()); @@ -2442,9 +2207,7 @@ static void SendBroadcast(const std::string& action, const std::vector<std::stri static void Vibrate(int duration_ms) { // clang-format off - std::vector<std::string> args = {"cmd", "vibrator_manager", "synced", "-f", "-d", "dumpstate", - "oneshot", std::to_string(duration_ms)}; - RunCommand("", args, + RunCommand("", {"cmd", "vibrator", "vibrate", "-f", std::to_string(duration_ms), "dumpstate"}, CommandOptions::WithTimeout(10) .Log("Vibrate: '%s'\n") .Always() @@ -2461,17 +2224,21 @@ static void MaybeResolveSymlink(std::string* path) { /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter - * and adds the version file. Return false if zip_file could not be open to write. + * if we are writing zip files and adds the version file. */ -static bool PrepareToWriteToFile() { +static void 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()); - char date[80]; - strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); - ds.name_ = date; + 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"; + } if (ds.options_->telephony_only) { ds.base_name_ += "-telephony"; @@ -2498,17 +2265,18 @@ static bool 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()); - 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; + 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.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); - ds.AddTextZipEntry("version.txt", ds.version_); - return true; } /* @@ -2516,9 +2284,14 @@ static bool PrepareToWriteToFile() { * printing zipped file status, etc. */ static void FinalizeFile() { - bool do_text_file = !ds.FinishZipFile(); - if (do_text_file) { - MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); + 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; + } } std::string final_path = ds.path_; @@ -2527,9 +2300,7 @@ static void FinalizeFile() { android::os::CopyFileToFile(ds.path_, final_path); } - if (ds.options_->stream_to_socket) { - android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_); - } else if (ds.options_->progress_updates_to_socket) { + if (ds.options_->use_control_socket) { if (do_text_file) { dprintf(ds.control_socket_fd_, "FAIL:could not create zip file, check %s " @@ -2573,6 +2344,7 @@ 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; @@ -2584,7 +2356,9 @@ 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; @@ -2597,6 +2371,7 @@ 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; @@ -2607,13 +2382,13 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( - "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 " + "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 " "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " "limited_only: %d args: %s\n", - options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, + options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, - options.telephony_only, options.wifi_only, + options.do_start_service, 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()); } @@ -2622,6 +2397,11 @@ 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())); @@ -2635,20 +2415,18 @@ 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': stream_to_socket = true; break; - case 'S': progress_updates_to_socket = true; break; + case 's': use_socket = true; break; + case 'S': use_control_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': - case 'd': - case 'z': - // compatibility no-op - break; + case 'V': break; // compatibility no-op case 'w': // This was already processed break; @@ -2677,15 +2455,19 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { - if (bugreport_fd.get() != -1 && stream_to_socket) { + if (bugreport_fd.get() != -1 && !do_zip_file) { + return false; + } + + if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) { return false; } - if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) { + if (use_control_socket && !do_zip_file) { return false; } - if (is_remote_mode && (do_progress_updates || stream_to_socket)) { + if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) { return false; } return true; @@ -2737,15 +2519,6 @@ 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); - } } /* @@ -2760,16 +2533,17 @@ void Dumpstate::Cancel() { * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * - * A bunch of other files and dumps are added to the zip archive. + * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final + * text file. * - * The temporary bugreport file and the log file also get added to the 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. * * 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"); @@ -2810,9 +2584,14 @@ 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 = - android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()); + is_redirecting + ? 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) { @@ -2824,6 +2603,15 @@ 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"); } @@ -2835,33 +2623,35 @@ 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_->stream_to_socket || options_->progress_updates_to_socket) { + if (options_->use_socket) { + if (!redirect_to_socket(stdout, "dumpstate")) { + return ERROR; + } + } + + if (options_->use_control_socket) { MYLOGD("Opening control socket\n"); - control_socket_fd_ = open_socket_fn_("dumpstate"); + control_socket_fd_ = open_socket("dumpstate"); if (control_socket_fd_ == -1) { return ERROR; } - if (options_->progress_updates_to_socket) { - options_->do_progress_updates = 1; - } + options_->do_progress_updates = 1; } - if (!PrepareToWriteToFile()) { - return ERROR; - } + if (is_redirecting) { + PrepareToWriteToFile(); - // 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()); + 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()); + } } } @@ -2876,83 +2666,76 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, Vibrate(150); } - if (zip_file != nullptr) { + if (options_->do_zip_file && 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; - // 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)); - } + 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 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(); - 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); + // TODO(b/158737089) reduce code repetition in if branches 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) { @@ -2964,10 +2747,14 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } /* close output if needed */ - TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); + if (is_redirecting) { + TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); + } // Zip the (now complete) .tmp file within the internal directory. - FinalizeFile(); + if (options_->OutputToFile()) { + FinalizeFile(); + } // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; @@ -3010,9 +2797,11 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, progress_->Save(); MYLOGI("done (id %d)\n", id_); - TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); + if (is_redirecting) { + TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); + } - if (control_socket_fd_ != -1) { + if (options_->use_control_socket && control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); close(control_socket_fd_); } @@ -3034,45 +2823,16 @@ void Dumpstate::MaybeTakeEarlyScreenshot() { TakeScreenshot(); } -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) { +void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid, + const std::string& calling_package) { 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. - listener_->onUiIntensiveBugreportDumpsFinished(); + android::String16 package(calling_package.c_str()); + listener_->onUiIntensiveBugreportDumpsFinished(package); } } @@ -3109,45 +2869,6 @@ 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() { @@ -3206,11 +2927,6 @@ 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. @@ -3265,8 +2981,7 @@ Dumpstate::Dumpstate(const std::string& version) options_(new Dumpstate::DumpOptions()), last_reported_percent_progress_(0), version_(version), - now_(time(nullptr)), - open_socket_fn_(open_socket) { + now_(time(nullptr)) { } Dumpstate& Dumpstate::GetInstance() { @@ -3274,9 +2989,8 @@ Dumpstate& Dumpstate::GetInstance() { return singleton_; } -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) { +DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose) + : title_(title), logcat_only_(logcat_only), verbose_(verbose) { if (!title_.empty()) { started_ = Nanotime(); } @@ -3290,8 +3004,7 @@ DurationReporter::~DurationReporter() { } if (!logcat_only_) { // Use "Yoda grammar" to make it easier to grep|sort sections. - dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n", - elapsed, title_.c_str()); + printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } } } @@ -3877,11 +3590,10 @@ 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, int out_fd) { - DurationReporter duration_reporter(title, false /* logcat_only */, - verbose_duration, out_fd); + const CommandOptions& options, bool verbose_duration) { + DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration); - int status = RunCommandToFd(out_fd, title, full_command, options); + int status = RunCommandToFd(STDOUT_FILENO, 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, @@ -3893,14 +3605,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, int out_fd) { + const CommandOptions& options, long dumpsysTimeoutMs) { 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, false, out_fd); + RunCommand(title, dumpsys, options); } -static int open_socket(const char* service) { +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)); @@ -3935,6 +3647,19 @@ static 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); @@ -4007,16 +3732,12 @@ 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); |