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

Commit 306633e5 authored by Kean Mariotti's avatar Kean Mariotti
Browse files

Add bugreport pre-dump functionality

Allow apps to trigger the dump of certain critical data, e.g. data stored in short
ring buffers that might get lost by the time a bugreport is requested.

Then, a bugreport request can specify whether the pre-dumped data should be used.

Fixes: 205138504
Test: atest com.android.os.bugreports.tests.BugreportManagerTest
Ignore-AOSP-First: depends on changes (surfaceflinger) that cannot go into AOSP
Change-Id: I976f2ed3189e83f5bd71dc81e20306527c411d10
parent eebf7c3b
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -154,7 +154,10 @@ cc_test {
        "dumpstate.cpp",
        "tests/dumpstate_test.cpp",
    ],
    static_libs: ["libgmock"],
    static_libs: [
        "libc++fs",
        "libgmock",
    ],
    test_config: "dumpstate_test.xml",
    data: [
        ":dumpstate_test_fixture",
+23 −7
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ static binder::Status exception(uint32_t code, const std::string& msg,

// Creates a bugreport and exits, thus preserving the oneshot nature of the service.
// Note: takes ownership of data.
[[noreturn]] static void* dumpstate_thread_main(void* data) {
[[noreturn]] static void* dumpstate_thread_bugreport(void* data) {
    std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
    ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
    MYLOGD("Finished taking a bugreport. Exiting.\n");
@@ -84,11 +84,28 @@ status_t DumpstateService::Start() {
    return android::OK;
}

binder::Status DumpstateService::preDumpUiData(const std::string&) {
    std::lock_guard<std::mutex> lock(lock_);
    MYLOGI("preDumpUiData()");

    if (ds_ != nullptr) {
        MYLOGE("Error! DumpstateService is currently already being used. Returning.");
        return exception(binder::Status::EX_SERVICE_SPECIFIC,
                         "DumpstateService is already being used");
    }

    ds_ = &(Dumpstate::GetInstance());
    ds_->PreDumpUiData();

    return binder::Status::ok();
}

binder::Status DumpstateService::startBugreport(int32_t calling_uid,
                                                const std::string& calling_package,
                                                android::base::unique_fd bugreport_fd,
                                                android::base::unique_fd screenshot_fd,
                                                int bugreport_mode,
                                                int bugreport_flags,
                                                const sp<IDumpstateListener>& listener,
                                                bool is_screenshot_requested) {
    MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
@@ -96,12 +113,12 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid,
    // Ensure there is only one bugreport in progress at a time.
    std::lock_guard<std::mutex> lock(lock_);
    if (ds_ != nullptr) {
        MYLOGE("Error! There is already a bugreport in progress. Returning.");
        MYLOGE("Error! DumpstateService is currently already being used. Returning.");
        if (listener != nullptr) {
            listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
        }
        return exception(binder::Status::EX_SERVICE_SPECIFIC,
                         "There is already a bugreport in progress");
                         "DumpstateService is already being used");
    }

    // From here on, all conditions that indicate we are done with this incoming request should
@@ -123,8 +140,8 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid,
    }

    std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
    options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
                        screenshot_fd, is_screenshot_requested);
    options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_flags,
                        bugreport_fd, screenshot_fd, is_screenshot_requested);

    if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
        MYLOGE("Invalid filedescriptor");
@@ -148,10 +165,9 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid,
    pthread_t thread;
    // Initialize dumpstate
    ds_->Initialize();
    status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
    status_t err = pthread_create(&thread, nullptr, dumpstate_thread_bugreport, ds_info);
    if (err != 0) {
        delete ds_info;
        ds_info = nullptr;
        MYLOGE("Could not create a thread");
        signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
    }
+5 −2
Original line number Diff line number Diff line
@@ -38,13 +38,16 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst

    status_t dump(int fd, const Vector<String16>& args) override;

    binder::Status preDumpUiData(const std::string& callingPackage) override;

    binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
                                  android::base::unique_fd bugreport_fd,
                                  android::base::unique_fd screenshot_fd, int bugreport_mode,
                                  const sp<IDumpstateListener>& listener,
                                  int bugreport_flags, const sp<IDumpstateListener>& listener,
                                  bool is_screenshot_requested) override;

    binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
    binder::Status cancelBugreport(int32_t calling_uid,
                                   const std::string& calling_package) override;

  private:
    // Dumpstate object which contains all the bugreporting logic.
