Merge "Check for focused window before raising 'no focused window' ANR"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ead491e..80d14ac 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,6 +105,8 @@
name: "dumpstate",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"main.cpp",
],
@@ -132,6 +134,8 @@
name: "dumpstate_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
@@ -148,10 +152,14 @@
name: "dumpstate_smoke_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_smoke_test.cpp",
],
static_libs: ["libgmock"],
+ test_config: "dumpstate_smoke_test.xml",
+ test_suites: ["device-tests"],
}
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
new file mode 100644
index 0000000..e15ac3f
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpPool.h"
+
+#include <array>
+#include <thread>
+
+#include <log/log.h>
+
+#include "dumpstate.h"
+#include "DumpstateInternal.h"
+#include "DumpstateUtil.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
+
+DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
+ log_duration_(true) {
+ assert(!tmp_root.empty());
+ deleteTempFiles(tmp_root_);
+}
+
+DumpPool::~DumpPool() {
+ shutdown();
+}
+
+void DumpPool::start(int thread_counts) {
+ assert(thread_counts > 0);
+ assert(threads_.empty());
+ if (thread_counts > MAX_THREAD_COUNT) {
+ thread_counts = MAX_THREAD_COUNT;
+ }
+ MYLOGI("Start thread pool:%d", thread_counts);
+ shutdown_ = false;
+ for (int i = 0; i < thread_counts; i++) {
+ threads_.emplace_back(std::thread([=]() {
+ setThreadName(pthread_self(), i + 1);
+ loop();
+ }));
+ }
+}
+
+void DumpPool::shutdown() {
+ std::unique_lock lock(lock_);
+ if (shutdown_ || threads_.empty()) {
+ return;
+ }
+ while (!tasks_.empty()) tasks_.pop();
+ futures_map_.clear();
+
+ shutdown_ = true;
+ condition_variable_.notify_all();
+ lock.unlock();
+
+ for (auto& thread : threads_) {
+ thread.join();
+ }
+ threads_.clear();
+ deleteTempFiles(tmp_root_);
+ MYLOGI("shutdown thread pool");
+}
+
+void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
+ int out_fd) {
+ DurationReporter duration_reporter("Wait for " + task_name, true);
+ auto iterator = futures_map_.find(task_name);
+ if (iterator == futures_map_.end()) {
+ MYLOGW("Task %s does not exist", task_name.c_str());
+ return;
+ }
+ Future future = iterator->second;
+ futures_map_.erase(iterator);
+
+ std::string result = future.get();
+ if (result.empty()) {
+ return;
+ }
+ DumpFileToFd(out_fd, title, result);
+ if (unlink(result.c_str())) {
+ MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+ }
+}
+
+void DumpPool::deleteTempFiles() {
+ deleteTempFiles(tmp_root_);
+}
+
+void DumpPool::setLogDuration(bool log_duration) {
+ log_duration_ = log_duration;
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func);
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func, out_fd);
+}
+
+std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
+ auto tmp_file_ptr = std::make_unique<TmpFile>();
+ std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
+ snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
+ tmp_root_.c_str());
+ tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
+ mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
+ if (tmp_file_ptr->fd.get() == -1) {
+ MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
+ tmp_file_ptr = nullptr;
+ return tmp_file_ptr;
+ }
+ return tmp_file_ptr;
+}
+
+void DumpPool::deleteTempFiles(const std::string& folder) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
+ return;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
+ strerror(errno));
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ if (unlinkat(dir_fd, file_name.c_str(), 0)) {
+ MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
+ strerror(errno));
+ }
+ }
+}
+
+void DumpPool::setThreadName(const pthread_t thread, int id) {
+ std::array<char, 15> name;
+ snprintf(name.data(), name.size(), "dumpstate_%d", id);
+ pthread_setname_np(thread, name.data());
+}
+
+void DumpPool::loop() {
+ std::unique_lock lock(lock_);
+ while (!shutdown_) {
+ if (tasks_.empty()) {
+ condition_variable_.wait(lock);
+ continue;
+ } else {
+ std::packaged_task<std::string()> task = std::move(tasks_.front());
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task);
+ lock.lock();
+ }
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
new file mode 100644
index 0000000..0c3c2cc
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,206 @@
+/*
+ * 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_DUMPPOOL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+
+#include <future>
+#include <map>
+#include <queue>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpPoolTest;
+
+/*
+ * A thread pool with the fixed number of threads to execute multiple dump tasks
+ * simultaneously for the dumpstate. The dump task is a callable function. It
+ * could include a file descriptor as a parameter to redirect dump results, if
+ * it needs to output results to the bugreport. This can avoid messing up
+ * bugreport's results when multiple dump tasks are running at the same time.
+ * Takes an example below for the usage of the DumpPool:
+ *
+ * void DumpFoo(int out_fd) {
+ * dprintf(out_fd, "Dump result to out_fd ...");
+ * }
+ * ...
+ * DumpPool pool(tmp_root);
+ * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * ...
+ * pool.waitForTask("TaskName");
+ *
+ * DumpFoo is a callable function included a out_fd parameter. Using the
+ * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
+ * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
+ */
+class DumpPool {
+ friend class android::os::dumpstate::DumpPoolTest;
+
+ public:
+ /*
+ * Creates a thread pool.
+ *
+ * |tmp_root| A path to a temporary folder for threads to create temporary
+ * files.
+ */
+ explicit DumpPool(const std::string& tmp_root);
+ ~DumpPool();
+
+ /*
+ * Starts the threads in the pool.
+ *
+ * |thread_counts| the number of threads to start.
+ */
+ void start(int thread_counts = MAX_THREAD_COUNT);
+
+ /*
+ * Requests to shutdown the pool and waits until all threads exit the loop.
+ */
+ void shutdown();
+
+ /*
+ * Adds a task into the queue of the thread pool.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments.
+ *
+ * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
+ */
+ template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(void)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Adds a task into the queue of the thread pool. The task takes a file
+ * descriptor as a parameter to redirect dump results to a temporary file.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
+ * argument needs to be included here.
+ */
+ template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(int)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+ */
+ void waitForTask(const std::string& task_name) {
+ waitForTask(task_name, "", STDOUT_FILENO);
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the specified
+ * out_fd.
+ *
+ * |task_name| The name of the task.
+ * |title| Dump title string to the out_fd, an empty string for nothing.
+ * |out_fd| The target file to dump the result from the task.
+ */
+ void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+
+ /*
+ * Deletes temporary files created by DumpPool.
+ */
+ void deleteTempFiles();
+
+ static const std::string PREFIX_TMPFILE_NAME;
+
+ private:
+ using Task = std::packaged_task<std::string()>;
+ using Future = std::shared_future<std::string>;
+
+ template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
+
+ template<class T> Future post(const std::string& task_name, T dump_func) {
+ Task packaged_task([=]() {
+ std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
+ if (!tmp_file_ptr) {
+ return std::string("");
+ }
+ invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
+ fsync(tmp_file_ptr->fd.get());
+ return std::string(tmp_file_ptr->path);
+ });
+ std::unique_lock lock(lock_);
+ auto future = packaged_task.get_future().share();
+ tasks_.push(std::move(packaged_task));
+ condition_variable_.notify_one();
+ return future;
+ }
+
+ typedef struct {
+ android::base::unique_fd fd;
+ char path[1024];
+ } TmpFile;
+
+ std::unique_ptr<TmpFile> createTempFile();
+ void deleteTempFiles(const std::string& folder);
+ void setThreadName(const pthread_t thread, int id);
+ void loop();
+
+ /*
+ * For test purpose only. Enables or disables logging duration of the task.
+ *
+ * |log_duration| if true, DurationReporter is initiated to log duration of
+ * the task.
+ */
+ void setLogDuration(bool log_duration);
+
+ private:
+ static const int MAX_THREAD_COUNT = 4;
+
+ /* A path to a temporary folder for threads to create temporary files. */
+ std::string tmp_root_;
+ bool shutdown_;
+ bool log_duration_; // For test purpose only, the default value is true.
+ std::mutex lock_; // A lock for the tasks_.
+ std::condition_variable condition_variable_;
+
+ std::vector<std::thread> threads_;
+ std::queue<Task> tasks_;
+ std::map<std::string, Future> futures_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(DumpPool);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4b69607..eeaa5a3 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -180,6 +180,7 @@
std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
+int PropertiesHelper::parallel_run_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -202,6 +203,14 @@
return unroot_ == 1;
}
+bool PropertiesHelper::IsParallelRun() {
+ if (parallel_run_ == -1) {
+ parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run",
+ /* default_value = */true) ? 1 : 0;
+ }
+ return parallel_run_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b7ac25c..b099443 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -176,10 +176,18 @@
*/
static bool IsUnroot();
+ /*
+ * Whether or not the parallel run is enabled. Setting the system property
+ * 'dumpstate.parallel_run' to false to disable it, otherwise it returns
+ * true by default.
+ */
+ static bool IsParallelRun();
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
+ static int parallel_run_;
};
/*
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 083944f..839a2c3 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -1,7 +1,28 @@
{
"presubmit": [
{
+ "name": "BugreportManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
+ {
+ "name": "dumpstate_smoke_test"
+ },
+ {
"name": "dumpstate_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "BugreportManagerTestCases"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/packages/Shell"
+ }
]
-}
\ No newline at end of file
+}
diff --git a/cmds/dumpstate/TaskQueue.cpp b/cmds/dumpstate/TaskQueue.cpp
new file mode 100644
index 0000000..8550aec
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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
diff --git a/cmds/dumpstate/TaskQueue.h b/cmds/dumpstate/TaskQueue.h
new file mode 100644
index 0000000..b7e72f1
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.h
@@ -0,0 +1,76 @@
+/*
+ * 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_
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 84de9f3..eefc84f 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -95,6 +95,7 @@
using ::android::hardware::dumpstate::V1_1::toString;
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
+using ::std::placeholders::_1;
// TODO: remove once moved to namespace
using android::defaultServiceManager;
@@ -113,7 +114,9 @@
using android::os::IDumpstateListener;
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
@@ -126,8 +129,8 @@
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.
@@ -196,8 +199,36 @@
func_ptr(__VA_ARGS__); \
RETURN_IF_USER_DENIED_CONSENT();
+// Runs func_ptr, and logs a duration report after it's finished.
+#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...) \
+ { \
+ DurationReporter duration_reporter_in_macro(log_title); \
+ func_ptr(__VA_ARGS__); \
+ }
+
+// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report
+// is output after a slow function is finished.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ 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()";
+static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
+static const std::string DUMP_APP_INFOS_TASK = "DUMP APP INFOS";
+
namespace android {
namespace os {
namespace {
@@ -306,8 +337,12 @@
static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
- long dumpsysTimeoutMs = 0) {
- return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs);
+ long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) {
+ return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ int out_fd) {
+ return ds.RunDumpsys(title, dumpsysArgs, Dumpstate::DEFAULT_DUMPSYS, 0, out_fd);
}
static int DumpFile(const std::string& title, const std::string& path) {
return ds.DumpFile(title, path);
@@ -762,8 +797,9 @@
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
- PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ options_->args.c_str(), options_->bugreport_mode.c_str());
printf("\n");
}
@@ -993,7 +1029,6 @@
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,
@@ -1008,9 +1043,11 @@
// 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());
}
- unlink(path.c_str());
}
static void DumpVisibleWindowViews() {
@@ -1305,15 +1342,20 @@
/* 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;
@@ -1335,6 +1377,7 @@
}, '_');
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(),
@@ -1349,13 +1392,14 @@
{"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);
}
-
- unlink(path.c_str());
+ if (!empty) {
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt",
+ path);
+ } else {
+ unlink(path.c_str());
+ }
}
});
@@ -1443,6 +1487,73 @@
printf("========================================================\n");
}
+/*
+ * |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 DumpCheckins(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Checkins\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}, out_fd);
+}
+
+/*
+ * Runs dumpsys on activity service to dump all application activities, services
+ * and providers in the device.
+ *
+ * |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 DumpAppInfos(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Activities\n");
+ dprintf(out_fd, "========================================================\n");
+
+ // The following dumpsys internally collects output from running apps, so it can take a long
+ // time. So let's extend the timeout.
+
+ const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
+
+ RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+}
+
// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
// 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
@@ -1450,6 +1561,19 @@
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);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _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).
@@ -1481,7 +1605,11 @@
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"});
@@ -1564,7 +1692,11 @@
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);
@@ -1586,57 +1718,17 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
- printf("========================================================\n");
- printf("== Checkins\n");
- printf("========================================================\n");
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
+ }
- RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
-
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"});
-
- RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
- RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
- RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
- RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
-
- printf("========================================================\n");
- printf("== Running Application Activities\n");
- printf("========================================================\n");
-
- // The following dumpsys internally collects output from running apps, so it can take a long
- // time. So let's extend the timeout.
-
- const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
-
- RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos);
+ }
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -1661,7 +1753,12 @@
// 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;
}
@@ -1682,7 +1779,18 @@
time_t logcat_ts = time(nullptr);
/* collect stack traces from Dalvik and native processes (needs root) */
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ // One thread is enough since we only need to enqueue DumpTraces here.
+ dump_pool_->start(/* thread_counts = */1);
+
+ // DumpTraces takes long time, post it to the another thread in the
+ // pool, if pool is available
+ dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
+ &dump_traces_path);
+ }
/* Run some operations that require root. */
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1725,6 +1833,15 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ dump_pool_->waitForTask(DUMP_TRACES_TASK);
+
+ // Current running thread in the pool is the root user also. Shutdown
+ // the pool and restart later to ensure all threads in the pool could
+ // drop the root user.
+ dump_pool_->shutdown();
+ }
if (!DropRootUser()) {
return Dumpstate::RunStatus::ERROR;
}
@@ -1736,31 +1853,39 @@
return status;
}
+// Common states for telephony and wifi which are needed to be collected before
+// dumpstate drop the root user.
+static void DumpstateRadioAsRoot() {
+ DumpIpTablesAsRoot();
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
// This method collects common dumpsys for telephony and wifi. Typically, wifi
// reports are fine to include all information, but telephony reports on user
// builds need to strip some content (see DumpstateTelephonyOnly).
static void DumpstateRadioCommon(bool include_sensitive_info = true) {
- DumpIpTablesAsRoot();
-
- ds.AddDir(LOGPERSIST_DATA_DIR, false);
-
- if (!DropRootUser()) {
- return;
- }
-
// We need to be picky about some stuff for telephony reports on user builds.
if (!include_sensitive_info) {
// Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
DoRadioLogcat();
} else {
+ // DumpHals takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ }
// Contains various system properties and process startup info.
do_dmesg();
// Logs other than the radio buffer may contain package/component names and potential PII.
DoLogcat();
// Too broad for connectivity problems.
DoKmsg();
- // Contains unrelated hardware info (camera, NFC, biometrics, ...).
- DumpHals();
+ // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
}
DumpPacketStats();
@@ -1784,6 +1909,21 @@
const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped, and two additional threads
+ // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/2);
+
+ // DumpstateBoard takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ }
+
DumpstateRadioCommon(include_sensitive_info);
if (include_sensitive_info) {
@@ -1860,12 +2000,29 @@
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
+
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
}
// This method collects dumpsys for wifi debugging only
static void DumpstateWifiOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped. Only one additional
+ // thread is needed for DumpHals in the DumpstateRadioCommon.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/1);
+ }
+
DumpstateRadioCommon();
printf("========================================================\n");
@@ -1883,8 +2040,6 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- DurationReporter duration_reporter("DUMP TRACES");
-
const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
@@ -1992,11 +2147,10 @@
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");
@@ -2122,8 +2276,9 @@
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());
}
}
@@ -2153,6 +2308,11 @@
}
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());
@@ -2356,7 +2516,6 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
// Currently, the dumpstate binder is only used by Shell to update progress.
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
@@ -2368,7 +2527,6 @@
options->dumpstate_hal_mode = DumpstateMode::REMOTE;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_zip_file = true;
options->do_screenshot = is_screenshot_requested;
@@ -2395,12 +2553,12 @@
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
MYLOGI(
"do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
- "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+ "is_remote_mode: %d show_header_only: %d telephony_only: %d "
"wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
"limited_only: %d args: %s\n",
options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
options.do_screenshot, options.is_remote_mode, options.show_header_only,
- options.do_start_service, options.telephony_only, options.wifi_only,
+ options.telephony_only, options.wifi_only,
options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
}
@@ -2531,6 +2689,15 @@
}
tombstone_data_.clear();
anr_data_.clear();
+
+ // Instead of shutdown the pool, we delete temporary files directly since
+ // shutdown blocking the call.
+ if (dump_pool_) {
+ dump_pool_->deleteTempFiles();
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/*do_cancel =*/ true);
+ }
}
/*
@@ -2556,6 +2723,7 @@
*/
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");
@@ -2615,15 +2783,6 @@
register_sig_handler();
- // TODO(b/111441001): maybe skip if already started?
- if (options_->do_start_service) {
- MYLOGI("Starting 'dumpstate' service\n");
- android::status_t ret;
- if ((ret = android::os::DumpstateService::Start()) != android::OK) {
- MYLOGE("Unable to start DumpstateService: %d\n", ret);
- }
- }
-
if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -2717,6 +2876,13 @@
// Don't buffer stdout
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([=]() {
+ 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.
@@ -2728,7 +2894,6 @@
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly(calling_package);
- DumpstateBoard();
} else if (options_->wifi_only) {
MaybeTakeEarlyScreenshot();
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2883,6 +3048,45 @@
android::os::UnlinkAndLogOnError(path_);
}
+void Dumpstate::EnableParallelRunIfNeeded() {
+ // The thread pool needs to create temporary files to receive dump results.
+ // That's why we only enable it when the bugreport client chooses to output
+ // to a file.
+ if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
+ return;
+ }
+ dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+ zip_entry_tasks_ = std::make_unique<TaskQueue>();
+}
+
+void Dumpstate::ShutdownDumpPool() {
+ if (dump_pool_) {
+ 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() {
MYLOGD("User denied consent; deleting files and returning\n");
CleanupTmpFiles();
@@ -3001,8 +3205,9 @@
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose)
- : title_(title), logcat_only_(logcat_only), verbose_(verbose) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose,
+ int duration_fd) : title_(title), logcat_only_(logcat_only), verbose_(verbose),
+ duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
}
@@ -3016,7 +3221,8 @@
}
if (!logcat_only_) {
// Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
+ dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
+ elapsed, title_.c_str());
}
}
}
@@ -3602,10 +3808,11 @@
}
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,
@@ -3617,11 +3824,11 @@
}
void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
- const CommandOptions& options, long dumpsysTimeoutMs) {
+ 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);
+ RunCommand(title, dumpsys, options, false, out_fd);
}
int open_socket(const char *service) {
@@ -3744,12 +3951,16 @@
fclose(fp);
}
-// TODO: make this function thread safe if sections are generated in parallel.
void Dumpstate::UpdateProgress(int32_t delta_sec) {
if (progress_ == nullptr) {
MYLOGE("UpdateProgress: progress_ not set\n");
return;
}
+ // This function updates progress related members of the dumpstate and reports
+ // progress percentage to the bugreport client. Since it could be called by
+ // different dump tasks at the same time if the parallel run is enabled, a
+ // mutex lock is necessary here to synchronize the call.
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
// Always update progess so stats can be tuned...
progress_->Inc(delta_sec);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0d25d30..9582c9d 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,8 @@
#include <ziparchive/zip_writer.h>
#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>
@@ -75,7 +77,7 @@
class DurationReporter {
public:
explicit DurationReporter(const std::string& title, bool logcat_only = false,
- bool verbose = false);
+ bool verbose = false, int duration_fd = STDOUT_FILENO);
~DurationReporter();
@@ -84,6 +86,7 @@
bool logcat_only_;
bool verbose_;
uint64_t started_;
+ int duration_fd_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
@@ -193,7 +196,7 @@
* that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
*/
class Dumpstate {
- friend class DumpstateTest;
+ friend class android::os::dumpstate::DumpstateTest;
public:
enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -227,11 +230,13 @@
* |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
@@ -244,10 +249,12 @@
* |options| optional argument defining the command's behavior.
* |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the
* timeout from `options`)
+ * |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.
*/
void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
- long dumpsys_timeout_ms = 0);
+ long dumpsys_timeout_ms = 0, int out_fd = STDOUT_FILENO);
/*
* Prints the contents of a file.
@@ -304,7 +311,12 @@
// 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.
@@ -361,6 +373,18 @@
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.
*/
struct DumpOptions {
@@ -374,7 +398,6 @@
bool is_screenshot_copied = false;
bool is_remote_mode = false;
bool show_header_only = false;
- bool do_start_service = false;
bool telephony_only = false;
bool wifi_only = false;
// Trimmed-down version of dumpstate to only include whitelisted logs.
@@ -490,6 +513,13 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // 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.
@@ -528,6 +558,10 @@
// but leaves the log file alone.
void CleanupTmpFiles();
+ // Create the thread pool to enable the parallel run function.
+ void EnableParallelRunIfNeeded();
+ void ShutdownDumpPool();
+
RunStatus HandleUserConsentDenied();
// Copies bugreport artifacts over to the caller's directories provided there is user consent or
@@ -539,6 +573,8 @@
android::sp<ConsentCallback> consent_callback_;
+ std::recursive_mutex mutex_;
+
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
new file mode 100644
index 0000000..0aff200
--- /dev/null
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for dumpstate_smoke_test">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="dumpstate_smoke_test" />
+ <option name="native-test-timeout" value="600000" />
+ </test>
+</configuration>
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 6f2d754..2c800c6 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -209,13 +209,12 @@
static std::shared_ptr<std::vector<SectionInfo>> sections;
static Dumpstate& ds;
static std::chrono::milliseconds duration;
- static void SetUpTestCase() {
+ static void GenerateBugreport() {
// clang-format off
char* argv[] = {
(char*)"dumpstate",
(char*)"-d",
- (char*)"-z",
- (char*)"-B"
+ (char*)"-z"
};
// clang-format on
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
@@ -236,20 +235,20 @@
std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
+ GenerateBugreport();
EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
}
-TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
+TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) {
struct stat st;
EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
- EXPECT_GE(st.st_size, 3000000 /* 3MB */);
- EXPECT_LE(st.st_size, 30000000 /* 30MB */);
+ EXPECT_GE(st.st_size, 1000000 /* 1MB */);
}
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
+TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
<< duration.count() << " s.";
- EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
+ EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
<< duration.count() << " s.";
}
@@ -266,7 +265,8 @@
CloseArchive(handle);
}
- void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
+ void FileExists(const char* filename, uint32_t minsize,
+ uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
ZipEntry entry;
GetEntry(handle, filename, &entry);
EXPECT_GT(entry.uncompressed_length, minsize);
@@ -285,7 +285,7 @@
main_entry.uncompressed_length);
// contains main entry file
- FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
+ FileExists(bugreport_txt_name.c_str(), 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
@@ -301,8 +301,9 @@
}
TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
- FileExists("dumpstate_board.bin", 1000000U, 80000000U);
- FileExists("dumpstate_board.txt", 100000U, 1000000U);
+ // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
+ // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
+ // FileExists("dumpstate_board.txt", 100000U, 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index c7df1bb..6b93692 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -21,6 +21,7 @@
#include "DumpstateService.h"
#include "android/os/BnDumpstate.h"
#include "dumpstate.h"
+#include "DumpPool.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -46,6 +47,7 @@
using ::android::hardware::dumpstate::V1_1::DumpstateMode;
using ::testing::EndsWith;
+using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsEmpty;
using ::testing::IsNull;
@@ -95,6 +97,10 @@
PropertiesHelper::unroot_ = unroot;
}
+ void SetParallelRun(bool parallel_run) const {
+ PropertiesHelper::parallel_run_ = parallel_run;
+ }
+
bool IsStandalone() const {
return calls_ == 1;
}
@@ -252,7 +258,6 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
- EXPECT_FALSE(options_.do_start_service);
EXPECT_FALSE(options_.limited_only);
}
@@ -261,7 +266,6 @@
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
@@ -297,7 +301,6 @@
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
// Other options retain default values
@@ -542,6 +545,10 @@
ds.options_.reset(new Dumpstate::DumpOptions());
}
+ void TearDown() {
+ ds.ShutdownDumpPool();
+ }
+
// Runs a command and capture `stdout` and `stderr`.
int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
@@ -569,6 +576,10 @@
ds.progress_.reset(new Progress(initial_max, progress, 1.2));
}
+ void EnableParallelRunIfNeeded() {
+ ds.EnableParallelRunIfNeeded();
+ }
+
std::string GetProgressMessage(int progress, int max,
int old_max = 0, bool update_progress = true) {
EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
@@ -1007,6 +1018,30 @@
ds.listener_.clear();
}
+TEST_F(DumpstateTest, DumpPool_withOutputToFileAndParallelRunEnabled_notNull) {
+ ds.options_->use_socket = false;
+ SetParallelRun(true);
+ EnableParallelRunIfNeeded();
+ EXPECT_TRUE(ds.options_->OutputToFile());
+ EXPECT_TRUE(ds.zip_entry_tasks_);
+ EXPECT_TRUE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
+ ds.options_->use_socket = true;
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.options_->OutputToFile());
+ EXPECT_FALSE(ds.zip_entry_tasks_);
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+ SetParallelRun(false);
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.zip_entry_tasks_);
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
class DumpstateServiceTest : public DumpstateBaseTest {
public:
DumpstateService dss;
@@ -1618,6 +1653,154 @@
EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
}
+class DumpPoolTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ dump_pool_ = std::make_unique<DumpPool>(kTestDataPath);
+ DumpstateBaseTest::SetUp();
+ CreateOutputFile();
+ }
+
+ void CreateOutputFile() {
+ out_path_ = kTestDataPath + "out.txt";
+ out_fd_.reset(TEMP_FAILURE_RETRY(open(out_path_.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ ASSERT_GE(out_fd_.get(), 0) << "could not create FD for path "
+ << out_path_;
+ }
+
+ int getTempFileCounts(const std::string& folder) {
+ int count = 0;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ return -1;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ return -1;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(DumpPool::PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ count++;
+ }
+ return count;
+ }
+
+ void setLogDuration(bool log_duration) {
+ dump_pool_->setLogDuration(log_duration);
+ }
+
+ std::unique_ptr<DumpPool> dump_pool_;
+ android::base::unique_fd out_fd_;
+ std::string out_path_;
+};
+
+TEST_F(DumpPoolTest, EnqueueTaskWithFd) {
+ auto dump_func_1 = [](int out_fd) {
+ dprintf(out_fd, "A");
+ };
+ auto dump_func_2 = [](int out_fd) {
+ dprintf(out_fd, "B");
+ sleep(1);
+ };
+ auto dump_func_3 = [](int out_fd) {
+ dprintf(out_fd, "C");
+ };
+ setLogDuration(/* log_duration = */false);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->waitForTask("2", "", out_fd_.get());
+ dump_pool_->waitForTask("3", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_THAT(result, StrEq("A\nB\nC\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) {
+ bool run_1 = false;
+ auto dump_func_1 = [&]() {
+ run_1 = true;
+ };
+
+ dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_TRUE(run_1);
+ EXPECT_THAT(result, StrEq("------ 0.000s was the duration of '1' ------\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+class TaskQueueTest : public DumpstateBaseTest {
+public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ }
+
+ TaskQueue task_queue_;
+};
+
+TEST_F(TaskQueueTest, runTask) {
+ bool is_task1_run = false;
+ bool is_task2_run = false;
+ auto task_1 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task1_run = true;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task2_run = true;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */false);
+
+ EXPECT_TRUE(is_task1_run);
+ EXPECT_TRUE(is_task2_run);
+}
+
+TEST_F(TaskQueueTest, runTask_withCancelled) {
+ bool is_task1_cancelled = false;
+ bool is_task2_cancelled = false;
+ auto task_1 = [&](bool task_cancelled) {
+ is_task1_cancelled = task_cancelled;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ is_task2_cancelled = task_cancelled;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */true);
+
+ EXPECT_TRUE(is_task1_cancelled);
+ EXPECT_TRUE(is_task2_cancelled);
+}
+
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index bd45005..7c9e3b2 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -41,6 +41,21 @@
"liblogwrap",
],
test_config: "installd_cache_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -66,6 +81,21 @@
"liblogwrap",
],
test_config: "installd_service_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -93,6 +123,21 @@
"libz",
],
test_config: "installd_dexopt_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
index e9df279..b2b8671 100644
--- a/libs/binder/ParcelableHolder.cpp
+++ b/libs/binder/ParcelableHolder.cpp
@@ -27,7 +27,6 @@
namespace android {
namespace os {
status_t ParcelableHolder::writeToParcel(Parcel* p) const {
- std::lock_guard<std::mutex> l(mMutex);
RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
if (this->mParcelPtr) {
RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
@@ -53,7 +52,6 @@
}
status_t ParcelableHolder::readFromParcel(const Parcel* p) {
- std::lock_guard<std::mutex> l(mMutex);
this->mStability = static_cast<Stability>(p->readInt32());
this->mParcelable = nullptr;
this->mParcelableName = std::nullopt;
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 5da2515..4ea3dd3 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -59,7 +59,6 @@
template <typename T>
bool setParcelable(std::shared_ptr<T> p) {
- std::lock_guard<std::mutex> l(mMutex);
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
if (p && this->getStability() > p->getStability()) {
return false;
@@ -73,7 +72,6 @@
template <typename T>
std::shared_ptr<T> getParcelable() const {
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
- std::lock_guard<std::mutex> l(mMutex);
const std::string& parcelableDesc = T::getParcelableDescriptor();
if (!this->mParcelPtr) {
if (!this->mParcelable || !this->mParcelableName) {
@@ -135,7 +133,6 @@
mutable std::optional<std::string> mParcelableName;
mutable std::unique_ptr<Parcel> mParcelPtr;
Stability mStability;
- mutable std::mutex mMutex;
};
} // namespace os
} // namespace android
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 5f2f382..dfcf4dc 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -50,7 +50,6 @@
virtual ~AParcelableHolder() = default;
binder_status_t writeToParcel(AParcel* parcel) const {
- std::lock_guard<std::mutex> l(mMutex);
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, AParcel_getDataSize(this->mParcel.get())));
RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0,
@@ -59,8 +58,6 @@
}
binder_status_t readFromParcel(const AParcel* parcel) {
- std::lock_guard<std::mutex> l(mMutex);
-
AParcel_reset(mParcel.get());
RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
@@ -86,7 +83,6 @@
template <typename T>
bool setParcelable(T* p) {
- std::lock_guard<std::mutex> l(mMutex);
if (p && this->mStability > T::_aidl_stability) {
return false;
}
@@ -98,7 +94,6 @@
template <typename T>
std::unique_ptr<T> getParcelable() const {
- std::lock_guard<std::mutex> l(mMutex);
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
if (AParcel_getDataSize(mParcel.get()) == 0) {
@@ -119,7 +114,6 @@
private:
mutable ndk::ScopedAParcel mParcel;
- mutable std::mutex mMutex;
parcelable_stability_t mStability;
};
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 9c80212..59ef3c8 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -15,6 +15,7 @@
*/
import java.io.PrintStream;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -201,6 +202,33 @@
out.println(iii + ");");
}
+ // Function to automatically generate properly formatted function calls that
+ // comply with clang format rules
+ public static String formatFunctionCall(String indent, String functionCall) {
+ final int MAXLEN = 100;
+ String tokens[] = functionCall.split("\\(|\\)", 2);
+ String params[] = tokens[1].split(",\\s*");
+ String formatted = indent + tokens[0] + "(";
+ char[] chars = new char[indent.length() + tokens[0].length() + 1];
+ Arrays.fill(chars, ' ');
+ String multiIndent = new String(chars);
+ ArrayList<String> lines = new ArrayList<String>();
+ for(int i = 0; i < params.length; i++) {
+ String terminator = ((i == params.length - 1) ? "" : ",");
+ if(indent.length() + formatted.length() + params[i].length() > MAXLEN) {
+ lines.add(formatted);
+ if (!indent.equals(multiIndent)) {
+ indent = multiIndent;
+ }
+ formatted = indent + params[i] + terminator;
+ } else {
+ formatted += (i == 0 ? "" : " ") + params[i] + terminator;
+ }
+ }
+ lines.add(formatted);
+ return String.join("\n", lines);
+ }
+
void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
String iii) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
@@ -1538,14 +1566,19 @@
"_exception ? JNI_ABORT : 0" : "0")) +
");");
} else {
- out.println(indent + indent +
+ String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
+ "_" + cfunc.getArgName(cIndex) + "BufferOffset";
+ String typeCast = "(char *)" + cfunc.getArgName(cIndex);
+ String withOffset = "(void *)(" + typeCast + " - " + bufferOffset + ")";
+ String releasePointerCall = (
"releasePointer(_env, " + array + ", " +
- cfunc.getArgName(cIndex) +
+ withOffset +
", " +
(cfunc.getArgType(cIndex).isConst() ?
"JNI_FALSE" : (emitExceptionCheck ?
"_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
");");
+ out.println(formatFunctionCall(indent + indent, releasePointerCall));
}
out.println(indent + "}");
}