/* * * Copyright 2017, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ #define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ #include <chrono> #include <condition_variable> #include <cstdio> #include <cstdlib> #include <memory> #include <mutex> #include <string> #include <thread> #include <functional> #include <inttypes.h> #include <unistd.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> #include "perfprofd_record.pb.h" #include "config.h" #include "dropbox.h" #include "perfprofdcore.h" #include "perfprofd_io.h" namespace android { namespace perfprofd { class ThreadedConfig : public Config { public: void Sleep(size_t seconds) override { if (seconds == 0) { return; } std::unique_lock<std::mutex> guard(mutex_); using namespace std::chrono_literals; cv_.wait_for(guard, seconds * 1s, [&]() { return interrupted_; }); } bool ShouldStopProfiling() override { std::unique_lock<std::mutex> guard(mutex_); return interrupted_; } void ResetStopProfiling() { std::unique_lock<std::mutex> guard(mutex_); interrupted_ = false; } void StopProfiling() { std::unique_lock<std::mutex> guard(mutex_); interrupted_ = true; cv_.notify_all(); } bool IsProfilingEnabled() const override { return true; } // Operator= to simplify setting the config values. This will retain the // original mutex, condition-variable etc. ThreadedConfig& operator=(const ThreadedConfig& rhs) { // Copy base fields. *static_cast<Config*>(this) = static_cast<const Config&>(rhs); return *this; } private: bool is_profiling = false; std::mutex mutex_; std::condition_variable cv_; bool interrupted_ = false; friend class ThreadedHandler; }; class ThreadedHandler { public: ThreadedHandler() : cur_config_(new ThreadedConfig()) {} explicit ThreadedHandler(ThreadedConfig* in) : cur_config_(in) { CHECK(cur_config_ != nullptr); } virtual ~ThreadedHandler() {} template <typename ConfigFn> bool StartProfiling(ConfigFn fn, std::string* error_msg) { std::lock_guard<std::mutex> guard(lock_); if (cur_config_->is_profiling) { *error_msg = "Already profiling"; return false; } cur_config_->is_profiling = true; cur_config_->ResetStopProfiling(); fn(*cur_config_); HandlerFn handler = GetResultHandler(); auto profile_runner = [handler](ThreadedHandler* service) { ProfilingLoop(*service->cur_config_, handler); // This thread is done. std::lock_guard<std::mutex> unset_guard(service->lock_); service->cur_config_->is_profiling = false; }; std::thread profiling_thread(profile_runner, this); profiling_thread.detach(); // Let it go. return true; } bool StopProfiling(std::string* error_msg) { std::lock_guard<std::mutex> guard(lock_); if (!cur_config_->is_profiling) { *error_msg = "Not profiling"; return false; } cur_config_->StopProfiling(); return true; } protected: // Handler for ProfilingLoop. virtual bool ResultHandler(android::perfprofd::PerfprofdRecord* encodedProfile, Config* config) { CHECK(config != nullptr); if (encodedProfile == nullptr) { return false; } if (static_cast<ThreadedConfig*>(config)->send_to_dropbox) { std::string error_msg; if (!dropbox::SendToDropbox(encodedProfile, config->destination_directory, &error_msg)) { LOG(WARNING) << "Failed dropbox submission: " << error_msg; return false; } return true; } if (encodedProfile == nullptr) { return false; } std::string data_file_path(config->destination_directory); data_file_path += "/perf.data"; std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq_); if (!SerializeProtobuf(encodedProfile, path.c_str(), config->compress)) { return false; } seq_++; return true; } template <typename Fn> void RunOnConfig(Fn& fn) { std::lock_guard<std::mutex> guard(lock_); fn(cur_config_->is_profiling, cur_config_.get()); } private: // Helper for the handler. HandlerFn GetResultHandler() { return HandlerFn(std::bind(&ThreadedHandler::ResultHandler, this, std::placeholders::_1, std::placeholders::_2)); } std::mutex lock_; std::unique_ptr<ThreadedConfig> cur_config_; int seq_ = 0; }; } // namespace perfprofd } // namespace android #endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_