| /* |
| ** |
| ** 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/IPCThreadState.h> |
| #include <binder/IResultReceiver.h> |
| #include <binder/PermissionCache.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)); |
| |
| const IPCThreadState* ipc = IPCThreadState::self(); |
| const int pid = ipc->getCallingPid(); |
| const int uid = ipc->getCallingUid(); |
| if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { |
| out << "Permission Denial: can't dump MediaExtractor from pid=" |
| << pid << ", uid=" << uid << std::endl; |
| return android::PERMISSION_DENIED; |
| } |
| |
| out << "Nothing to log, yet!" << 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 = base::StringPrintf("Could not parse %s", arg.c_str()); |
| return Status::fromExceptionCode(1, error_msg.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); |
| } |
| auto config_fn = [&proto_config](ThreadedConfig& config) { |
| config = ThreadedConfig(); // 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 |
| }; |
| 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) { |
| 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 = [&](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() { |
| 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 |