+20 −2
Original line number Diff line number Diff line
@@ -49,6 +49,23 @@ interface IDumpstate {
    // Default mode.
    const int BUGREPORT_MODE_DEFAULT = 6;

    // Use pre-dumped data.
    const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1;

    /**
     * Speculatively pre-dumps UI data for a bugreport request that might come later.
     *
     * <p>Triggers the dump of certain critical UI data, e.g. traces stored in short
     * ring buffers that might get lost by the time the actual bugreport is requested.
     *
     * <p>{@code startBugreport} will then pick the pre-dumped data if:
     * - {@link BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA} is specified.
     * - {@code preDumpUiData} and {@code startBugreport} were called by the same UID.
     *
     * @param callingPackage package of the original application that requested the report.
     */
    void preDumpUiData(@utf8InCpp String callingPackage);

    /**
     * Starts a bugreport in the background.
     *
@@ -63,13 +80,14 @@ interface IDumpstate {
     * @param bugreportFd the file to which the zipped bugreport should be written
     * @param screenshotFd the file to which screenshot should be written
     * @param bugreportMode the mode that specifies other run time options; must be one of above
     * @param bugreportFlags flags to customize the bugreport generation
     * @param listener callback for updates; optional
     * @param isScreenshotRequested indicates screenshot is requested or not
     */
    void startBugreport(int callingUid, @utf8InCpp String callingPackage,
                        FileDescriptor bugreportFd, FileDescriptor screenshotFd,
                        int bugreportMode, IDumpstateListener listener,
                        boolean isScreenshotRequested);
                        int bugreportMode, int bugreportFlags,
                        IDumpstateListener listener, boolean isScreenshotRequested);

    /**
     * Cancels the bugreport currently in progress.
+64 −14
Original line number Diff line number Diff line
@@ -237,6 +237,7 @@ static const std::string DUMP_NETSTATS_PROTO_TASK = "DUMP NETSTATS PROTO";
static const std::string DUMP_HALS_TASK = "DUMP HALS";
static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES";

namespace android {
namespace os {
@@ -1588,16 +1589,16 @@ static void DumpAppInfos(int out_fd = STDOUT_FILENO) {
// via the consent they are shown. Ignores other errors that occur while running various
// commands. The consent checking is currently done around long running tasks, which happen to
// be distributed fairly evenly throughout the function.
static Dumpstate::RunStatus dumpstate() {
Dumpstate::RunStatus Dumpstate::dumpstate() {
    DurationReporter duration_reporter("DUMPSTATE");

    // Enqueue slow functions into the thread pool, if the parallel run is enabled.
    std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins,
            dump_netstats_report;
            dump_netstats_report, post_process_ui_traces;
    if (ds.dump_pool_) {
        // Pool was shutdown in DumpstateDefaultAfterCritical method in order to
        // drop root user. Restarts it with two threads for the parallel run.
        ds.dump_pool_->start(/* thread_counts = */2);
        // drop root user. Restarts it.
        ds.dump_pool_->start(/* thread_counts = */3);

        dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
        dump_incident_report = ds.dump_pool_->enqueueTask(
@@ -1607,6 +1608,8 @@ static Dumpstate::RunStatus dumpstate() {
        dump_board = ds.dump_pool_->enqueueTaskWithFd(
            DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
        dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
        post_process_ui_traces = ds.dump_pool_->enqueueTask(
            POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds);
    }

    // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1731,11 +1734,6 @@ static Dumpstate::RunStatus dumpstate() {
    DumpFile("BINDER STATS", binder_logs_dir + "/stats");
    DumpFile("BINDER STATE", binder_logs_dir + "/state");

    /* Add window and surface trace files. */
    if (!PropertiesHelper::IsUserBuild()) {
        ds.AddDir(WMTRACE_DATA_DIR, false);
    }

    ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);

    if (ds.dump_pool_) {
@@ -1815,6 +1813,14 @@ static Dumpstate::RunStatus dumpstate() {
                DumpIncidentReport);
    }

    if (ds.dump_pool_) {
        WaitForTask(std::move(post_process_ui_traces));
    } else {
        RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces);
    }

    MaybeAddUiTracesToZip();

    return Dumpstate::RunStatus::OK;
}

@@ -2783,9 +2789,11 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
}

void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
                                        int bugreport_flags,
                                        const android::base::unique_fd& bugreport_fd_in,
                                        const android::base::unique_fd& screenshot_fd_in,
                                        bool is_screenshot_requested) {
    this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
    // Duplicate the fds because the passed in fds don't outlive the binder transaction.
    bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
    screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -2912,6 +2920,10 @@ void Dumpstate::Cancel() {
    }
}

void Dumpstate::PreDumpUiData() {
    MaybeSnapshotUiTraces();
}

/*
 * Dumps relevant information to a bugreport based on the given options.
 *
@@ -3103,9 +3115,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
        // 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();
        // Snapshot the UI traces now (if running).
        // The trace files will be added to bugreport later.
        MaybeSnapshotUiTraces();
    }
    onUiIntensiveBugreportDumpsFinished(calling_uid);
    MaybeCheckUserConsent(calling_uid, calling_package);
@@ -3219,15 +3231,53 @@ void Dumpstate::MaybeSnapshotSystemTrace() {
    // file in the later stages.
}

void Dumpstate::MaybeSnapshotWinTrace() {
void Dumpstate::MaybeSnapshotUiTraces() {
    if (PropertiesHelper::IsUserBuild() || options_->use_predumped_ui_data) {
        return;
    }

    // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
    for (const auto& service : {"window", "input_method"}) {
    for (const auto& service : {"input_method", "window"}) {
        RunCommand(
            // Empty name because it's not intended to be classified as a bugreport section.
            // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
            "", {"cmd", service, "tracing", "save-for-bugreport"},
            CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
    }

    static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
        "service", "call", "SurfaceFlinger", "1042"
    };
    // Empty name because it's not intended to be classified as a bugreport section.
    // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
    RunCommand(
        "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES,
        CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build());
}

void Dumpstate::MaybePostProcessUiTraces() {
    if (PropertiesHelper::IsUserBuild()) {
        return;
    }

    RunCommand(
        // Empty name because it's not intended to be classified as a bugreport section.
        // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
        "", {
            "/system/xbin/su", "system",
            "/system/bin/layertracegenerator",
            "/data/misc/wmtrace/transactions_trace.winscope",
            "/data/misc/wmtrace/layers_trace_from_transactions.winscope"
        },
        CommandOptions::WithTimeout(120).Always().RedirectStderr().Build());
}

void Dumpstate::MaybeAddUiTracesToZip() {
    if (PropertiesHelper::IsUserBuild()) {
        return;
    }

    ds.AddDir(WMTRACE_DATA_DIR, false);
}

void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
Loading