Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd86372f authored by Narayan Kamath's avatar Narayan Kamath
Browse files

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
parent cc15d22f
Loading
Loading
Loading
Loading
+53 −37
Original line number Original line Diff line number Diff line
@@ -85,10 +85,10 @@ void add_mountinfo();
// with tombstoned, we should just put it in a common header.
// with tombstoned, we should just put it in a common header.
//
//
// File: system/core/debuggerd/tombstoned/tombstoned.cpp
// File: system/core/debuggerd/tombstoned/tombstoned.cpp
static const std::string TOMBSTONE_DIR = "/data/tombstones";
static const std::string TOMBSTONE_DIR = "/data/tombstones/";
static const std::string TOMBSTONE_FILE_PREFIX = "/data/tombstones/tombstone_";
static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
static const std::string ANR_DIR = "/data/anr";
static const std::string ANR_DIR = "/data/anr/";
static const std::string ANR_FILE_PREFIX = "/data/anr/anr_";
static const std::string ANR_FILE_PREFIX = "anr_";


struct DumpData {
struct DumpData {
    std::string name;
    std::string name;
@@ -97,7 +97,7 @@ struct DumpData {
};
};


static bool operator<(const DumpData& d1, const DumpData& d2) {
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;
static std::unique_ptr<std::vector<DumpData>> tombstone_data;
@@ -134,44 +134,49 @@ static constexpr char PROPERTY_EXTRA_DESCRIPTION[] = "dumpstate.options.descript
static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();


/*
/*
 * Returns a vector of dump fds under |file_prefix|. The returned vector
 * Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
 * is sorted by the mtimes of the dumps. If |limit_by_mtime| is set, the
 * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
 * vector only contains files that were written in the last 30 minutes.
 * 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;
    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>());
    std::unique_ptr<std::vector<DumpData>> dump_data(new std::vector<DumpData>());
    while (true) {
    std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir);
        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));
            }


            break;
    struct dirent* entry = nullptr;
    while ((entry = readdir(dump_dir.get()))) {
        if (entry->d_type != DT_REG) {
            continue;
        }
        }


        struct stat st;
        const std::string base_name(entry->d_name);
        if (fstat(fd, &st) == -1) {
        if (base_name.find(file_prefix) != 0) {
            MYLOGW("Unable to stat dump file: %s %s\n", name.c_str(), strerror(errno));
            continue;
            continue;
        }
        }


        if (!S_ISREG(st.st_mode)) {
        const std::string abs_path = dir_path + base_name;
            MYLOGW("Unexpected mode for dump file: %s %x\n", name.c_str(), st.st_mode);
        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;
            continue;
        }
        }


        if (limit_by_mtime && st.st_mtime >= thirty_minutes_ago) {
        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;
            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);
        dump_data->push_back(data);
    }
    }
@@ -181,12 +186,13 @@ static std::vector<DumpData>* GetDumpFds(const std::string& file_prefix, bool li
    return dump_data.release();
    return dump_data.release();
}
}


static bool AddDumps(const std::vector<DumpData>& dump_list, const char* type_name,
static bool AddDumps(const std::vector<DumpData>::const_iterator start,
                     const bool add_to_zip) {
                     const std::vector<DumpData>::const_iterator end,
                     const char* type_name, const bool add_to_zip) {
    bool dumped = false;
    bool dumped = false;
    for (size_t i = 0; i < dump_list.size(); i++) {
    for (auto it = start; it != end; ++it) {
        const std::string& name = dump_list[i].name;
        const std::string& name = it->name;
        const int fd = dump_list[i].fd;
        const int fd = it->fd;
        dumped = true;
        dumped = true;
        if (ds.IsZipping() && add_to_zip) {
        if (ds.IsZipping() && add_to_zip) {
            if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
            if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
@@ -991,8 +997,16 @@ static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_
        }
        }
    }
    }


    const bool anr_traces_dumped = AddDumps(*anr_data, "ANR", add_to_zip);
    // Add a specific message for the first ANR Dump.
    if (!anr_traces_dumped) {
    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());
        printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
    }
    }
}
}
@@ -1011,9 +1025,10 @@ static void AddAnrTraceFiles() {
    if (anr_traces_dir.empty()) {
    if (anr_traces_dir.empty()) {
        anr_traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
        anr_traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
        if (!anr_traces_file.empty()) {
        if (!anr_traces_file.empty()) {
            is_global_trace_file = true;
            anr_traces_dir = dirname(anr_traces_file.c_str());
            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,
    // We have neither configured a global trace file nor a trace directory,
@@ -1117,7 +1132,8 @@ static void dumpstate() {


    // NOTE: tombstones are always added as separate entries in the zip archive
    // NOTE: tombstones are always added as separate entries in the zip archive
    // and are not interspersed with the main report.
    // 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) {
    if (!tombstones_dumped) {
        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR.c_str());
        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR.c_str());
    }
    }
@@ -1798,8 +1814,8 @@ int main(int argc, char *argv[]) {
        dump_traces_path = dump_traces();
        dump_traces_path = dump_traces();


        /* Run some operations that require root. */
        /* Run some operations that require root. */
        tombstone_data.reset(GetDumpFds(TOMBSTONE_FILE_PREFIX, !ds.IsZipping()));
        tombstone_data.reset(GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()));
        anr_data.reset(GetDumpFds(ANR_FILE_PREFIX, !ds.IsZipping()));
        anr_data.reset(GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()));


        ds.AddDir(RECOVERY_DIR, true);
        ds.AddDir(RECOVERY_DIR, true);
        ds.AddDir(RECOVERY_DATA_DIR, true);
        ds.AddDir(RECOVERY_DATA_DIR, true);