summaryrefslogtreecommitdiff
path: root/cmds/dumpstate/dumpstate.cpp
diff options
context:
space:
mode:
author Bill Yi <byi@google.com> 2016-12-06 15:07:48 -0800
committer Bill Yi <byi@google.com> 2016-12-06 15:07:48 -0800
commitecea1bfeae1211f9a363925c172c0f23062107fb (patch)
tree99d7fc7c78f261a3ffde997e8354f8134c60bfbe /cmds/dumpstate/dumpstate.cpp
parentf5c3b20f062ca646572ee6c07713eba691971c95 (diff)
parent6079aa6a8a845d8312435fe3e991bbe14588d018 (diff)
Merge remote-tracking branch 'goog/stage-aosp-master' into HEAD
Diffstat (limited to 'cmds/dumpstate/dumpstate.cpp')
-rw-r--r--cmds/dumpstate/dumpstate.cpp317
1 files changed, 283 insertions, 34 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ad06e30673..5b01be1274 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -23,6 +23,7 @@
#include <memory>
#include <regex>
#include <set>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -37,7 +38,9 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <android-base/file.h>
#include <cutils/properties.h>
+#include <hardware_legacy/power.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -53,14 +56,14 @@ using android::base::StringPrintf;
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
-// TODO: should be part of dumpstate object
+// TODO: variables below should be part of dumpstate object
static unsigned long id;
static char build_type[PROPERTY_VALUE_MAX];
static time_t now;
static std::unique_ptr<ZipWriter> zip_writer;
static std::set<std::string> mount_points;
void add_mountinfo();
-static int control_socket_fd;
+int control_socket_fd = -1;
/* suffix of the bugreport files - it's typically the date (when invoked with -d),
* although it could be changed by the user using a system property */
static std::string suffix;
@@ -68,16 +71,19 @@ static std::string suffix;
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
-#define RAFT_DIR "/data/misc/raft/"
+#define RAFT_DIR "/data/misc/raft"
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
+#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
#define TOMBSTONE_DIR "/data/tombstones"
#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
/* Can accomodate a tombstone number up to 9999. */
#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
#define NUM_TOMBSTONES 10
#define WLUTIL "/vendor/xbin/wlutil"
+#define WAKE_LOCK_NAME "dumpstate_wakelock"
typedef struct {
char name[TOMBSTONE_MAX_LEN];
@@ -176,6 +182,136 @@ static void dump_dev_files(const char *title, const char *driverpath, const char
closedir(d);
}
+// 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) {
+ 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 (!zip_writer) {
MYLOGD("Not dumping systrace because zip_writer is not set\n");
@@ -217,6 +353,40 @@ static void dump_systrace() {
}
}
+static void dump_raft() {
+ if (is_user_build()) {
+ return;
+ }
+
+ std::string raft_log_path = bugreport_dir + "/raft_log.txt";
+ if (raft_log_path.empty()) {
+ MYLOGD("raft_log_path is empty\n");
+ return;
+ }
+
+ struct stat s;
+ if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) {
+ MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR);
+ return;
+ }
+
+ if (!zip_writer) {
+ // Write compressed and encoded raft logs to stdout if not zip_writer.
+ run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+ return;
+ }
+
+ run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
+ "-o", raft_log_path.c_str(), NULL);
+ if (!add_zip_entry("raft_log.txt", raft_log_path)) {
+ MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+ } else {
+ if (remove(raft_log_path.c_str())) {
+ MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+ }
+ }
+}
+
static bool skip_not_stat(const char *path) {
static const char stat[] = "/stat";
size_t len = strlen(path);
@@ -417,7 +587,7 @@ static void print_header(std::string version) {
property_get("ro.build.display.id", build, "(unknown)");
property_get("ro.build.fingerprint", fingerprint, "(unknown)");
property_get("ro.build.type", build_type, "(unknown)");
- property_get("ro.baseband", radio, "(unknown)");
+ property_get("gsm.version.baseband", radio, "(unknown)");
property_get("ro.bootloader", bootloader, "(unknown)");
property_get("gsm.operator.alpha", network, "(unknown)");
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
@@ -441,18 +611,39 @@ static void print_header(std::string version) {
printf("\n");
}
+// List of file extensions that can cause a zip file attachment to be rejected by some email
+// service providers.
+static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
+ ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp",
+ ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct",
+ ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
+};
+
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",
entry_name.c_str());
return false;
}
+ std::string valid_name = entry_name;
+
+ // Rename extension if necessary.
+ size_t idx = entry_name.rfind(".");
+ if (idx != std::string::npos) {
+ std::string extension = entry_name.substr(idx);
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) {
+ valid_name = entry_name + ".renamed";
+ MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str());
+ }
+ }
+
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(),
+ int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
ZipWriter::kCompress, get_mtime(fd, now));
if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
ZipWriter::ErrorCodeString(err));
return false;
}
@@ -498,6 +689,7 @@ static int _add_file_from_fd(const char *title, const char *path, int fd) {
return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
+// 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);
@@ -541,10 +733,12 @@ static bool add_text_zip_entry(const std::string& entry_name, const std::string&
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 dumpstate(const std::string& screenshot_path, const std::string& version) {
@@ -626,8 +820,6 @@ static void dumpstate(const std::string& screenshot_path, const std::string& ver
run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
- run_command("RAFT LOGS", 600, SU_PATH, "root", "logcompressor", "-r", RAFT_DIR, NULL);
-
/* show the traces we collected in main(), if that was done */
if (dump_traces_path != NULL) {
dump_file("VM TRACES JUST NOW", dump_traces_path);
@@ -841,7 +1033,7 @@ static void dumpstate(const std::string& screenshot_path, const std::string& ver
printf("== Running Application Providers\n");
printf("========================================================\n");
- run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+ run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
printf("========================================================\n");
@@ -875,11 +1067,31 @@ static void usage() {
VERSION_DEFAULT.c_str());
}
-static void sigpipe_handler(int n) {
- // don't complain to stderr or stdout
+static void wake_lock_releaser() {
+ if (release_wake_lock(WAKE_LOCK_NAME) < 0) {
+ MYLOGE("Failed to release wake lock: %s \n", strerror(errno));
+ } else {
+ MYLOGD("Wake lock released.\n");
+ }
+}
+
+static void sig_handler(int signo) {
+ wake_lock_releaser();
_exit(EXIT_FAILURE);
}
+static void register_sig_handler() {
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sig_handler;
+ sigaction(SIGPIPE, &sa, NULL); // broken pipe
+ sigaction(SIGSEGV, &sa, NULL); // segment fault
+ sigaction(SIGINT, &sa, NULL); // ctrl-c
+ sigaction(SIGTERM, &sa, NULL); // killed
+ sigaction(SIGQUIT, &sa, NULL); // quit
+}
+
/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
temporary file.
*/
@@ -948,7 +1160,6 @@ static std::string SHA256_file_hash(std::string filepath) {
}
int main(int argc, char *argv[]) {
- struct sigaction sigact;
int do_add_date = 0;
int do_zip_file = 0;
int do_vibrate = 1;
@@ -965,6 +1176,14 @@ int main(int argc, char *argv[]) {
MYLOGI("begin\n");
+ if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
+ MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
+ } else {
+ MYLOGD("Wake lock acquired.\n");
+ atexit(wake_lock_releaser);
+ register_sig_handler();
+ }
+
/* gets the sequential id */
char last_id[PROPERTY_VALUE_MAX];
property_get("dumpstate.last_id", last_id, "0");
@@ -973,17 +1192,20 @@ int main(int argc, char *argv[]) {
property_set("dumpstate.last_id", last_id);
MYLOGI("dumpstate id: %lu\n", id);
- /* clear SIGPIPE handler */
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = sigpipe_handler;
- sigaction(SIGPIPE, &sigact, NULL);
-
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
- FILE *oom_adj = fopen("/proc/self/oom_adj", "we");
+
+ FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
if (oom_adj) {
- fputs("-17", oom_adj);
+ fputs("-1000", oom_adj);
fclose(oom_adj);
+ } else {
+ /* fallback to kernels <= 2.6.35 */
+ oom_adj = fopen("/proc/self/oom_adj", "we");
+ if (oom_adj) {
+ fputs("-17", oom_adj);
+ fclose(oom_adj);
+ }
}
/* parse arguments */
@@ -1050,6 +1272,7 @@ int main(int argc, char *argv[]) {
if (use_control_socket) {
MYLOGD("Opening control socket\n");
control_socket_fd = open_socket("dumpstate");
+ do_update_progress = 1;
}
/* full path of the temporary file containing the bugreport */
@@ -1123,14 +1346,21 @@ int main(int argc, char *argv[]) {
}
if (do_update_progress) {
- std::vector<std::string> am_args = {
- "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
- "--es", "android.intent.extra.NAME", suffix,
- "--ei", "android.intent.extra.ID", std::to_string(id),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
- "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
- };
- send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+ if (do_broadcast) {
+ // clang-format off
+ std::vector<std::string> am_args = {
+ "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
+ "--es", "android.intent.extra.NAME", suffix,
+ "--ei", "android.intent.extra.ID", std::to_string(id),
+ "--ei", "android.intent.extra.PID", std::to_string(getpid()),
+ "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+ };
+ // clang-format on
+ send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+ }
+ if (use_control_socket) {
+ dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str());
+ }
}
}
@@ -1192,7 +1422,14 @@ int main(int argc, char *argv[]) {
print_header(version);
// Dumps systrace right away, otherwise it will be filled with unnecessary events.
- dump_systrace();
+ // 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();
// Invoking the following dumpsys calls before dump_traces() to try and
// keep the system stats as close to its initial state as possible.
@@ -1207,9 +1444,19 @@ int main(int argc, char *argv[]) {
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();
+ // Capture any IPSec policies in play. No keys are exposed here.
+ run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr);
+
+ // Run ss as root so we can see socket marks.
+ run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL);
+
if (!drop_root_user()) {
return -1;
}
@@ -1306,6 +1553,7 @@ int main(int argc, char *argv[]) {
if (do_broadcast) {
if (!path.empty()) {
MYLOGI("Final bugreport path: %s\n", path.c_str());
+ // clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
"--ei", "android.intent.extra.ID", std::to_string(id),
@@ -1314,6 +1562,7 @@ int main(int argc, char *argv[]) {
"--es", "android.intent.extra.BUGREPORT", path,
"--es", "android.intent.extra.DUMPSTATE_LOG", log_path
};
+ // clang-format on
if (do_fb) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
@@ -1339,9 +1588,9 @@ int main(int argc, char *argv[]) {
fclose(stderr);
}
- if (use_control_socket && control_socket_fd >= 0) {
- MYLOGD("Closing control socket\n");
- close(control_socket_fd);
+ if (use_control_socket && control_socket_fd != -1) {
+ MYLOGD("Closing control socket\n");
+ close(control_socket_fd);
}
return 0;