1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/nnapi/nnapi_implementation.h"
16
17 #include <dlfcn.h>
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <algorithm>
24 #include <cstdlib>
25
26 #ifdef __ANDROID__
27 #include <sys/system_properties.h>
28 #endif // __ANDROID__
29
30 #define NNAPI_LOG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__);
31
32 namespace {
33
34 #ifdef __ANDROID__
GetAndroidSdkVersion()35 int32_t GetAndroidSdkVersion() {
36 const char* sdkProp = "ro.build.version.sdk";
37 char sdkVersion[PROP_VALUE_MAX];
38 int length = __system_property_get(sdkProp, sdkVersion);
39 if (length != 0) {
40 int32_t result = 0;
41 for (int i = 0; i < length; ++i) {
42 int digit = sdkVersion[i] - '0';
43 if (digit < 0 || digit > 9) {
44 // Non-numeric SDK version, assume it's higher than expected;
45 return 0xffff;
46 }
47 result = result * 10 + digit;
48 }
49 return result;
50 }
51 return 0;
52 }
53 #endif // __ANDROID__
54
LoadFunction(void * handle,const char * name,bool optional)55 void* LoadFunction(void* handle, const char* name, bool optional) {
56 if (handle == nullptr) {
57 return nullptr;
58 }
59 void* fn = dlsym(handle, name);
60 if (fn == nullptr && !optional) {
61 NNAPI_LOG("nnapi error: unable to open function %s", name);
62 }
63 return fn;
64 }
65
66 #ifndef __ANDROID__
67 // Add /dev/shm implementation of shared memory for non-Android platforms
ASharedMemory_create(const char *,size_t size)68 int ASharedMemory_create(const char* /* name */, size_t size) {
69 // Each call to ASharedMemory_create produces a unique memory space, hence
70 // name should not be used to create the shared memory file, otherwise
71 // two calls to create memory regions using the same 'name', will collide.
72 char shm_name_buffer[L_tmpnam];
73 if (tmpnam(shm_name_buffer) == nullptr) {
74 return -1;
75 }
76
77 // tmpnam will produce a string containing with slashes, but shm_open
78 // won't like that.
79 std::string shm_region_name = std::string(shm_name_buffer);
80 std::replace(shm_region_name.begin(), shm_region_name.end(), '/', '-');
81
82 int fd = shm_open(shm_region_name.c_str(), O_RDWR | O_CREAT, 0644);
83 if (fd < 0) {
84 return fd;
85 }
86 int result = ftruncate(fd, size);
87 if (result < 0) {
88 close(fd);
89 return -1;
90 }
91 return fd;
92 }
93
94 // Determine the NnApi version from loaded entry points
CalculateAndroidSdkVersion(NnApi const & nnapi)95 uint32_t CalculateAndroidSdkVersion(NnApi const& nnapi) {
96 // Test for specific NNAPI 1.0, 1.1, 1.2 and 1.3 functions
97 bool has_10 = nnapi.ANeuralNetworksMemory_createFromFd != nullptr;
98 bool has_11 =
99 nnapi.ANeuralNetworksModel_relaxComputationFloat32toFloat16 != nullptr;
100 bool has_12 = nnapi.ANeuralNetworks_getDeviceCount != nullptr;
101 bool has_13 = nnapi.ANeuralNetworksCompilation_setTimeout != nullptr;
102
103 uint32_t sdk_version = 0;
104 if (has_10) {
105 sdk_version = 27;
106 }
107 if (sdk_version == 27 && has_11) {
108 sdk_version = 28;
109 }
110 if (sdk_version == 28 && has_12) {
111 sdk_version = 29;
112 }
113 if (sdk_version == 29 && has_13) {
114 sdk_version = 30;
115 }
116 return sdk_version;
117 }
118 #endif // __ANDROID__
119
120 #define LOAD_FUNCTION(handle, name) \
121 nnapi.name = reinterpret_cast<name##_fn>( \
122 LoadFunction(handle, #name, /*optional*/ false));
123
124 #define LOAD_FUNCTION_OPTIONAL(handle, name) \
125 nnapi.name = reinterpret_cast<name##_fn>( \
126 LoadFunction(handle, #name, /*optional*/ true));
127
128 #define LOAD_FUNCTION_RENAME(handle, name, symbol) \
129 nnapi.name = reinterpret_cast<name##_fn>( \
130 LoadFunction(handle, symbol, /*optional*/ false));
131
LoadNnApi()132 const NnApi LoadNnApi() {
133 NnApi nnapi = {};
134 nnapi.android_sdk_version = 0;
135
136 #ifdef __ANDROID__
137 nnapi.android_sdk_version = GetAndroidSdkVersion();
138 if (nnapi.android_sdk_version < 27) {
139 NNAPI_LOG("nnapi error: requires android sdk version to be at least %d",
140 27);
141 nnapi.nnapi_exists = false;
142 return nnapi;
143 }
144 #endif // __ANDROID__
145
146 void* libneuralnetworks = nullptr;
147 // TODO(b/123243014): change RTLD_LOCAL? Assumes there can be multiple
148 // instances of nn api RT
149 static const char nnapi_library_name[] = "libneuralnetworks.so";
150 libneuralnetworks = dlopen(nnapi_library_name, RTLD_LAZY | RTLD_LOCAL);
151 if (libneuralnetworks == nullptr) {
152 const char* error = dlerror();
153 if (error) {
154 NNAPI_LOG("%s\n", error);
155 }
156 NNAPI_LOG("nnapi error: unable to open library %s", nnapi_library_name);
157 }
158
159 nnapi.nnapi_exists = libneuralnetworks != nullptr;
160
161 // API 27 (NN 1.0) methods.
162 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksMemory_createFromFd);
163 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksMemory_free);
164 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_create);
165 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_free);
166 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_finish);
167 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_addOperand);
168 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_setOperandValue);
169 LOAD_FUNCTION_OPTIONAL(
170 libneuralnetworks,
171 ANeuralNetworksModel_setOperandSymmPerChannelQuantParams);
172 LOAD_FUNCTION(libneuralnetworks,
173 ANeuralNetworksModel_setOperandValueFromMemory);
174 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_addOperation);
175 LOAD_FUNCTION(libneuralnetworks,
176 ANeuralNetworksModel_identifyInputsAndOutputs);
177 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_create);
178 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_free);
179 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_setPreference);
180 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_finish);
181 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_create);
182 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_free);
183 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setInput);
184 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setInputFromMemory);
185 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setOutput);
186 LOAD_FUNCTION(libneuralnetworks,
187 ANeuralNetworksExecution_setOutputFromMemory);
188 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_startCompute);
189 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksEvent_wait);
190 LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksEvent_free);
191
192 // ASharedMemory_create has different implementations in Android depending on
193 // the partition. Generally it can be loaded from libandroid.so but in vendor
194 // partition (e.g. if a HAL wants to use NNAPI) it is only accessible through
195 // libcutils.
196 #ifdef __ANDROID__
197 void* libandroid = nullptr;
198 libandroid = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
199 if (libandroid != nullptr) {
200 LOAD_FUNCTION(libandroid, ASharedMemory_create);
201 } else {
202 void* cutils_handle = dlopen("libcutils.so", RTLD_LAZY | RTLD_LOCAL);
203 if (cutils_handle != nullptr) {
204 LOAD_FUNCTION_RENAME(cutils_handle, ASharedMemory_create,
205 "ashmem_create_region");
206 } else {
207 NNAPI_LOG("nnapi error: unable to open neither libraries %s and %s",
208 "libandroid.so", "libcutils.so");
209 }
210 }
211 #else
212 // Mock ASharedMemory_create only if libneuralnetworks.so was successfully
213 // loaded. This ensures identical behaviour on platforms which use this
214 // implementation, but don't have libneuralnetworks.so library, and
215 // platforms which use nnapi_implementation_disabled.cc stub.
216 if (libneuralnetworks != nullptr) {
217 nnapi.ASharedMemory_create = ASharedMemory_create;
218 }
219 #endif // __ANDROID__
220
221 // API 28 (NN 1.1) methods.
222 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
223 ANeuralNetworksModel_relaxComputationFloat32toFloat16);
224
225 // API 29 (NN 1.2) methods.
226 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworks_getDeviceCount);
227 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworks_getDevice);
228 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksDevice_getName);
229 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksDevice_getVersion);
230 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
231 ANeuralNetworksDevice_getFeatureLevel);
232 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksDevice_getType);
233 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
234 ANeuralNetworksModel_getSupportedOperationsForDevices);
235 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
236 ANeuralNetworksCompilation_createForDevices);
237 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
238 ANeuralNetworksCompilation_setCaching);
239 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksExecution_compute);
240 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
241 ANeuralNetworksExecution_getOutputOperandRank);
242 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
243 ANeuralNetworksExecution_getOutputOperandDimensions);
244 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksBurst_create);
245 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksBurst_free);
246 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
247 ANeuralNetworksExecution_burstCompute);
248 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
249 ANeuralNetworksMemory_createFromAHardwareBuffer);
250 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
251 ANeuralNetworksExecution_setMeasureTiming);
252 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
253 ANeuralNetworksExecution_getDuration);
254 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
255 ANeuralNetworksDevice_getExtensionSupport);
256 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
257 ANeuralNetworksModel_getExtensionOperandType);
258 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
259 ANeuralNetworksModel_getExtensionOperationType);
260 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
261 ANeuralNetworksModel_setOperandExtensionData);
262
263 // API 30 (NNAPI 1.3) methods.
264 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
265 ANeuralNetworksCompilation_setTimeout);
266 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
267 ANeuralNetworksCompilation_setPriority);
268 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
269 ANeuralNetworksExecution_setTimeout);
270 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
271 ANeuralNetworksExecution_setLoopTimeout);
272 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksMemoryDesc_create);
273 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksMemoryDesc_free);
274 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
275 ANeuralNetworksMemoryDesc_addInputRole);
276 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
277 ANeuralNetworksMemoryDesc_addOutputRole);
278 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
279 ANeuralNetworksMemoryDesc_setDimensions);
280 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksMemoryDesc_finish);
281 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
282 ANeuralNetworksMemory_createFromDesc);
283 LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksMemory_copy);
284 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
285 ANeuralNetworksEvent_createFromSyncFenceFd);
286 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
287 ANeuralNetworksEvent_getSyncFenceFd);
288 LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
289 ANeuralNetworksExecution_startComputeWithDependencies);
290
291 #ifndef __ANDROID__
292 // If libneuralnetworks.so is loaded, but android_sdk_version is not set,
293 // then determine android_sdk_version by testing which functions are
294 // available.
295 if (nnapi.nnapi_exists && nnapi.android_sdk_version == 0) {
296 nnapi.android_sdk_version = CalculateAndroidSdkVersion(nnapi);
297 }
298 #endif // __ANDROID__
299
300 return nnapi;
301 }
302
303 } // namespace
304
NnApiImplementation()305 const NnApi* NnApiImplementation() {
306 static const NnApi nnapi = LoadNnApi();
307 return &nnapi;
308 }
309