Bugreport分析

1,961 阅读10分钟
 aosp12.0
 framework/native/cmds/bugreport
 framework/native/cmds/bugreportz
 framework/native/cmds/dumpstate

参考 : gityuan.com/2016/06/10/… (6.0) github.com/lqktz/docum… (8.1)


bugreport

bugreport.cpp

main()

int main() {
    fprintf(stderr,
            "=============================================================================\n");
    fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n");
    fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n");
    fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n");
    fprintf(stderr, "WARNING: On the device use: bugreportz\n");
    fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n");
    fprintf(stderr,
            "=============================================================================\n\n\n");
    return 0;
}

现在的bugreport.cpp只打印警告,父目录下有bugreportz目录,应该是现在的bugreport所在


bugreportz

mian.cpp

参数处理:

int main(int argc, char* argv[]) {
    bool show_progress = false;
    bool stream_data = false;
    if (argc > 1) {
        /* parse arguments */
        int c;
        while ((c = getopt(argc, argv, "hpsv")) != -1) {
            switch (c) {
                case 'h':
                    show_usage();
                    return EXIT_SUCCESS;
                case 'p':
                    show_progress = true;
                    break;
                case 's':
                    stream_data = true;
                    break;
                case 'v':
                    show_version();
                    return EXIT_SUCCESS;
                default:
                    show_usage();
                    return EXIT_FAILURE;
            }
        }
    }

    // We don't support any non-option arguments.
    if (optind != argc) {
        show_usage();
        return EXIT_FAILURE;
    }
    ...
}

使用 getopt() 进行参数处理

参数有4个,hpsv

  • h : 打印参数列表及说明
  • p : 显示进度
  • s : 流内容传输到标准输出
  • v : 打印版本

启动服务

int main(){
    // 参数处理
    ...
    if (stream_data) {
    // 启动服务
        property_set("ctl.start", "dumpstate");
    } else {
        property_set("ctl.start", "dumpstatez");
    }
    // service 开始前 ,套接字将不可使用
    int s = -1;
    for (int i = 0; i < 20; i++) {
        s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
        if (s >= 0) break;
        // Try again in 1 second.
        sleep(1);
    }
    if (s == -1) {
        printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    //当10分钟没有任何数据可读,则超时停止读取并退出。
    //dumpstate服务中不存在大于1分钟的timetout,因而不可预见的超时的情况下留有很大的回旋余地。
    struct timeval tv;
    tv.tv_sec = 10 * 60;
    tv.tv_usec = 0;
    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
        fprintf(stderr,
                "WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n",
                strerror(errno));
    }

    int ret;
    if (stream_data) {
        ret = bugreportz_stream(s);
    } else {
        ret = bugreportz(s, show_progress);
    }

    if (close(s) == -1) {
        fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
        ret = EXIT_FAILURE;
    }
    return ret;
}

bugreportz.cpp

bugreportz(int s, bool show_progress)

int bugreportz(int s, bool show_progress) {
    std::string line;
    while (1) {
        char buffer[65536];
        // 忽略中断错误 
        // <https://zhuanlan.zhihu.com/p/411656960>
        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
        if (bytes_read == 0) {
            break;
        } else if (bytes_read == -1) {
            // EAGAIN意味着timeout,Bugreport读异常终止
            if (errno == EAGAIN) {
                errno = ETIMEDOUT;
            }
            printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
            return EXIT_FAILURE;
        }

        // 逐行写入
        for (int i = 0; i < bytes_read; i++) {
            char c = buffer[i];
            line.append(1, c);
            if (c == '\n') {
                write_line(line, show_progress);
                line.clear();
            }
        }
    }
    // 处理最后一行,以防它没有用换行符结束
    write_line(line, show_progress);
    return EXIT_SUCCESS;
}

bugreportz_stream(int s)

