| // Copyright (C) 2020 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 "DebuggerImpl.h" |
| #include "ProfilingType.pb.h" |
| #include "ConfigurationCommand.pb.h" |
| #include "StatusUtil.h" |
| |
| #include <android-base/logging.h> |
| #include <android-base/file.h> |
| #include <binder/ParcelFileDescriptor.h> |
| |
| #include <errno.h> |
| |
| namespace android { |
| namespace automotive { |
| namespace computepipe { |
| namespace runner { |
| namespace client_interface { |
| namespace aidl_client { |
| namespace { |
| |
| using ::std::literals::chrono_literals::operator""ms; |
| using ::aidl::android::automotive::computepipe::runner::PipeProfilingType; |
| using ::aidl::android::automotive::computepipe::runner::ProfilingData; |
| |
| using ::ndk::ScopedAStatus; |
| |
| constexpr std::chrono::milliseconds kProfilingDataReadTimeout = 50ms; |
| |
| proto::ProfilingType ToProtoProfilingType(PipeProfilingType type) { |
| switch (type) { |
| case PipeProfilingType::LATENCY: |
| return proto::ProfilingType::LATENCY; |
| case PipeProfilingType::TRACE_EVENTS: |
| return proto::ProfilingType::TRACE_EVENTS; |
| } |
| } |
| |
| PipeProfilingType ToAidlProfilingType(proto::ProfilingType type) { |
| switch (type) { |
| case proto::ProfilingType::LATENCY: |
| return PipeProfilingType::LATENCY; |
| case proto::ProfilingType::TRACE_EVENTS: |
| return PipeProfilingType::TRACE_EVENTS; |
| case proto::ProfilingType::DISABLED: |
| LOG(ERROR) << "Attempt to convert invalid profiling type to aidl type."; |
| return PipeProfilingType::LATENCY; |
| } |
| } |
| |
| Status RecursiveCreateDir(const std::string& dirName) { |
| if (dirName == "/") { |
| return Status::SUCCESS; |
| } |
| |
| DIR *directory = opendir(dirName.c_str()); |
| // Check if directory exists. |
| if (directory) { |
| closedir(directory); |
| return Status::SUCCESS; |
| } |
| |
| std::string parentDirName = android::base::Dirname(dirName); |
| if (!parentDirName.empty()) { |
| Status status = RecursiveCreateDir(parentDirName); |
| if (status != Status::SUCCESS) { |
| return status; |
| } |
| } |
| |
| // mkdir expects the flag as bits it is essential to send 0777 which is |
| // interpreted in octal rather than 777 which is interpreted in decimal. |
| if (!mkdir(dirName.c_str(), 0777)) { |
| return Status::SUCCESS; |
| } else { |
| LOG(ERROR) << "Failed to create directory - " << errno; |
| return Status::INTERNAL_ERROR; |
| } |
| } |
| |
| } // namespace |
| |
| ndk::ScopedAStatus DebuggerImpl::setPipeProfileOptions(PipeProfilingType in_type) { |
| mProfilingType = in_type; |
| proto::ConfigurationCommand command; |
| command.mutable_set_profile_options()->set_profile_type(ToProtoProfilingType(mProfilingType)); |
| std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock(); |
| if (!engineSp) { |
| return ToNdkStatus(Status::INTERNAL_ERROR); |
| } |
| Status status = engineSp->processClientConfigUpdate(command); |
| return ToNdkStatus(status); |
| } |
| |
| ScopedAStatus DebuggerImpl::startPipeProfiling() { |
| if (mGraphState != GraphState::RUNNING) { |
| LOG(ERROR) << "Attempting to start profiling when the graph is not in the running state."; |
| return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| proto::ControlCommand controlCommand; |
| *controlCommand.mutable_start_pipe_profile() = proto::StartPipeProfile(); |
| std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock(); |
| if (!engineSp) { |
| return ToNdkStatus(Status::INTERNAL_ERROR); |
| } |
| Status status = engineSp->processClientCommand(controlCommand); |
| return ToNdkStatus(status); |
| } |
| |
| ScopedAStatus DebuggerImpl::stopPipeProfiling() { |
| proto::ControlCommand controlCommand; |
| *controlCommand.mutable_stop_pipe_profile() = proto::StopPipeProfile(); |
| std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock(); |
| if (!engineSp) { |
| return ToNdkStatus(Status::INTERNAL_ERROR); |
| } |
| Status status = engineSp->processClientCommand(controlCommand); |
| if (status != Status::SUCCESS) { |
| return ToNdkStatus(status); |
| } |
| |
| proto::ControlCommand controlCommand2; |
| *controlCommand2.mutable_read_debug_data() = proto::ReadDebugData(); |
| status = engineSp->processClientCommand(controlCommand2); |
| if (status != Status::SUCCESS) { |
| return ToNdkStatus(status); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus DebuggerImpl::getPipeProfilingInfo(ProfilingData* _aidl_return) { |
| if (!_aidl_return) { |
| return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| } |
| |
| std::unique_lock<std::mutex> lk(mLock); |
| if (!mWait.wait_for(lk, kProfilingDataReadTimeout, [this]() { |
| return mProfilingData.size != 0; |
| })) { |
| LOG(ERROR) << "No profiling data was found."; |
| proto::ProfilingType profilingType = ToProtoProfilingType(mProfilingType); |
| if (profilingType == proto::ProfilingType::DISABLED) { |
| LOG(ERROR) << "Profiling was disabled."; |
| return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| _aidl_return->type = ToAidlProfilingType(profilingType); |
| _aidl_return->size = 0; |
| return ScopedAStatus::ok(); |
| } |
| |
| _aidl_return->type = mProfilingData.type; |
| _aidl_return->size = mProfilingData.size; |
| _aidl_return->dataFds = std::move(mProfilingData.dataFds); |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus DebuggerImpl::releaseDebugger() { |
| if (mGraphState == GraphState::RUNNING || mGraphState == GraphState::RESET) { |
| return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| |
| proto::ControlCommand controlCommand; |
| *controlCommand.mutable_release_debugger() = proto::ReleaseDebugger(); |
| std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock(); |
| if (!engineSp) { |
| return ToNdkStatus(Status::INTERNAL_ERROR); |
| } |
| Status status = engineSp->processClientCommand(controlCommand); |
| |
| std::lock_guard<std::mutex> lk(mLock); |
| mProfilingData.size = 0; |
| mProfilingData.dataFds.clear(); |
| return ToNdkStatus(status); |
| } |
| |
| Status DebuggerImpl::handleConfigPhase(const ClientConfig& e) { |
| if (e.isTransitionComplete()) { |
| mGraphState = GraphState::CONFIG_DONE; |
| } |
| return Status::SUCCESS; |
| } |
| |
| Status DebuggerImpl::handleExecutionPhase(const RunnerEvent& e) { |
| if (e.isTransitionComplete()) { |
| mGraphState = GraphState::RUNNING; |
| } |
| if (e.isAborted()) { |
| mGraphState = GraphState::ERR_HALT; |
| } |
| return Status::SUCCESS; |
| } |
| |
| Status DebuggerImpl::handleStopWithFlushPhase(const RunnerEvent& e) { |
| if (e.isTransitionComplete()) { |
| mGraphState = GraphState::DONE; |
| } |
| if (e.isAborted()) { |
| mGraphState = GraphState::ERR_HALT; |
| } |
| return Status::SUCCESS; |
| } |
| |
| Status DebuggerImpl::handleStopImmediatePhase(const RunnerEvent& e) { |
| if (e.isTransitionComplete() || e.isAborted()) { |
| mGraphState = GraphState::ERR_HALT; |
| } |
| return Status::SUCCESS; |
| } |
| |
| Status DebuggerImpl::handleResetPhase(const RunnerEvent& e) { |
| if (e.isPhaseEntry()) { |
| mGraphState = GraphState::RESET; |
| } |
| return Status::SUCCESS; |
| } |
| |
| Status DebuggerImpl::deliverGraphDebugInfo(const std::string& debugData) { |
| Status status = RecursiveCreateDir(mProfilingDataDirName); |
| if (status != Status::SUCCESS) { |
| return status; |
| } |
| |
| std::string profilingDataFilePath = mProfilingDataDirName + "/" + mGraphOptions.graph_name(); |
| std::string fileRemoveError; |
| if (!android::base::RemoveFileIfExists(profilingDataFilePath, &fileRemoveError)) { |
| LOG(ERROR) << "Failed to remove file " << profilingDataFilePath << ", error: " |
| << fileRemoveError; |
| return Status::INTERNAL_ERROR; |
| } |
| if (!android::base::WriteStringToFile(debugData, profilingDataFilePath)) { |
| LOG(ERROR) << "Failed to write profiling data to file at path " << profilingDataFilePath; |
| return Status::INTERNAL_ERROR; |
| } |
| |
| std::lock_guard<std::mutex> lk(mLock); |
| mProfilingData.type = mProfilingType; |
| mProfilingData.size = debugData.size(); |
| mProfilingData.dataFds.emplace_back( |
| ndk::ScopedFileDescriptor(open(profilingDataFilePath.c_str(), O_CREAT, O_RDWR))); |
| mWait.notify_one(); |
| return Status::SUCCESS; |
| } |
| |
| } // namespace aidl_client |
| } // namespace client_interface |
| } // namespace runner |
| } // namespace computepipe |
| } // namespace automotive |
| } // namespace android |