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

Commit 8f00ed03 authored by Felipe Leme's avatar Felipe Leme
Browse files

Fixed buffering issues.

- Replace all printf(...) and puts(...) calls to dprintf(STDOUT_FILENO).
- Replace all fflush(stdout) calls to fsync(STDOUT_FILENO).
- Added duration reporters on for_each functions.

Change-Id: If14b4d14ffc23bfead2ca8ad23b7b3027de01156
Fixes: 33128765
Test: manual verification
Test: dumpstate_test passes
parent d46a65c3
Loading
Loading
Loading
Loading
+60 −60
Original line number Diff line number Diff line
@@ -602,7 +602,7 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int f
        path += sizeof(mmcblk0) - 1;
    }

    printf("%s: %s\n", path, buffer);
    dprintf(STDOUT_FILENO, "%s: %s\n", path, buffer);
    free(buffer);

    if (fields[__STAT_IO_TICKS]) {
@@ -639,10 +639,10 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int f
                                 / fields[__STAT_IO_TICKS];

        if (!write_perf && !write_ios) {
            printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n",
                   path, read_perf, read_ios, queue);
            dprintf(STDOUT_FILENO, "%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n", path, read_perf,
                    read_ios, queue);
        } else {
            printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
            dprintf(STDOUT_FILENO, "%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
                    path, read_perf, read_ios, write_perf, write_ios, queue);
        }

@@ -673,27 +673,27 @@ void Dumpstate::PrintHeader() const {
    network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));

    printf("========================================================\n");
    printf("== dumpstate: %s\n", date);
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== dumpstate: %s\n", date);
    dprintf(STDOUT_FILENO, "========================================================\n");

    printf("\n");
    printf("Build: %s\n", build.c_str());
    dprintf(STDOUT_FILENO, "\n");
    dprintf(STDOUT_FILENO, "Build: %s\n", build.c_str());
    // NOTE: fingerprint entry format is important for other tools.
    printf("Build fingerprint: '%s'\n", fingerprint.c_str());
    printf("Bootloader: %s\n", bootloader.c_str());
    printf("Radio: %s\n", radio.c_str());
    printf("Network: %s\n", network.c_str());
    dprintf(STDOUT_FILENO, "Build fingerprint: '%s'\n", fingerprint.c_str());
    dprintf(STDOUT_FILENO, "Bootloader: %s\n", bootloader.c_str());
    dprintf(STDOUT_FILENO, "Radio: %s\n", radio.c_str());
    dprintf(STDOUT_FILENO, "Network: %s\n", network.c_str());

    printf("Kernel: ");
    fflush(stdout);
    dprintf(STDOUT_FILENO, "Kernel: ");
    fsync(STDOUT_FILENO);
    DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
    printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
    printf("Bugreport format version: %s\n", version_.c_str());
    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
           PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
    printf("\n");
    fflush(stdout);
    dprintf(STDOUT_FILENO, "Command line: %s\n", strtok(cmdline_buf, "\n"));
    dprintf(STDOUT_FILENO, "Bugreport format version: %s\n", version_.c_str());
    dprintf(STDOUT_FILENO, "Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n",
            id_, pid_, PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
    dprintf(STDOUT_FILENO, "\n");
    fsync(STDOUT_FILENO);
}

