Add ANR traces to their own zip entries.
These files can be very large (O(20k) lines), so it makes more sense to
add them to their own zip entry.
Since this change could break tools that are expecting that section,
it's only enabled when setting the dumpstate version to 2.0-dev-1:
adb shell setprop dumpstate.version 2.0-dev-1
Test: DumpstateTest passes; manual verification
Bug: 32470528
Bug: 32064548
Change-Id: I4726b2209b15722c22defa87bffb24dc633d5bcd
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index 388b506..b995b80 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -55,6 +55,10 @@
- `title.txt`: whose value is a single-line summary of the problem.
- `description.txt`: whose value is a multi-line, detailed description of the problem.
+## Android O versions
+On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made:
+- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`).
+
## Intermediate versions
During development, the versions will be suffixed with _-devX_ or
_-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 29589cd..e3b8c5a 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -751,6 +751,87 @@
RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
}
+static void AddAnrTraceFiles() {
+ bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+ std::string dump_traces_dir;
+
+ /* show the traces we collected in main(), if that was done */
+ if (dump_traces_path != nullptr) {
+ if (add_to_zip) {
+ dump_traces_dir = dirname(dump_traces_path);
+ MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+ ds.AddDir(dump_traces_dir, true);
+ } else {
+ MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+ dump_traces_path);
+ ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+ }
+ }
+
+ std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+ // Make sure directory is not added twice.
+ // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+ // generated by dump_traces() - and anr_traces_path - which is retrieved from a system
+ // property - but in reality they're the same path (although the former could be nullptr).
+ // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+ // be revisited.
+ bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+ MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+ dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+ if (anr_traces_path.empty()) {
+ printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+ } else {
+ int fd = TEMP_FAILURE_RETRY(
+ open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+ if (fd < 0) {
+ printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+ strerror(errno));
+ } else {
+ if (add_to_zip) {
+ if (!already_dumped) {
+ MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+ anr_traces_dir.c_str());
+ ds.AddDir(anr_traces_dir, true);
+ already_dumped = true;
+ }
+ } else {
+ MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+ anr_traces_path.c_str());
+ dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+ }
+ }
+ }
+
+ if (add_to_zip && already_dumped) {
+ MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+ return;
+ }
+
+ /* slow traces for slow operations */
+ struct stat st;
+ if (!anr_traces_path.empty()) {
+ int tail = anr_traces_path.size() - 1;
+ while (tail > 0 && anr_traces_path.at(tail) != '/') {
+ tail--;
+ }
+ int i = 0;
+ while (1) {
+ anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+ android::base::StringPrintf("slow%02d.txt", i);
+ if (stat(anr_traces_path.c_str(), &st)) {
+ // No traces file at this index, done with the files.
+ break;
+ }
+ ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+ i++;
+ }
+ }
+}
+
static void dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
unsigned long timeout;
@@ -828,44 +909,7 @@
RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
- /* show the traces we collected in main(), if that was done */
- if (dump_traces_path != NULL) {
- DumpFile("VM TRACES JUST NOW", dump_traces_path);
- }
-
- /* only show ANR traces if they're less than 15 minutes old */
- struct stat st;
- std::string anrTracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
- if (anrTracesPath.empty()) {
- printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
- } else {
- int fd = TEMP_FAILURE_RETRY(
- open(anrTracesPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
- if (fd < 0) {
- printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anrTracesPath.c_str(), strerror(errno));
- } else {
- dump_file_from_fd("VM TRACES AT LAST ANR", anrTracesPath.c_str(), fd);
- }
- }
-
- /* slow traces for slow operations */
- if (!anrTracesPath.empty()) {
- int tail = anrTracesPath.size() - 1;
- while (tail > 0 && anrTracesPath.at(tail) != '/') {
- tail--;
- }
- int i = 0;
- while (1) {
- anrTracesPath =
- anrTracesPath.substr(0, tail + 1) + android::base::StringPrintf("slow%02d.txt", i);
- if (stat(anrTracesPath.c_str(), &st)) {
- // No traces file at this index, done with the files.
- break;
- }
- DumpFile("VM TRACES WHEN SLOW", anrTracesPath.c_str());
- i++;
- }
- }
+ AddAnrTraceFiles();
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -894,6 +938,7 @@
DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
+ struct stat st;
if (!stat(PSTORE_LAST_KMSG, &st)) {
/* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
@@ -1287,9 +1332,10 @@
ds.version_ = VERSION_CURRENT;
}
- if (ds.version_ != VERSION_CURRENT) {
- MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s')\n",
- ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str());
+ if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
+ MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
+ ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
+ VERSION_SPLIT_ANR.c_str());
exit(1);
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 568256a..ab36c41c 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -200,6 +200,12 @@
static std::string VERSION_CURRENT = "1.0";
/*
+ * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version
+ * will be bumped to 2.0-dev-1.
+ */
+static std::string VERSION_SPLIT_ANR = "2.0-dev-1";
+
+/*
* "Alias" for the current version.
*/
static std::string VERSION_DEFAULT = "default";
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 31c8697..c41cca4 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -17,24 +17,25 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <libgen.h>
#include <limits.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string>
#include <string.h>
#include <sys/capability.h>
#include <sys/inotify.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
-#include <sys/klog.h>
#include <time.h>
#include <unistd.h>
+#include <string>
#include <vector>
-#include <sys/prctl.h>
#define LOG_TAG "dumpstate"
@@ -1137,32 +1138,31 @@
/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
const char *dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES", nullptr);
- if (IsDryRun()) return nullptr;
+ DurationReporter duration_reporter("DUMP TRACES");
const char* result = nullptr;
- std::string tracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
- if (tracesPath.empty()) return nullptr;
+ std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ if (traces_path.empty()) return nullptr;
/* move the old traces.txt (if any) out of the way temporarily */
- std::string anrTracesPath = tracesPath + ".anr";
- if (rename(tracesPath.c_str(), anrTracesPath.c_str()) && errno != ENOENT) {
- MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), anrTracesPath.c_str(), strerror(errno));
+ std::string anrtraces_path = traces_path + ".anr";
+ if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno));
return nullptr; // Can't rename old traces.txt -- no permission? -- leave it alone instead
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = TEMP_FAILURE_RETRY(open(tracesPath.c_str(),
+ int fd = TEMP_FAILURE_RETRY(open(traces_path.c_str(),
O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
0666)); /* -rw-rw-rw- */
if (fd < 0) {
- MYLOGE("%s: %s\n", tracesPath.c_str(), strerror(errno));
+ MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
return nullptr;
}
int chmod_ret = fchmod(fd, 0666);
if (chmod_ret < 0) {
- MYLOGE("fchmod on %s failed: %s\n", tracesPath.c_str(), strerror(errno));
+ MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno));
close(fd);
return nullptr;
}
@@ -1185,9 +1185,9 @@
goto error_close_fd;
}
- wfd = inotify_add_watch(ifd, tracesPath.c_str(), IN_CLOSE_WRITE);
+ wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
if (wfd < 0) {
- MYLOGE("inotify_add_watch(%s): %s\n", tracesPath.c_str(), strerror(errno));
+ MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
goto error_close_ifd;
}
@@ -1271,15 +1271,17 @@
MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
}
- static std::string dumpTracesPath = tracesPath + ".bugreport";
- if (rename(tracesPath.c_str(), dumpTracesPath.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), dumpTracesPath.c_str(), strerror(errno));
+ static std::string dumptraces_path = android::base::StringPrintf(
+ "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+ if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+ strerror(errno));
goto error_close_ifd;
}
- result = dumpTracesPath.c_str();
+ result = dumptraces_path.c_str();
/* replace the saved [ANR] traces.txt file */
- rename(anrTracesPath.c_str(), tracesPath.c_str());
+ rename(anrtraces_path.c_str(), traces_path.c_str());
error_close_ifd:
close(ifd);