#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/properties.h>
#include "private/android_filesystem_config.h"
}
void add_mountinfo() {
- if (!zip_writer) return;
+ if (!is_zipping()) return;
const char *title = "MOUNT INFO";
mount_points.clear();
DurationReporter duration_reporter(title, NULL);
closedir(d);
}
-static void dump_systrace() {
+// return pid of a userspace process. If not found or error, return 0.
+static unsigned int pid_of_process(const char* ps_name) {
+ DIR *proc_dir;
+ struct dirent *ps;
+ unsigned int pid;
+ std::string cmdline;
+
+ if (!(proc_dir = opendir("/proc"))) {
+ MYLOGE("Can't open /proc\n");
+ return 0;
+ }
+
+ while ((ps = readdir(proc_dir))) {
+ if (!(pid = atoi(ps->d_name))) {
+ continue;
+ }
+ android::base::ReadFileToString("/proc/"
+ + std::string(ps->d_name) + "/cmdline", &cmdline);
+ if (cmdline.find(ps_name) == std::string::npos) {
+ continue;
+ } else {
+ closedir(proc_dir);
+ return pid;
+ }
+ }
+ closedir(proc_dir);
+ return 0;
+}
+
+// dump anrd's trace and add to the zip file.
+// 1. check if anrd is running on this device.
+// 2. send a SIGUSR1 to its pid which will dump anrd's trace.
+// 3. wait until the trace generation completes and add to the zip file.
+static bool dump_anrd_trace() {
+ unsigned int pid;
+ char buf[50], path[PATH_MAX];
+ struct dirent *trace;
+ struct stat st;
+ DIR *trace_dir;
+ int retry = 5;
+ long max_ctime = 0, old_mtime;
+ long long cur_size = 0;
+ const char *trace_path = "/data/misc/anrd/";
+
if (!zip_writer) {
- MYLOGD("Not dumping systrace because zip_writer is not set\n");
+ MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+ return false;
+ }
+
+ // find anrd's pid if it is running.
+ pid = pid_of_process("/system/xbin/anrd");
+
+ if (pid > 0) {
+ if (stat(trace_path, &st) == 0) {
+ old_mtime = st.st_mtime;
+ } else {
+ MYLOGE("Failed to find: %s\n", trace_path);
+ return false;
+ }
+
+ // send SIGUSR1 to the anrd to generate a trace.
+ sprintf(buf, "%u", pid);
+ if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) {
+ MYLOGE("anrd signal timed out. Please manually collect trace\n");
+ return false;
+ }
+
+ while (retry-- > 0 && old_mtime == st.st_mtime) {
+ sleep(1);
+ stat(trace_path, &st);
+ }
+
+ if (retry < 0 && old_mtime == st.st_mtime) {
+ MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path);
+ return false;
+ }
+
+ // identify the trace file by its creation time.
+ if (!(trace_dir = opendir(trace_path))) {
+ MYLOGE("Can't open trace file under %s\n", trace_path);
+ }
+ while ((trace = readdir(trace_dir))) {
+ if (strcmp(trace->d_name, ".") == 0
+ || strcmp(trace->d_name, "..") == 0) {
+ continue;
+ }
+ sprintf(path, "%s%s", trace_path, trace->d_name);
+ if (stat(path, &st) == 0) {
+ if (st.st_ctime > max_ctime) {
+ max_ctime = st.st_ctime;
+ sprintf(buf, "%s", trace->d_name);
+ }
+ }
+ }
+ closedir(trace_dir);
+
+ // Wait until the dump completes by checking the size of the trace.
+ if (max_ctime > 0) {
+ sprintf(path, "%s%s", trace_path, buf);
+ while(true) {
+ sleep(1);
+ if (stat(path, &st) == 0) {
+ if (st.st_size == cur_size) {
+ break;
+ } else if (st.st_size > cur_size) {
+ cur_size = st.st_size;
+ } else {
+ return false;
+ }
+ } else {
+ MYLOGE("Cant stat() %s anymore\n", path);
+ return false;
+ }
+ }
+ // Add to the zip file.
+ if (!add_zip_entry("anrd_trace.txt", path)) {
+ MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
+ } else {
+ if (remove(path)) {
+ MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno));
+ }
+ return true;
+ }
+ } else {
+ MYLOGE("Can't stats any trace file under %s\n", trace_path);
+ }
+ }
+ return false;
+}
+
+static void dump_systrace() {
+ if (!is_zipping()) {
+ MYLOGD("Not dumping systrace because dumpstate is not zipping\n");
return;
}
std::string systrace_path = bugreport_dir + "/systrace-" + suffix + ".txt";
return;
}
- if (!zip_writer) {
+ if (!is_zipping()) {
// Write compressed and encoded raft logs to stdout if not zip_writer.
run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
return;
}
}
+/**
+ * Finds the last modified file in the directory dir whose name starts with file_prefix
+ * Function returns empty string when it does not find a file
+ */
+static std::string get_last_modified_file_matching_prefix(const std::string& dir,
+ const std::string& file_prefix) {
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
+ if (d == nullptr) {
+ MYLOGD("Error %d opening %s\n", errno, dir.c_str());
+ return "";
+ }
+
+ // Find the newest file matching the file_prefix in dir
+ struct dirent *de;
+ time_t last_modified = 0;
+ std::string last_modified_file = "";
+ struct stat s;
+
+ while ((de = readdir(d.get()))) {
+ std::string file = std::string(de->d_name);
+ if (!file_prefix.empty()) {
+ if (!android::base::StartsWith(file, file_prefix.c_str())) continue;
+ }
+ file = dir + "/" + file;
+ int ret = stat(file.c_str(), &s);
+
+ if ((ret == 0) && (s.st_mtime > last_modified)) {
+ last_modified_file = file;
+ last_modified = s.st_mtime;
+ }
+ }
+
+ return last_modified_file;
+}
+
+void dump_modem_logs() {
+ DurationReporter duration_reporter("dump_modem_logs");
+ if (is_user_build()) {
+ return;
+ }
+
+ if (!is_zipping()) {
+ MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
+ return;
+ }
+
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.radio.log_prefix", property, "");
+ std::string file_prefix = std::string(property);
+ if(file_prefix.empty()) {
+ MYLOGD("No modem log : file_prefix is empty\n");
+ return;
+ }
+
+ MYLOGD("dump_modem_logs: directory is %s and file_prefix is %s\n",
+ bugreport_dir.c_str(), file_prefix.c_str());
+
+ std::string modem_log_file =
+ get_last_modified_file_matching_prefix(bugreport_dir, file_prefix);
+
+ struct stat s;
+ if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
+ MYLOGD("Modem log %s does not exist\n", modem_log_file.c_str());
+ return;
+ }
+
+ std::string filename = basename(modem_log_file.c_str());
+ if (!add_zip_entry(filename, modem_log_file)) {
+ MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
+ } else {
+ MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
+ if (remove(modem_log_file.c_str())) {
+ MYLOGE("Error removing modem log %s\n", modem_log_file.c_str());
+ }
+ }
+}
+
static bool skip_not_stat(const char *path) {
static const char stat[] = "/stat";
size_t len = strlen(path);
};
bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
- if (!zip_writer) {
- MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
+ if (!is_zipping()) {
+ MYLOGD("Not adding entry %s from fd because dumpstate is not zipping\n",
entry_name.c_str());
return false;
}
// TODO: move to util.cpp
void add_dir(const char *dir, bool recursive) {
- if (!zip_writer) {
- MYLOGD("Not adding dir %s because zip_writer is not set\n", dir);
+ if (!is_zipping()) {
+ MYLOGD("Not adding dir %s because dumpstate is not zipping\n", dir);
return;
}
MYLOGD("Adding dir %s (recursive: %d)\n", dir, recursive);
dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
}
+bool is_zipping() {
+ return zip_writer != nullptr;
+}
+
/* adds a text entry entry to the existing zip file. */
static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
- if (!zip_writer) {
- MYLOGD("Not adding text zip entry %s because zip_writer is not set\n", entry_name.c_str());
+ if (!is_zipping()) {
+ MYLOGD("Not adding text entry %s because dumpstate is not zipping\n", entry_name.c_str());
return false;
}
MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
static void dump_iptables() {
run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
- run_command("IPTABLE NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
+ run_command("IPTABLES NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
/* no ip6 nat */
- run_command("IPTABLE RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
- run_command("IP6TABLE RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
+ run_command("IPTABLES MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL);
+ run_command("IP6TABLES MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL);
+ run_command("IPTABLES RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
+ run_command("IP6TABLES RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
+}
+
+static void do_kmsg() {
+ struct stat st;
+ if (!stat(PSTORE_LAST_KMSG, &st)) {
+ /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
+ dump_file("LAST KMSG", PSTORE_LAST_KMSG);
+ } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
+ dump_file("LAST KMSG", ALT_PSTORE_LAST_KMSG);
+ } else {
+ /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
+ dump_file("LAST KMSG", "/proc/last_kmsg");
+ }
+}
+
+static void do_logcat() {
+ unsigned long timeout;
+ // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+ // calculate timeout
+ timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
+ if (timeout < 20000) {
+ timeout = 20000;
+ }
+ run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime",
+ "-v", "printable",
+ "-d",
+ "*:v", NULL);
+ timeout = logcat_timeout("events");
+ if (timeout < 20000) {
+ timeout = 20000;
+ }
+ run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events",
+ "-v", "threadtime",
+ "-v", "printable",
+ "-d",
+ "*:v", NULL);
+ timeout = logcat_timeout("radio");
+ if (timeout < 20000) {
+ timeout = 20000;
+ }
+ run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio",
+ "-v", "threadtime",
+ "-v", "printable",
+ "-d",
+ "*:v", NULL);
+
+ run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
+
+ /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
+ run_command("LAST LOGCAT", 10, "logcat", "-L",
+ "-b", "all",
+ "-v", "threadtime",
+ "-v", "printable",
+ "-d",
+ "*:v", NULL);
}
static void dumpstate(const std::string& screenshot_path, const std::string& version) {
DurationReporter duration_reporter("DUMPSTATE");
- unsigned long timeout;
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
run_command("UPTIME", 10, "uptime", NULL);
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 */
+ add_dir("/data/misc/bluetooth/logs", true);
+
if (!screenshot_path.empty()) {
MYLOGI("taking late screenshot\n");
take_screenshot(screenshot_path);
MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
}
- // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
- // calculate timeout
- timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
- if (timeout < 20000) {
- timeout = 20000;
- }
- run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
- timeout = logcat_timeout("events");
- if (timeout < 20000) {
- timeout = 20000;
- }
- run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events",
- "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
- timeout = logcat_timeout("radio");
- if (timeout < 20000) {
- timeout = 20000;
- }
- run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio",
- "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
-
- run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
+ do_logcat();
/* show the traces we collected in main(), if that was done */
if (dump_traces_path != NULL) {
dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
- if (!stat(PSTORE_LAST_KMSG, &st)) {
- /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
- dump_file("LAST KMSG", PSTORE_LAST_KMSG);
- } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
- dump_file("LAST KMSG", ALT_PSTORE_LAST_KMSG);
- } else {
- /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
- dump_file("LAST KMSG", "/proc/last_kmsg");
- }
-
- /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
- run_command("LAST LOGCAT", 10, "logcat", "-L",
- "-b", "all",
- "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
+ do_kmsg();
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+ // dump_modem_logs adds the modem logs if available to the bugreport.
+ // Do this at the end to allow for sufficient time for the modem logs to be
+ // collected.
+ dump_modem_logs();
printf("========================================================\n");
printf("== Final progress (pid %d): %d/%d (originally %d)\n",
static void usage() {
fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
- "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-t]"
+ "[-z] [-s] [-S] [-q] [-B] [-P] [-R] [-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 file (instead of stdout)\n"
" -d: append date to filename (requires -o)\n"
" -p: capture screenshot to filename.png (requires -o)\n"
+ " -t: only captures telephony sections\n"
" -z: generate zipped file (requires -o)\n"
" -s: write output to control socket (for init)\n"
" -S: write file location to control socket (for init; requires -o and -z)"
int do_broadcast = 0;
int do_early_screenshot = 0;
int is_remote_mode = 0;
+ bool telephony_only = false;
+
std::string version = VERSION_DEFAULT;
now = time(NULL);
format_args(argc, const_cast<const char **>(argv), &args);
MYLOGD("Dumpstate command line: %s\n", args.c_str());
int c;
- while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzptPBRSV:")) != -1) {
switch (c) {
case 'd': do_add_date = 1; break;
+ case 't': telephony_only = true; break;
case 'z': do_zip_file = 1; break;
case 'o': use_outfile = optarg; break;
case 's': use_socket = 1; break;
char build_id[PROPERTY_VALUE_MAX];
property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
base_name = base_name + "-" + build_id;
+ if (telephony_only) {
+ base_name = base_name + "-telephony";
+ }
if (do_fb) {
// TODO: if dumpstate was an object, the paths could be internal variables and then
// we could have a function to calculate the derived values, such as:
// duration is logged into MYLOG instead.
print_header(version);
- // Dumps systrace right away, otherwise it will be filled with unnecessary events.
- dump_systrace();
+ if (telephony_only) {
+ dump_iptables();
+ if (!drop_root_user()) {
+ return -1;
+ }
+ do_dmesg();
+ do_logcat();
+ do_kmsg();
+ dumpstate_board();
+ dump_modem_logs();
+ } else {
+ // Dumps systrace right away, otherwise it will be filled with unnecessary events.
+ // First try to dump anrd trace if the daemon is running. Otherwise, dump
+ // the raw trace.
+ if (!dump_anrd_trace()) {
+ dump_systrace();
+ }
- // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
- dump_raft();
+ // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+ dump_raft();
+
+ // Invoking the following dumpsys calls before dump_traces() to try and
+ // keep the system stats as close to its initial state as possible.
+ run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
+ run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
+
+ /* collect stack traces from Dalvik and native processes (needs root) */
+ dump_traces_path = dump_traces();
+
+ /* Run some operations that require root. */
+ get_tombstone_fds(tombstone_data);
+ add_dir(RECOVERY_DIR, true);
+ add_dir(RECOVERY_DATA_DIR, true);
+ add_dir(LOGPERSIST_DATA_DIR, false);
+ if (!is_user_build()) {
+ add_dir(PROFILE_DATA_DIR_CUR, true);
+ add_dir(PROFILE_DATA_DIR_REF, true);
+ }
+ add_mountinfo();
+ dump_iptables();
- // Invoking the following dumpsys calls before dump_traces() to try and
- // keep the system stats as close to its initial state as possible.
- run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
- run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
+ // Capture any IPSec policies in play. No keys are exposed here.
+ run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr);
- /* collect stack traces from Dalvik and native processes (needs root) */
- dump_traces_path = dump_traces();
+ // Run ss as root so we can see socket marks.
+ run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL);
- /* Run some operations that require root. */
- get_tombstone_fds(tombstone_data);
- add_dir(RECOVERY_DIR, true);
- add_dir(RECOVERY_DATA_DIR, true);
- add_dir(LOGPERSIST_DATA_DIR, false);
- if (!is_user_build()) {
- add_dir(PROFILE_DATA_DIR_CUR, true);
- add_dir(PROFILE_DATA_DIR_REF, true);
- }
- add_mountinfo();
- dump_iptables();
+ if (!drop_root_user()) {
+ return -1;
+ }
- if (!drop_root_user()) {
- return -1;
+ dumpstate(do_early_screenshot ? "": screenshot_path, version);
}
- dumpstate(do_early_screenshot ? "": screenshot_path, version);
-
/* close output if needed */
if (is_redirecting) {
fclose(stdout);