dumpstate: Further cleanups related to new stack dumping scheme.

- Rewrite AddDumpFds to iterate over all files in the dump directory
  instead of assuming a specific file pattern. This gives us additional
  flexibility in generating dump file names.

- Fix a bug in deducing the value of is_global_stack_trace_file.

- Add a better section header for the most recent ANR dump. The most
  recent dump will be titled "VM TRACES AT LAST ANR" and all others will
  be titled "HISTORICAL ANR". Also fixes the sort order so that the most
  recent ANR shows up first.

Bug: 36024548
Test: Manual; adb bugreport

Change-Id: I461712954d033ef3a3911d5b419a101cac71c8fb
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index f16a96e..745361c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -85,10 +85,10 @@
 // with tombstoned, we should just put it in a common header.
 //
 // File: system/core/debuggerd/tombstoned/tombstoned.cpp
-static const std::string TOMBSTONE_DIR = "/data/tombstones";
-static const std::string TOMBSTONE_FILE_PREFIX = "/data/tombstones/tombstone_";
-static const std::string ANR_DIR = "/data/anr";
-static const std::string ANR_FILE_PREFIX = "/data/anr/anr_";
+static const std::string TOMBSTONE_DIR = "/data/tombstones/";
+static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
+static const std::string ANR_DIR = "/data/anr/";
+static const std::string ANR_FILE_PREFIX = "anr_";
 
 struct DumpData {
     std::string name;
@@ -97,7 +97,7 @@
 };
 
 static bool operator<(const DumpData& d1, const DumpData& d2) {
-    return d1.mtime < d2.mtime;
+    return d1.mtime > d2.mtime;
 }
 
 static std::unique_ptr<std::vector<DumpData>> tombstone_data;
@@ -134,44 +134,49 @@
 static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
 
 /*
- * Returns a vector of dump fds under |file_prefix|. The returned vector
- * is sorted by the mtimes of the dumps. If |limit_by_mtime| is set, the
- * vector only contains files that were written in the last 30 minutes.
+ * Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
+ * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
+ * is set, the vector only contains files that were written in the last 30 minutes.
  */
-static std::vector<DumpData>* GetDumpFds(const std::string& file_prefix, bool limit_by_mtime) {
+static std::vector<DumpData>* GetDumpFds(const std::string& dir_path,
+                                         const std::string& file_prefix,
+                                         bool limit_by_mtime) {
     const time_t thirty_minutes_ago = ds.now_ - 60 * 30;
 
-    size_t i = 0;
     std::unique_ptr<std::vector<DumpData>> dump_data(new std::vector<DumpData>());
-    while (true) {
-        const std::string name = android::base::StringPrintf("%s%02zu", file_prefix.c_str(), i++);
-        android::base::unique_fd fd(
-            TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)));
-        if (fd == -1) {
-            if (errno != ENOENT) {
-                MYLOGW("Unable to open dump file: %s %s\n", name.c_str(), strerror(errno));
-            }
+    std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir);
 
-            break;
-        }
-
-        struct stat st;
-        if (fstat(fd, &st) == -1) {
-            MYLOGW("Unable to stat dump file: %s %s\n", name.c_str(), strerror(errno));
+    struct dirent* entry = nullptr;
+    while ((entry = readdir(dump_dir.get()))) {
+        if (entry->d_type != DT_REG) {
             continue;
         }
 
-        if (!S_ISREG(st.st_mode)) {
-            MYLOGW("Unexpected mode for dump file: %s %x\n", name.c_str(), st.st_mode);
+        const std::string base_name(entry->d_name);
+        if (base_name.find(file_prefix) != 0) {
+            continue;
+        }
+
+        const std::string abs_path = dir_path + base_name;
+        android::base::unique_fd fd(
+            TEMP_FAILURE_RETRY(open(abs_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)));
+        if (fd == -1) {
+            MYLOGW("Unable to open dump file: %s %s\n", abs_path.c_str(), strerror(errno));
+            break;
+        }
+
+        struct stat st = {};
+        if (fstat(fd, &st) == -1) {
+            MYLOGW("Unable to stat dump file: %s %s\n", abs_path.c_str(), strerror(errno));
             continue;
         }
 
         if (limit_by_mtime && st.st_mtime >= thirty_minutes_ago) {
-            MYLOGI("Excluding stale dump file: %s\n", name.c_str());
+            MYLOGI("Excluding stale dump file: %s\n", abs_path.c_str());
             continue;
         }
 
-        DumpData data = {.name = name, .fd = fd.release(), .mtime = st.st_mtime};
+        DumpData data = {.name = abs_path, .fd = fd.release(), .mtime = st.st_mtime};
 
         dump_data->push_back(data);
     }
@@ -181,12 +186,13 @@
     return dump_data.release();
 }
 
-static bool AddDumps(const std::vector<DumpData>& dump_list, const char* type_name,
-                     const bool add_to_zip) {
+static bool AddDumps(const std::vector<DumpData>::const_iterator start,
+                     const std::vector<DumpData>::const_iterator end,
+                     const char* type_name, const bool add_to_zip) {
     bool dumped = false;
-    for (size_t i = 0; i < dump_list.size(); i++) {
-        const std::string& name = dump_list[i].name;
-        const int fd = dump_list[i].fd;
+    for (auto it = start; it != end; ++it) {
+        const std::string& name = it->name;
+        const int fd = it->fd;
         dumped = true;
         if (ds.IsZipping() && add_to_zip) {
             if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
@@ -991,8 +997,16 @@
         }
     }
 
-    const bool anr_traces_dumped = AddDumps(*anr_data, "ANR", add_to_zip);
-    if (!anr_traces_dumped) {
+    // Add a specific message for the first ANR Dump.
+    if (anr_data->size() > 0) {
+        AddDumps(anr_data->begin(), anr_data->begin() + 1,
+                 "VM TRACES AT LAST ANR", add_to_zip);
+
+        if (anr_data->size() > 1) {
+            AddDumps(anr_data->begin() + 1, anr_data->end(),
+                     "HISTORICAL ANR", add_to_zip);
+        }
+    } else {
         printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
     }
 }
@@ -1011,9 +1025,10 @@
     if (anr_traces_dir.empty()) {
         anr_traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
         if (!anr_traces_file.empty()) {
-            is_global_trace_file = true;
             anr_traces_dir = dirname(anr_traces_file.c_str());
         }
+    } else {
+        is_global_trace_file = false;
     }
 
     // We have neither configured a global trace file nor a trace directory,
@@ -1117,7 +1132,8 @@
 
     // 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(*tombstone_data, "TOMBSTONE", true /* add_to_zip */);
+    const bool tombstones_dumped = AddDumps(tombstone_data->begin(), tombstone_data->end(),
+                                            "TOMBSTONE", true /* add_to_zip */);
     if (!tombstones_dumped) {
         printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR.c_str());
     }
@@ -1798,8 +1814,8 @@
         dump_traces_path = dump_traces();
 
         /* Run some operations that require root. */
-        tombstone_data.reset(GetDumpFds(TOMBSTONE_FILE_PREFIX, !ds.IsZipping()));
-        anr_data.reset(GetDumpFds(ANR_FILE_PREFIX, !ds.IsZipping()));
+        tombstone_data.reset(GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()));
+        anr_data.reset(GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()));
 
         ds.AddDir(RECOVERY_DIR, true);
         ds.AddDir(RECOVERY_DATA_DIR, true);