int bugreportz_stream(int s) {
    while (1) {
        char buffer[65536];
        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
        if (bytes_read == 0) {
            break;
        } else if (bytes_read == -1) {
            // EAGAIN意味着timeout,Bugreport读异常终止
            if (errno == EAGAIN) {
                errno = ETIMEDOUT;
            }
            printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
            return EXIT_FAILURE;
        }

        if (!android::base::WriteFully(android::base::borrowed_fd(STDOUT_FILENO), buffer,
                                       bytes_read)) {
            printf("Failed to write data to stdout: trying to send %zd bytes (%s)\n", bytes_read,
                   strerror(errno));
            return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}

write_line(const std::string& line, bool show_progress)

static void write_line(const std::string& line, bool show_progress) {
    if (line.empty()) return;
    //当没有使用-p选项调用时,它必须跳过开始和进度行,否则它将破坏adb(它期望的是OK或FAIL)。
    //android::base::StartsWith : 如果line的开头是以'PROGRESS_PREFIX' 或 'BEGIN_PREFIX'开头
    if (!show_progress && (android::base::StartsWith(line, PROGRESS_PREFIX) ||
                           android::base::StartsWith(line, BEGIN_PREFIX)))
        return;

    android::base::WriteStringToFd(line, STDOUT_FILENO);
}

property_set(“ctl.start”, “dumpstate”)会触发init进程,来fork进程/system/bin/dumpstate, 作为dumpstate服务的进程. Bugreport再通过socket建立于dumpstate的通信,这个过程会尝试20次socket连接建立直到成功连接。 在socket通道中如果持续10分钟没有任何数据可读,则超时停止读取并退出。由于dumpstate服务中不存在大于1分钟的timetout,因而不可预见的超时的情况下留有很大的回旋余地。

当从socket读取到数据后,写入到标准时输出或者重定向到文件。可见bugreport数据的来源都是dumpstate服务,那么接下来去看看dumpstate服务的工作。


dumpstate

main.cpp

bool ShouldStartServiceAndWait(int argc, char* argv[]) {
    bool do_wait = false;
    int c;
    // Keep flags in sync with Dumpstate::DumpOptions::Initialize.
    while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1 && !do_wait) {
        switch (c) {
            case 'w':
                do_wait = true;
                break;
            default:
                // Ignore all other options
                break;
        }
    }

    // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to
    // parse bugreport options.
    optind = 1;
    return do_wait;
}

int main(int argc, char* argv[]) {
    //参数中带有 w 会进入此
    if (ShouldStartServiceAndWait(argc, argv)) {
        int ret;
        if ((ret = android::os::DumpstateService::Start()) != android::OK) {
            MYLOGE("Unable to start 'dumpstate' service: %d", ret);
            exit(1);
        }
        MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");

        // Waits forever for an incoming connection.
        // TODO(b/111441001): should this time out?
        android::IPCThreadState::self()->joinThreadPool();
        return 0;
    } else {
        // 一般情况下不会有什么影响
        return run_main(argc, argv);
    }
}

mian()函数中只做一件事,判断是否带有参数 -w ,如果没有w参数就执行 run_main() 函数

dumpstate.cpp

run_main()

int run_main(int argc, char* argv[]) {
    Dumpstate::RunStatus status = ds.ParseCommandlineAndRun(argc, argv);

    switch (status) {
        case Dumpstate::RunStatus::OK:
            exit(0);
        case Dumpstate::RunStatus::HELP:
            ShowUsage();
            exit(0);
        case Dumpstate::RunStatus::INVALID_INPUT:
            fprintf(stderr, "Invalid combination of args\n");
            ShowUsage();
            exit(1);
        case Dumpstate::RunStatus::ERROR:
            FALLTHROUGH_INTENDED;
        case Dumpstate::RunStatus::USER_CONSENT_DENIED:
            FALLTHROUGH_INTENDED;
        case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
            exit(2);
    }
}

首先看ds

static Dumpstate& ds = Dumpstate::GetInstance();

Dumpstate& Dumpstate::GetInstance() {
    // 构造 使用了 open_socket
    static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT));
    return singleton_;
}

Dumpstate::Dumpstate(const std::string& version)
    : pid_(getpid()),
      options_(new Dumpstate::DumpOptions()),
      last_reported_percent_progress_(0),
      version_(version),
      now_(time(nullptr)),
      open_socket_fn_(open_socket) {
}

在构造函数中使用到了open_socket

static int open_socket(const char* service) {
    // 获取 UNIX 域套接字 FD
    int s = android_get_control_socket(service);
    if (s < 0) {
        MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
        return -1;
    }
    ////fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
    fcntl(s, F_SETFD, FD_CLOEXEC);

    //将losk设置为0以确保队列大小最小。在Linux中,由于最小队列将为1,因此如果其他客户端已经调用了connect(),并且连接请求未被接受,则connect()将被阻止。
    if (listen(s, 0) < 0) {
        MYLOGE("listen(control socket): %s\n", strerror(errno));
        return -1;
    }

    struct sockaddr addr;
    socklen_t alen = sizeof(addr);
    int fd = accept4(s, &addr, &alen, SOCK_CLOEXEC);

    // 在accept()后立即关闭套接字,以确保客户端connect()在其他服务使用套接字时连接会出现错误。接受和接近之间仍然存在竞态条件的可能性,但没有办法原子接近接受。
    close(s);

    if (fd < 0) {
        MYLOGE("accept(control socket): %s\n", strerror(errno));
        return -1;
    }

    return fd;
}

