• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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