1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #ifndef FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
18*38e8c45fSAndroid Build Coastguard Worker #define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
19*38e8c45fSAndroid Build Coastguard Worker
20*38e8c45fSAndroid Build Coastguard Worker #include <future>
21*38e8c45fSAndroid Build Coastguard Worker #include <queue>
22*38e8c45fSAndroid Build Coastguard Worker #include <string>
23*38e8c45fSAndroid Build Coastguard Worker
24*38e8c45fSAndroid Build Coastguard Worker #include <android-base/file.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <android-base/macros.h>
26*38e8c45fSAndroid Build Coastguard Worker
27*38e8c45fSAndroid Build Coastguard Worker namespace android {
28*38e8c45fSAndroid Build Coastguard Worker namespace os {
29*38e8c45fSAndroid Build Coastguard Worker namespace dumpstate {
30*38e8c45fSAndroid Build Coastguard Worker
31*38e8c45fSAndroid Build Coastguard Worker class DumpPoolTest;
32*38e8c45fSAndroid Build Coastguard Worker
33*38e8c45fSAndroid Build Coastguard Worker /*
34*38e8c45fSAndroid Build Coastguard Worker * Waits until the task is finished. Dumps the task results to the specified
35*38e8c45fSAndroid Build Coastguard Worker * out_fd.
36*38e8c45fSAndroid Build Coastguard Worker *
37*38e8c45fSAndroid Build Coastguard Worker * |future| The task future.
38*38e8c45fSAndroid Build Coastguard Worker * |title| Dump title string to the out_fd, an empty string for nothing.
39*38e8c45fSAndroid Build Coastguard Worker * |out_fd| The target file to dump the result from the task.
40*38e8c45fSAndroid Build Coastguard Worker */
41*38e8c45fSAndroid Build Coastguard Worker void WaitForTask(std::future<std::string> future, const std::string& title, int out_fd);
42*38e8c45fSAndroid Build Coastguard Worker
43*38e8c45fSAndroid Build Coastguard Worker /*
44*38e8c45fSAndroid Build Coastguard Worker * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
45*38e8c45fSAndroid Build Coastguard Worker */
46*38e8c45fSAndroid Build Coastguard Worker
WaitForTask(std::future<std::string> future)47*38e8c45fSAndroid Build Coastguard Worker inline void WaitForTask(std::future<std::string> future) {
48*38e8c45fSAndroid Build Coastguard Worker WaitForTask(std::move(future), "", STDOUT_FILENO);
49*38e8c45fSAndroid Build Coastguard Worker }
50*38e8c45fSAndroid Build Coastguard Worker
51*38e8c45fSAndroid Build Coastguard Worker /*
52*38e8c45fSAndroid Build Coastguard Worker * A thread pool with the fixed number of threads to execute multiple dump tasks
53*38e8c45fSAndroid Build Coastguard Worker * simultaneously for dumpstate. The dump task is a callable function. It
54*38e8c45fSAndroid Build Coastguard Worker * could include a file descriptor as a parameter to redirect dump results, if
55*38e8c45fSAndroid Build Coastguard Worker * it needs to output results to the bugreport. This can avoid messing up
56*38e8c45fSAndroid Build Coastguard Worker * bugreport's results when multiple dump tasks are running at the same time.
57*38e8c45fSAndroid Build Coastguard Worker * Takes an example below for the usage of the DumpPool:
58*38e8c45fSAndroid Build Coastguard Worker *
59*38e8c45fSAndroid Build Coastguard Worker * void DumpFoo(int out_fd) {
60*38e8c45fSAndroid Build Coastguard Worker * dprintf(out_fd, "Dump result to out_fd ...");
61*38e8c45fSAndroid Build Coastguard Worker * }
62*38e8c45fSAndroid Build Coastguard Worker * ...
63*38e8c45fSAndroid Build Coastguard Worker * DumpPool pool(tmp_root);
64*38e8c45fSAndroid Build Coastguard Worker * auto task = pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
65*38e8c45fSAndroid Build Coastguard Worker * ...
66*38e8c45fSAndroid Build Coastguard Worker * WaitForTask(task);
67*38e8c45fSAndroid Build Coastguard Worker *
68*38e8c45fSAndroid Build Coastguard Worker * DumpFoo is a callable function included a out_fd parameter. Using the
69*38e8c45fSAndroid Build Coastguard Worker * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
70*38e8c45fSAndroid Build Coastguard Worker * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
71*38e8c45fSAndroid Build Coastguard Worker *
72*38e8c45fSAndroid Build Coastguard Worker * std::futures returned by `enqueueTask*()` must all have their `get` methods
73*38e8c45fSAndroid Build Coastguard Worker * called, or have been destroyed before the DumpPool itself is destroyed.
74*38e8c45fSAndroid Build Coastguard Worker */
75*38e8c45fSAndroid Build Coastguard Worker class DumpPool {
76*38e8c45fSAndroid Build Coastguard Worker friend class android::os::dumpstate::DumpPoolTest;
77*38e8c45fSAndroid Build Coastguard Worker
78*38e8c45fSAndroid Build Coastguard Worker public:
79*38e8c45fSAndroid Build Coastguard Worker /*
80*38e8c45fSAndroid Build Coastguard Worker * Creates a thread pool.
81*38e8c45fSAndroid Build Coastguard Worker *
82*38e8c45fSAndroid Build Coastguard Worker * |tmp_root| A path to a temporary folder for threads to create temporary
83*38e8c45fSAndroid Build Coastguard Worker * files.
84*38e8c45fSAndroid Build Coastguard Worker */
85*38e8c45fSAndroid Build Coastguard Worker explicit DumpPool(const std::string& tmp_root);
86*38e8c45fSAndroid Build Coastguard Worker
87*38e8c45fSAndroid Build Coastguard Worker /*
88*38e8c45fSAndroid Build Coastguard Worker * Will waits until all threads exit the loop. Destroying DumpPool before destroying the
89*38e8c45fSAndroid Build Coastguard Worker * associated std::futures created by `enqueueTask*` will cause an abort on Android because
90*38e8c45fSAndroid Build Coastguard Worker * Android is built with `-fno-exceptions`.
91*38e8c45fSAndroid Build Coastguard Worker */
92*38e8c45fSAndroid Build Coastguard Worker ~DumpPool();
93*38e8c45fSAndroid Build Coastguard Worker
94*38e8c45fSAndroid Build Coastguard Worker /*
95*38e8c45fSAndroid Build Coastguard Worker * Starts the threads in the pool.
96*38e8c45fSAndroid Build Coastguard Worker *
97*38e8c45fSAndroid Build Coastguard Worker * |thread_counts| the number of threads to start.
98*38e8c45fSAndroid Build Coastguard Worker */
99*38e8c45fSAndroid Build Coastguard Worker void start(int thread_counts = MAX_THREAD_COUNT);
100*38e8c45fSAndroid Build Coastguard Worker
101*38e8c45fSAndroid Build Coastguard Worker /*
102*38e8c45fSAndroid Build Coastguard Worker * Adds a task into the queue of the thread pool.
103*38e8c45fSAndroid Build Coastguard Worker *
104*38e8c45fSAndroid Build Coastguard Worker * |duration_title| The name of the task. It's also the title of the
105*38e8c45fSAndroid Build Coastguard Worker * DurationReporter log.
106*38e8c45fSAndroid Build Coastguard Worker * |f| Callable function to execute the task.
107*38e8c45fSAndroid Build Coastguard Worker * |args| A list of arguments.
108*38e8c45fSAndroid Build Coastguard Worker *
109*38e8c45fSAndroid Build Coastguard Worker * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
110*38e8c45fSAndroid Build Coastguard Worker */
111*38e8c45fSAndroid Build Coastguard Worker template<class F, class... Args>
enqueueTask(const std::string & duration_title,F && f,Args &&...args)112*38e8c45fSAndroid Build Coastguard Worker std::future<std::string> enqueueTask(const std::string& duration_title, F&& f, Args&&... args) {
113*38e8c45fSAndroid Build Coastguard Worker std::function<void(void)> func = std::bind(std::forward<F>(f),
114*38e8c45fSAndroid Build Coastguard Worker std::forward<Args>(args)...);
115*38e8c45fSAndroid Build Coastguard Worker auto future = post(duration_title, func);
116*38e8c45fSAndroid Build Coastguard Worker if (threads_.empty()) {
117*38e8c45fSAndroid Build Coastguard Worker start();
118*38e8c45fSAndroid Build Coastguard Worker }
119*38e8c45fSAndroid Build Coastguard Worker return future;
120*38e8c45fSAndroid Build Coastguard Worker }
121*38e8c45fSAndroid Build Coastguard Worker
122*38e8c45fSAndroid Build Coastguard Worker /*
123*38e8c45fSAndroid Build Coastguard Worker * Adds a task into the queue of the thread pool. The task takes a file
124*38e8c45fSAndroid Build Coastguard Worker * descriptor as a parameter to redirect dump results to a temporary file.
125*38e8c45fSAndroid Build Coastguard Worker *
126*38e8c45fSAndroid Build Coastguard Worker * |duration_title| The title of the DurationReporter log.
127*38e8c45fSAndroid Build Coastguard Worker * |f| Callable function to execute the task.
128*38e8c45fSAndroid Build Coastguard Worker * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
129*38e8c45fSAndroid Build Coastguard Worker * argument needs to be included here.
130*38e8c45fSAndroid Build Coastguard Worker */
enqueueTaskWithFd(const std::string & duration_title,F && f,Args &&...args)131*38e8c45fSAndroid Build Coastguard Worker template<class F, class... Args> std::future<std::string> enqueueTaskWithFd(
132*38e8c45fSAndroid Build Coastguard Worker const std::string& duration_title, F&& f, Args&&... args) {
133*38e8c45fSAndroid Build Coastguard Worker std::function<void(int)> func = std::bind(std::forward<F>(f),
134*38e8c45fSAndroid Build Coastguard Worker std::forward<Args>(args)...);
135*38e8c45fSAndroid Build Coastguard Worker auto future = post(duration_title, func);
136*38e8c45fSAndroid Build Coastguard Worker if (threads_.empty()) {
137*38e8c45fSAndroid Build Coastguard Worker start();
138*38e8c45fSAndroid Build Coastguard Worker }
139*38e8c45fSAndroid Build Coastguard Worker return future;
140*38e8c45fSAndroid Build Coastguard Worker }
141*38e8c45fSAndroid Build Coastguard Worker
142*38e8c45fSAndroid Build Coastguard Worker /*
143*38e8c45fSAndroid Build Coastguard Worker * Deletes temporary files created by DumpPool.
144*38e8c45fSAndroid Build Coastguard Worker */
145*38e8c45fSAndroid Build Coastguard Worker void deleteTempFiles();
146*38e8c45fSAndroid Build Coastguard Worker
147*38e8c45fSAndroid Build Coastguard Worker static const std::string PREFIX_TMPFILE_NAME;
148*38e8c45fSAndroid Build Coastguard Worker
149*38e8c45fSAndroid Build Coastguard Worker private:
150*38e8c45fSAndroid Build Coastguard Worker using Task = std::packaged_task<std::string()>;
151*38e8c45fSAndroid Build Coastguard Worker
152*38e8c45fSAndroid Build Coastguard Worker template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
153*38e8c45fSAndroid Build Coastguard Worker
154*38e8c45fSAndroid Build Coastguard Worker template<class T>
post(const std::string & duration_title,T dump_func)155*38e8c45fSAndroid Build Coastguard Worker std::future<std::string> post(const std::string& duration_title, T dump_func) {
156*38e8c45fSAndroid Build Coastguard Worker Task packaged_task([=]() {
157*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
158*38e8c45fSAndroid Build Coastguard Worker if (!tmp_file_ptr) {
159*38e8c45fSAndroid Build Coastguard Worker return std::string("");
160*38e8c45fSAndroid Build Coastguard Worker }
161*38e8c45fSAndroid Build Coastguard Worker invokeTask(dump_func, duration_title, tmp_file_ptr->fd.get());
162*38e8c45fSAndroid Build Coastguard Worker fsync(tmp_file_ptr->fd.get());
163*38e8c45fSAndroid Build Coastguard Worker return std::string(tmp_file_ptr->path);
164*38e8c45fSAndroid Build Coastguard Worker });
165*38e8c45fSAndroid Build Coastguard Worker std::unique_lock lock(lock_);
166*38e8c45fSAndroid Build Coastguard Worker auto future = packaged_task.get_future();
167*38e8c45fSAndroid Build Coastguard Worker tasks_.push(std::move(packaged_task));
168*38e8c45fSAndroid Build Coastguard Worker condition_variable_.notify_one();
169*38e8c45fSAndroid Build Coastguard Worker return future;
170*38e8c45fSAndroid Build Coastguard Worker }
171*38e8c45fSAndroid Build Coastguard Worker
172*38e8c45fSAndroid Build Coastguard Worker typedef struct {
173*38e8c45fSAndroid Build Coastguard Worker android::base::unique_fd fd;
174*38e8c45fSAndroid Build Coastguard Worker char path[1024];
175*38e8c45fSAndroid Build Coastguard Worker } TmpFile;
176*38e8c45fSAndroid Build Coastguard Worker
177*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<TmpFile> createTempFile();
178*38e8c45fSAndroid Build Coastguard Worker void deleteTempFiles(const std::string& folder);
179*38e8c45fSAndroid Build Coastguard Worker void setThreadName(const pthread_t thread, int id);
180*38e8c45fSAndroid Build Coastguard Worker void loop();
181*38e8c45fSAndroid Build Coastguard Worker
182*38e8c45fSAndroid Build Coastguard Worker /*
183*38e8c45fSAndroid Build Coastguard Worker * For test purpose only. Enables or disables logging duration of the task.
184*38e8c45fSAndroid Build Coastguard Worker *
185*38e8c45fSAndroid Build Coastguard Worker * |log_duration| if true, DurationReporter is initiated to log duration of
186*38e8c45fSAndroid Build Coastguard Worker * the task.
187*38e8c45fSAndroid Build Coastguard Worker */
188*38e8c45fSAndroid Build Coastguard Worker void setLogDuration(bool log_duration);
189*38e8c45fSAndroid Build Coastguard Worker
190*38e8c45fSAndroid Build Coastguard Worker private:
191*38e8c45fSAndroid Build Coastguard Worker static const int MAX_THREAD_COUNT = 4;
192*38e8c45fSAndroid Build Coastguard Worker
193*38e8c45fSAndroid Build Coastguard Worker /* A path to a temporary folder for threads to create temporary files. */
194*38e8c45fSAndroid Build Coastguard Worker std::string tmp_root_;
195*38e8c45fSAndroid Build Coastguard Worker bool shutdown_;
196*38e8c45fSAndroid Build Coastguard Worker bool log_duration_; // For test purpose only, the default value is true.
197*38e8c45fSAndroid Build Coastguard Worker std::mutex lock_; // A lock for the tasks_.
198*38e8c45fSAndroid Build Coastguard Worker std::condition_variable condition_variable_;
199*38e8c45fSAndroid Build Coastguard Worker
200*38e8c45fSAndroid Build Coastguard Worker std::vector<std::thread> threads_;
201*38e8c45fSAndroid Build Coastguard Worker std::queue<Task> tasks_;
202*38e8c45fSAndroid Build Coastguard Worker
203*38e8c45fSAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(DumpPool);
204*38e8c45fSAndroid Build Coastguard Worker };
205*38e8c45fSAndroid Build Coastguard Worker
206*38e8c45fSAndroid Build Coastguard Worker } // namespace dumpstate
207*38e8c45fSAndroid Build Coastguard Worker } // namespace os
208*38e8c45fSAndroid Build Coastguard Worker } // namespace android
209*38e8c45fSAndroid Build Coastguard Worker
210*38e8c45fSAndroid Build Coastguard Worker #endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
211