diff options
Diffstat (limited to 'perfprofd/perfprofd_threaded_handler.h')
-rw-r--r-- | perfprofd/perfprofd_threaded_handler.h | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/perfprofd/perfprofd_threaded_handler.h b/perfprofd/perfprofd_threaded_handler.h new file mode 100644 index 00000000..76cdd67a --- /dev/null +++ b/perfprofd/perfprofd_threaded_handler.h @@ -0,0 +1,198 @@ +/* + * + * 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_ |