blob: 7324ead7c60d1eb4290a4dea1d245e4f534256e1 [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
36DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false) {
37 assert(!tmp_root.empty());
38 deleteTempFiles(tmp_root_);
39}
40
41DumpPool::~DumpPool() {
42 shutdown();
43}
44
45void DumpPool::start(int thread_counts) {
46 assert(thread_counts > 0);
47 assert(threads_.empty());
48 if (thread_counts > MAX_THREAD_COUNT) {
49 thread_counts = MAX_THREAD_COUNT;
50 }
51 MYLOGI("Start thread pool:%d", thread_counts);
52 shutdown_ = false;
53 for (int i = 0; i < thread_counts; i++) {
54 threads_.emplace_back(std::thread([=]() {
55 setThreadName(pthread_self(), i + 1);
56 loop();
57 }));
58 }
59}
60
61void DumpPool::shutdown() {
62 std::unique_lock lock(lock_);
63 if (shutdown_ || threads_.empty()) {
64 return;
65 }
66 while (!tasks_.empty()) tasks_.pop();
67 futures_map_.clear();
68
69 shutdown_ = true;
70 condition_variable_.notify_all();
71 lock.unlock();
72
73 for (auto& thread : threads_) {
74 thread.join();
75 }
76 threads_.clear();
77 deleteTempFiles(tmp_root_);
78 MYLOGI("shutdown thread pool");
79}
80
81void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
82 int out_fd) {
83 DurationReporter duration_reporter("Wait for " + task_name, true);
84 auto iterator = futures_map_.find(task_name);
85 if (iterator == futures_map_.end()) {
86 MYLOGW("Task %s does not exist", task_name.c_str());
87 return;
88 }
89 Future future = iterator->second;
90 futures_map_.erase(iterator);
91
92 std::string result = future.get();
93 if (result.empty()) {
94 return;
95 }
96 DumpFileToFd(out_fd, title, result);
97 if (unlink(result.c_str())) {
98 MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
99 }
100}
101
102std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
103 auto tmp_file_ptr = std::make_unique<TmpFile>();
104 std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
105 snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
106 tmp_root_.c_str());
107 tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
108 mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
109 if (tmp_file_ptr->fd.get() == -1) {
110 MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
111 tmp_file_ptr = nullptr;
112 return tmp_file_ptr;
113 }
114 return tmp_file_ptr;
115}
116
117void DumpPool::deleteTempFiles(const std::string& folder) {
118 std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
119 &closedir);
120 if (!dir_ptr) {
121 MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
122 return;
123 }
124 int dir_fd = dirfd(dir_ptr.get());
125 if (dir_fd < 0) {
126 MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
127 strerror(errno));
128 return;
129 }
130
131 struct dirent* de;
132 while ((de = readdir(dir_ptr.get()))) {
133 if (de->d_type != DT_REG) {
134 continue;
135 }
136 std::string file_name(de->d_name);
137 if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
138 continue;
139 }
140 if (unlinkat(dir_fd, file_name.c_str(), 0)) {
141 MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
142 strerror(errno));
143 }
144 }
145}
146
147void DumpPool::setThreadName(const pthread_t thread, int id) {
148 std::array<char, 15> name;
149 snprintf(name.data(), name.size(), "dumpstate_%d", id);
150 pthread_setname_np(thread, name.data());
151}
152
153void DumpPool::loop() {
154 std::unique_lock lock(lock_);
155 while (!shutdown_) {
156 if (tasks_.empty()) {
157 condition_variable_.wait(lock);
158 continue;
159 } else {
160 std::packaged_task<std::string()> task = std::move(tasks_.front());
161 tasks_.pop();
162 lock.unlock();
163 std::invoke(task);
164 lock.lock();
165 }
166 }
167}
168
169} // namespace dumpstate
170} // namespace os
171} // namespace android