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