1 /*
2  * Copyright (C) 2020 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 <android/hardware/neuralnetworks/1.2/ADevice.h>
18 #include <gtest/gtest.h>
19 
20 #include <algorithm>
21 #include <map>
22 #include <set>
23 #include <string>
24 #include <tuple>
25 #include <utility>
26 #include <vector>
27 
28 #include "HalInterfaces.h"
29 #include "Manager.h"
30 #include "Memory.h"
31 #include "SampleDriver.h"
32 #include "SampleDriverFull.h"
33 #include "TestNeuralNetworksWrapper.h"
34 #include "TestUtils.h"
35 
36 using namespace android::nn;
37 using namespace hal;
38 using Result = test_wrapper::Result;
39 using Type = test_wrapper::Type;
40 
41 namespace {
42 
43 // A buffer for test that does nothing.
44 class TestBuffer : public IBuffer {
45    public:
copyTo(const hidl_memory &)46     Return<ErrorStatus> copyTo(const hidl_memory&) override {
47         return ErrorStatus::DEVICE_UNAVAILABLE;
48     }
copyFrom(const hidl_memory &,const hidl_vec<uint32_t> &)49     Return<ErrorStatus> copyFrom(const hidl_memory&, const hidl_vec<uint32_t>&) override {
50         return ErrorStatus::DEVICE_UNAVAILABLE;
51     }
52 };
53 
54 enum class AllocateReturn { OK, BAD_TOKEN, BAD_IBUFFER, BAD_STATUS, NOT_SUPPORTED };
55 
56 // Print AllocateReturn enum for better GTEST failure messages
operator <<(std::ostream & os,AllocateReturn allocateReturn)57 std::ostream& operator<<(std::ostream& os, AllocateReturn allocateReturn) {
58     switch (allocateReturn) {
59         case AllocateReturn::OK:
60             return os << "OK";
61         case AllocateReturn::BAD_IBUFFER:
62             return os << "BAD_IBUFFER";
63         case AllocateReturn::BAD_TOKEN:
64             return os << "BAD_TOKEN";
65         case AllocateReturn::BAD_STATUS:
66             return os << "BAD_STATUS";
67         case AllocateReturn::NOT_SUPPORTED:
68             return os << "NOT_SUPPORTED";
69     }
70     LOG(FATAL) << "Invalid AllocateReturn code " << static_cast<int>(allocateReturn);
71     return os;
72 }
73 
74 class TestDriverLatest : public sample_driver::SampleDriver {
75    public:
TestDriverLatest(const char * name,std::set<OperationType> supportedOperations,AllocateReturn allocateReturn)76     TestDriverLatest(const char* name, std::set<OperationType> supportedOperations,
77                      AllocateReturn allocateReturn)
78         : SampleDriver(name),
79           kSupportedOperations(std::move(supportedOperations)),
80           kAllocateReturn(allocateReturn) {}
81 
getCapabilities_1_3(getCapabilities_1_3_cb cb)82     Return<void> getCapabilities_1_3(getCapabilities_1_3_cb cb) override {
83         android::nn::initVLogMask();
84         // Faster than cpu.
85         const PerformanceInfo kPerf = {.execTime = 0.1, .powerUsage = 0.1};
86         const Capabilities capabilities = {
87                 .relaxedFloat32toFloat16PerformanceScalar = kPerf,
88                 .relaxedFloat32toFloat16PerformanceTensor = kPerf,
89                 .operandPerformance = nonExtensionOperandPerformance<HalVersion::V1_3>(kPerf),
90                 .ifPerformance = kPerf,
91                 .whilePerformance = kPerf};
92         cb(ErrorStatus::NONE, capabilities);
93         return Void();
94     }
95 
getSupportedOperations_1_3(const Model & model,getSupportedOperations_1_3_cb cb)96     Return<void> getSupportedOperations_1_3(const Model& model,
97                                             getSupportedOperations_1_3_cb cb) override {
98         // The tests will never use a referenced model.
99         CHECK(model.referenced.size() == 0);
100         std::vector<bool> supported(model.main.operations.size(), false);
101         std::transform(
102                 model.main.operations.begin(), model.main.operations.end(), supported.begin(),
103                 [this](const Operation& op) { return kSupportedOperations.count(op.type) > 0; });
104         cb(ErrorStatus::NONE, supported);
105         return Void();
106     }
107 
allocate(const BufferDesc &,const hidl_vec<sp<IPreparedModel>> &,const hidl_vec<BufferRole> &,const hidl_vec<BufferRole> &,allocate_cb cb)108     Return<void> allocate(const BufferDesc&, const hidl_vec<sp<IPreparedModel>>&,
109                           const hidl_vec<BufferRole>&, const hidl_vec<BufferRole>&,
110                           allocate_cb cb) override {
111         switch (kAllocateReturn) {
112             case AllocateReturn::OK:
113                 cb(ErrorStatus::NONE, new TestBuffer(), mValidBufferToken++);
114                 return Void();
115             case AllocateReturn::BAD_IBUFFER:
116                 cb(ErrorStatus::NONE, nullptr, mValidBufferToken++);
117                 return Void();
118             case AllocateReturn::BAD_TOKEN:
119                 cb(ErrorStatus::NONE, new TestBuffer(), 0);
120                 return Void();
121             case AllocateReturn::BAD_STATUS:
122                 cb(ErrorStatus::GENERAL_FAILURE, new TestBuffer(), mValidBufferToken++);
123                 return Void();
124             case AllocateReturn::NOT_SUPPORTED:
125                 cb(ErrorStatus::GENERAL_FAILURE, nullptr, 0);
126                 return Void();
127         }
128         LOG(FATAL) << "Invalid AllocateReturn code " << static_cast<int>(kAllocateReturn);
129         return Void();
130     }
131 
132    private:
133     const std::set<OperationType> kSupportedOperations;
134     const AllocateReturn kAllocateReturn;
135     uint32_t mValidBufferToken = 1;
136 };
137 
138 // Create the following model for test.
139 //
140 //     input0 ---+
141 //               +--- ADD ---> output0 ---+
142 //     input1 ---+                        +--- MUL ---> output1 (dynamic shape)
143 //               +--- SUB ---> temp    ---+
144 //     input2 ---+
145 //
createTestModel()146 test_wrapper::Model createTestModel() {
147     test_wrapper::Model model;
148     test_wrapper::OperandType tensorTypeFullySpecified(Type::TENSOR_FLOAT32, {1});
149     test_wrapper::OperandType tensorTypeDynamicShape(Type::TENSOR_FLOAT32, {0});
150     test_wrapper::OperandType actType(Type::INT32, {});
151     uint32_t input0 = model.addOperand(&tensorTypeFullySpecified);
152     uint32_t input1 = model.addOperand(&tensorTypeFullySpecified);
153     uint32_t input2 = model.addOperand(&tensorTypeFullySpecified);
154     uint32_t temp = model.addOperand(&tensorTypeFullySpecified);
155     uint32_t output0 = model.addOperand(&tensorTypeFullySpecified);
156     uint32_t output1 = model.addOperand(&tensorTypeDynamicShape);
157     uint32_t act = model.addOperand(&actType);
158     int32_t activation = 0;
159     model.setOperandValue(act, &activation, sizeof(int32_t));
160     model.addOperation(ANEURALNETWORKS_ADD, {input0, input1, act}, {output0});
161     model.addOperation(ANEURALNETWORKS_SUB, {input1, input2, act}, {temp});
162     model.addOperation(ANEURALNETWORKS_MUL, {output0, temp, act}, {output1});
163     model.identifyInputsAndOutputs({input0, input1, input2}, {output0, output1});
164     EXPECT_EQ(model.finish(), Result::NO_ERROR);
165     return model;
166 }
167 
168 class MemoryDomainTestBase : public ::testing::Test {
169    protected:
SetUp()170     void SetUp() override {
171         ::testing::Test::SetUp();
172         if (DeviceManager::get()->getUseCpuOnly()) {
173             GTEST_SKIP();
174         }
175         // Clear the device list.
176         DeviceManager::get()->forTest_setDevices({});
177     }
178 
TearDown()179     void TearDown() override {
180         DeviceManager::get()->forTest_reInitializeDeviceList();
181         ::testing::Test::TearDown();
182     }
183 
184     // If "deviceNames" is not empty, the compilation is created with explicit device list;
185     // otherwise, it is created normally.
createCompilation(const std::vector<std::string> & deviceNames)186     test_wrapper::Compilation createCompilation(const std::vector<std::string>& deviceNames) {
187         test_wrapper::Compilation compilation;
188         if (!deviceNames.empty()) {
189             // Map device names to ANeuralNetworksDevice.
190             std::map<std::string, ANeuralNetworksDevice*> deviceMap;
191             uint32_t numDevices = 0;
192             EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);
193             for (uint32_t i = 0; i < numDevices; i++) {
194                 ANeuralNetworksDevice* device = nullptr;
195                 const char* name = nullptr;
196                 EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
197                 EXPECT_EQ(ANeuralNetworksDevice_getName(device, &name), ANEURALNETWORKS_NO_ERROR);
198                 deviceMap.emplace(name, device);
199             }
200             std::vector<const ANeuralNetworksDevice*> devices(deviceNames.size());
201             std::transform(deviceNames.begin(), deviceNames.end(), devices.begin(),
202                            [&deviceMap](const std::string& name) { return deviceMap.at(name); });
203             Result result;
204             std::tie(result, compilation) =
205                     test_wrapper::Compilation::createForDevices(&kModel, devices);
206             EXPECT_EQ(result, Result::NO_ERROR);
207         } else {
208             compilation = test_wrapper::Compilation(&kModel);
209         }
210         EXPECT_EQ(compilation.finish(), Result::NO_ERROR);
211         return compilation;
212     }
213 
allocateDeviceMemory(const test_wrapper::Compilation & compilation,const std::vector<uint32_t> & inputIndexes,const std::vector<uint32_t> & outputIndexes)214     std::pair<int, test_wrapper::Memory> allocateDeviceMemory(
215             const test_wrapper::Compilation& compilation, const std::vector<uint32_t>& inputIndexes,
216             const std::vector<uint32_t>& outputIndexes) {
217         const auto* annCompilation = compilation.getHandle();
218         ANeuralNetworksMemoryDesc* desc = nullptr;
219         EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
220         for (uint32_t index : inputIndexes) {
221             EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, annCompilation, index, 1.0f),
222                       ANEURALNETWORKS_NO_ERROR);
223         }
224         for (uint32_t index : outputIndexes) {
225             EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, annCompilation, index, 1.0f),
226                       ANEURALNETWORKS_NO_ERROR);
227         }
228         EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);
229 
230         ANeuralNetworksMemory* memory;
231         int n = ANeuralNetworksMemory_createFromDesc(desc, &memory);
232         ANeuralNetworksMemoryDesc_free(desc);
233         return {n, test_wrapper::Memory(memory)};
234     }
235 
236     static const test_wrapper::Model kModel;
237 };
238 
239 const test_wrapper::Model MemoryDomainTestBase::kModel = createTestModel();
240 
241 // Test memory domain with the following parameters
242 // - If true, use a V1_2 driver, otherwise, use the latest version;
243 // - If true, compile with explicit device list, otherwise, compile in the default way;
244 // - The return of the allocate function.
245 using MemoryDomainTestParam = std::tuple<bool, bool, AllocateReturn>;
246 
247 class MemoryDomainTest : public MemoryDomainTestBase,
248                          public ::testing::WithParamInterface<MemoryDomainTestParam> {
249    protected:
250     // If kUseV1_2Driver, allocateReturn must be AllocateReturn::NOT_SUPPORTED.
createAndRegisterDriver(const char * name,std::set<OperationType> supportedOperations,AllocateReturn allocateReturn)251     void createAndRegisterDriver(const char* name, std::set<OperationType> supportedOperations,
252                                  AllocateReturn allocateReturn) {
253         sp<V1_0::IDevice> driver;
254         if (kUseV1_2Driver) {
255             CHECK(allocateReturn == AllocateReturn::NOT_SUPPORTED);
256             const sp<TestDriverLatest> testDriver =
257                     new TestDriverLatest(name, supportedOperations, AllocateReturn::NOT_SUPPORTED);
258             driver = new V1_2::ADevice(testDriver);
259         } else {
260             driver = new TestDriverLatest(name, std::move(supportedOperations), allocateReturn);
261         }
262         DeviceManager::get()->forTest_registerDevice(name, driver);
263     }
264 
265     // If not kCompileWithExplicitDeviceList, the input argument "deviceNames" is ignored.
createCompilation(const std::vector<std::string> & deviceNames)266     test_wrapper::Compilation createCompilation(const std::vector<std::string>& deviceNames) {
267         if (kCompileWithExplicitDeviceList) {
268             return MemoryDomainTestBase::createCompilation(deviceNames);
269         } else {
270             return MemoryDomainTestBase::createCompilation({});
271         }
272     }
273 
274     const bool kUseV1_2Driver = std::get<0>(GetParam());
275     const bool kCompileWithExplicitDeviceList = std::get<1>(GetParam());
276     const AllocateReturn kAllocateReturn = std::get<2>(GetParam());
277 };
278 
279 // Test device memory allocation on a compilation with only a single partition.
TEST_P(MemoryDomainTest,SinglePartition)280 TEST_P(MemoryDomainTest, SinglePartition) {
281     createAndRegisterDriver("test_driver",
282                             {OperationType::ADD, OperationType::SUB, OperationType::MUL},
283                             kAllocateReturn);
284     auto compilation = createCompilation({"test_driver"});
285     ASSERT_NE(compilation.getHandle(), nullptr);
286 
287     auto [n, memory] = allocateDeviceMemory(compilation, {0}, {0});
288     if (kAllocateReturn == AllocateReturn::OK) {
289         // The memory should be backed by the IBuffer returned from the driver.
290         ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
291         const Memory* m = reinterpret_cast<const Memory*>(memory.get());
292         ASSERT_NE(m, nullptr);
293         EXPECT_NE(m->getIBuffer(), nullptr);
294     } else {
295         if (kCompileWithExplicitDeviceList) {
296             // Should not fallback when the compiled with explicit device list.
297             ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
298         } else {
299             // The memory should fallback to ashmem or blob ahwb based on the driver version.
300             ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
301             const Memory* m = reinterpret_cast<const Memory*>(memory.get());
302             ASSERT_NE(m, nullptr);
303             EXPECT_EQ(m->getIBuffer(), nullptr);
304             const auto& hidlMemory = m->getHidlMemory();
305             EXPECT_TRUE(hidlMemory.valid());
306             if (kUseV1_2Driver) {
307                 EXPECT_EQ(hidlMemory.name(), "ashmem");
308             } else {
309                 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
310             }
311         }
312     }
313 }
314 
315 // Test device memory allocation on a compilation with multiple partitions.
TEST_P(MemoryDomainTest,MultiplePartitions)316 TEST_P(MemoryDomainTest, MultiplePartitions) {
317     createAndRegisterDriver("test_driver_add", {OperationType::ADD}, kAllocateReturn);
318     createAndRegisterDriver("test_driver_sub", {OperationType::SUB}, kAllocateReturn);
319     createAndRegisterDriver("test_driver_mul", {OperationType::MUL}, kAllocateReturn);
320     auto compilation = createCompilation({"test_driver_add", "test_driver_sub", "test_driver_mul"});
321     ASSERT_NE(compilation.getHandle(), nullptr);
322 
323     {
324         // input0 is only used in one single partition.
325         auto [n, memory] = allocateDeviceMemory(compilation, {0}, {});
326         if (kAllocateReturn == AllocateReturn::OK) {
327             // The memory should be backed by the IBuffer returned from the driver.
328             ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
329             const Memory* m = reinterpret_cast<const Memory*>(memory.get());
330             ASSERT_NE(m, nullptr);
331             EXPECT_NE(m->getIBuffer(), nullptr);
332         } else {
333             if (kCompileWithExplicitDeviceList) {
334                 // Should not fallback when the compiled with explicit device list.
335                 ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
336             } else {
337                 // The memory should fallback to ashmem or blob ahwb based on the driver version.
338                 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
339                 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
340                 ASSERT_NE(m, nullptr);
341                 EXPECT_EQ(m->getIBuffer(), nullptr);
342                 const auto& hidlMemory = m->getHidlMemory();
343                 EXPECT_TRUE(hidlMemory.valid());
344                 if (kUseV1_2Driver) {
345                     EXPECT_EQ(hidlMemory.name(), "ashmem");
346                 } else {
347                     EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
348                 }
349             }
350         }
351     }
352 
353     {
354         // input1 is shared by two partitions with different drivers, so the runtime will not
355         // attempt to allocate on device.
356         auto [n, memory] = allocateDeviceMemory(compilation, {1}, {});
357         if (kCompileWithExplicitDeviceList) {
358             // Should not fallback when the compiled with explicit device list.
359             ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
360         } else {
361             // The memory should fallback to ashmem or blob ahwb based on the driver version.
362             ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
363             const Memory* m = reinterpret_cast<const Memory*>(memory.get());
364             ASSERT_NE(m, nullptr);
365             EXPECT_EQ(m->getIBuffer(), nullptr);
366             const auto& hidlMemory = m->getHidlMemory();
367             EXPECT_TRUE(hidlMemory.valid());
368             if (kUseV1_2Driver) {
369                 EXPECT_EQ(hidlMemory.name(), "ashmem");
370             } else {
371                 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
372             }
373         }
374     }
375 
376     {
377         // output0 is shared by two partitions with different drivers, so the runtime will not
378         // attempt to allocate on device.
379         auto [n, memory] = allocateDeviceMemory(compilation, {}, {0});
380         if (kCompileWithExplicitDeviceList) {
381             // Should not fallback when the compiled with explicit device list.
382             ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
383         } else {
384             // The memory should fallback to ashmem or blob ahwb based on the driver version.
385             ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
386             const Memory* m = reinterpret_cast<const Memory*>(memory.get());
387             ASSERT_NE(m, nullptr);
388             EXPECT_EQ(m->getIBuffer(), nullptr);
389             const auto& hidlMemory = m->getHidlMemory();
390             EXPECT_TRUE(hidlMemory.valid());
391             if (kUseV1_2Driver) {
392                 EXPECT_EQ(hidlMemory.name(), "ashmem");
393             } else {
394                 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
395             }
396         }
397     }
398 }
399 
400 // Test device memory allocation with dynamic shape.
TEST_P(MemoryDomainTest,DynamicShape)401 TEST_P(MemoryDomainTest, DynamicShape) {
402     createAndRegisterDriver("test_driver",
403                             {OperationType::ADD, OperationType::SUB, OperationType::MUL},
404                             kAllocateReturn);
405     auto compilation = createCompilation({"test_driver"});
406     ASSERT_NE(compilation.getHandle(), nullptr);
407 
408     auto [n, memory] = allocateDeviceMemory(compilation, {}, {1});
409     if (kAllocateReturn == AllocateReturn::OK) {
410         // The memory should be backed by the IBuffer returned from the driver.
411         ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
412         const Memory* m = reinterpret_cast<const Memory*>(memory.get());
413         ASSERT_NE(m, nullptr);
414         EXPECT_NE(m->getIBuffer(), nullptr);
415     } else {
416         // We do not fallback in the case of dynamic shape.
417         ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
418     }
419 }
420 
421 static const auto kAllocateReturnChoices =
422         testing::Values(AllocateReturn::OK, AllocateReturn::BAD_TOKEN, AllocateReturn::BAD_IBUFFER,
423                         AllocateReturn::BAD_STATUS, AllocateReturn::NOT_SUPPORTED);
424 
425 INSTANTIATE_TEST_CASE_P(DeviceVersionLatest, MemoryDomainTest,
426                         testing::Combine(testing::Values(false), testing::Bool(),
427                                          kAllocateReturnChoices));
428 INSTANTIATE_TEST_CASE_P(DeviceVersionV1_2, MemoryDomainTest,
429                         testing::Combine(testing::Values(true), testing::Bool(),
430                                          testing::Values(AllocateReturn::NOT_SUPPORTED)));
431 
432 class MemoryDomainCopyTest : public MemoryDomainTestBase {};
433 
TEST_F(MemoryDomainCopyTest,MemoryCopyTest)434 TEST_F(MemoryDomainCopyTest, MemoryCopyTest) {
435     sp<sample_driver::SampleDriverFull> driver(new sample_driver::SampleDriverFull(
436             "test_driver", {.execTime = 0.1f, .powerUsage = 0.1f}));
437     DeviceManager::get()->forTest_registerDevice("test_driver", driver);
438     auto compilation = createCompilation({"test_driver"});
439     ASSERT_NE(compilation.getHandle(), nullptr);
440 
441     // Allocate ashmem.
442     const float initValue1 = 3.14f, initValue2 = 2.72f;
443     auto ashmem1 = TestAshmem::createFrom(&initValue1, sizeof(float));
444     auto ashmem2 = TestAshmem::createFrom(&initValue2, sizeof(float));
445     ASSERT_NE(ashmem1, nullptr);
446     ASSERT_NE(ashmem2, nullptr);
447 
448     // Allocate device memories.
449     auto [n1, memory1] = allocateDeviceMemory(compilation, {0}, {});
450     auto [n2, memory2] = allocateDeviceMemory(compilation, {0}, {});
451     ASSERT_EQ(n1, ANEURALNETWORKS_NO_ERROR);
452     ASSERT_EQ(n2, ANEURALNETWORKS_NO_ERROR);
453 
454     // Test memory copying: ashmem1 -> memory1 -> memory2 -> ashmem2
455     ASSERT_EQ(ANeuralNetworksMemory_copy(ashmem1->get()->get(), memory1.get()),
456               ANEURALNETWORKS_NO_ERROR);
457     ASSERT_EQ(ANeuralNetworksMemory_copy(memory1.get(), memory2.get()), ANEURALNETWORKS_NO_ERROR);
458     ASSERT_EQ(ANeuralNetworksMemory_copy(memory2.get(), ashmem2->get()->get()),
459               ANEURALNETWORKS_NO_ERROR);
460 
461     EXPECT_EQ(ashmem2->dataAs<float>()[0], initValue1);
462 }
463 
464 }  // namespace
465