1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "1.0/Utils.h"
18 #include "1.3/Callbacks.h"
19 #include "1.3/Utils.h"
20 #include "GeneratedTestHarness.h"
21 #include "Utils.h"
22 
23 namespace android::hardware::neuralnetworks::V1_3::vts::functional {
24 
25 using implementation::ExecutionCallback;
26 using implementation::PreparedModelCallback;
27 using test_helper::TestBuffer;
28 using test_helper::TestModel;
29 using V1_1::ExecutionPreference;
30 using V1_2::MeasureTiming;
31 using V1_2::OutputShape;
32 using V1_2::Timing;
33 
34 using HidlToken =
35         hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
36 
37 enum class DeadlineBoundType { NOW, UNLIMITED, SHORT };
38 constexpr std::array<DeadlineBoundType, 3> deadlineBounds = {
39         DeadlineBoundType::NOW, DeadlineBoundType::UNLIMITED, DeadlineBoundType::SHORT};
toString(DeadlineBoundType type)40 std::string toString(DeadlineBoundType type) {
41     switch (type) {
42         case DeadlineBoundType::NOW:
43             return "NOW";
44         case DeadlineBoundType::UNLIMITED:
45             return "UNLIMITED";
46         case DeadlineBoundType::SHORT:
47             return "SHORT";
48     }
49     LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
50     return {};
51 }
52 
53 constexpr auto kShortDuration = std::chrono::milliseconds{5};
54 
55 using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
56 using MaybeResults = std::optional<Results>;
57 
58 using ExecutionFunction =
59         std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
60                                    const OptionalTimePoint& deadline)>;
61 
makeDeadline(DeadlineBoundType deadlineBoundType)62 static OptionalTimePoint makeDeadline(DeadlineBoundType deadlineBoundType) {
63     const auto getNanosecondsSinceEpoch = [](const auto& time) -> uint64_t {
64         const auto timeSinceEpoch = time.time_since_epoch();
65         return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
66     };
67 
68     std::chrono::steady_clock::time_point timePoint;
69     switch (deadlineBoundType) {
70         case DeadlineBoundType::NOW:
71             timePoint = std::chrono::steady_clock::now();
72             break;
73         case DeadlineBoundType::UNLIMITED:
74             timePoint = std::chrono::steady_clock::time_point::max();
75             break;
76         case DeadlineBoundType::SHORT:
77             timePoint = std::chrono::steady_clock::now() + kShortDuration;
78             break;
79     }
80 
81     OptionalTimePoint deadline;
82     deadline.nanosecondsSinceEpoch(getNanosecondsSinceEpoch(timePoint));
83     return deadline;
84 }
85 
runPrepareModelTest(const sp<IDevice> & device,const Model & model,Priority priority,std::optional<DeadlineBoundType> deadlineBound)86 void runPrepareModelTest(const sp<IDevice>& device, const Model& model, Priority priority,
87                          std::optional<DeadlineBoundType> deadlineBound) {
88     OptionalTimePoint deadline;
89     if (deadlineBound.has_value()) {
90         deadline = makeDeadline(deadlineBound.value());
91     }
92 
93     // see if service can handle model
94     bool fullySupportsModel = false;
95     const Return<void> supportedCall = device->getSupportedOperations_1_3(
96             model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
97                 ASSERT_EQ(ErrorStatus::NONE, status);
98                 ASSERT_NE(0ul, supported.size());
99                 fullySupportsModel = std::all_of(supported.begin(), supported.end(),
100                                                  [](bool valid) { return valid; });
101             });
102     ASSERT_TRUE(supportedCall.isOk());
103 
104     // launch prepare model
105     const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
106     const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
107             model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
108             hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
109     ASSERT_TRUE(prepareLaunchStatus.isOk());
110     ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
111 
112     // retrieve prepared model
113     preparedModelCallback->wait();
114     const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
115     const sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
116     const sp<IPreparedModel> preparedModel =
117             IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
118 
119     // The getSupportedOperations_1_3 call returns a list of operations that are
120     // guaranteed not to fail if prepareModel_1_3 is called, and
121     // 'fullySupportsModel' is true i.f.f. the entire model is guaranteed.
122     // If a driver has any doubt that it can prepare an operation, it must
123     // return false. So here, if a driver isn't sure if it can support an
124     // operation, but reports that it successfully prepared the model, the test
125     // can continue.
126     if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
127         ASSERT_EQ(nullptr, preparedModel.get());
128         return;
129     }
130 
131     // verify return status
132     if (!deadlineBound.has_value()) {
133         EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
134     } else {
135         switch (deadlineBound.value()) {
136             case DeadlineBoundType::NOW:
137             case DeadlineBoundType::SHORT:
138                 // Either the driver successfully completed the task or it
139                 // aborted and returned MISSED_DEADLINE_*.
140                 EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE ||
141                             prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
142                             prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
143                 break;
144             case DeadlineBoundType::UNLIMITED:
145                 // If an unlimited deadline is supplied, we expect the execution to
146                 // proceed normally. In this case, check it normally by breaking out
147                 // of the switch statement.
148                 EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
149                 break;
150         }
151     }
152     ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
153 }
154 
runPrepareModelTests(const sp<IDevice> & device,const Model & model)155 void runPrepareModelTests(const sp<IDevice>& device, const Model& model) {
156     // test priority
157     for (auto priority : hidl_enum_range<Priority>{}) {
158         SCOPED_TRACE("priority: " + toString(priority));
159         if (priority == kDefaultPriority) continue;
160         runPrepareModelTest(device, model, priority, {});
161     }
162 
163     // test deadline
164     for (auto deadlineBound : deadlineBounds) {
165         SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
166         runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
167     }
168 }
169 
executeAsynchronously(const sp<IPreparedModel> & preparedModel,const Request & request,const OptionalTimePoint & deadline)170 static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
171                                           const Request& request,
172                                           const OptionalTimePoint& deadline) {
173     SCOPED_TRACE("asynchronous");
174     const MeasureTiming measure = MeasureTiming::NO;
175 
176     // launch execution
177     const sp<ExecutionCallback> callback = new ExecutionCallback();
178     Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, {}, callback);
179     EXPECT_TRUE(ret.isOk());
180     EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
181     if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
182 
183     // retrieve execution results
184     callback->wait();
185     const ErrorStatus status = callback->getStatus();
186     hidl_vec<OutputShape> outputShapes = callback->getOutputShapes();
187     const Timing timing = callback->getTiming();
188 
189     // return results
190     return Results{status, std::move(outputShapes), timing};
191 }
192 
executeSynchronously(const sp<IPreparedModel> & preparedModel,const Request & request,const OptionalTimePoint & deadline)193 static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
194                                          const Request& request,
195                                          const OptionalTimePoint& deadline) {
196     SCOPED_TRACE("synchronous");
197     const MeasureTiming measure = MeasureTiming::NO;
198 
199     // configure results callback
200     MaybeResults results;
201     const auto cb = [&results](ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
202                                const Timing& timing) {
203         results.emplace(status, outputShapes, timing);
204     };
205 
206     // run execution
207     const Return<void> ret =
208             preparedModel->executeSynchronously_1_3(request, measure, deadline, {}, cb);
209     EXPECT_TRUE(ret.isOk());
210     if (!ret.isOk()) return std::nullopt;
211 
212     // return results
213     return results;
214 }
215 
runExecutionTest(const sp<IPreparedModel> & preparedModel,const TestModel & testModel,const Request & request,const ExecutionContext & context,bool synchronous,DeadlineBoundType deadlineBound)216 void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
217                       const Request& request, const ExecutionContext& context, bool synchronous,
218                       DeadlineBoundType deadlineBound) {
219     const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
220     const auto deadline = makeDeadline(deadlineBound);
221 
222     // Perform execution and unpack results.
223     const auto results = execute(preparedModel, request, deadline);
224     if (!results.has_value()) return;
225     const auto& [status, outputShapes, timing] = results.value();
226 
227     // Verify no timing information was returned
228     EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
229     EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
230 
231     // Validate deadline information if applicable.
232     switch (deadlineBound) {
233         case DeadlineBoundType::NOW:
234         case DeadlineBoundType::SHORT:
235             // Either the driver successfully completed the task or it
236             // aborted and returned MISSED_DEADLINE_*.
237             ASSERT_TRUE(status == ErrorStatus::NONE ||
238                         status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
239                         status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
240             break;
241         case DeadlineBoundType::UNLIMITED:
242             // If an unlimited deadline is supplied, we expect the execution to
243             // proceed normally. In this case, check it normally by breaking out
244             // of the switch statement.
245             ASSERT_EQ(ErrorStatus::NONE, status);
246             break;
247     }
248 
249     // If the model output operands are fully specified, outputShapes must be either
250     // either empty, or have the same number of elements as the number of outputs.
251     ASSERT_TRUE(outputShapes.size() == 0 ||
252                 outputShapes.size() == testModel.main.outputIndexes.size());
253 
254     // Go through all outputs, check returned output shapes.
255     for (uint32_t i = 0; i < outputShapes.size(); i++) {
256         EXPECT_TRUE(outputShapes[i].isSufficient);
257         const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
258         const std::vector<uint32_t> actual = outputShapes[i].dimensions;
259         EXPECT_EQ(expect, actual);
260     }
261 
262     // Retrieve execution results.
263     ASSERT_TRUE(nn::compliantWithV1_0(request));
264     const V1_0::Request request10 = nn::convertToV1_0(request);
265     const std::vector<TestBuffer> outputs = context.getOutputBuffers(request10);
266 
267     // We want "close-enough" results.
268     if (status == ErrorStatus::NONE) {
269         checkResults(testModel, outputs);
270     }
271 }
272 
runExecutionTests(const sp<IPreparedModel> & preparedModel,const TestModel & testModel,const Request & request,const ExecutionContext & context)273 void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
274                        const Request& request, const ExecutionContext& context) {
275     for (bool synchronous : {false, true}) {
276         for (auto deadlineBound : deadlineBounds) {
277             runExecutionTest(preparedModel, testModel, request, context, synchronous,
278                              deadlineBound);
279         }
280     }
281 }
282 
runTests(const sp<IDevice> & device,const TestModel & testModel)283 void runTests(const sp<IDevice>& device, const TestModel& testModel) {
284     // setup
285     const Model model = createModel(testModel);
286 
287     // run prepare model tests
288     runPrepareModelTests(device, model);
289 
290     // prepare model
291     sp<IPreparedModel> preparedModel;
292     createPreparedModel(device, model, &preparedModel);
293     if (preparedModel == nullptr) return;
294 
295     // run execution tests
296     ExecutionContext context;
297     const Request request = nn::convertToV1_3(context.createRequest(testModel));
298     runExecutionTests(preparedModel, testModel, request, context);
299 }
300 
301 class DeadlineTest : public GeneratedTestBase {};
302 
TEST_P(DeadlineTest,Test)303 TEST_P(DeadlineTest, Test) {
304     runTests(kDevice, kTestModel);
305 }
306 
307 INSTANTIATE_GENERATED_TEST(DeadlineTest,
__anon0aa7c0a40502(const TestModel& testModel) 308                            [](const TestModel& testModel) { return !testModel.expectFailure; });
309 
310 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
311