/* ** ** 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. */ #include "perfprofd_binder.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "android/os/BnPerfProfd.h" #include "perfprofd_config.pb.h" #include "perfprofd_record.pb.h" #include "config.h" #include "configreader.h" #include "dropbox.h" #include "perfprofdcore.h" #include "perfprofd_io.h" namespace android { namespace perfprofd { namespace binder { using Status = ::android::binder::Status; class BinderConfig : public Config { public: bool is_profiling = false; void Sleep(size_t seconds) override { if (seconds == 0) { return; } std::unique_lock guard(mutex_); using namespace std::chrono_literals; cv_.wait_for(guard, seconds * 1s, [&]() { return interrupted_; }); } bool ShouldStopProfiling() override { std::unique_lock guard(mutex_); return interrupted_; } void ResetStopProfiling() { std::unique_lock guard(mutex_); interrupted_ = false; } void StopProfiling() { std::unique_lock 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. BinderConfig& operator=(const BinderConfig& rhs) { // Copy base fields. *static_cast(this) = static_cast(rhs); return *this; } private: std::mutex mutex_; std::condition_variable cv_; bool interrupted_ = false; }; class PerfProfdNativeService : public BinderService, public ::android::os::BnPerfProfd { public: static status_t start(); static int Main(); static char const* getServiceName() { return "perfprofd"; } status_t dump(int fd, const Vector &args) override; Status startProfiling(int32_t profilingDuration, int32_t profilingInterval, int32_t iterations) override; Status startProfilingProtobuf(const std::vector& config_proto) override; Status stopProfiling() override; // Override onTransact so we can handle shellCommand. status_t onTransact(uint32_t _aidl_code, const Parcel& _aidl_data, Parcel* _aidl_reply, uint32_t _aidl_flags = 0) override; private: // Handler for ProfilingLoop. bool BinderHandler(android::perfprofd::PerfprofdRecord* encodedProfile, Config* config); // Helper for the handler. HandlerFn GetBinderHandler(); status_t shellCommand(int /*in*/, int out, int err, Vector& args); template Status StartProfiling(ConfigFn fn); template Status StartProfilingProtobuf(ProtoLoaderFn fn); Status StartProfilingProtobufFd(int fd); std::mutex lock_; BinderConfig cur_config_; int seq_ = 0; }; bool PerfProfdNativeService::BinderHandler( android::perfprofd::PerfprofdRecord* encodedProfile, Config* config) { CHECK(config != nullptr); if (encodedProfile == nullptr) { return false; } if (static_cast(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; } HandlerFn PerfProfdNativeService::GetBinderHandler() { return HandlerFn(std::bind(&PerfProfdNativeService::BinderHandler, this, std::placeholders::_1, std::placeholders::_2)); } status_t PerfProfdNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService::publish(); if (ret != android::OK) { return ret; } sp ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); return android::OK; } status_t PerfProfdNativeService::dump(int fd, const Vector &args) { auto out = std::fstream(base::StringPrintf("/proc/self/fd/%d", fd)); out << "Nothing to log, yet!" << std::endl; return NO_ERROR; } Status PerfProfdNativeService::startProfiling(int32_t profilingDuration, int32_t profilingInterval, int32_t iterations) { auto config_fn = [&](BinderConfig& config) { config = BinderConfig(); // Reset to a default config. config.sample_duration_in_s = static_cast(profilingDuration); config.collection_interval_in_s = static_cast(profilingInterval); config.main_loop_iterations = static_cast(iterations); }; return StartProfiling(config_fn); } Status PerfProfdNativeService::startProfilingProtobuf(const std::vector& config_proto) { auto proto_loader_fn = [&config_proto](ProfilingConfig& proto_config) { return proto_config.ParseFromArray(config_proto.data(), config_proto.size()); }; return StartProfilingProtobuf(proto_loader_fn); } template Status PerfProfdNativeService::StartProfiling(ConfigFn fn) { std::lock_guard guard(lock_); if (cur_config_.is_profiling) { // TODO: Define error code? return binder::Status::fromServiceSpecificError(1); } cur_config_.is_profiling = true; cur_config_.ResetStopProfiling(); fn(cur_config_); HandlerFn handler = GetBinderHandler(); auto profile_runner = [handler](PerfProfdNativeService* service) { ProfilingLoop(service->cur_config_, handler); // This thread is done. std::lock_guard unset_guard(service->lock_); service->cur_config_.is_profiling = false; }; std::thread profiling_thread(profile_runner, this); profiling_thread.detach(); // Let it go. return binder::Status::ok(); } template Status PerfProfdNativeService::StartProfilingProtobuf(ProtoLoaderFn fn) { ProfilingConfig proto_config; if (!fn(proto_config)) { return binder::Status::fromExceptionCode(2); } auto config_fn = [&proto_config](BinderConfig& config) { config = BinderConfig(); // Reset to a default config. // Copy proto values. #define CHECK_AND_COPY_FROM_PROTO(name) \ if (proto_config.has_ ## name ()) { \ config. name = proto_config. name (); \ } CHECK_AND_COPY_FROM_PROTO(collection_interval_in_s) CHECK_AND_COPY_FROM_PROTO(use_fixed_seed) CHECK_AND_COPY_FROM_PROTO(main_loop_iterations) CHECK_AND_COPY_FROM_PROTO(destination_directory) CHECK_AND_COPY_FROM_PROTO(config_directory) CHECK_AND_COPY_FROM_PROTO(perf_path) CHECK_AND_COPY_FROM_PROTO(sampling_period) CHECK_AND_COPY_FROM_PROTO(sample_duration_in_s) CHECK_AND_COPY_FROM_PROTO(only_debug_build) CHECK_AND_COPY_FROM_PROTO(hardwire_cpus) CHECK_AND_COPY_FROM_PROTO(hardwire_cpus_max_duration_in_s) CHECK_AND_COPY_FROM_PROTO(max_unprocessed_profiles) CHECK_AND_COPY_FROM_PROTO(stack_profile) CHECK_AND_COPY_FROM_PROTO(collect_cpu_utilization) CHECK_AND_COPY_FROM_PROTO(collect_charging_state) CHECK_AND_COPY_FROM_PROTO(collect_booting) CHECK_AND_COPY_FROM_PROTO(collect_camera_active) CHECK_AND_COPY_FROM_PROTO(process) CHECK_AND_COPY_FROM_PROTO(use_elf_symbolizer) CHECK_AND_COPY_FROM_PROTO(send_to_dropbox) CHECK_AND_COPY_FROM_PROTO(compress) #undef CHECK_AND_COPY_FROM_PROTO }; return StartProfiling(config_fn); } Status PerfProfdNativeService::StartProfilingProtobufFd(int fd) { auto proto_loader_fn = [fd](ProfilingConfig& proto_config) { struct IstreamCopyingInputStream : public google::protobuf::io::CopyingInputStream { IstreamCopyingInputStream(int fd_in) : stream(base::StringPrintf("/proc/self/fd/%d", fd_in), std::ios::binary | std::ios::in) { } int Read(void* buffer, int size) override { stream.read(reinterpret_cast(buffer), size); size_t count = stream.gcount(); if (count > 0) { return count; } return -1; } std::ifstream stream; }; std::unique_ptr is(new IstreamCopyingInputStream(fd)); std::unique_ptr is_adaptor( new google::protobuf::io::CopyingInputStreamAdaptor(is.get())); return proto_config.ParseFromZeroCopyStream(is_adaptor.get()); }; return StartProfilingProtobuf(proto_loader_fn); } Status PerfProfdNativeService::stopProfiling() { std::lock_guard guard(lock_); if (!cur_config_.is_profiling) { // TODO: Define error code? return Status::fromServiceSpecificError(1); } cur_config_.StopProfiling(); return Status::ok(); } status_t PerfProfdNativeService::shellCommand(int in, int out, int err_fd, Vector& args) { if (android::base::kEnableDChecks) { LOG(VERBOSE) << "Perfprofd::shellCommand"; for (size_t i = 0, n = args.size(); i < n; i++) { LOG(VERBOSE) << " arg[" << i << "]: '" << String8(args[i]).string() << "'"; } } auto err_str = std::fstream(base::StringPrintf("/proc/self/fd/%d", err_fd)); if (args.size() >= 1) { if (args[0] == String16("dump")) { dump(out, args); return OK; } else if (args[0] == String16("startProfiling")) { ConfigReader reader; for (size_t i = 1; i < args.size(); ++i) { if (!reader.Read(String8(args[i]).string(), /* fail_on_error */ true)) { err_str << base::StringPrintf("Could not parse %s", String8(args[i]).string()) << std::endl; return BAD_VALUE; } } auto config_fn = [&](BinderConfig& config) { config = BinderConfig(); // Reset to a default config. reader.FillConfig(&config); }; Status status = StartProfiling(config_fn); if (status.isOk()) { return OK; } else { err_str << status.toString8() << std::endl; return UNKNOWN_ERROR; } } else if (args[0] == String16("startProfilingProto")) { if (args.size() < 2) { return BAD_VALUE; } int fd = -1; if (args[1] == String16("-")) { fd = in; } else { // TODO: Implement reading from disk? } if (fd < 0) { err_str << "Bad file descriptor " << args[1] << std::endl; return BAD_VALUE; } binder::Status status = StartProfilingProtobufFd(fd); if (status.isOk()) { return OK; } else { err_str << status.toString8() << std::endl; return UNKNOWN_ERROR; } } else if (args[0] == String16("stopProfiling")) { Status status = stopProfiling(); if (status.isOk()) { return OK; } else { err_str << status.toString8() << std::endl; return UNKNOWN_ERROR; } } } return BAD_VALUE; } status_t PerfProfdNativeService::onTransact(uint32_t _aidl_code, const Parcel& _aidl_data, Parcel* _aidl_reply, uint32_t _aidl_flags) { switch (_aidl_code) { case IBinder::SHELL_COMMAND_TRANSACTION: { int in = _aidl_data.readFileDescriptor(); int out = _aidl_data.readFileDescriptor(); int err = _aidl_data.readFileDescriptor(); int argc = _aidl_data.readInt32(); Vector args; for (int i = 0; i < argc && _aidl_data.dataAvail() > 0; i++) { args.add(_aidl_data.readString16()); } sp unusedCallback; sp resultReceiver; status_t status; if ((status = _aidl_data.readNullableStrongBinder(&unusedCallback)) != OK) return status; if ((status = _aidl_data.readNullableStrongBinder(&resultReceiver)) != OK) return status; status = shellCommand(in, out, err, args); if (resultReceiver != nullptr) { resultReceiver->send(status); } return OK; } default: return BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags); } } int Main() { android::status_t ret; if ((ret = PerfProfdNativeService::start()) != android::OK) { LOG(ERROR) << "Unable to start InstalldNativeService: %d" << ret; exit(1); } android::IPCThreadState::self()->joinThreadPool(); LOG(INFO) << "Exiting perfprofd"; return 0; } } // namespace binder } // namespace perfprofd } // namespace android