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 // This test only tests internal APIs, and has dependencies on internal header
18 // files, including NN API HIDL definitions.
19 // It is not part of CTS.
20
21 #include "TestMemory.h"
22
23 #include "Manager.h"
24 #include "Memory.h"
25 #include "TestNeuralNetworksWrapper.h"
26
27 #include <android/sharedmem.h>
28 #include <gtest/gtest.h>
29
30 #include <fstream>
31 #include <string>
32
33 using WrapperCompilation = ::android::nn::test_wrapper::Compilation;
34 using WrapperExecution = ::android::nn::test_wrapper::Execution;
35 using WrapperMemory = ::android::nn::test_wrapper::Memory;
36 using WrapperModel = ::android::nn::test_wrapper::Model;
37 using WrapperOperandType = ::android::nn::test_wrapper::OperandType;
38 using WrapperResult = ::android::nn::test_wrapper::Result;
39 using WrapperType = ::android::nn::test_wrapper::Type;
40
41 namespace {
42
43 // Tests to ensure that various kinds of memory leaks do not occur.
44 //
45 // The fixture checks that no anonymous shared memory regions are leaked by
46 // comparing the count of /dev/ashmem mappings in SetUp and TearDown. This could
47 // break if the test or framework starts lazily instantiating something that
48 // creates a mapping - at that point the way the test works needs to be
49 // reinvestigated. The filename /dev/ashmem is a documented part of the Android
50 // kernel interface (see
51 // https://source.android.com/devices/architecture/kernel/reqs-interfaces).
52 //
53 // (We can also get very unlucky and mask a memory leak by unrelated unmapping
54 // somewhere else. This seems unlikely enough to not deal with.)
55 class MemoryLeakTest : public ::testing::Test {
56 protected:
57 void SetUp() override;
58 void TearDown() override;
59
60 private:
61 size_t GetAshmemMappingsCount();
62
63 size_t mStartingMapCount = 0;
64 bool mIsCpuOnly;
65 };
66
SetUp()67 void MemoryLeakTest::SetUp() {
68 mIsCpuOnly = android::nn::DeviceManager::get()->getUseCpuOnly();
69 mStartingMapCount = GetAshmemMappingsCount();
70 }
71
TearDown()72 void MemoryLeakTest::TearDown() {
73 android::nn::DeviceManager::get()->setUseCpuOnly(mIsCpuOnly);
74 const size_t endingMapCount = GetAshmemMappingsCount();
75 ASSERT_EQ(mStartingMapCount, endingMapCount);
76 }
77
GetAshmemMappingsCount()78 size_t MemoryLeakTest::GetAshmemMappingsCount() {
79 std::ifstream mappingsStream("/proc/self/maps");
80 if (!mappingsStream.good()) {
81 // errno is set by std::ifstream on Linux
82 ADD_FAILURE() << "Failed to open /proc/self/maps: " << std::strerror(errno);
83 return 0;
84 }
85 std::string line;
86 int mapCount = 0;
87 while (std::getline(mappingsStream, line)) {
88 if (line.find("/dev/ashmem") != std::string::npos) {
89 ++mapCount;
90 }
91 }
92 return mapCount;
93 }
94
95 // As well as serving as a functional test for ASharedMemory, also
96 // serves as a regression test for http://b/69685100 "RunTimePoolInfo
97 // leaks shared memory regions".
98 //
99 // TODO: test non-zero offset.
TEST_F(MemoryLeakTest,TestASharedMemory)100 TEST_F(MemoryLeakTest, TestASharedMemory) {
101 // Layout where to place matrix2 and matrix3 in the memory we'll allocate.
102 // We have gaps to test that we don't assume contiguity.
103 constexpr uint32_t offsetForMatrix2 = 20;
104 constexpr uint32_t offsetForMatrix3 = offsetForMatrix2 + sizeof(matrix2) + 30;
105 constexpr uint32_t weightsSize = offsetForMatrix3 + sizeof(matrix3) + 60;
106
107 int weightsFd = ASharedMemory_create("weights", weightsSize);
108 ASSERT_GT(weightsFd, -1);
109 uint8_t* weightsData =
110 (uint8_t*)mmap(nullptr, weightsSize, PROT_READ | PROT_WRITE, MAP_SHARED, weightsFd, 0);
111 ASSERT_NE(weightsData, nullptr);
112 memcpy(weightsData + offsetForMatrix2, matrix2, sizeof(matrix2));
113 memcpy(weightsData + offsetForMatrix3, matrix3, sizeof(matrix3));
114 WrapperMemory weights(weightsSize, PROT_READ | PROT_WRITE, weightsFd, 0);
115 ASSERT_TRUE(weights.isValid());
116
117 WrapperModel model;
118 WrapperOperandType matrixType(WrapperType::TENSOR_FLOAT32, {3, 4});
119 WrapperOperandType scalarType(WrapperType::INT32, {});
120 int32_t activation(0);
121 auto a = model.addOperand(&matrixType);
122 auto b = model.addOperand(&matrixType);
123 auto c = model.addOperand(&matrixType);
124 auto d = model.addOperand(&matrixType);
125 auto e = model.addOperand(&matrixType);
126 auto f = model.addOperand(&scalarType);
127
128 model.setOperandValueFromMemory(e, &weights, offsetForMatrix2, sizeof(Matrix3x4));
129 model.setOperandValueFromMemory(a, &weights, offsetForMatrix3, sizeof(Matrix3x4));
130 model.setOperandValue(f, &activation, sizeof(activation));
131 model.addOperation(ANEURALNETWORKS_ADD, {a, c, f}, {b});
132 model.addOperation(ANEURALNETWORKS_ADD, {b, e, f}, {d});
133 model.identifyInputsAndOutputs({c}, {d});
134 ASSERT_TRUE(model.isValid());
135 model.finish();
136
137 // Test the two node model.
138 constexpr uint32_t offsetForMatrix1 = 20;
139 constexpr size_t inputSize = offsetForMatrix1 + sizeof(Matrix3x4);
140 int inputFd = ASharedMemory_create("input", inputSize);
141 ASSERT_GT(inputFd, -1);
142 uint8_t* inputData =
143 (uint8_t*)mmap(nullptr, inputSize, PROT_READ | PROT_WRITE, MAP_SHARED, inputFd, 0);
144 ASSERT_NE(inputData, nullptr);
145 memcpy(inputData + offsetForMatrix1, matrix1, sizeof(Matrix3x4));
146 WrapperMemory input(inputSize, PROT_READ, inputFd, 0);
147 ASSERT_TRUE(input.isValid());
148
149 constexpr uint32_t offsetForActual = 32;
150 constexpr size_t outputSize = offsetForActual + sizeof(Matrix3x4);
151 int outputFd = ASharedMemory_create("output", outputSize);
152 ASSERT_GT(outputFd, -1);
153 uint8_t* outputData =
154 (uint8_t*)mmap(nullptr, outputSize, PROT_READ | PROT_WRITE, MAP_SHARED, outputFd, 0);
155 ASSERT_NE(outputData, nullptr);
156 memset(outputData, 0, outputSize);
157 WrapperMemory actual(outputSize, PROT_READ | PROT_WRITE, outputFd, 0);
158 ASSERT_TRUE(actual.isValid());
159
160 WrapperCompilation compilation2(&model);
161 ASSERT_EQ(compilation2.finish(), WrapperResult::NO_ERROR);
162
163 WrapperExecution execution2(&compilation2);
164 ASSERT_EQ(execution2.setInputFromMemory(0, &input, offsetForMatrix1, sizeof(Matrix3x4)),
165 WrapperResult::NO_ERROR);
166 ASSERT_EQ(execution2.setOutputFromMemory(0, &actual, offsetForActual, sizeof(Matrix3x4)),
167 WrapperResult::NO_ERROR);
168 ASSERT_EQ(execution2.compute(), WrapperResult::NO_ERROR);
169 ASSERT_EQ(
170 CompareMatrices(expected3, *reinterpret_cast<Matrix3x4*>(outputData + offsetForActual)),
171 0);
172
173 munmap(weightsData, weightsSize);
174 munmap(inputData, inputSize);
175 munmap(outputData, outputSize);
176 close(weightsFd);
177 close(inputFd);
178 close(outputFd);
179 }
180
181 #ifndef NNTEST_ONLY_PUBLIC_API
182 // Regression test for http://b/73663843, conv_2d trying to allocate too much memory.
TEST_F(MemoryLeakTest,convTooLarge)183 TEST_F(MemoryLeakTest, convTooLarge) {
184 android::nn::DeviceManager::get()->setUseCpuOnly(true);
185 WrapperModel model;
186
187 // This kernel/input size will make convQuant8 allocate 12 * 13 * 13 * 128 * 92 * 92, which is
188 // just outside of signed int range (0x82F56000) - this will fail due to CPU implementation
189 // limitations
190 WrapperOperandType type3(WrapperType::INT32, {});
191 WrapperOperandType type2(WrapperType::TENSOR_INT32, {128}, 0.25, 0);
192 WrapperOperandType type0(WrapperType::TENSOR_QUANT8_ASYMM, {12, 104, 104, 128}, 0.5, 0);
193 WrapperOperandType type4(WrapperType::TENSOR_QUANT8_ASYMM, {12, 92, 92, 128}, 1.0, 0);
194 WrapperOperandType type1(WrapperType::TENSOR_QUANT8_ASYMM, {128, 13, 13, 128}, 0.5, 0);
195
196 // Operands
197 auto op1 = model.addOperand(&type0);
198 auto op2 = model.addOperand(&type1);
199 auto op3 = model.addOperand(&type2);
200 auto pad0 = model.addOperand(&type3);
201 auto act = model.addOperand(&type3);
202 auto stride = model.addOperand(&type3);
203 auto op4 = model.addOperand(&type4);
204
205 // Operations
206 uint8_t op2_init[128 * 13 * 13 * 128] = {};
207 model.setOperandValue(op2, op2_init, sizeof(op2_init));
208 int32_t op3_init[128] = {};
209 model.setOperandValue(op3, op3_init, sizeof(op3_init));
210 int32_t pad0_init[] = {0};
211 model.setOperandValue(pad0, pad0_init, sizeof(pad0_init));
212 int32_t act_init[] = {0};
213 model.setOperandValue(act, act_init, sizeof(act_init));
214 int32_t stride_init[] = {1};
215 model.setOperandValue(stride, stride_init, sizeof(stride_init));
216 model.addOperation(ANEURALNETWORKS_CONV_2D,
217 {op1, op2, op3, pad0, pad0, pad0, pad0, stride, stride, act}, {op4});
218
219 // Inputs and outputs
220 model.identifyInputsAndOutputs({op1}, {op4});
221 ASSERT_TRUE(model.isValid());
222 model.finish();
223
224 // Compilation
225 WrapperCompilation compilation(&model);
226 ASSERT_EQ(WrapperResult::NO_ERROR, compilation.finish());
227 WrapperExecution execution(&compilation);
228
229 // Set input and outputs
230 static uint8_t input[12 * 104 * 104 * 128] = {};
231 ASSERT_EQ(WrapperResult::NO_ERROR, execution.setInput(0, input, sizeof(input)));
232 static uint8_t output[12 * 92 * 92 * 128] = {};
233 ASSERT_EQ(WrapperResult::NO_ERROR, execution.setOutput(0, output, sizeof(output)));
234
235 // This shouldn't segfault
236 WrapperResult r = execution.compute();
237
238 ASSERT_EQ(WrapperResult::OP_FAILED, r);
239 }
240 #endif // NNTEST_ONLY_PUBLIC_API
241
242 } // end namespace
243