进入函数 ds.ParseCommandlineAndRun()

ParseCommandlineAndRun()

//解析命令行并运行
Dumpstate::RunStatus Dumpstate::ParseCommandlineAndRun(int argc, char* argv[]) {
    // make_unique 智能指针
    std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
    // 处理参数
    Dumpstate::RunStatus status = options->Initialize(argc, argv);
    if (status == Dumpstate::RunStatus::OK) {
        //将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义
        //https://blog.csdn.net/p942005405/article/details/84644069
        SetOptions(std::move(options));
        // 当直接运行dumpstate二进制文件时,不期望将输出写入任何外部文件描述符。
        assert(options_->bugreport_fd.get() == -1);

        // calling_uid和calling_package是为了让用户同意与应用程序共享错误报告;它们在这里无关紧要,因为错误报告是通过命令行触发的。在调用Run()之前更新上次ID。
        Initialize();
        status = Run(-1 /* calling_uid */, "" /* calling_package */);
    }
    return status;
}

Dumpstate::DumpOptions::Initialize()

处理参数

Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
    RunStatus status = RunStatus::OK;
    int c;
    while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) {
        switch (c) {
            // clang-format off
            case 'o': out_dir = optarg;              break;
            case 's': stream_to_socket = true;       break;
            case 'S': progress_updates_to_socket = true;    break;
            case 'v': show_header_only = true;       break;
            case 'q': do_vibrate = false;            break;
            case 'p': do_screenshot = true;          break;
            case 'P': do_progress_updates = true;    break;
            case 'R': is_remote_mode = true;         break;
            case 'L': limited_only = true;           break;
            case 'V':
            case 'd':
            case 'z':
                // 兼容性无操作
                break;
            case 'w':
                // This was already processed
                break;
            case 'h':
                status = RunStatus::HELP;
                break;
            default:
                fprintf(stderr, "Invalid option: %c\n", c);
                status = RunStatus::INVALID_INPUT;
                break;
                // clang-format on
        }
    }

    for (int i = 0; i < argc; i++) {
        args += argv[i];
        if (i < argc - 1) {
            args += " ";
        }
    }

    // Reset next index used by getopt so this can be called multiple times, for eg, in tests.
    optind = 1;

    return status;
}

Initialize()

void Dumpstate::Initialize() {
    /* gets the sequential id 获取顺序id */  
    uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
    id_ = ++last_id;
    // 设置顺序id last_id ++
    android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
}

Run()

Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
    Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
    if (listener_ != nullptr) {
        switch (status) {
            case Dumpstate::RunStatus::OK:
                listener_->onFinished();
                break;
            case Dumpstate::RunStatus::HELP:
                break;
            case Dumpstate::RunStatus::INVALID_INPUT:
                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
                break;
            case Dumpstate::RunStatus::ERROR:
                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
                break;
            case Dumpstate::RunStatus::USER_CONSENT_DENIED:
                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT);
                break;
            case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
                break;
        }
    }
    return status;
}