// List of file extensions that can cause a zip file attachment to be rejected by some email
@@ -857,13 +857,13 @@ static void AddAnrTraceFiles() {
           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");
        dprintf(STDOUT_FILENO, "*** 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));
            dprintf(STDOUT_FILENO, "*** NO ANR VM TRACES FILE (%s): %s\n\n",
                    anr_traces_path.c_str(), strerror(errno));
        } else {
            if (add_to_zip) {
                if (!already_dumped) {
@@ -1003,7 +1003,7 @@ static void dumpstate() {
        }
    }
    if (!dumped) {
        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
        dprintf(STDOUT_FILENO, "*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
    }

    DumpFile("NETWORK DEV INFO", "/proc/net/dev");
@@ -1076,18 +1076,18 @@ static void dumpstate() {

    RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});

    printf("------ BACKLIGHTS ------\n");
    printf("LCD brightness=");
    dprintf(STDOUT_FILENO, "------ BACKLIGHTS ------\n");
    dprintf(STDOUT_FILENO, "LCD brightness=");
    DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
    printf("Button brightness=");
    dprintf(STDOUT_FILENO, "Button brightness=");
    DumpFile("", "/sys/class/leds/button-backlight/brightness");
    printf("Keyboard brightness=");
    dprintf(STDOUT_FILENO, "Keyboard brightness=");
    DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
    printf("ALS mode=");
    dprintf(STDOUT_FILENO, "ALS mode=");
    DumpFile("", "/sys/class/leds/lcd-backlight/als");
    printf("LCD driver registers:\n");
    dprintf(STDOUT_FILENO, "LCD driver registers:\n");
    DumpFile("", "/sys/class/leds/lcd-backlight/registers");
    printf("\n");
    dprintf(STDOUT_FILENO, "\n");

    /* Binder state is expensive to look at as it uses a lot of memory. */
    DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
@@ -1112,16 +1112,16 @@ static void dumpstate() {
        RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
    }

    printf("========================================================\n");
    printf("== Android Framework Services\n");
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Android Framework Services\n");
    dprintf(STDOUT_FILENO, "========================================================\n");

    RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
               10);

    printf("========================================================\n");
    printf("== Checkins\n");
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Checkins\n");
    dprintf(STDOUT_FILENO, "========================================================\n");

    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
    RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
@@ -1130,21 +1130,21 @@ static void dumpstate() {
    RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
    RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});

    printf("========================================================\n");
    printf("== Running Application Activities\n");
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Running Application Activities\n");
    dprintf(STDOUT_FILENO, "========================================================\n");

    RunDumpsys("APP ACTIVITIES", {"activity", "all"});

    printf("========================================================\n");
    printf("== Running Application Services\n");
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Running Application Services\n");
    dprintf(STDOUT_FILENO, "========================================================\n");

    RunDumpsys("APP SERVICES", {"activity", "service", "all"});

    printf("========================================================\n");
    printf("== Running Application Providers\n");
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Running Application Providers\n");
    dprintf(STDOUT_FILENO, "========================================================\n");

    RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});

@@ -1153,20 +1153,20 @@ static void dumpstate() {
    // collected.
    DumpModemLogs();

    printf("========================================================\n");
    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
    printf("========================================================\n");
    printf("== dumpstate: done (id %d)\n", ds.id_);
    printf("========================================================\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_,
            ds.progress_->Get(), ds.progress_->GetMax(), ds.progress_->GetInitialMax());
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== dumpstate: done (id %d)\n", ds.id_);
    dprintf(STDOUT_FILENO, "========================================================\n");
}

void Dumpstate::DumpstateBoard() {
    DurationReporter duration_reporter("dumpstate_board()");
    printf("========================================================\n");
    printf("== Board\n");
    printf("========================================================\n");
    fflush(stdout);
    dprintf(STDOUT_FILENO, "========================================================\n");
    dprintf(STDOUT_FILENO, "== Board\n");
    dprintf(STDOUT_FILENO, "========================================================\n");
    fsync(STDOUT_FILENO);

    android::sp<android::hardware::dumpstate::V1_0::IDumpstateDevice> dumpstate_device(
        android::hardware::dumpstate::V1_0::IDumpstateDevice::getService("DumpstateDevice"));
@@ -1204,8 +1204,8 @@ void Dumpstate::DumpstateBoard() {
    dumpstate_device->dumpstateBoard(handle);

    AddZipEntry("dumpstate-board.txt", path);
    printf("*** See dumpstate-board.txt entry ***\n");
    fflush(stdout);
    dprintf(STDOUT_FILENO, "*** See dumpstate-board.txt entry ***\n");
    fsync(STDOUT_FILENO);

    native_handle_close(handle);
    native_handle_delete(handle);
+66 −61
Original line number Diff line number Diff line
@@ -97,9 +97,9 @@ DurationReporter::~DurationReporter() {
            MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
        } else {
            // Use "Yoda grammar" to make it easier to grep|sort sections.
            printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC,
                   title_.c_str());
            fflush(stdout);
            dprintf(STDOUT_FILENO, "------ %.3fs was the duration of '%s' ------\n",
                    (float)elapsed / NANOS_PER_SEC, title_.c_str());
            fsync(STDOUT_FILENO);
        }
    }
}
@@ -227,16 +227,19 @@ void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) {
}

