1 /* 2 * 3 * Copyright 2017, The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ 19 #define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ 20 21 #include <chrono> 22 #include <condition_variable> 23 #include <cstdio> 24 #include <cstdlib> 25 #include <memory> 26 #include <mutex> 27 #include <string> 28 #include <thread> 29 #include <functional> 30 31 #include <inttypes.h> 32 #include <unistd.h> 33 34 #include <android-base/logging.h> 35 #include <android-base/stringprintf.h> 36 37 #include "perfprofd_record.pb.h" 38 39 #include "config.h" 40 #include "dropbox.h" 41 #include "perfprofdcore.h" 42 #include "perfprofd_io.h" 43 44 namespace android { 45 namespace perfprofd { 46 47 class ThreadedConfig : public Config { 48 public: Sleep(size_t seconds)49 void Sleep(size_t seconds) override { 50 if (seconds == 0) { 51 return; 52 } 53 std::unique_lock<std::mutex> guard(mutex_); 54 using namespace std::chrono_literals; 55 cv_.wait_for(guard, seconds * 1s, [&]() { return interrupted_; }); 56 } ShouldStopProfiling()57 bool ShouldStopProfiling() override { 58 std::unique_lock<std::mutex> guard(mutex_); 59 return interrupted_; 60 } 61 ResetStopProfiling()62 void ResetStopProfiling() { 63 std::unique_lock<std::mutex> guard(mutex_); 64 interrupted_ = false; 65 } StopProfiling()66 void StopProfiling() { 67 std::unique_lock<std::mutex> guard(mutex_); 68 interrupted_ = true; 69 cv_.notify_all(); 70 } 71 IsProfilingEnabled()72 bool IsProfilingEnabled() const override { 73 return true; 74 } 75 76 // Operator= to simplify setting the config values. This will retain the 77 // original mutex, condition-variable etc. 78 ThreadedConfig& operator=(const ThreadedConfig& rhs) { 79 // Copy base fields. 80 *static_cast<Config*>(this) = static_cast<const Config&>(rhs); 81 82 return *this; 83 } 84 85 private: 86 bool is_profiling = false; 87 std::mutex mutex_; 88 std::condition_variable cv_; 89 bool interrupted_ = false; 90 91 friend class ThreadedHandler; 92 }; 93 94 class ThreadedHandler { 95 public: ThreadedHandler()96 ThreadedHandler() : cur_config_(new ThreadedConfig()) {} ThreadedHandler(ThreadedConfig * in)97 explicit ThreadedHandler(ThreadedConfig* in) : cur_config_(in) { 98 CHECK(cur_config_ != nullptr); 99 } 100 ~ThreadedHandler()101 virtual ~ThreadedHandler() {} 102 StartProfiling(ConfigFn fn,std::string * error_msg)103 template <typename ConfigFn> bool StartProfiling(ConfigFn fn, std::string* error_msg) { 104 std::lock_guard<std::mutex> guard(lock_); 105 106 if (cur_config_->is_profiling) { 107 *error_msg = "Already profiling"; 108 return false; 109 } 110 cur_config_->is_profiling = true; 111 cur_config_->ResetStopProfiling(); 112 113 fn(*cur_config_); 114 115 HandlerFn handler = GetResultHandler(); 116 auto profile_runner = [handler](ThreadedHandler* service) { 117 ProfilingLoop(*service->cur_config_, handler); 118 119 // This thread is done. 120 std::lock_guard<std::mutex> unset_guard(service->lock_); 121 service->cur_config_->is_profiling = false; 122 }; 123 std::thread profiling_thread(profile_runner, this); 124 profiling_thread.detach(); // Let it go. 125 126 return true; 127 } 128 StopProfiling(std::string * error_msg)129 bool StopProfiling(std::string* error_msg) { 130 std::lock_guard<std::mutex> guard(lock_); 131 if (!cur_config_->is_profiling) { 132 *error_msg = "Not profiling"; 133 return false; 134 } 135 136 cur_config_->StopProfiling(); 137 138 return true; 139 } 140 141 protected: 142 // Handler for ProfilingLoop. ResultHandler(android::perfprofd::PerfprofdRecord * encodedProfile,Config * config)143 virtual bool ResultHandler(android::perfprofd::PerfprofdRecord* encodedProfile, 144 Config* config) { 145 CHECK(config != nullptr); 146 if (encodedProfile == nullptr) { 147 return false; 148 } 149 150 if (static_cast<ThreadedConfig*>(config)->send_to_dropbox) { 151 std::string error_msg; 152 if (!dropbox::SendToDropbox(encodedProfile, config->destination_directory, &error_msg)) { 153 LOG(WARNING) << "Failed dropbox submission: " << error_msg; 154 return false; 155 } 156 return true; 157 } 158 159 if (encodedProfile == nullptr) { 160 return false; 161 } 162 std::string data_file_path(config->destination_directory); 163 data_file_path += "/perf.data"; 164 std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq_); 165 if (!SerializeProtobuf(encodedProfile, path.c_str(), config->compress)) { 166 return false; 167 } 168 169 seq_++; 170 return true; 171 } 172 173 template <typename Fn> RunOnConfig(Fn & fn)174 void RunOnConfig(Fn& fn) { 175 std::lock_guard<std::mutex> guard(lock_); 176 fn(cur_config_->is_profiling, cur_config_.get()); 177 } 178 179 private: 180 // Helper for the handler. GetResultHandler()181 HandlerFn GetResultHandler() { 182 return HandlerFn(std::bind(&ThreadedHandler::ResultHandler, 183 this, 184 std::placeholders::_1, 185 std::placeholders::_2)); 186 } 187 188 std::mutex lock_; 189 190 std::unique_ptr<ThreadedConfig> cur_config_; 191 192 int seq_ = 0; 193 }; 194 195 } // namespace perfprofd 196 } // namespace android 197 198 #endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ 199