• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 #define LOG_TAG "neuralnetworks_hidl_hal_test"
18 
19 #include "VtsHalNeuralnetworks.h"
20 
21 #include "Callbacks.h"
22 #include "TestHarness.h"
23 #include "Utils.h"
24 
25 #include <android-base/logging.h>
26 #include <android/hidl/memory/1.0/IMemory.h>
27 #include <hidlmemory/mapping.h>
28 
29 namespace android {
30 namespace hardware {
31 namespace neuralnetworks {
32 namespace V1_0 {
33 namespace vts {
34 namespace functional {
35 
36 using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
37 using ::android::hidl::memory::V1_0::IMemory;
38 using test_helper::for_all;
39 using test_helper::MixedTyped;
40 using test_helper::MixedTypedExample;
41 
42 ///////////////////////// UTILITY FUNCTIONS /////////////////////////
43 
44 // Primary validation function. This function will take a valid request, apply a
45 // mutation to it to invalidate the request, then pass it to interface calls
46 // that use the request. Note that the request here is passed by value, and any
47 // mutation to the request does not leave this function.
validate(const sp<IPreparedModel> & preparedModel,const std::string & message,Request request,const std::function<void (Request *)> & mutation)48 static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
49                      Request request, const std::function<void(Request*)>& mutation) {
50     mutation(&request);
51     SCOPED_TRACE(message + " [execute]");
52 
53     sp<ExecutionCallback> executionCallback = new ExecutionCallback();
54     ASSERT_NE(nullptr, executionCallback.get());
55     Return<ErrorStatus> executeLaunchStatus = preparedModel->execute(request, executionCallback);
56     ASSERT_TRUE(executeLaunchStatus.isOk());
57     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
58 
59     executionCallback->wait();
60     ErrorStatus executionReturnStatus = executionCallback->getStatus();
61     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus);
62 }
63 
64 // Delete element from hidl_vec. hidl_vec doesn't support a "remove" operation,
65 // so this is efficiently accomplished by moving the element to the end and
66 // resizing the hidl_vec to one less.
67 template <typename Type>
hidl_vec_removeAt(hidl_vec<Type> * vec,uint32_t index)68 static void hidl_vec_removeAt(hidl_vec<Type>* vec, uint32_t index) {
69     if (vec) {
70         std::rotate(vec->begin() + index, vec->begin() + index + 1, vec->end());
71         vec->resize(vec->size() - 1);
72     }
73 }
74 
75 template <typename Type>
hidl_vec_push_back(hidl_vec<Type> * vec,const Type & value)76 static uint32_t hidl_vec_push_back(hidl_vec<Type>* vec, const Type& value) {
77     // assume vec is valid
78     const uint32_t index = vec->size();
79     vec->resize(index + 1);
80     (*vec)[index] = value;
81     return index;
82 }
83 
84 ///////////////////////// REMOVE INPUT ////////////////////////////////////
85 
removeInputTest(const sp<IPreparedModel> & preparedModel,const Request & request)86 static void removeInputTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
87     for (size_t input = 0; input < request.inputs.size(); ++input) {
88         const std::string message = "removeInput: removed input " + std::to_string(input);
89         validate(preparedModel, message, request,
90                  [input](Request* request) { hidl_vec_removeAt(&request->inputs, input); });
91     }
92 }
93 
94 ///////////////////////// REMOVE OUTPUT ////////////////////////////////////
95 
removeOutputTest(const sp<IPreparedModel> & preparedModel,const Request & request)96 static void removeOutputTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
97     for (size_t output = 0; output < request.outputs.size(); ++output) {
98         const std::string message = "removeOutput: removed Output " + std::to_string(output);
99         validate(preparedModel, message, request,
100                  [output](Request* request) { hidl_vec_removeAt(&request->outputs, output); });
101     }
102 }
103 
104 ///////////////////////////// ENTRY POINT //////////////////////////////////
105 
createRequests(const std::vector<MixedTypedExample> & examples)106 std::vector<Request> createRequests(const std::vector<MixedTypedExample>& examples) {
107     const uint32_t INPUT = 0;
108     const uint32_t OUTPUT = 1;
109 
110     std::vector<Request> requests;
111 
112     for (const MixedTypedExample& example : examples) {
113         const MixedTyped& inputs = example.operands.first;
114         const MixedTyped& outputs = example.operands.second;
115 
116         std::vector<RequestArgument> inputs_info, outputs_info;
117         uint32_t inputSize = 0, outputSize = 0;
118 
119         // This function only partially specifies the metadata (vector of RequestArguments).
120         // The contents are copied over below.
121         for_all(inputs, [&inputs_info, &inputSize](int index, auto, auto s) {
122             if (inputs_info.size() <= static_cast<size_t>(index)) inputs_info.resize(index + 1);
123             RequestArgument arg = {
124                 .location = {.poolIndex = INPUT, .offset = 0, .length = static_cast<uint32_t>(s)},
125                 .dimensions = {},
126             };
127             RequestArgument arg_empty = {
128                 .hasNoValue = true,
129             };
130             inputs_info[index] = s ? arg : arg_empty;
131             inputSize += s;
132         });
133         // Compute offset for inputs 1 and so on
134         {
135             size_t offset = 0;
136             for (auto& i : inputs_info) {
137                 if (!i.hasNoValue) i.location.offset = offset;
138                 offset += i.location.length;
139             }
140         }
141 
142         // Go through all outputs, initialize RequestArgument descriptors
143         for_all(outputs, [&outputs_info, &outputSize](int index, auto, auto s) {
144             if (outputs_info.size() <= static_cast<size_t>(index)) outputs_info.resize(index + 1);
145             RequestArgument arg = {
146                 .location = {.poolIndex = OUTPUT, .offset = 0, .length = static_cast<uint32_t>(s)},
147                 .dimensions = {},
148             };
149             outputs_info[index] = arg;
150             outputSize += s;
151         });
152         // Compute offset for outputs 1 and so on
153         {
154             size_t offset = 0;
155             for (auto& i : outputs_info) {
156                 i.location.offset = offset;
157                 offset += i.location.length;
158             }
159         }
160         std::vector<hidl_memory> pools = {nn::allocateSharedMemory(inputSize),
161                                           nn::allocateSharedMemory(outputSize)};
162         if (pools[INPUT].size() == 0 || pools[OUTPUT].size() == 0) {
163             return {};
164         }
165 
166         // map pool
167         sp<IMemory> inputMemory = mapMemory(pools[INPUT]);
168         if (inputMemory == nullptr) {
169             return {};
170         }
171         char* inputPtr = reinterpret_cast<char*>(static_cast<void*>(inputMemory->getPointer()));
172         if (inputPtr == nullptr) {
173             return {};
174         }
175 
176         // initialize pool
177         inputMemory->update();
178         for_all(inputs, [&inputs_info, inputPtr](int index, auto p, auto s) {
179             char* begin = (char*)p;
180             char* end = begin + s;
181             // TODO: handle more than one input
182             std::copy(begin, end, inputPtr + inputs_info[index].location.offset);
183         });
184         inputMemory->commit();
185 
186         requests.push_back({.inputs = inputs_info, .outputs = outputs_info, .pools = pools});
187     }
188 
189     return requests;
190 }
191 
validateRequests(const sp<IPreparedModel> & preparedModel,const std::vector<Request> & requests)192 void ValidationTest::validateRequests(const sp<IPreparedModel>& preparedModel,
193                                       const std::vector<Request>& requests) {
194     // validate each request
195     for (const Request& request : requests) {
196         removeInputTest(preparedModel, request);
197         removeOutputTest(preparedModel, request);
198     }
199 }
200 
201 }  // namespace functional
202 }  // namespace vts
203 }  // namespace V1_0
204 }  // namespace neuralnetworks
205 }  // namespace hardware
206 }  // namespace android
207