1/*
2 * Copyright 2012, 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_NDEBUG 0
18#include "bcc/Renderscript/RSInfo.h"
19
20#if !defined(_WIN32)  /* TODO create a HAVE_DLFCN_H */
21#include <dlfcn.h>
22#endif
23
24#include <cstring>
25#include <new>
26#include <string>
27
28#include "bcc/Support/FileBase.h"
29#include "bcc/Support/Log.h"
30
31#ifdef HAVE_ANDROID_OS
32#include <cutils/properties.h>
33#endif
34
35using namespace bcc;
36
37const char RSInfo::LibBCCPath[] = "/system/lib/libbcc.so";
38const char RSInfo::LibCompilerRTPath[] = "/system/lib/libcompiler_rt.so";
39const char RSInfo::LibRSPath[] = "/system/lib/libRS.so";
40const char RSInfo::LibCLCorePath[] = "/system/lib/libclcore.bc";
41const char RSInfo::LibCLCoreDebugPath[] = "/system/lib/libclcore_debug.bc";
42#if defined(ARCH_X86_HAVE_SSE2)
43const char RSInfo::LibCLCoreX86Path[] = "/system/lib/libclcore_x86.bc";
44#endif
45#if defined(ARCH_ARM_HAVE_NEON)
46const char RSInfo::LibCLCoreNEONPath[] = "/system/lib/libclcore_neon.bc";
47#endif
48
49const uint8_t *RSInfo::LibBCCSHA1 = NULL;
50const uint8_t *RSInfo::LibCompilerRTSHA1 = NULL;
51const uint8_t *RSInfo::LibRSSHA1 = NULL;
52const uint8_t *RSInfo::LibCLCoreSHA1 = NULL;
53const uint8_t *RSInfo::LibCLCoreDebugSHA1 = NULL;
54#if defined(ARCH_ARM_HAVE_NEON)
55const uint8_t *RSInfo::LibCLCoreNEONSHA1 = NULL;
56#endif
57
58bool RSInfo::LoadBuiltInSHA1Information() {
59#ifdef TARGET_BUILD
60  if (LibBCCSHA1 != NULL) {
61    // Loaded before.
62    return true;
63  }
64
65  void *h = ::dlopen("/system/lib/libbcc.sha1.so", RTLD_LAZY | RTLD_NOW);
66  if (h == NULL) {
67    ALOGE("Failed to load SHA-1 information from shared library '"
68          "/system/lib/libbcc.sha1.so'! (%s)", ::dlerror());
69    return false;
70  }
71
72  LibBCCSHA1 = reinterpret_cast<const uint8_t *>(::dlsym(h, "libbcc_so_SHA1"));
73  LibCompilerRTSHA1 =
74      reinterpret_cast<const uint8_t *>(::dlsym(h, "libcompiler_rt_so_SHA1"));
75  LibRSSHA1 = reinterpret_cast<const uint8_t *>(::dlsym(h, "libRS_so_SHA1"));
76  LibCLCoreSHA1 =
77      reinterpret_cast<const uint8_t *>(::dlsym(h, "libclcore_bc_SHA1"));
78  LibCLCoreDebugSHA1 =
79      reinterpret_cast<const uint8_t *>(::dlsym(h, "libclcore_debug_bc_SHA1"));
80#if defined(ARCH_ARM_HAVE_NEON)
81  LibCLCoreNEONSHA1 =
82      reinterpret_cast<const uint8_t *>(::dlsym(h, "libclcore_neon_bc_SHA1"));
83#endif
84
85  return true;
86#else  // TARGET_BUILD
87  return false;
88#endif  // TARGET_BUILD
89}
90
91android::String8 RSInfo::GetPath(const char *pFilename) {
92  android::String8 result(pFilename);
93  result.append(".info");
94  return result;
95}
96
97#define PRINT_DEPENDENCY(PREFIX, N, X) \
98        ALOGV("\t" PREFIX "Source name: %s, "                                 \
99                          "SHA-1: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"   \
100                                 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",  \
101              (N), (X)[ 0], (X)[ 1], (X)[ 2], (X)[ 3], (X)[ 4], (X)[ 5],      \
102                   (X)[ 6], (X)[ 7], (X)[ 8], (X)[ 9], (X)[10], (X)[11],      \
103                   (X)[12], (X)[13], (X)[14], (X)[15], (X)[16], (X)[17],      \
104                   (X)[18], (X)[19]);
105
106bool RSInfo::CheckDependency(const RSInfo &pInfo,
107                             const char *pInputFilename,
108                             const DependencyTableTy &pDeps) {
109  // Built-in dependencies are libbcc.so, libRS.so and libclcore.bc plus
110  // libclcore_neon.bc if NEON is available on the target device.
111#if !defined(ARCH_ARM_HAVE_NEON)
112  static const unsigned NumBuiltInDependencies = 5;
113#else
114  static const unsigned NumBuiltInDependencies = 6;
115#endif
116
117  LoadBuiltInSHA1Information();
118
119  if (pInfo.mDependencyTable.size() != (pDeps.size() + NumBuiltInDependencies)) {
120    ALOGD("Number of dependencies recorded mismatch (%lu v.s. %lu) in %s!",
121          static_cast<unsigned long>(pInfo.mDependencyTable.size()),
122          static_cast<unsigned long>(pDeps.size()), pInputFilename);
123    return false;
124  } else {
125    // Built-in dependencies always go first.
126    const std::pair<const char *, const uint8_t *> &cache_libbcc_dep =
127        pInfo.mDependencyTable[0];
128    const std::pair<const char *, const uint8_t *> &cache_libcompiler_rt_dep =
129        pInfo.mDependencyTable[1];
130    const std::pair<const char *, const uint8_t *> &cache_libRS_dep =
131        pInfo.mDependencyTable[2];
132    const std::pair<const char *, const uint8_t *> &cache_libclcore_dep =
133        pInfo.mDependencyTable[3];
134    const std::pair<const char *, const uint8_t *> &cache_libclcore_debug_dep =
135        pInfo.mDependencyTable[4];
136#if defined(ARCH_ARM_HAVE_NEON)
137    const std::pair<const char *, const uint8_t *> &cache_libclcore_neon_dep =
138        pInfo.mDependencyTable[5];
139#endif
140
141    // Check libbcc.so.
142    if (::memcmp(cache_libbcc_dep.second, LibBCCSHA1, SHA1_DIGEST_LENGTH) != 0) {
143        ALOGD("Cache %s is dirty due to %s has been updated.", pInputFilename,
144              LibBCCPath);
145        PRINT_DEPENDENCY("current - ", LibBCCPath, LibBCCSHA1);
146        PRINT_DEPENDENCY("cache - ", cache_libbcc_dep.first,
147                                     cache_libbcc_dep.second);
148        return false;
149    }
150
151    // Check libcompiler_rt.so.
152    if (::memcmp(cache_libcompiler_rt_dep.second, LibCompilerRTSHA1,
153                 SHA1_DIGEST_LENGTH) != 0) {
154        ALOGD("Cache %s is dirty due to %s has been updated.", pInputFilename,
155              LibCompilerRTPath);
156        PRINT_DEPENDENCY("current - ", LibCompilerRTPath, LibCompilerRTSHA1);
157        PRINT_DEPENDENCY("cache - ", cache_libcompiler_rt_dep.first,
158                                     cache_libcompiler_rt_dep.second);
159        return false;
160    }
161
162    // Check libRS.so.
163    if (::memcmp(cache_libRS_dep.second, LibRSSHA1, SHA1_DIGEST_LENGTH) != 0) {
164        ALOGD("Cache %s is dirty due to %s has been updated.", pInputFilename,
165              LibRSPath);
166        PRINT_DEPENDENCY("current - ", LibRSPath, LibRSSHA1);
167        PRINT_DEPENDENCY("cache - ", cache_libRS_dep.first,
168                                     cache_libRS_dep.second);
169        return false;
170    }
171
172    // Check libclcore.bc.
173    if (::memcmp(cache_libclcore_dep.second, LibCLCoreSHA1,
174                 SHA1_DIGEST_LENGTH) != 0) {
175        ALOGD("Cache %s is dirty due to %s has been updated.", pInputFilename,
176              LibCLCorePath);
177        PRINT_DEPENDENCY("current - ", LibCLCorePath, LibCLCoreSHA1);
178        PRINT_DEPENDENCY("cache - ", cache_libclcore_dep.first,
179                                     cache_libclcore_dep.second);
180        return false;
181    }
182
183    // Check libclcore_debug.bc.
184    if (::memcmp(cache_libclcore_debug_dep.second, LibCLCoreDebugSHA1,
185                 SHA1_DIGEST_LENGTH) != 0) {
186        ALOGD("Cache %s is dirty due to %s has been updated.", pInputFilename,
187              LibCLCoreDebugPath);
188        PRINT_DEPENDENCY("current - ", LibCLCoreDebugPath, LibCLCoreDebugSHA1);
189        PRINT_DEPENDENCY("cache - ", cache_libclcore_debug_dep.first,
190                                     cache_libclcore_debug_dep.second);
191        return false;
192    }
193
194#if defined(ARCH_ARM_HAVE_NEON)
195    // Check libclcore_neon.bc if NEON is available.
196    if (::memcmp(cache_libclcore_neon_dep.second, LibCLCoreNEONSHA1,
197                 SHA1_DIGEST_LENGTH) != 0) {
198        ALOGD("Cache %s is dirty due to %s has been updated.", pInputFilename,
199              LibCLCoreNEONPath);
200        PRINT_DEPENDENCY("current - ", LibCLCoreNEONPath, LibCLCoreNEONSHA1);
201        PRINT_DEPENDENCY("cache - ", cache_libclcore_neon_dep.first,
202                                     cache_libclcore_neon_dep.second);
203        return false;
204    }
205#endif
206
207    for (unsigned i = 0; i < pDeps.size(); i++) {
208      const std::pair<const char *, const uint8_t *> &cache_dep =
209          pInfo.mDependencyTable[i + NumBuiltInDependencies];
210
211      if ((::strcmp(pDeps[i].first, cache_dep.first) != 0) ||
212          (::memcmp(pDeps[i].second, cache_dep.second,
213                    SHA1_DIGEST_LENGTH) != 0)) {
214        ALOGD("Cache %s is dirty due to the source it dependends on has been "
215              "changed:", pInputFilename);
216        PRINT_DEPENDENCY("given - ", pDeps[i].first, pDeps[i].second);
217        PRINT_DEPENDENCY("cache - ", cache_dep.first, cache_dep.second);
218        return false;
219      }
220    }
221  }
222
223  return true;
224}
225
226RSInfo::RSInfo(size_t pStringPoolSize) : mStringPool(NULL) {
227  ::memset(&mHeader, 0, sizeof(mHeader));
228
229  ::memcpy(mHeader.magic, RSINFO_MAGIC, sizeof(mHeader.magic));
230  ::memcpy(mHeader.version, RSINFO_VERSION, sizeof(mHeader.version));
231
232  mHeader.headerSize = sizeof(mHeader);
233
234  mHeader.dependencyTable.itemSize = sizeof(rsinfo::DependencyTableItem);
235  mHeader.pragmaList.itemSize = sizeof(rsinfo::PragmaItem);
236  mHeader.objectSlotList.itemSize = sizeof(rsinfo::ObjectSlotItem);
237  mHeader.exportVarNameList.itemSize = sizeof(rsinfo::ExportVarNameItem);
238  mHeader.exportFuncNameList.itemSize = sizeof(rsinfo::ExportFuncNameItem);
239  mHeader.exportForeachFuncList.itemSize = sizeof(rsinfo::ExportForeachFuncItem);
240
241  if (pStringPoolSize > 0) {
242    mHeader.strPoolSize = pStringPoolSize;
243    mStringPool = new (std::nothrow) char [ mHeader.strPoolSize ];
244    if (mStringPool == NULL) {
245      ALOGE("Out of memory when allocate memory for string pool in RSInfo "
246            "constructor (size: %u)!", mHeader.strPoolSize);
247    }
248  }
249}
250
251RSInfo::~RSInfo() {
252  delete [] mStringPool;
253}
254
255bool RSInfo::layout(off_t initial_offset) {
256  mHeader.dependencyTable.offset = initial_offset +
257                                   mHeader.headerSize +
258                                   mHeader.strPoolSize;
259  mHeader.dependencyTable.count = mDependencyTable.size();
260
261#define AFTER(_list) ((_list).offset + (_list).itemSize * (_list).count)
262  mHeader.pragmaList.offset = AFTER(mHeader.dependencyTable);
263  mHeader.pragmaList.count = mPragmas.size();
264
265  mHeader.objectSlotList.offset = AFTER(mHeader.pragmaList);
266  mHeader.objectSlotList.count = mObjectSlots.size();
267
268  mHeader.exportVarNameList.offset = AFTER(mHeader.objectSlotList);
269  mHeader.exportVarNameList.count = mExportVarNames.size();
270
271  mHeader.exportFuncNameList.offset = AFTER(mHeader.exportVarNameList);
272  mHeader.exportFuncNameList.count = mExportFuncNames.size();
273
274  mHeader.exportForeachFuncList.offset = AFTER(mHeader.exportFuncNameList);
275  mHeader.exportForeachFuncList.count = mExportForeachFuncs.size();
276#undef AFTER
277
278  return true;
279}
280
281void RSInfo::dump() const {
282  // Hide the codes to save the code size when debugging is disabled.
283#if !LOG_NDEBUG
284
285  // Dump header
286  ALOGV("RSInfo Header:");
287  ALOGV("\tIs threadable: %s", ((mHeader.isThreadable) ? "true" : "false"));
288  ALOGV("\tHeader size: %u", mHeader.headerSize);
289  ALOGV("\tString pool size: %u", mHeader.strPoolSize);
290
291#define DUMP_LIST_HEADER(_name, _header) do { \
292  ALOGV(_name ":"); \
293  ALOGV("\toffset: %u", (_header).offset);  \
294  ALOGV("\t# of item: %u", (_header).count);  \
295  ALOGV("\tsize of each item: %u", (_header).itemSize); \
296} while (false)
297  DUMP_LIST_HEADER("Dependency table", mHeader.dependencyTable);
298  for (DependencyTableTy::const_iterator dep_iter = mDependencyTable.begin(),
299          dep_end = mDependencyTable.end(); dep_iter != dep_end; dep_iter++) {
300    PRINT_DEPENDENCY("", dep_iter->first, dep_iter->second);
301  }
302
303  DUMP_LIST_HEADER("Pragma list", mHeader.pragmaList);
304  for (PragmaListTy::const_iterator pragma_iter = mPragmas.begin(),
305        pragma_end = mPragmas.end(); pragma_iter != pragma_end; pragma_iter++) {
306    ALOGV("\tkey: %s, value: %s", pragma_iter->first, pragma_iter->second);
307  }
308
309  DUMP_LIST_HEADER("RS object slots", mHeader.objectSlotList);
310  for (ObjectSlotListTy::const_iterator slot_iter = mObjectSlots.begin(),
311          slot_end = mObjectSlots.end(); slot_iter != slot_end; slot_iter++) {
312    ALOGV("slot: %u", *slot_iter);
313  }
314
315  DUMP_LIST_HEADER("RS export variables", mHeader.exportVarNameList);
316  for (ExportVarNameListTy::const_iterator var_iter = mExportVarNames.begin(),
317          var_end = mExportVarNames.end(); var_iter != var_end; var_iter++) {
318    ALOGV("name: %s", *var_iter);
319  }
320
321  DUMP_LIST_HEADER("RS export functions", mHeader.exportFuncNameList);
322  for (ExportFuncNameListTy::const_iterator func_iter = mExportFuncNames.begin(),
323        func_end = mExportFuncNames.end(); func_iter != func_end; func_iter++) {
324    ALOGV("name: %s", *func_iter);
325  }
326
327  DUMP_LIST_HEADER("RS foreach list", mHeader.exportForeachFuncList);
328  for (ExportForeachFuncListTy::const_iterator
329          foreach_iter = mExportForeachFuncs.begin(),
330          foreach_end = mExportForeachFuncs.end(); foreach_iter != foreach_end;
331          foreach_iter++) {
332    ALOGV("name: %s, signature: %05x", foreach_iter->first,
333                                       foreach_iter->second);
334  }
335#undef DUMP_LIST_HEADER
336
337#endif // LOG_NDEBUG
338  return;
339}
340
341const char *RSInfo::getStringFromPool(rsinfo::StringIndexTy pStrIdx) const {
342  // String pool uses direct indexing. Ensure that the pStrIdx is within the
343  // range.
344  if (pStrIdx >= mHeader.strPoolSize) {
345    ALOGE("String index #%u is out of range in string pool (size: %u)!",
346          pStrIdx, mHeader.strPoolSize);
347    return NULL;
348  }
349  return &mStringPool[ pStrIdx ];
350}
351
352rsinfo::StringIndexTy RSInfo::getStringIdxInPool(const char *pStr) const {
353  // Assume we are on the flat memory architecture (i.e., the memory space is
354  // continuous.)
355  if ((mStringPool + mHeader.strPoolSize) < pStr) {
356    ALOGE("String %s does not in the string pool!", pStr);
357    return rsinfo::gInvalidStringIndex;
358  }
359  return (pStr - mStringPool);
360}
361
362RSInfo::FloatPrecision RSInfo::getFloatPrecisionRequirement() const {
363  // Check to see if we have any FP precision-related pragmas.
364  std::string relaxed_pragma("rs_fp_relaxed");
365  std::string imprecise_pragma("rs_fp_imprecise");
366  std::string full_pragma("rs_fp_full");
367  bool relaxed_pragma_seen = false;
368  bool imprecise_pragma_seen = false;
369  RSInfo::FloatPrecision result = FP_Full;
370
371  for (PragmaListTy::const_iterator pragma_iter = mPragmas.begin(),
372           pragma_end = mPragmas.end(); pragma_iter != pragma_end;
373       pragma_iter++) {
374    const char *pragma_key = pragma_iter->first;
375    if (!relaxed_pragma.compare(pragma_key)) {
376      if (relaxed_pragma_seen || imprecise_pragma_seen) {
377        ALOGE("Multiple float precision pragmas specified!");
378      }
379      relaxed_pragma_seen = true;
380    } else if (!imprecise_pragma.compare(pragma_key)) {
381      if (relaxed_pragma_seen || imprecise_pragma_seen) {
382        ALOGE("Multiple float precision pragmas specified!");
383      }
384      imprecise_pragma_seen = true;
385    }
386  }
387
388  // Imprecise is selected over Relaxed precision.
389  // In the absence of both, we stick to the default Full precision.
390  if (imprecise_pragma_seen) {
391    result = FP_Imprecise;
392  } else if (relaxed_pragma_seen) {
393    result = FP_Relaxed;
394  }
395
396#ifdef HAVE_ANDROID_OS
397  // Provide an override for precsion via adb shell setprop
398  // adb shell setprop debug.rs.precision rs_fp_full
399  // adb shell setprop debug.rs.precision rs_fp_relaxed
400  // adb shell setprop debug.rs.precision rs_fp_imprecise
401  char precision_prop_buf[PROPERTY_VALUE_MAX];
402  property_get("debug.rs.precision", precision_prop_buf, "");
403
404  if (precision_prop_buf[0]) {
405    if (!relaxed_pragma.compare(precision_prop_buf)) {
406      ALOGI("Switching to RS FP relaxed mode via setprop");
407      result = FP_Relaxed;
408    } else if (!imprecise_pragma.compare(precision_prop_buf)) {
409      ALOGI("Switching to RS FP imprecise mode via setprop");
410      result = FP_Imprecise;
411    } else if (!full_pragma.compare(precision_prop_buf)) {
412      ALOGI("Switching to RS FP full mode via setprop");
413      result = FP_Full;
414    }
415  }
416#endif
417
418  return result;
419}
420