RunInternal()

Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
                                            const std::string& calling_package) {
    DurationReporter duration_reporter("RUN INTERNAL", /* logcat_only = */true);
    LogDumpOptions(*options_);
    if (!options_->ValidateOptions()) {
        MYLOGE("Invalid options specified\n");
        return RunStatus::INVALID_INPUT;
    }
    /* set as high priority, and protect from OOM killer 设置为高优先级,并保护免受OOM杀死*/
    setpriority(PRIO_PROCESS, 0, -20);


    FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
    if (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);
        }
    }


    if (version_ == VERSION_DEFAULT) {
        version_ = VERSION_CURRENT;
    }


    if (version_ != VERSION_CURRENT && version_ != VERSION_SPLIT_ANR) {
        MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
               version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
               VERSION_SPLIT_ANR.c_str());
        return RunStatus::INVALID_INPUT;
    }

    // 只打印头  dumpstate -v
    if (options_->show_header_only) {
        // 打印头部内容
        PrintHeader();
        return RunStatus::OK;
    }


    MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
            calling_uid, calling_package.c_str());


    // TODO: temporarily set progress until it's part of the Dumpstate constructor
    std::string stats_path =
        android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str());
    progress_.reset(new Progress(stats_path));


    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");
    }
    // 设置一个函数来处理信号
    // SIGPIPE : 当一个进程向某个已收到RST的套接字执行写操作时,内核向该进程发送SIGPIPE信号
    register_sig_handler();
    // 判断 dumpstate.dry_run 是否为false
    if (PropertiesHelper::IsDryRun()) {
        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
    }


    MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
           id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());


    do_early_screenshot_ = options_->do_progress_updates;


    // If we are going to use a socket, do it as early as possible
    // to avoid timeouts from bugreport.
    if (options_->stream_to_socket || options_->progress_updates_to_socket) {
        MYLOGD("Opening control socket\n");
        // open_socket("dumpstate")
        control_socket_fd_ = open_socket_fn_("dumpstate");
        if (control_socket_fd_ == -1) {
            return ERROR;
        }
        if (options_->progress_updates_to_socket) {
            options_->do_progress_updates = 1;
        }
    }
    // 准备写入文件
    if (!PrepareToWriteToFile()) {
        return ERROR;
    }


    // Interactive, wear & telephony modes are default to true.
    // and may enable from cli option or when using control socket
    if (options_->do_progress_updates) {
        // clang-format off
        std::vector<std::string> am_args = {
                "--receiver-permission", "android.permission.DUMP",
        };
        // clang-format on
        // Send STARTED broadcast for apps that listen to bugreport generation events
        // 发送状态
        SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
        if (options_->progress_updates_to_socket) {
            dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
        }
    }


    /* read /proc/cmdline before dropping root */
    // cmdline C++命令行解释器
    FILE *cmdline = fopen("/proc/cmdline", "re");
    if (cmdline) {
        fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
        fclose(cmdline);
    }


    if (options_->do_vibrate) {
        Vibrate(150);
    }


    if (zip_file != nullptr) {
        // chown 改变指定文件的所有者
        if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) {
            MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(),
                    strerror(errno));
        }
    }


    int dup_stdout_fd;
    int dup_stderr_fd;
    // Redirect stderr to log_path_ for debugging.
    // 忽略系统中断造成的错误
    TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
    // 创建文件夹 重定向到文件
    if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) {
        return ERROR;
    }
    if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) {
        MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(),
                strerror(errno));
    }

    // Redirect stdout to tmp_path_. This is the main bugreport entry and will be
    // moved into zip file later, if zipping.
    TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
    // TODO: why not write to a file instead of stdout to overcome this problem?
    /* TODO: rather than generating a text file now and zipping it later,
        it would be more efficient to redirect stdout to the zip entry
        directly, but the libziparchive doesn't support that option yet. */
    if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) {
        return ERROR;
    }
    if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
        MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
                tmp_path_.c_str(), strerror(errno));
    }

    // Don't buffer stdout 定义流stream应如何缓冲(不缓冲)
    setvbuf(stdout, nullptr, _IONBF, 0);

    // Enable the parallel run if the client requests to output to a file.
    EnableParallelRunIfNeeded();
    // Using scope guard to make sure the dump pool can be shut down correctly.
    auto scope_guard_to_shutdown_pool = android::base::make_scope_guard([=]() {
        // 关闭dumppool
        ShutdownDumpPool();
    });

    // NOTE: there should be no stdout output until now, otherwise it would break the header.
    // In particular, DurationReport objects should be created passing 'title, NULL', so their
    // duration is logged into MYLOG instead.
    PrintHeader();

    bool is_dumpstate_restricted = options_->telephony_only
                                   || options_->wifi_only
                                   || options_->limited_only;
    if (!is_dumpstate_restricted) {
        // Invoke critical dumpsys first to preserve system state, before doing anything else.
        RunDumpsysCritical();
    }
    MaybeTakeEarlyScreenshot();

    if (!is_dumpstate_restricted) {
        // Snapshot the system trace now (if running) to avoid that dumpstate's
        // own activity pushes out interesting data from the trace ring buffer.
        // The trace file is added to the zip by MaybeAddSystemTraceToZip().
        MaybeSnapshotSystemTrace();

        // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later
        // from WMTRACE_DATA_DIR.
        MaybeSnapshotWinTrace();
    }
    onUiIntensiveBugreportDumpsFinished(calling_uid);
    MaybeCheckUserConsent(calling_uid, calling_package);
    if (options_->telephony_only) {
        // ->
        DumpstateTelephonyOnly(calling_package);
    } else if (options_->wifi_only) {
        DumpstateWifiOnly();
    } else if (options_->limited_only) {
        DumpstateLimitedOnly();
    } else {
        // Dump state for the default case. This also drops root.
        RunStatus s = DumpstateDefaultAfterCritical();
        if (s != RunStatus::OK) {
            if (s == RunStatus::USER_CONSENT_DENIED) {
                HandleUserConsentDenied();
            }
            return s;
        }
    }

    /* close output if needed */
    TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));

    // Zip the (now complete) .tmp file within the internal directory.
    FinalizeFile();

    // Share the final file with the caller if the user has consented or Shell is the caller.
    Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
    if (CalledByApi()) {
        // TODO
        status = CopyBugreportIfUserConsented(calling_uid);
        if (status != Dumpstate::RunStatus::OK &&
            status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
            // Do an early return if there were errors. We make an exception for consent
            // timing out because it's possible the user got distracted. In this case the
            // bugreport is not shared but made available for manual retrieval.
            MYLOGI("User denied consent. Returning\n");
            return status;
        }
        if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
            MYLOGI(
                "Did not receive user consent yet."
                " Will not copy the bugreport artifacts to caller.\n");
            const String16 incidentcompanion("incidentcompanion");
            sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
            if (ics != nullptr) {
                MYLOGD("Canceling user consent request via incidentcompanion service\n");
                android::interface_cast<android::os::IIncidentCompanion>(ics)->cancelAuthorization(
                        consent_callback_.get());
            } else {
                MYLOGD("Unable to cancel user consent; incidentcompanion service unavailable\n");
            }
        }
    }

    /* vibrate a few but shortly times to let user know it's finished */
    if (options_->do_vibrate) {
        for (int i = 0; i < 3; i++) {
            Vibrate(75);
            usleep((75 + 50) * 1000);
        }
    }

    MYLOGD("Final progress: %d/%d (estimated %d)\n", progress_->Get(), progress_->GetMax(),
           progress_->GetInitialMax());
    progress_->Save();
    MYLOGI("done (id %d)\n", id_);

    TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));

    if (control_socket_fd_ != -1) {
        MYLOGD("Closing control socket\n");
        close(control_socket_fd_);
    }

    tombstone_data_.clear();
    anr_data_.clear();

    return (consent_callback_ != nullptr &&
            consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
               ? USER_CONSENT_TIMED_OUT
               : RunStatus::OK;
}

