1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "DebuggerImpl.h"
16 #include "ProfilingType.pb.h"
17 #include "ConfigurationCommand.pb.h"
18 #include "StatusUtil.h"
19
20 #include <android-base/logging.h>
21 #include <android-base/file.h>
22 #include <binder/ParcelFileDescriptor.h>
23
24 #include <errno.h>
25
26 namespace android {
27 namespace automotive {
28 namespace computepipe {
29 namespace runner {
30 namespace client_interface {
31 namespace aidl_client {
32 namespace {
33
34 using ::std::literals::chrono_literals::operator""ms;
35 using ::aidl::android::automotive::computepipe::runner::PipeProfilingType;
36 using ::aidl::android::automotive::computepipe::runner::ProfilingData;
37
38 using ::ndk::ScopedAStatus;
39
40 constexpr std::chrono::milliseconds kProfilingDataReadTimeout = 50ms;
41
ToProtoProfilingType(PipeProfilingType type)42 proto::ProfilingType ToProtoProfilingType(PipeProfilingType type) {
43 switch (type) {
44 case PipeProfilingType::LATENCY:
45 return proto::ProfilingType::LATENCY;
46 case PipeProfilingType::TRACE_EVENTS:
47 return proto::ProfilingType::TRACE_EVENTS;
48 }
49 }
50
ToAidlProfilingType(proto::ProfilingType type)51 PipeProfilingType ToAidlProfilingType(proto::ProfilingType type) {
52 switch (type) {
53 case proto::ProfilingType::LATENCY:
54 return PipeProfilingType::LATENCY;
55 case proto::ProfilingType::TRACE_EVENTS:
56 return PipeProfilingType::TRACE_EVENTS;
57 case proto::ProfilingType::DISABLED:
58 LOG(ERROR) << "Attempt to convert invalid profiling type to aidl type.";
59 return PipeProfilingType::LATENCY;
60 }
61 }
62
RecursiveCreateDir(const std::string & dirName)63 Status RecursiveCreateDir(const std::string& dirName) {
64 if (dirName == "/") {
65 return Status::SUCCESS;
66 }
67
68 DIR *directory = opendir(dirName.c_str());
69 // Check if directory exists.
70 if (directory) {
71 closedir(directory);
72 return Status::SUCCESS;
73 }
74
75 std::string parentDirName = android::base::Dirname(dirName);
76 if (!parentDirName.empty()) {
77 Status status = RecursiveCreateDir(parentDirName);
78 if (status != Status::SUCCESS) {
79 return status;
80 }
81 }
82
83 // mkdir expects the flag as bits it is essential to send 0777 which is
84 // interpreted in octal rather than 777 which is interpreted in decimal.
85 if (!mkdir(dirName.c_str(), 0777)) {
86 return Status::SUCCESS;
87 } else {
88 LOG(ERROR) << "Failed to create directory - " << errno;
89 return Status::INTERNAL_ERROR;
90 }
91 }
92
93 } // namespace
94
setPipeProfileOptions(PipeProfilingType in_type)95 ndk::ScopedAStatus DebuggerImpl::setPipeProfileOptions(PipeProfilingType in_type) {
96 mProfilingType = in_type;
97 proto::ConfigurationCommand command;
98 command.mutable_set_profile_options()->set_profile_type(ToProtoProfilingType(mProfilingType));
99 std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock();
100 if (!engineSp) {
101 return ToNdkStatus(Status::INTERNAL_ERROR);
102 }
103 Status status = engineSp->processClientConfigUpdate(command);
104 return ToNdkStatus(status);
105 }
106
startPipeProfiling()107 ScopedAStatus DebuggerImpl::startPipeProfiling() {
108 if (mGraphState != GraphState::RUNNING) {
109 LOG(ERROR) << "Attempting to start profiling when the graph is not in the running state.";
110 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
111 }
112 proto::ControlCommand controlCommand;
113 *controlCommand.mutable_start_pipe_profile() = proto::StartPipeProfile();
114 std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock();
115 if (!engineSp) {
116 return ToNdkStatus(Status::INTERNAL_ERROR);
117 }
118 Status status = engineSp->processClientCommand(controlCommand);
119 return ToNdkStatus(status);
120 }
121
stopPipeProfiling()122 ScopedAStatus DebuggerImpl::stopPipeProfiling() {
123 proto::ControlCommand controlCommand;
124 *controlCommand.mutable_stop_pipe_profile() = proto::StopPipeProfile();
125 std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock();
126 if (!engineSp) {
127 return ToNdkStatus(Status::INTERNAL_ERROR);
128 }
129 Status status = engineSp->processClientCommand(controlCommand);
130 if (status != Status::SUCCESS) {
131 return ToNdkStatus(status);
132 }
133
134 proto::ControlCommand controlCommand2;
135 *controlCommand2.mutable_read_debug_data() = proto::ReadDebugData();
136 status = engineSp->processClientCommand(controlCommand2);
137 if (status != Status::SUCCESS) {
138 return ToNdkStatus(status);
139 }
140 return ScopedAStatus::ok();
141 }
142
getPipeProfilingInfo(ProfilingData * _aidl_return)143 ScopedAStatus DebuggerImpl::getPipeProfilingInfo(ProfilingData* _aidl_return) {
144 if (!_aidl_return) {
145 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
146 }
147
148 std::unique_lock<std::mutex> lk(mLock);
149 if (!mWait.wait_for(lk, kProfilingDataReadTimeout, [this]() {
150 return mProfilingData.size != 0;
151 })) {
152 LOG(ERROR) << "No profiling data was found.";
153 proto::ProfilingType profilingType = ToProtoProfilingType(mProfilingType);
154 if (profilingType == proto::ProfilingType::DISABLED) {
155 LOG(ERROR) << "Profiling was disabled.";
156 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
157 }
158 _aidl_return->type = ToAidlProfilingType(profilingType);
159 _aidl_return->size = 0;
160 return ScopedAStatus::ok();
161 }
162
163 _aidl_return->type = mProfilingData.type;
164 _aidl_return->size = mProfilingData.size;
165 _aidl_return->dataFds = std::move(mProfilingData.dataFds);
166 return ScopedAStatus::ok();
167 }
168
releaseDebugger()169 ScopedAStatus DebuggerImpl::releaseDebugger() {
170 if (mGraphState == GraphState::RUNNING || mGraphState == GraphState::RESET) {
171 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
172 }
173
174 proto::ControlCommand controlCommand;
175 *controlCommand.mutable_release_debugger() = proto::ReleaseDebugger();
176 std::shared_ptr<ClientEngineInterface> engineSp = mEngine.lock();
177 if (!engineSp) {
178 return ToNdkStatus(Status::INTERNAL_ERROR);
179 }
180 Status status = engineSp->processClientCommand(controlCommand);
181
182 std::lock_guard<std::mutex> lk(mLock);
183 mProfilingData.size = 0;
184 mProfilingData.dataFds.clear();
185 return ToNdkStatus(status);
186 }
187
handleConfigPhase(const ClientConfig & e)188 Status DebuggerImpl::handleConfigPhase(const ClientConfig& e) {
189 if (e.isTransitionComplete()) {
190 mGraphState = GraphState::CONFIG_DONE;
191 }
192 return Status::SUCCESS;
193 }
194
handleExecutionPhase(const RunnerEvent & e)195 Status DebuggerImpl::handleExecutionPhase(const RunnerEvent& e) {
196 if (e.isTransitionComplete()) {
197 mGraphState = GraphState::RUNNING;
198 }
199 if (e.isAborted()) {
200 mGraphState = GraphState::ERR_HALT;
201 }
202 return Status::SUCCESS;
203 }
204
handleStopWithFlushPhase(const RunnerEvent & e)205 Status DebuggerImpl::handleStopWithFlushPhase(const RunnerEvent& e) {
206 if (e.isTransitionComplete()) {
207 mGraphState = GraphState::DONE;
208 }
209 if (e.isAborted()) {
210 mGraphState = GraphState::ERR_HALT;
211 }
212 return Status::SUCCESS;
213 }
214
handleStopImmediatePhase(const RunnerEvent & e)215 Status DebuggerImpl::handleStopImmediatePhase(const RunnerEvent& e) {
216 if (e.isTransitionComplete() || e.isAborted()) {
217 mGraphState = GraphState::ERR_HALT;
218 }
219 return Status::SUCCESS;
220 }
221
handleResetPhase(const RunnerEvent & e)222 Status DebuggerImpl::handleResetPhase(const RunnerEvent& e) {
223 if (e.isPhaseEntry()) {
224 mGraphState = GraphState::RESET;
225 }
226 return Status::SUCCESS;
227 }
228
deliverGraphDebugInfo(const std::string & debugData)229 Status DebuggerImpl::deliverGraphDebugInfo(const std::string& debugData) {
230 Status status = RecursiveCreateDir(mProfilingDataDirName);
231 if (status != Status::SUCCESS) {
232 return status;
233 }
234
235 std::string profilingDataFilePath = mProfilingDataDirName + "/" + mGraphOptions.graph_name();
236 std::string fileRemoveError;
237 if (!android::base::RemoveFileIfExists(profilingDataFilePath, &fileRemoveError)) {
238 LOG(ERROR) << "Failed to remove file " << profilingDataFilePath << ", error: "
239 << fileRemoveError;
240 return Status::INTERNAL_ERROR;
241 }
242 if (!android::base::WriteStringToFile(debugData, profilingDataFilePath)) {
243 LOG(ERROR) << "Failed to write profiling data to file at path " << profilingDataFilePath;
244 return Status::INTERNAL_ERROR;
245 }
246
247 std::lock_guard<std::mutex> lk(mLock);
248 mProfilingData.type = mProfilingType;
249 mProfilingData.size = debugData.size();
250 mProfilingData.dataFds.emplace_back(
251 ndk::ScopedFileDescriptor(open(profilingDataFilePath.c_str(), O_CREAT, O_RDWR)));
252 mWait.notify_one();
253 return Status::SUCCESS;
254 }
255
256 } // namespace aidl_client
257 } // namespace client_interface
258 } // namespace runner
259 } // namespace computepipe
260 } // namespace automotive
261 } // namespace android
262