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

Commit 751e681a authored by Rhed Jao's avatar Rhed Jao Committed by Automerger Merge Worker
Browse files

Faster bugreports (3/n) am: 4875aa6b am: 8b193f40

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1441344

Change-Id: I760d97d0595d9285e6f46f2a873c167761121254
parents 1c59181e 8b193f40
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ cc_binary {
    defaults: ["dumpstate_defaults"],
    srcs: [
        "DumpPool.cpp",
        "TaskQueue.cpp",
        "dumpstate.cpp",
        "main.cpp",
    ],
@@ -134,6 +135,7 @@ cc_test {
    defaults: ["dumpstate_defaults"],
    srcs: [
        "DumpPool.cpp",
        "TaskQueue.cpp",
        "dumpstate.cpp",
        "tests/dumpstate_test.cpp",
    ],
@@ -151,6 +153,7 @@ cc_test {
    defaults: ["dumpstate_defaults"],
    srcs: [
        "DumpPool.cpp",
        "TaskQueue.cpp",
        "dumpstate.cpp",
        "tests/dumpstate_smoke_test.cpp",
    ],
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "TaskQueue.h"

namespace android {
namespace os {
namespace dumpstate {

TaskQueue::~TaskQueue() {
    run(/* do_cancel = */true);
}

void TaskQueue::run(bool do_cancel) {
    std::unique_lock lock(lock_);
    while (!tasks_.empty()) {
        auto task = tasks_.front();
        tasks_.pop();
        lock.unlock();
        std::invoke(task, do_cancel);
        lock.lock();
    }
}

}  // namespace dumpstate
}  // namespace os
}  // namespace android
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
#define FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_

#include <mutex>
#include <queue>

#include <android-base/macros.h>

namespace android {
namespace os {
namespace dumpstate {

/*
 * A task queue for dumpstate to collect tasks such as adding file to the zip
 * which are needed to run in a single thread. The task is a callable function
 * included a cancel task boolean parameter. The TaskQueue could
 * cancel the task in the destructor if the task has never been called.
 */
class TaskQueue {
  public:
    TaskQueue() = default;
    ~TaskQueue();

    /*
     * Adds a task into the queue.
     *
     * |f| Callable function to execute the task. The function must include a
     *     boolean parameter for TaskQueue to notify whether the task is
     *     cancelled or not.
     * |args| A list of arguments.
     */
    template<class F, class... Args> void add(F&& f, Args&&... args) {
        auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        std::unique_lock lock(lock_);
        tasks_.emplace([=](bool cancelled) {
            std::invoke(func, cancelled);
        });
    }

    /*
     * Invokes all tasks in the task queue.
     *
     * |do_cancel| true to cancel all tasks in the queue.
     */
    void run(bool do_cancel);

  private:
    using Task = std::function<void(bool)>;

    std::mutex lock_;
    std::queue<Task> tasks_;

    DISALLOW_COPY_AND_ASSIGN(TaskQueue);
};

}  // namespace dumpstate
}  // namespace os
}  // namespace android

#endif //FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+100 −29
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
using android::os::dumpstate::TaskQueue;

// Keep in sync with
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -128,8 +129,8 @@ static const int32_t WEIGHT_FILE = 5;
static Dumpstate& ds = Dumpstate::GetInstance();
static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                      const CommandOptions& options = CommandOptions::DEFAULT,
                      bool verbose_duration = false) {
    return ds.RunCommand(title, full_command, options, verbose_duration);
                      bool verbose_duration = false, int out_fd = STDOUT_FILENO) {
    return ds.RunCommand(title, full_command, options, verbose_duration, out_fd);
}

// Reasonable value for max stats.
@@ -212,11 +213,19 @@ static const std::string ANR_FILE_PREFIX = "anr_";
    RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__);               \
    RETURN_IF_USER_DENIED_CONSENT();

#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \
    RETURN_IF_USER_DENIED_CONSENT();                      \
    pool_ptr->waitForTask(task_name);                     \
    RETURN_IF_USER_DENIED_CONSENT();

static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";

// Names of parallel tasks, they are used for the DumpPool to identify the dump
// task and the log title of the duration report.
static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT";
static const std::string DUMP_HALS_TASK = "DUMP HALS";
static const std::string DUMP_BOARD_TASK = "dumpstate_board()";

namespace android {
namespace os {
@@ -1014,7 +1023,6 @@ static void DumpIncidentReport() {
        MYLOGD("Not dumping incident report because it's not a zipped bugreport\n");
        return;
    }
    DurationReporter duration_reporter("INCIDENT REPORT");
    const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report";
    auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
                O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
@@ -1029,10 +1037,12 @@ static void DumpIncidentReport() {
        // Use a different name from "incident.proto"
        // /proto/incident.proto is reserved for incident service dump
        // i.e. metadata for debugging.
        ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path);
    }
        ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt,
                path);
    } else {
        unlink(path.c_str());
    }
}

