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