| /* |
| * |
| * 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; |
| } |
| |
| 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_ |