void for_each_userid(void (*func)(int), const char *header) {
    std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf(
                                                                    "for_each_userid(%s)", header);
    DurationReporter duration_reporter(title);
    if (PropertiesHelper::IsDryRun()) return;

    DIR *d;
    struct dirent *de;

    if (header) printf("\n------ %s ------\n", header);
    if (header) dprintf(STDOUT_FILENO, "\n------ %s ------\n", header);
    func(0);

    if (!(d = opendir("/data/system/users"))) {
        printf("Failed to open /data/system/users (%s)\n", strerror(errno));
        dprintf(STDOUT_FILENO, "Failed to open /data/system/users (%s)\n", strerror(errno));
        return;
    }

@@ -256,11 +259,11 @@ static void __for_each_pid(void (*helper)(int, const char *, void *), const char
    struct dirent *de;

    if (!(d = opendir("/proc"))) {
        printf("Failed to open /proc (%s)\n", strerror(errno));
        dprintf(STDOUT_FILENO, "Failed to open /proc (%s)\n", strerror(errno));
        return;
    }

    if (header) printf("\n------ %s ------\n", header);
    if (header) dprintf(STDOUT_FILENO, "\n------ %s ------\n", header);
    while ((de = readdir(d))) {
        int pid;
        int fd;
@@ -310,6 +313,9 @@ static void for_each_pid_helper(int pid, const char *cmdline, void *arg) {
}

void for_each_pid(for_each_pid_func func, const char *header) {
    std::string title = header == nullptr ? "for_each_pid"
                                          : android::base::StringPrintf("for_each_pid(%s)", header);
    DurationReporter duration_reporter(title);
    if (PropertiesHelper::IsDryRun()) return;

    __for_each_pid(for_each_pid_helper, header, (void *) func);
@@ -324,7 +330,7 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
    snprintf(taskpath, sizeof(taskpath), "/proc/%d/task", pid);

    if (!(d = opendir(taskpath))) {
        printf("Failed to open %s (%s)\n", taskpath, strerror(errno));
        dprintf(STDOUT_FILENO, "Failed to open %s (%s)\n", taskpath, strerror(errno));
        return;
    }

@@ -364,6 +370,9 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
}

void for_each_tid(for_each_tid_func func, const char *header) {
    std::string title = header == nullptr ? "for_each_tid"
                                          : android::base::StringPrintf("for_each_tid(%s)", header);
    DurationReporter duration_reporter(title);
    if (PropertiesHelper::IsDryRun()) return;

    __for_each_pid(for_each_tid_helper, header, (void *) func);
@@ -381,7 +390,7 @@ void show_wchan(int pid, int tid, const char *name) {

    snprintf(path, sizeof(path), "/proc/%d/wchan", tid);
    if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) {
        printf("Failed to open '%s' (%s)\n", path, strerror(errno));
        dprintf(STDOUT_FILENO, "Failed to open '%s' (%s)\n", path, strerror(errno));
        return;
    }

@@ -390,14 +399,14 @@ void show_wchan(int pid, int tid, const char *name) {
    close(fd);

    if (ret < 0) {
        printf("Failed to read '%s' (%s)\n", path, strerror(save_errno));
        dprintf(STDOUT_FILENO, "Failed to read '%s' (%s)\n", path, strerror(save_errno));
        return;
    }

    snprintf(name_buffer, sizeof(name_buffer), "%*s%s",
             pid == tid ? 0 : 3, "", name);

    printf("%-7d %-32s %s\n", tid, name_buffer, buffer);
    dprintf(STDOUT_FILENO, "%-7d %-32s %s\n", tid, name_buffer, buffer);

    return;
}
@@ -447,7 +456,7 @@ void show_showtime(int pid, const char *name) {

    snprintf(path, sizeof(path), "/proc/%d/stat", pid);
    if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) {
        printf("Failed to open '%s' (%s)\n", path, strerror(errno));
        dprintf(STDOUT_FILENO, "Failed to open '%s' (%s)\n", path, strerror(errno));
        return;
    }

@@ -456,7 +465,7 @@ void show_showtime(int pid, const char *name) {
    close(fd);

    if (ret < 0) {
        printf("Failed to read '%s' (%s)\n", path, strerror(save_errno));
        dprintf(STDOUT_FILENO, "Failed to read '%s' (%s)\n", path, strerror(save_errno));
        return;
    }

@@ -495,7 +504,7 @@ void show_showtime(int pid, const char *name) {
    if (iotime) {
        snprdec(buffer, sizeof(buffer), 79, permille);
    }
    puts(buffer); // adds a trailing newline
    dprintf(STDOUT_FILENO, "%s\n", buffer);

    return;
}
@@ -503,29 +512,29 @@ void show_showtime(int pid, const char *name) {
void do_dmesg() {
    const char *title = "KERNEL LOG (dmesg)";
    DurationReporter duration_reporter(title);
    printf("------ %s ------\n", title);
    dprintf(STDOUT_FILENO, "------ %s ------\n", title);

    if (PropertiesHelper::IsDryRun()) return;

    /* Get size of kernel buffer */
    int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
    if (size <= 0) {
        printf("Unexpected klogctl return value: %d\n\n", size);
        dprintf(STDOUT_FILENO, "Unexpected klogctl return value: %d\n\n", size);
        return;
    }
    char *buf = (char *) malloc(size + 1);
    if (buf == NULL) {
        printf("memory allocation failed\n\n");
        dprintf(STDOUT_FILENO, "memory allocation failed\n\n");
        return;
    }
    int retval = klogctl(KLOG_READ_ALL, buf, size);
    if (retval < 0) {
        printf("klogctl failure\n\n");
        dprintf(STDOUT_FILENO, "klogctl failure\n\n");
        free(buf);
        return;
    }
    buf[retval] = '\0';
    printf("%s\n\n", buf);
    dprintf(STDOUT_FILENO, "%s\n\n", buf);
    free(buf);
    return;
}
@@ -546,7 +555,7 @@ int Dumpstate::DumpFile(const std::string& title, const std::string& path) {

    UpdateProgress(WEIGHT_FILE);

    fflush(stdout);
    fsync(STDOUT_FILENO);

    return status;
}
@@ -588,7 +597,7 @@ int dump_files(const std::string& title, const char* dir, bool (*skip)(const cha
    int fd, retval = 0;

    if (!title.empty()) {
        printf("------ %s (%s) ------\n", title.c_str(), dir);
        dprintf(STDOUT_FILENO, "------ %s (%s) ------\n", title.c_str(), dir);
    }
    if (PropertiesHelper::IsDryRun()) return 0;

@@ -630,14 +639,14 @@ int dump_files(const std::string& title, const char* dir, bool (*skip)(const cha
        fd = TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
        if (fd < 0) {
            retval = fd;
            printf("*** %s: %s\n", newpath, strerror(errno));
            dprintf(STDOUT_FILENO, "*** %s: %s\n", newpath, strerror(errno));
            continue;
        }
        (*dump_from_fd)(NULL, newpath, fd);
    }
    closedir(dirp);
    if (!title.empty()) {
        printf("\n");
        dprintf(STDOUT_FILENO, "\n");
    }
    return retval;
}
@@ -651,11 +660,12 @@ int dump_file_from_fd(const char *title, const char *path, int fd) {

    int flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
        printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
        dprintf(STDOUT_FILENO, "*** %s: failed to get flags on fd %d: %s\n", path, fd,
                strerror(errno));
        close(fd);
        return -1;
    } else if (!(flags & O_NONBLOCK)) {
        printf("*** %s: fd must have O_NONBLOCK set.\n", path);
        dprintf(STDOUT_FILENO, "*** %s: fd must have O_NONBLOCK set.\n", path);
        close(fd);
        return -1;
    }
@@ -674,7 +684,7 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin
     * Ideally, it should use a options.EstimatedDuration() instead...*/
    UpdateProgress(options.Timeout());

    fflush(stdout);
    fsync(STDOUT_FILENO);

    return status;
}
@@ -720,7 +730,7 @@ static int compare_prop(const void *a, const void *b) {
void print_properties() {
    const char* title = "SYSTEM PROPERTIES";
    DurationReporter duration_reporter(title);
    printf("------ %s ------\n", title);
    dprintf(STDOUT_FILENO, "------ %s ------\n", title);
    if (PropertiesHelper::IsDryRun()) return;
    size_t i;
    num_props = 0;
@@ -731,7 +741,7 @@ void print_properties() {
        fputs(props[i], stdout);
        free(props[i]);
    }
    printf("\n");
    dprintf(STDOUT_FILENO, "\n");
}

int open_socket(const char *service) {
@@ -985,7 +995,7 @@ void dump_route_tables() {
    ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
    FILE* fp = fopen(RT_TABLES_PATH, "re");
    if (!fp) {
        printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
        dprintf(STDOUT_FILENO, "*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
        return;
    }
    char table[16];
@@ -1092,44 +1102,42 @@ void dump_emmc_ecsd(const char *ext_csd_path) {
        return;
    }

    printf("------ %s Extended CSD ------\n", ext_csd_path);
    dprintf(STDOUT_FILENO, "------ %s Extended CSD ------\n", ext_csd_path);

    if (buffer.length() < (EXT_CSD_REV + sizeof(hex))) {
        printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length());
        dprintf(STDOUT_FILENO, "*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length());
        return;
    }

    int ext_csd_rev = 0;
    std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex));
    if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) {
        printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n",
               ext_csd_path, sub.c_str());
        dprintf(STDOUT_FILENO, "*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path,
                sub.c_str());
        return;
    }

    static const char *ver_str[] = {
        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
    };
    printf("rev 1.%d (MMC %s)\n",
           ext_csd_rev,
           (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
               ver_str[ext_csd_rev] :
               "Unknown");
    dprintf(STDOUT_FILENO, "rev 1.%d (MMC %s)\n", ext_csd_rev,
            (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev]
                                                                        : "Unknown");
    if (ext_csd_rev < 7) {
        printf("\n");
        dprintf(STDOUT_FILENO, "\n");
        return;
    }

    if (buffer.length() < (EXT_PRE_EOL_INFO + sizeof(hex))) {
        printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length());
        dprintf(STDOUT_FILENO, "*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length());
        return;
    }

    int ext_pre_eol_info = 0;
    sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex));
    if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) {
        printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n",
               ext_csd_path, sub.c_str());
        dprintf(STDOUT_FILENO, "*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path,
                sub.c_str());
        return;
    }

@@ -1139,11 +1147,10 @@ void dump_emmc_ecsd(const char *ext_csd_path) {
        "Warning (consumed 80% of reserve)",
        "Urgent (consumed 90% of reserve)"
    };
    printf("PRE_EOL_INFO %d (MMC %s)\n",
           ext_pre_eol_info,
           eol_str[(ext_pre_eol_info < (int)
                       (sizeof(eol_str) / sizeof(eol_str[0]))) ?
                           ext_pre_eol_info : 0]);
    dprintf(
        STDOUT_FILENO, "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info,
        eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info
                                                                                 : 0]);

    for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A;
            lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B;
@@ -1165,28 +1172,26 @@ void dump_emmc_ecsd(const char *ext_csd_path) {
        };

        if (buffer.length() < (lifetime + sizeof(hex))) {
            printf("*** %s: truncated content %zu\n", ext_csd_path, buffer.length());
            dprintf(STDOUT_FILENO, "*** %s: truncated content %zu\n", ext_csd_path, buffer.length());
            break;
        }

        ext_device_life_time_est = 0;
        sub = buffer.substr(lifetime, sizeof(hex));
        if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) {
            printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n",
            dprintf(STDOUT_FILENO, "*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n",
                    ext_csd_path,
                   (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
                              sizeof(hex)) + 'A',
                    (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
                    sub.c_str());
            continue;
        }
        printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n",
               (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
                          sizeof(hex)) + 'A',
        dprintf(STDOUT_FILENO, "DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n",
                (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
                ext_device_life_time_est,
               est_str[(ext_device_life_time_est < (int)
                           (sizeof(est_str) / sizeof(est_str[0]))) ?
                               ext_device_life_time_est : 0]);
                est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0])))
                            ? ext_device_life_time_est
                            : 0]);
    }

    printf("\n");
    dprintf(STDOUT_FILENO, "\n");
}