blob: 4d3a67bcd3be586548150b2c518d06ea6bca86cd [file] [log] [blame]
Rhed Jao27077b12020-07-14 18:38:08 +08001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "dumpstate"
18
19#include "DumpPool.h"
20
21#include <array>
22#include <thread>
23
24#include <log/log.h>
25
26#include "dumpstate.h"
27#include "DumpstateInternal.h"
28#include "DumpstateUtil.h"
29
30namespace android {
31namespace os {
32namespace dumpstate {
33
34const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
35
Chris Morinbc223142022-02-04 14:17:11 -080036
37void WaitForTask(std::future<std::string> future, const std::string& title, int out_fd) {
38 DurationReporter duration_reporter("Wait for " + title, true);
39
40 std::string result = future.get();
41 if (result.empty()) {
42 return;
43 }
44 DumpFileToFd(out_fd, title, result);
45 if (unlink(result.c_str())) {
46 MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
47 }
48}
49
Rhed Jao1c855122020-07-16 17:37:39 +080050DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
51 log_duration_(true) {
Rhed Jao27077b12020-07-14 18:38:08 +080052 assert(!tmp_root.empty());
53 deleteTempFiles(tmp_root_);
54}
55
56DumpPool::~DumpPool() {
Rhed Jao27077b12020-07-14 18:38:08 +080057 std::unique_lock lock(lock_);
58 if (shutdown_ || threads_.empty()) {
59 return;
60 }
Rhed Jao34b041f2021-03-17 18:53:39 +080061 while (!tasks_.empty()) tasks_.pop();
Rhed Jao27077b12020-07-14 18:38:08 +080062
63 shutdown_ = true;
64 condition_variable_.notify_all();
65 lock.unlock();
66
67 for (auto& thread : threads_) {
68 thread.join();
69 }
70 threads_.clear();
71 deleteTempFiles(tmp_root_);
Chris Morinbc223142022-02-04 14:17:11 -080072 MYLOGI("shutdown thread pool\n");
Rhed Jao27077b12020-07-14 18:38:08 +080073}
74
Chris Morinbc223142022-02-04 14:17:11 -080075void DumpPool::start(int thread_counts) {
76 assert(thread_counts > 0);
77 assert(threads_.empty());
78 if (thread_counts > MAX_THREAD_COUNT) {
79 thread_counts = MAX_THREAD_COUNT;
Rhed Jao27077b12020-07-14 18:38:08 +080080 }
Chris Morinbc223142022-02-04 14:17:11 -080081 MYLOGI("Start thread pool:%d\n", thread_counts);
82 shutdown_ = false;
83 for (int i = 0; i < thread_counts; i++) {
84 threads_.emplace_back(std::thread([=]() {
85 setThreadName(pthread_self(), i + 1);
86 loop();
87 }));
Rhed Jao27077b12020-07-14 18:38:08 +080088 }
89}
90
Rhed Jaoe96bcd52020-08-21 14:48:20 +080091void DumpPool::deleteTempFiles() {
92 deleteTempFiles(tmp_root_);
93}
94
Rhed Jao1c855122020-07-16 17:37:39 +080095void DumpPool::setLogDuration(bool log_duration) {
96 log_duration_ = log_duration;
97}
98
99template <>
100void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
101 const std::string& duration_title, int out_fd) {
102 DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
103 /*verbose =*/false, out_fd);
104 std::invoke(dump_func);
105}
106
107template <>
108void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
109 const std::string& duration_title, int out_fd) {
110 DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
111 /*verbose =*/false, out_fd);
112 std::invoke(dump_func, out_fd);
113}
114
Rhed Jao27077b12020-07-14 18:38:08 +0800115std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
116 auto tmp_file_ptr = std::make_unique<TmpFile>();
117 std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
118 snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
119 tmp_root_.c_str());
120 tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
121 mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
122 if (tmp_file_ptr->fd.get() == -1) {
123 MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
124 tmp_file_ptr = nullptr;
125 return tmp_file_ptr;
126 }
127 return tmp_file_ptr;
128}
129
130void DumpPool::deleteTempFiles(const std::string& folder) {
131 std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
132 &closedir);
133 if (!dir_ptr) {
134 MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
135 return;
136 }
137 int dir_fd = dirfd(dir_ptr.get());
138 if (dir_fd < 0) {
139 MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
140 strerror(errno));
141 return;
142 }
143
144 struct dirent* de;
145 while ((de = readdir(dir_ptr.get()))) {
146 if (de->d_type != DT_REG) {
147 continue;
148 }
149 std::string file_name(de->d_name);
150 if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
151 continue;
152 }
153 if (unlinkat(dir_fd, file_name.c_str(), 0)) {
154 MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
155 strerror(errno));
156 }
157 }
158}
159
160void DumpPool::setThreadName(const pthread_t thread, int id) {
161 std::array<char, 15> name;
162 snprintf(name.data(), name.size(), "dumpstate_%d", id);
163 pthread_setname_np(thread, name.data());
164}
165
166void DumpPool::loop() {
167 std::unique_lock lock(lock_);
168 while (!shutdown_) {
169 if (tasks_.empty()) {
170 condition_variable_.wait(lock);
171 continue;
172 } else {
173 std::packaged_task<std::string()> task = std::move(tasks_.front());
174 tasks_.pop();
175 lock.unlock();
176 std::invoke(task);
177 lock.lock();
178 }
179 }
180}
181
182} // namespace dumpstate
183} // namespace os
184} // namespace android