编译分析(.bp文件)

cc_defaults {
    name: "dumpstate_cflag_defaults",
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-missing-field-initializers",
        "-Wno-unused-variable",
        "-Wunused-parameter",
    ],
}
// 动态库 libdumpstateutil.so
cc_library_shared {
    name: "libdumpstateutil",
    defaults: ["dumpstate_cflag_defaults"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
    srcs: [
        "DumpstateInternal.cpp",
        "DumpstateUtil.cpp",
    ],
    shared_libs: [
        "libbase",
        "liblog",
    ],
    export_include_dirs: ["."],
    export_shared_lib_headers: [
        "libbase",
    ],
}
// 动态库 libdumpstateaidl.so
cc_library_shared {
    name: "libdumpstateaidl",
    defaults: ["dumpstate_cflag_defaults"],
    shared_libs: [
        "libbinder",
        "libutils",
    ],
    aidl: {
        local_include_dirs: ["binder"],
        export_aidl_headers: true,
    },
    srcs: [
        ":dumpstate_aidl",
    ],
    export_include_dirs: ["binder"],
}
// 文件组
filegroup {
    name: "dumpstate_aidl",
    srcs: [
        "binder/android/os/IDumpstateListener.aidl",
        "binder/android/os/IDumpstate.aidl",
    ],
    path: "binder",
}
// 默认模块 用于编译所有C或C模块的属性
cc_defaults {
    name: "dumpstate_defaults",
    defaults: ["dumpstate_cflag_defaults"],
    shared_libs: [
        "android.hardware.dumpstate@1.0",
        "android.hardware.dumpstate@1.1",
        "libziparchive",
        "libbase",
        "libbinder",
        "libcrypto",
        "libcutils",
        "libdebuggerd_client",
        "libdumpstateaidl",
        "libdumpstateutil",
        "libdumputils",
        "libhardware_legacy",
        "libhidlbase",
        "liblog",
        "libutils",
        "libbinderdebug",
    ],
    srcs: [
        "DumpstateService.cpp",
    ],
    static_libs: [
        "libincidentcompanion",
        "libdumpsys",
        "libserviceutils",
    ],
}
// 输出可执行文件
cc_binary {
    name: "dumpstate",
    defaults: ["dumpstate_defaults"],
    srcs: [
        "DumpPool.cpp",
        "TaskQueue.cpp",
        "dumpstate.cpp",
        "main.cpp",
    ],
    // 需要安装的其他模块
    required: [
        "atrace",
        "dmabuf_dump",
        "ip",
        "iptables",
        "librank",
        "logcat",
        "lpdump",
        "lpdumpd",
        "procrank",
        "screencap",
        "showmap",
        "ss",
        "storaged",
        "toolbox",
        "toybox",
        "vdc",
    ],
    init_rc: ["dumpstate.rc"],
}

RunCommand()

dumpstate.cppRunCommand() 调用了很多次,主要用来执行指令,在 bugreport.txt 中可以找到相应的日志,如:

RunCommand(
        "KERNEL LOG",
        {"logcat", "-b", "kernel", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
        CommandOptions::WithTimeoutInMs(timeout_ms).Build());

查找 KERNEL LOG 就可以找到

KERNEL_LOG.png 执行括号内的指令,可以正常执行,获得日志

// dumpstate.cpp
static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                      const CommandOptions& options = CommandOptions::DEFAULT,
                      bool verbose_duration = false, int out_fd = STDOUT_FILENO) {
    //STDOUT_FILENO 向屏幕输出
    return ds.RunCommand(title, full_command, options, verbose_duration, out_fd);
}


int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                          const CommandOptions& options, bool verbose_duration, int out_fd) {
    DurationReporter duration_reporter(title, false /* logcat_only */,
                                       verbose_duration, out_fd);

    int status = RunCommandToFd(out_fd, title, full_command, options);

    /* TODO: for now we're simplifying the progress calculation by using the
     * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
     * where its weight should be much higher proportionally to its timeout.
     * Ideally, it should use a options.EstimatedDuration() instead...*/
    UpdateProgress(options.Timeout());

    return status;
}
// DumpstateUtil.cpp
int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
                   const CommandOptions& options) {
    if (full_command.empty()) {
        MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str());
        return -1;
    }


    int size = full_command.size() + 1;  // null terminated
    int starting_index = 0;
    if (options.PrivilegeMode() == SU_ROOT) {
        starting_index = 2;  // "su" "root"
        size += starting_index;
    }


    std::vector<const char*> args;
    args.resize(size);
    // 切到根目录
    std::string command_string;
    if (options.PrivilegeMode() == SU_ROOT) {
        args[0] = kSuPath; // /system/xbin/su
        command_string += kSuPath;
        args[1] = "root";
        command_string += " root ";
    }
    // 拼接命令
    for (size_t i = 0; i < full_command.size(); i++) {
        args[i + starting_index] = full_command[i].data();
        command_string += args[i + starting_index];
        if (i != full_command.size() - 1) {
            command_string += " ";
        }
    }
    args[size - 1] = nullptr;


    const char* command = command_string.c_str();