static void DumpVisibleWindowViews() {
    if (!ds.IsZipping()) {
@@ -1326,15 +1336,20 @@ static Dumpstate::RunStatus RunDumpsysNormal() {
                           /* timeout= */ 90s, /* service_timeout= */ 10s);
}

static void DumpHals() {
/*
 * |out_fd| A fd to support the DumpPool to output results to a temporary file.
 * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
 * if it's not running in the parallel task.
 */
static void DumpHals(int out_fd = STDOUT_FILENO) {
    if (!ds.IsZipping()) {
        RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
                   CommandOptions::WithTimeout(60).AsRootIfAvailable().Build());
        return;
    }
    DurationReporter duration_reporter("DUMP HALS");
    RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
               CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
               CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
               false, out_fd);

    using android::hidl::manager::V1_0::IServiceManager;
    using android::hardware::defaultServiceManager;
@@ -1356,6 +1371,7 @@ static void DumpHals() {
                            }, '_');
            const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;

            bool empty = false;
            {
                auto fd = android::base::unique_fd(
                    TEMP_FAILURE_RETRY(open(path.c_str(),
@@ -1370,14 +1386,15 @@ static void DumpHals() {
                        {"lshal", "debug", "-E", interface},
                        CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());

                bool empty = 0 == lseek(fd, 0, SEEK_END);
                if (!empty) {
                    ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path);
                }
                empty = 0 == lseek(fd, 0, SEEK_END);
            }

            if (!empty) {
                ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt",
                        path);
            } else {
                unlink(path.c_str());
            }
        }
    });

    if (!ret.isOk()) {
@@ -1471,6 +1488,17 @@ static void DumpstateLimitedOnly() {
static Dumpstate::RunStatus dumpstate() {
    DurationReporter duration_reporter("DUMPSTATE");

    // Enqueue slow functions into the thread pool, if the parallel run is enabled.
    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);

        ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
        ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
        ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
    }

    // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
    // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped
    // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK).
@@ -1502,7 +1530,11 @@ static Dumpstate::RunStatus dumpstate() {
    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
                                         CommandOptions::AS_ROOT);

    DumpHals();
    if (ds.dump_pool_) {
        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
    } else {
        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
    }

    RunCommand("PRINTENV", {"printenv"});
    RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -1585,7 +1617,11 @@ static Dumpstate::RunStatus dumpstate() {

    ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);

    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
    if (ds.dump_pool_) {
        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
    } else {
        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
    }

    /* Migrate the ril_dumpstate to a device specific dumpstate? */
    int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
@@ -1682,7 +1718,12 @@ static Dumpstate::RunStatus dumpstate() {
    // Add linker configuration directory
    ds.AddDir(LINKERCONFIG_DIR, true);

    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
    if (ds.dump_pool_) {
        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
    } else {
        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
                DumpIncidentReport);
    }

    return Dumpstate::RunStatus::OK;
}
@@ -1801,7 +1842,8 @@ static void DumpstateRadioCommon(bool include_sensitive_info = true) {
        // Too broad for connectivity problems.
        DoKmsg();
        // Contains unrelated hardware info (camera, NFC, biometrics, ...).
        DumpHals();
        // TODO(b/136262402) Using thread pool for DumpHals
        RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
    }

    DumpPacketStats();
