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