/* ** ** 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 <cstdio> #include <cstdlib> #include <fstream> #include <memory> #include <mutex> #include <string> #include <thread> #include <inttypes.h> #include <unistd.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <binder/BinderService.h> #include <binder/IResultReceiver.h> #include <binder/Status.h> #include <google/protobuf/io/zero_copy_stream_impl_lite.h> #include <utils/String16.h> #include <utils/String8.h> #include <utils/Vector.h> #include "android/os/BnPerfProfd.h" #include "perfprofd_config.pb.h" #include "perfprofd_record.pb.h" #include "config.h" #include "configreader.h" #include "perfprofdcore.h" #include "perfprofd_threaded_handler.h" namespace android { namespace perfprofd { namespace binder { namespace { using Status = ::android::binder::Status; class PerfProfdNativeService : public BinderService<PerfProfdNativeService>, public ::android::os::BnPerfProfd, public ThreadedHandler { public: static status_t start(); static int Main(); static char const* getServiceName() { return "perfprofd"; } status_t dump(int fd, const Vector<String16> &args) override; Status startProfiling(int32_t collectionInterval, int32_t iterations, int32_t process, int32_t samplingPeriod, int32_t samplingFrequency, int32_t sampleDuration, bool stackProfile, bool useElfSymbolizer, bool sendToDropbox) override; Status startProfilingString(const String16& config) override; Status startProfilingProtobuf(const std::vector<uint8_t>& 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: status_t shellCommand(int /*in*/, int out, int err, Vector<String16>& args); template <typename ProtoLoaderFn> Status StartProfilingProtobuf(ProtoLoaderFn fn); Status StartProfilingProtobufFd(int fd); }; status_t PerfProfdNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService<PerfProfdNativeService>::publish(); if (ret != android::OK) { return ret; } sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); return android::OK; } status_t PerfProfdNativeService::dump(int fd, const Vector<String16> &args) { auto out = std::fstream(base::StringPrintf("/proc/self/fd/%d", fd)); auto print_config = [&out](bool is_profiling, const Config* config) { if (is_profiling) { out << "Profiling with config: " << ConfigReader::ConfigToString(*config); } else { out << "Not actively profiling."; } }; RunOnConfig(print_config); out << std::endl; return NO_ERROR; } Status PerfProfdNativeService::startProfiling(int32_t collectionInterval, int32_t iterations, int32_t process, int32_t samplingPeriod, int32_t samplingFrequency, int32_t sampleDuration, bool stackProfile, bool useElfSymbolizer, bool sendToDropbox) { auto config_fn = [&](ThreadedConfig& config) { config = ThreadedConfig(); // Reset to a default config. if (collectionInterval >= 0) { config.collection_interval_in_s = collectionInterval; } if (iterations >= 0) { config.main_loop_iterations = iterations; } if (process >= 0) { config.process = process; } if (samplingPeriod > 0) { config.sampling_period = samplingPeriod; } if (samplingFrequency > 0) { config.sampling_frequency = samplingFrequency; } if (sampleDuration > 0) { config.sample_duration_in_s = sampleDuration; } config.stack_profile = stackProfile; config.use_elf_symbolizer = useElfSymbolizer; config.send_to_dropbox = sendToDropbox; }; std::string error_msg; if (!StartProfiling(config_fn, &error_msg)) { return Status::fromExceptionCode(1, error_msg.c_str()); } return Status::ok(); } Status PerfProfdNativeService::startProfilingString(const String16& config) { ConfigReader reader; std::string error_msg; // Split configuration along colon. std::vector<std::string> args = base::Split(String8(config).string(), ":"); for (auto& arg : args) { if (!reader.Read(arg, /* fail_on_error */ true, &error_msg)) { std::string tmp = base::StringPrintf("Could not parse %s: %s", arg.c_str(), error_msg.c_str()); return Status::fromExceptionCode(1, tmp.c_str()); } } auto config_fn = [&](ThreadedConfig& config) { config = ThreadedConfig(); // Reset to a default config. reader.FillConfig(&config); }; if (!StartProfiling(config_fn, &error_msg)) { return Status::fromExceptionCode(1, error_msg.c_str()); } return Status::ok(); } Status PerfProfdNativeService::startProfilingProtobuf(const std::vector<uint8_t>& 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 <typename ProtoLoaderFn> Status PerfProfdNativeService::StartProfilingProtobuf(ProtoLoaderFn fn) { ProfilingConfig proto_config; if (!fn(proto_config)) { return binder::Status::fromExceptionCode(2, "Could not read protobuf"); } auto config_fn = [&proto_config](ThreadedConfig& config) { config = ThreadedConfig(); // Reset to a default config. ConfigReader::ProtoToConfig(proto_config, &config); }; std::string error_msg; if (!StartProfiling(config_fn, &error_msg)) { return Status::fromExceptionCode(1, error_msg.c_str()); } return Status::ok(); } 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<char*>(buffer), size); size_t count = stream.gcount(); if (count > 0) { return count; } return -1; } std::ifstream stream; }; std::unique_ptr<IstreamCopyingInputStream> is(new IstreamCopyingInputStream(fd)); std::unique_ptr<google::protobuf::io::CopyingInputStreamAdaptor> 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::string error_msg; if (!StopProfiling(&error_msg)) { Status::fromExceptionCode(1, error_msg.c_str()); } return Status::ok(); } status_t PerfProfdNativeService::shellCommand(int in, int out, int err_fd, Vector<String16>& 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) { std::string error_msg; if (!reader.Read(String8(args[i]).string(), /* fail_on_error */ true, &error_msg)) { err_str << "Could not parse '" << String8(args[i]).string() << "': " << error_msg << std::endl; return BAD_VALUE; } } auto config_fn = [&](ThreadedConfig& config) { config = ThreadedConfig(); // Reset to a default config. reader.FillConfig(&config); }; std::string error_msg; if (!StartProfiling(config_fn, &error_msg)) { err_str << error_msg << std::endl; return UNKNOWN_ERROR; } return OK; } 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<String16> args; for (int i = 0; i < argc && _aidl_data.dataAvail() > 0; i++) { args.add(_aidl_data.readString16()); } sp<IBinder> unusedCallback; sp<IResultReceiver> 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 ::android::os::BnPerfProfd::onTransact( _aidl_code, _aidl_data, _aidl_reply, _aidl_flags); } } } // namespace int Main() { { struct DummyConfig : public Config { void Sleep(size_t seconds) override {} bool IsProfilingEnabled() const override { return false; } }; DummyConfig config; GlobalInit(config.perf_path); } 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