summaryrefslogtreecommitdiff
path: root/perfprofd/binder_interface/perfprofd_binder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'perfprofd/binder_interface/perfprofd_binder.cc')
-rw-r--r--perfprofd/binder_interface/perfprofd_binder.cc377
1 files changed, 377 insertions, 0 deletions
diff --git a/perfprofd/binder_interface/perfprofd_binder.cc b/perfprofd/binder_interface/perfprofd_binder.cc
new file mode 100644
index 00000000..a1c77e24
--- /dev/null
+++ b/perfprofd/binder_interface/perfprofd_binder.cc
@@ -0,0 +1,377 @@
+/*
+**
+** 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