diff options
author | 2020-07-16 17:37:39 +0800 | |
---|---|---|
committer | 2020-10-19 04:08:01 +0000 | |
commit | 1c855127ea2e2da223253b0dadcadde9a3ee94d0 (patch) | |
tree | 366b8fc392bb51b0929dd9709ba8b159a3437501 | |
parent | 27077b1393e20c0e90fb7beeea7e3bba6014638e (diff) |
Faster bugreports (2/n)
- Having a system property to disable parallel run function.
- Post 'DumpTraces' to the thread pool. 3% is
improved compared with single thread run.
Bug: 136262402
Test: atest dumpstate_test
Test: atest dumpstate_smoke_test
Test: Manual enable and disable parallel run
Test: Manual trigger report using hardware key
Test: Manual trigger using bugreport shortcut
Change-Id: I2185cc3e2f429a150605d6268324c52d137db385
Merged-In: I2185cc3e2f429a150605d6268324c52d137db385
(cherry picked from commit 5377d797fff94e0e070fdbbadc89288d90cc5f2c)
-rw-r--r-- | cmds/dumpstate/DumpPool.cpp | 23 | ||||
-rw-r--r-- | cmds/dumpstate/DumpPool.h | 76 | ||||
-rw-r--r-- | cmds/dumpstate/DumpstateUtil.cpp | 9 | ||||
-rw-r--r-- | cmds/dumpstate/DumpstateUtil.h | 8 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 82 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.h | 13 | ||||
-rw-r--r-- | cmds/dumpstate/tests/dumpstate_test.cpp | 74 |
7 files changed, 247 insertions, 38 deletions
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp index 7324ead7c6..e174c8eff3 100644 --- a/cmds/dumpstate/DumpPool.cpp +++ b/cmds/dumpstate/DumpPool.cpp @@ -33,7 +33,8 @@ namespace dumpstate { const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp."; -DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false) { +DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false), + log_duration_(true) { assert(!tmp_root.empty()); deleteTempFiles(tmp_root_); } @@ -99,6 +100,26 @@ void DumpPool::waitForTask(const std::string& task_name, const std::string& titl } } +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"; diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h index 266d519b6d..a4ea875541 100644 --- a/cmds/dumpstate/DumpPool.h +++ b/cmds/dumpstate/DumpPool.h @@ -29,26 +29,32 @@ 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 - * included a file descriptor as a parameter, and the task could dump results to - * that fd. For example: + * 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 DumpXXXX(int out_fd) { + * void DumpFoo(int out_fd) { * dprintf(out_fd, "Dump result to out_fd ..."); * } * ... * DumpPool pool(tmp_root); - * pool.enqueueTask("TaskName", &DumpXXXX, std::placeholders::_1); + * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1); * ... * pool.waitForTask("TaskName"); * - * DumpXXXX is a callable function included a out_fd parameter. Using the - * enqueueTask method in DumpPool to enqueue the task to the pool. The - * std::placeholders::_1 is placeholder for DumpPool to pass a fd argument. + * 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. @@ -72,17 +78,40 @@ class DumpPool { void shutdown(); /* - * Adds a task with a task name into the queue of the thread pool. + * Adds a task into the queue of the thread pool. * - * |task_name| The name of the task. - * |f| Callable function to execute the task. This function must - * include a parameter of file descriptor to output dump result. + * |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) { - auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...); - futures_map_[task_name] = post(func); + 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(); } @@ -111,13 +140,15 @@ class DumpPool { using Task = std::packaged_task<std::string()>; using Future = std::shared_future<std::string>; - template<class T> Future post(T dump_func) { + 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(""); } - std::invoke(dump_func, tmp_file_ptr->fd.get()); + invokeTask(dump_func, task_name, tmp_file_ptr->fd.get()); fsync(tmp_file_ptr->fd.get()); return std::string(tmp_file_ptr->path); }); @@ -138,12 +169,21 @@ class DumpPool { 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_; diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 4b69607156..eeaa5a3de0 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -180,6 +180,7 @@ CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeoutInMs(int64_t ti 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 @@ bool PropertiesHelper::IsUnroot() { 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 b7ac25c81e..b099443e32 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -176,10 +176,18 @@ class PropertiesHelper { */ 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/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 84de9f3596..472a0f1b2e 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -95,6 +95,7 @@ using ::android::hardware::dumpstate::V1_1::DumpstateStatus; 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,6 +114,7 @@ using android::base::StringPrintf; using android::os::IDumpstateListener; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; +using android::os::dumpstate::DumpPool; using android::os::dumpstate::PropertiesHelper; // Keep in sync with @@ -196,8 +198,26 @@ static const std::string ANR_FILE_PREFIX = "anr_"; 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(); + 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"; + namespace android { namespace os { namespace { @@ -762,8 +782,9 @@ void Dumpstate::PrintHeader() const { 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"); } @@ -1682,7 +1703,18 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { 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 +1757,15 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { 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; } @@ -1883,8 +1924,6 @@ static void DumpstateWifiOnly() { } 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]); @@ -2556,6 +2595,7 @@ void Dumpstate::Cancel() { */ 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"); @@ -2717,6 +2757,13 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // 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. @@ -2883,6 +2930,23 @@ void Dumpstate::CleanupTmpFiles() { 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_); +} + +void Dumpstate::ShutdownDumpPool() { + if (dump_pool_) { + dump_pool_->shutdown(); + dump_pool_ = nullptr; + } +} + Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() { MYLOGD("User denied consent; deleting files and returning\n"); CleanupTmpFiles(); @@ -3001,8 +3065,9 @@ Dumpstate& Dumpstate::GetInstance() { 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 +3081,8 @@ DurationReporter::~DurationReporter() { } 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()); } } } diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 0d25d307a6..d400dc7127 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -35,6 +35,7 @@ #include <ziparchive/zip_writer.h> #include "DumpstateUtil.h" +#include "DumpPool.h" // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to // std::vector<std::string> @@ -75,7 +76,7 @@ extern "C" { 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 +85,7 @@ class DurationReporter { bool logcat_only_; bool verbose_; uint64_t started_; + int duration_fd_; DISALLOW_COPY_AND_ASSIGN(DurationReporter); }; @@ -193,7 +195,7 @@ struct DumpData { * 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 }; @@ -490,6 +492,9 @@ class Dumpstate { // 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 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 +533,10 @@ class Dumpstate { // 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 diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index d0a48266f2..b3cb4349f3 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -97,6 +97,10 @@ class DumpstateBaseTest : public Test { PropertiesHelper::unroot_ = unroot; } + void SetParallelRun(bool parallel_run) const { + PropertiesHelper::parallel_run_ = parallel_run; + } + bool IsStandalone() const { return calls_ == 1; } @@ -544,6 +548,10 @@ class DumpstateTest : public DumpstateBaseTest { 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) { @@ -571,6 +579,10 @@ class DumpstateTest : public DumpstateBaseTest { 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"; @@ -1009,6 +1021,27 @@ TEST_F(DumpstateTest, DumpFileUpdateProgress) { 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.dump_pool_); +} + +TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) { + ds.options_->use_socket = true; + EnableParallelRunIfNeeded(); + EXPECT_FALSE(ds.options_->OutputToFile()); + EXPECT_FALSE(ds.dump_pool_); +} + +TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) { + SetParallelRun(false); + EnableParallelRunIfNeeded(); + EXPECT_FALSE(ds.dump_pool_); +} + class DumpstateServiceTest : public DumpstateBaseTest { public: DumpstateService dss; @@ -1623,6 +1656,7 @@ TEST_F(DumpstateUtilTest, DumpFileOnDryRun) { class DumpPoolTest : public DumpstateBaseTest { public: void SetUp() { + dump_pool_ = std::make_unique<DumpPool>(kTestDataPath); DumpstateBaseTest::SetUp(); CreateOutputFile(); } @@ -1662,12 +1696,16 @@ class DumpPoolTest : public DumpstateBaseTest { 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, EnqueueTask) { - DumpPool pool(kTestDataPath); +TEST_F(DumpPoolTest, EnqueueTaskWithFd) { auto dump_func_1 = [](int out_fd) { dprintf(out_fd, "A"); }; @@ -1678,19 +1716,37 @@ TEST_F(DumpPoolTest, EnqueueTask) { auto dump_func_3 = [](int out_fd) { dprintf(out_fd, "C"); }; - pool.enqueueTask(/* task_name = */"1", dump_func_1, std::placeholders::_1); - pool.enqueueTask(/* task_name = */"2", dump_func_2, std::placeholders::_1); - pool.enqueueTask(/* task_name = */"3", dump_func_3, std::placeholders::_1); + 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); - pool.waitForTask("1", "", out_fd_.get()); - pool.waitForTask("2", "", out_fd_.get()); - pool.waitForTask("3", "", out_fd_.get()); + 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)); - pool.shutdown(); +} + +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)); } |