@@ -2031,11 +2073,10 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
    return RunStatus::OK;
}

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

    if (!IsZipping()) {
        MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
@@ -2161,8 +2202,9 @@ void Dumpstate::DumpstateBoard() {
            MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
            continue;
        }
        AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
        printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
        remover[i].Disable();
        EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]);
        dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
    }
}

@@ -2192,6 +2234,11 @@ static void register_sig_handler() {
}

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());
@@ -2775,7 +2822,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
        onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
        MaybeCheckUserConsent(calling_uid, calling_package);
        DumpstateTelephonyOnly(calling_package);
        DumpstateBoard();
        // TODO(b/136262402) Using thread pool for DumpstateBoard
        RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, DumpstateBoard);
    } else if (options_->wifi_only) {
        MaybeTakeEarlyScreenshot();
        onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2938,6 +2986,7 @@ void Dumpstate::EnableParallelRunIfNeeded() {
        return;
    }
    dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
    zip_entry_tasks_ = std::make_unique<TaskQueue>();
}

void Dumpstate::ShutdownDumpPool() {
@@ -2945,6 +2994,27 @@ void Dumpstate::ShutdownDumpPool() {
        dump_pool_->shutdown();
        dump_pool_ = nullptr;
    }
    if (zip_entry_tasks_) {
        zip_entry_tasks_->run(/* do_cancel = */true);
        zip_entry_tasks_ = nullptr;
    }
}

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);
    }
}

Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
@@ -3668,10 +3738,11 @@ int dump_file_from_fd(const char *title, const char *path, int fd) {
}

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

    int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
    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,
+26 −2
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@

#include "DumpstateUtil.h"
#include "DumpPool.h"
#include "TaskQueue.h"

// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -229,11 +230,13 @@ class Dumpstate {
     * |full_command| array containing the command (first entry) and its arguments.
     * Must contain at least one element.
     * |options| optional argument defining the command's behavior.
     * |out_fd| A fd to support the DumpPool to output results to a temporary
     * file. Using STDOUT_FILENO if it's not running in the parallel task.
     */
    int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
                   const android::os::dumpstate::CommandOptions& options =
                       android::os::dumpstate::CommandOptions::DEFAULT,
                   bool verbose_duration = false);
                   bool verbose_duration = false, int out_fd = STDOUT_FILENO);

    /*
     * Runs `dumpsys` with the given arguments, automatically setting its timeout
@@ -306,7 +309,12 @@ class Dumpstate {
    // Returns OK in all other cases.
    RunStatus DumpTraces(const char** path);

    void DumpstateBoard();
    /*
     * |out_fd| A fd to support the DumpPool to output results to a temporary file.
     * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
     * if it's not running in the parallel task.
     */
    void DumpstateBoard(int out_fd = STDOUT_FILENO);

    /*
     * Updates the overall progress of the bugreport generation by the given weight increment.
@@ -362,6 +370,18 @@ class Dumpstate {
     */
    bool CalledByApi() const;

    /*
     * Enqueues a task to the dumpstate's TaskQueue if the parallel run is enabled,
     * otherwise invokes it immediately. The task adds file at path entry_path
     * as a zip file entry with name entry_name. Unlinks entry_path when done.
     *
     * All enqueued tasks will be executed in the dumpstate's FinishZipFile method
     * before the zip file is finished. Tasks will be cancelled in dumpstate's
     * ShutdownDumpPool method if they have never been called.
     */
    void EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
            const std::string& entry_path);

    /*
     * Structure to hold options that determine the behavior of dumpstate.
     */
@@ -495,6 +515,10 @@ class Dumpstate {
    // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
    std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;

    // A task queue to collect adding zip entry tasks inside dump tasks if the
    // parallel run is enabled.
    std::unique_ptr<android::os::dumpstate::TaskQueue> zip_entry_tasks_;

    // A callback to IncidentCompanion service, which checks user consent for sharing the
    // bugreport with the calling app. If the user has not responded yet to the dialog it will
    // be neither confirmed nor denied.
Loading