// 
/** 
 * 跳过切换了root用户部分指令
Skipping '/system/xbin/su root procrank' on user build.
Skipping '/system/xbin/su root librank' on user build.
Skipping '/system/xbin/su root sh -c cat /proc/modules | cut -d' ' -f1 |     while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; done' on user build.
Skipping '/system/xbin/su root lsof' on user build.
 * */
    if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) {
        dprintf(fd, "Skipping '%s' on user build.\n", command);
        return 0;
    }


    if (!title.empty()) {
        dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
        // 等待输出
        fsync(fd);
    }


    const std::string& logging_message = options.LoggingMessage();
    if (!logging_message.empty()) {
        MYLOGI(logging_message.c_str(), command_string.c_str());
    }


    bool silent = (options.OutputMode() == REDIRECT_TO_STDERR ||
                   options.ShouldCloseAllFileDescriptorsOnExec());
    bool redirecting_to_fd = STDOUT_FILENO != fd; // 默认传入为 STDOUT_FILENO
// 获取 dumpstate.dry_run 属性值,未获取到
    if (PropertiesHelper::IsDryRun() && !options.Always()) {
        if (!title.empty()) {
            dprintf(fd, "\t(skipped on dry run)\n");
        } else if (redirecting_to_fd) {
            // There is no title, but we should still print a dry-run message
            dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
        }
        fsync(fd);
        return 0;
    }


    const char* path = args[0];


    uint64_t start = Nanotime();
    // 通过fork创建子进程
    pid_t pid = fork();


    /* handle error case  处理错误*/
    if (pid < 0) {
        if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno));
        MYLOGE("*** fork: %s\n", strerror(errno));
        return pid;
    }


    /* handle child case 执行子进程*/
    if (pid == 0) {
        if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) {
            if (!silent) {
                dprintf(fd, "*** failed to drop root before running %s: %s\n", command,
                        strerror(errno));
            }
            MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
            return -1;
        }


        if (options.ShouldCloseAllFileDescriptorsOnExec()) {
            int devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
            TEMP_FAILURE_RETRY(dup2(devnull_fd, STDIN_FILENO));
            close(devnull_fd);
            devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
            TEMP_FAILURE_RETRY(dup2(devnull_fd, STDOUT_FILENO));
            TEMP_FAILURE_RETRY(dup2(devnull_fd, STDERR_FILENO));
            close(devnull_fd);
            // This is to avoid leaking FDs that, accidentally, have not been
            // marked as O_CLOEXEC. Leaking FDs across exec can cause failures
            // when execing a process that has a SELinux auto_trans rule.
            // Here we assume that the dumpstate process didn't open more than
            // 1000 FDs. In theory we could iterate through /proc/self/fd/, but
            // doing that in a fork-safe way is too complex and not worth it
            // (opendir()/readdir() do heap allocations and take locks).
            for (int i = 0; i < 1000; i++) {
                if (i != STDIN_FILENO && i!= STDOUT_FILENO && i != STDERR_FILENO) {
                    close(i);
                }
            }
        } else if (silent) {
            // Redirects stdout to stderr
            TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
        } else if (redirecting_to_fd) {
            // Redirect stdout to fd
            TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
            close(fd);
        }


        /* make sure the child dies when dumpstate dies */
        // 确保dumpstate 结束才能关闭子进程
        prctl(PR_SET_PDEATHSIG, SIGKILL);


        /* just ignore SIGPIPE, will go down with parent's */
        struct sigaction sigact;
        memset(&sigact, 0, sizeof(sigact));
        sigact.sa_handler = SIG_IGN;
        // 忽略SIGPIPE
        sigaction(SIGPIPE, &sigact, nullptr);
        // 执行
        execvp(path, (char**)args.data());
        // execvp's result will be handled after waitpid_with_timeout() below, but
        // if it failed, it's safer to exit dumpstate.
        MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
        // Must call _exit (instead of exit), otherwise it will corrupt the zip
        // file.
        _exit(EXIT_FAILURE);
    }


    /* handle parent case 父进程执行,处理子进程退出*/
    int status;
    bool ret = waitpid_with_timeout(pid, options.TimeoutInMs(), &status);
    fsync(fd);


    uint64_t elapsed = Nanotime() - start;
    if (!ret) {
        if (errno == ETIMEDOUT) {
            if (!silent)
                dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
                        static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
            MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
                   static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
        } else {
            if (!silent)
                dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command,
                        static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
            MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
                   static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
        }
        kill(pid, SIGTERM);
        if (!waitpid_with_timeout(pid, 5000, nullptr)) {
            kill(pid, SIGKILL);
            if (!waitpid_with_timeout(pid, 5000, nullptr)) {
                if (!silent)
                    dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n",
                            command, pid);
                MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
            }
        }
        return -1;
    }


    if (WIFSIGNALED(status)) {
        if (!silent)
            dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
        MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
        status = WEXITSTATUS(status);
        if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status);
        MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
    }


    return status;
}

