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