RunDumpsys()

RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});

RunDumpsys.png 日志形式和RunCommand()很像

static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,

    const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,

    long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) {

    return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd);

}


void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,

    const CommandOptions& options, long dumpsysTimeoutMs, int out_fd) {

    long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs();

    std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)};

    dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());

    RunCommand(title, dumpsys, options, false, out_fd);

}

进入函数后发现,其实就是给命令添加了前缀/system/bin/dumpsys, -T, std::to_string(timeout_ms),然后再调用RunCommand()

文件生成

file_name.png

version.txt

static bool PrepareToWriteToFile() {
    ...
    ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
    MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
    create_parent_dirs(ds.path_.c_str());
    ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); // 指向新对象
    if (ds.zip_file == nullptr) {
        MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
        return false;
    }
    ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));  // 指向新对象 ZipWriter 空类,未找到实现
    ds.AddTextZipEntry("version.txt", ds.version_);
    return true;
}

main_entry.txt & dumpstate_log.txt

bool Dumpstate::FinishZipFile() {
    // Runs all enqueued adding zip entry and cleanup tasks before finishing the zip file.
    if (zip_entry_tasks_) {
        zip_entry_tasks_->run(/* do_cancel = */false);
    }

    std::string entry_name = base_name_ + "-" + name_ + ".txt";
    MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
           tmp_path_.c_str());
    // Final timestamp
    char date[80];
    time_t the_real_now_please_stand_up = time(nullptr);
    strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
    MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
           the_real_now_please_stand_up - ds.now_);

    if (!ds.AddZipEntry(entry_name, tmp_path_)) {
        MYLOGE("Failed to add text entry to .zip file\n");
        return false;
    }
    if (!AddTextZipEntry("main_entry.txt", entry_name)) {
        MYLOGE("Failed to add main_entry.txt to .zip file\n");
        return false;
    }

    // Add log file (which contains stderr output) to zip...
    fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
    if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
        MYLOGE("Failed to add dumpstate log to .zip file\n");
        return false;
    }
    // ------------ 大概在这里返回
    // TODO: Should truncate the existing file.
    // ... and re-open it for further logging.
    if (!redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()))) {
        return false;
    }
    fprintf(stderr, "\n");

    int32_t err = zip_writer_->Finish();
    if (err != 0) {
        MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
        return false;
    }

    // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
    ds.zip_file.reset(nullptr);

    MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
    android::os::UnlinkAndLogOnError(tmp_path_);


    return true;
}

dumpstate_board.txt

static const std::string kDumpstateBoardFiles[] = {
    "dumpstate_board.txt",
    "dumpstate_board.bin"
};

void Dumpstate::DumpstateBoard(int out_fd) {
    dprintf(out_fd, "========================================================\n");
    dprintf(out_fd, "== Board\n");
    dprintf(out_fd, "========================================================\n");

    ...

    for (size_t i = 0; i < paths.size(); i++) {
        if (file_sizes[i] == -1) {
            continue;
        }
        if (file_sizes[i] == 0) {
            MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
            continue;
        }
        remover[i].Disable();
        EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]);
        dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
    }
}

void Dumpstate::EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
        const std::string& entry_path) {
    auto func_add_zip_entry_and_cleanup = [=](bool task_cancelled) {
        if (!task_cancelled) {
            AddZipEntry(entry_name, entry_path);
        }
        android::os::UnlinkAndLogOnError(entry_path);
    };
    if (zip_entry_tasks_) {
        // Enqueues AddZipEntryAndCleanup function if the parallel run is enabled.
        zip_entry_tasks_->add(func_add_zip_entry_and_cleanup, _1);
    } else {
        // Invokes AddZipEntryAndCleanup immediately
        std::invoke(func_add_zip_entry_and_cleanup, /* task_cancelled = */false);
    }
}

AddZipEntry()

bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
    android::base::unique_fd fd(
        TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
    if (fd == -1) {
        MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
        return false;
    }
    return (AddZipEntryFromFd(entry_name, fd.get()) == OK);
}

AddZipEntryFromFd()

status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd,
                                      std::chrono::milliseconds timeout = 0ms) {
    if (!IsZipping()) {
        MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
               entry_name.c_str());
        return INVALID_OPERATION;
    }
    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(valid_name.c_str(), ZipWriter::kCompress,
                                                  get_mtime(fd, ds.now_));
    if (err != 0) {
        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
               ZipWriter::ErrorCodeString(err));
        return UNKNOWN_ERROR;
    }
    bool finished_entry = false;
    auto finish_entry = [this, &finished_entry] {
        if (!finished_entry) {
            // This should only be called when we're going to return an earlier error,
            // which would've been logged. This may imply the file is already corrupt
            // and any further logging from FinishEntry is more likely to mislead than
            // not.
            this->zip_writer_->FinishEntry();
        }
    };
    auto scope_guard = android::base::make_scope_guard(finish_entry);
    auto start = std::chrono::steady_clock::now();
    auto end = start + timeout;
    struct pollfd pfd = {fd, POLLIN};

    std::vector<uint8_t> buffer(65536);
    while (1) {
        if (timeout.count() > 0) {
            // lambda to recalculate the timeout.
            auto time_left_ms = [end]() {
                auto now = std::chrono::steady_clock::now();
                auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
                return std::max(diff.count(), 0LL);
            };


            int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
            if (rc < 0) {
                MYLOGE("Error in poll while adding from fd to zip entry %s:%s\n",
                       entry_name.c_str(), strerror(errno));
                return -errno;
            } else if (rc == 0) {
                MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms\n",
                       entry_name.c_str(), strerror(errno), timeout.count());
                return TIMED_OUT;
            }
        }

        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
        if (bytes_read == 0) {
            break;
        } else if (bytes_read == -1) {
            MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
            return -errno;
        }
        err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
        if (err) {
            MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
            return UNKNOWN_ERROR;
        }
    }

    err = zip_writer_->FinishEntry();
    finished_entry = true;
    if (err != 0) {
        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
        return UNKNOWN_ERROR;
    }

    return OK;
}

AddTextZipEntry()

bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
    if (!IsZipping()) {
        MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
               entry_name.c_str());
        return false;
    }
    MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
    int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
    if (err != 0) {
        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
               ZipWriter::ErrorCodeString(err));
        return false;
    }

    err = zip_writer_->WriteBytes(content.c_str(), content.length());
    if (err != 0) {
        MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
               ZipWriter::ErrorCodeString(err));
        return false;
    }

    err = zip_writer_->FinishEntry();
    if (err != 0) {
        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
        return false;
    }

    return true;
}