C++程序  |  785行  |  17.3 KB

/*
 * copyright 2010, the android open source project
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *     http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

#include "Script.h"

#include "Config.h"

#if USE_OLD_JIT
#include "OldJIT/CacheReader.h"
#include "OldJIT/CacheWriter.h"
#endif

#include "MCCacheReader.h"
#include "MCCacheWriter.h"

#if USE_OLD_JIT
#include "OldJIT/ContextManager.h"
#endif

#include "DebugHelper.h"
#include "FileHandle.h"
#include "ScriptCompiled.h"
#include "ScriptCached.h"
#include "Sha1Helper.h"
#include "SourceInfo.h"

#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <new>
#include <string.h>
#include <cutils/properties.h>


namespace {

bool getBooleanProp(const char *str) {
  char buf[PROPERTY_VALUE_MAX];
  property_get(str, buf, "0");
  return strcmp(buf, "0") != 0;
}

} // namespace anonymous

namespace bcc {

Script::~Script() {
  switch (mStatus) {
  case ScriptStatus::Compiled:
    delete mCompiled;
    break;

#if USE_CACHE
  case ScriptStatus::Cached:
    delete mCached;
    break;
#endif

  default:
    break;
  }

  for (size_t i = 0; i < 2; ++i) {
    delete mSourceList[i];
  }
}


int Script::addSourceBC(size_t idx,
                        char const *resName,
                        const char *bitcode,
                        size_t bitcodeSize,
                        unsigned long flags) {

  if (!resName) {
    mErrorCode = BCC_INVALID_VALUE;
    LOGE("Invalid argument: resName = NULL\n");
    return 1;
  }

  if (mStatus != ScriptStatus::Unknown) {
    mErrorCode = BCC_INVALID_OPERATION;
    LOGE("Bad operation: Adding source after bccPrepareExecutable\n");
    return 1;
  }

  if (!bitcode) {
    mErrorCode = BCC_INVALID_VALUE;
    LOGE("Invalid argument: bitcode = NULL\n");
    return 1;
  }

  mSourceList[idx] = SourceInfo::createFromBuffer(resName,
                                                  bitcode, bitcodeSize,
                                                  flags);

  if (!mSourceList[idx]) {
    mErrorCode = BCC_OUT_OF_MEMORY;
    LOGE("Out of memory while adding source bitcode\n");
    return 1;
  }

  return 0;
}


int Script::addSourceModule(size_t idx,
                            llvm::Module *module,
                            unsigned long flags) {
  if (mStatus != ScriptStatus::Unknown) {
    mErrorCode = BCC_INVALID_OPERATION;
    LOGE("Bad operation: Adding source after bccPrepareExecutable\n");
    return 1;
  }

  if (!module) {
    mErrorCode = BCC_INVALID_VALUE;
    LOGE("Invalid argument: module = NULL\n");
    return 1;
  }

  mSourceList[idx] = SourceInfo::createFromModule(module, flags);

  if (!mSourceList[idx]) {
    mErrorCode = BCC_OUT_OF_MEMORY;
    LOGE("Out of memory when add source module\n");
    return 1;
  }

  return 0;
}


int Script::addSourceFile(size_t idx,
                          char const *path,
                          unsigned long flags) {
  if (mStatus != ScriptStatus::Unknown) {
    mErrorCode = BCC_INVALID_OPERATION;
    LOGE("Bad operation: Adding source after bccPrepareExecutable\n");
    return 1;
  }

  if (!path) {
    mErrorCode = BCC_INVALID_VALUE;
    LOGE("Invalid argument: path = NULL\n");
    return 1;
  }

  struct stat sb;
  if (stat(path, &sb) != 0) {
    mErrorCode = BCC_INVALID_VALUE;
    LOGE("File not found: %s\n", path);
    return 1;
  }

  mSourceList[idx] = SourceInfo::createFromFile(path, flags);

  if (!mSourceList[idx]) {
    mErrorCode = BCC_OUT_OF_MEMORY;
    LOGE("Out of memory while adding source file\n");
    return 1;
  }

  return 0;
}

int Script::prepareSharedObject(char const *cacheDir,
                                char const *cacheName,
                                unsigned long flags) {
#if USE_CACHE
  if (cacheDir && cacheName) {
    // Set Cache Directory and File Name
    mCacheDir = cacheDir;
    mCacheName = cacheName;

    if (!mCacheDir.empty() && *mCacheDir.rbegin() != '/') {
      mCacheDir.push_back('/'); // Ensure mCacheDir is end with '/'
    }

    // Check Cache File
    if (internalLoadCache(true) == 0) {
      return 0;
    }
  }
#endif
  int status = internalCompile(true);
  if (status != 0) {
    LOGE("LLVM error message: %s\n", getCompilerErrorMessage());
  }
  return status;
}


int Script::prepareExecutable(char const *cacheDir,
                              char const *cacheName,
                              unsigned long flags) {
  if (mStatus != ScriptStatus::Unknown) {
    mErrorCode = BCC_INVALID_OPERATION;
    LOGE("Invalid operation: %s\n", __func__);
    return 1;
  }

#if USE_CACHE
  if (cacheDir && cacheName) {
    // Set Cache Directory and File Name
    mCacheDir = cacheDir;
    mCacheName = cacheName;

    if (!mCacheDir.empty() && *mCacheDir.rbegin() != '/') {
      mCacheDir.push_back('/'); // Ensure mCacheDir is end with '/'
    }

    // Load Cache File
    if (internalLoadCache(false) == 0) {
      return 0;
    }
  }
#endif

  int status = internalCompile(false);
  if (status != 0) {
    LOGE("LLVM error message: %s\n", getCompilerErrorMessage());
  }
  return status;
}


#if USE_CACHE
int Script::internalLoadCache(bool checkOnly) {
  if (getBooleanProp("debug.bcc.nocache")) {
    // Android system environment property disable the cache mechanism by
    // setting "debug.bcc.nocache".  So we will not load the cache file any
    // way.
    return 1;
  }

  if (mCacheDir.empty() || mCacheName.empty()) {
    // The application developer has not specify the cachePath, so
    // we don't know where to open the cache file.
    return 1;
  }

#if USE_OLD_JIT
  std::string objPath(mCacheDir + mCacheName + ".jit-image");
  std::string infoPath(mCacheDir + mCacheName + ".oBCC"); // TODO: .info instead
#elif USE_MCJIT
  std::string objPath(mCacheDir + mCacheName + ".o");
  std::string infoPath(mCacheDir + mCacheName + ".info");
#endif

  FileHandle objFile;
  if (objFile.open(objPath.c_str(), OpenMode::Read) < 0) {
    // Unable to open the executable file in read mode.
    return 1;
  }

  FileHandle infoFile;
  if (infoFile.open(infoPath.c_str(), OpenMode::Read) < 0) {
    // Unable to open the metadata information file in read mode.
    return 1;
  }

#if USE_OLD_JIT
  CacheReader reader;
#elif USE_MCJIT
  MCCacheReader reader;

  // Register symbol lookup function
  if (mpExtSymbolLookupFn) {
    reader.registerSymbolCallback(mpExtSymbolLookupFn,
                                      mpExtSymbolLookupFnContext);
  }
#endif

  // Dependencies
  reader.addDependency(BCC_FILE_RESOURCE, pathLibBCC_SHA1, sha1LibBCC_SHA1);
  reader.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS);

  for (size_t i = 0; i < 2; ++i) {
    if (mSourceList[i]) {
      mSourceList[i]->introDependency(reader);
    }
  }

  if (checkOnly)
    return !reader.checkCacheFile(&objFile, &infoFile, this);

  // Read cache file
  ScriptCached *cached = reader.readCacheFile(&objFile, &infoFile, this);

  if (!cached) {
    mIsContextSlotNotAvail = reader.isContextSlotNotAvail();
    return 1;
  }

  mCached = cached;
  mStatus = ScriptStatus::Cached;

  // Dirty hack for libRS.
  // TODO(all):  This dirty hack should be removed in the future.
  if (!cached->isLibRSThreadable() && mpExtSymbolLookupFn) {
    mpExtSymbolLookupFn(mpExtSymbolLookupFnContext, "__clearThreadable");
  }

  return 0;
}
#endif

int Script::internalCompile(bool compileOnly) {
  // Create the ScriptCompiled object
  mCompiled = new (std::nothrow) ScriptCompiled(this);

  if (!mCompiled) {
    mErrorCode = BCC_OUT_OF_MEMORY;
    LOGE("Out of memory: %s %d\n", __FILE__, __LINE__);
    return 1;
  }

  mStatus = ScriptStatus::Compiled;

  // Register symbol lookup function
  if (mpExtSymbolLookupFn) {
    mCompiled->registerSymbolCallback(mpExtSymbolLookupFn,
                                      mpExtSymbolLookupFnContext);
  }

  // Parse Bitcode File (if necessary)
  for (size_t i = 0; i < 2; ++i) {
    if (mSourceList[i] && mSourceList[i]->prepareModule(mCompiled) != 0) {
      LOGE("Unable to parse bitcode for source[%lu]\n", (unsigned long)i);
      return 1;
    }
  }

  // Set the main source module
  if (!mSourceList[0] || !mSourceList[0]->getModule()) {
    LOGE("Source bitcode is not setted.\n");
    return 1;
  }

  if (mCompiled->readModule(mSourceList[0]->takeModule()) != 0) {
    LOGE("Unable to read source module\n");
    return 1;
  }

  // Link the source module with the library module
  if (mSourceList[1]) {
    if (mCompiled->linkModule(mSourceList[1]->takeModule()) != 0) {
      LOGE("Unable to link library module\n");
      return 1;
    }
  }

  // Compile and JIT the code
  if (mCompiled->compile(compileOnly) != 0) {
    LOGE("Unable to compile.\n");
    return 1;
  }

#if USE_CACHE
  // Note: If we re-compile the script because the cached context slot not
  // available, then we don't have to write the cache.

  // Note: If the address of the context is not in the context slot, then
  // we don't have to cache it.

  if (!mCacheDir.empty() &&
      !mCacheName.empty() &&
#if USE_OLD_JIT
      !mIsContextSlotNotAvail &&
      ContextManager::get().isManagingContext(getContext()) &&
#endif
      !getBooleanProp("debug.bcc.nocache")) {

#if USE_OLD_JIT
    std::string objPath(mCacheDir + mCacheName + ".jit-image");
    std::string infoPath(mCacheDir + mCacheName + ".oBCC");
#elif USE_MCJIT
    std::string objPath(mCacheDir + mCacheName + ".o");
    std::string infoPath(mCacheDir + mCacheName + ".info");
#endif


    // Remove the file if it already exists before writing the new file.
    // The old file may still be mapped elsewhere in memory and we do not want
    // to modify its contents.  (The same script may be running concurrently in
    // the same process or a different process!)
    ::unlink(objPath.c_str());
#if !USE_OLD_JIT && USE_MCJIT
    ::unlink(infoPath.c_str());
#endif

    FileHandle objFile;
    FileHandle infoFile;

    if (objFile.open(objPath.c_str(), OpenMode::Write) >= 0 &&
        infoFile.open(infoPath.c_str(), OpenMode::Write) >= 0) {

#if USE_OLD_JIT
      CacheWriter writer;
#elif USE_MCJIT
      MCCacheWriter writer;
#endif

#ifdef TARGET_BUILD
      // Dependencies
      writer.addDependency(BCC_FILE_RESOURCE, pathLibBCC_SHA1, sha1LibBCC_SHA1);
      writer.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS);
#endif

      for (size_t i = 0; i < 2; ++i) {
        if (mSourceList[i]) {
          mSourceList[i]->introDependency(writer);
        }
      }

      // libRS is threadable dirty hack
      // TODO: This should be removed in the future
      uint32_t libRS_threadable = 0;
      if (mpExtSymbolLookupFn) {
        libRS_threadable =
          (uint32_t)mpExtSymbolLookupFn(mpExtSymbolLookupFnContext,
                                        "__isThreadable");
      }

      if (!writer.writeCacheFile(&objFile, &infoFile, this, libRS_threadable)) {
        objFile.truncate();
        objFile.close();

        if (unlink(objPath.c_str()) != 0) {
          LOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n",
               objPath.c_str(), strerror(errno));
        }

        infoFile.truncate();
        infoFile.close();

        if (unlink(infoPath.c_str()) != 0) {
          LOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n",
               infoPath.c_str(), strerror(errno));
        }
      }
    }
  }
#endif // USE_CACHE

  return 0;
}


char const *Script::getCompilerErrorMessage() {
  if (mStatus != ScriptStatus::Compiled) {
    mErrorCode = BCC_INVALID_OPERATION;
    return NULL;
  }

  return mCompiled->getCompilerErrorMessage();
}


void *Script::lookup(const char *name) {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->lookup(name);
    }

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->lookup(name);
    }
#endif

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
      return NULL;
    }
  }
}


size_t Script::getExportVarCount() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getExportVarCount();
    }

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->getExportVarCount();
    }
#endif

    default: {
      return 0;
    }
  }
}


size_t Script::getExportFuncCount() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getExportFuncCount();
    }

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->getExportFuncCount();
    }
#endif

    default: {
      return 0;
    }
  }
}


size_t Script::getPragmaCount() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getPragmaCount();
    }

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->getPragmaCount();
    }
#endif

    default: {
      return 0;
    }
  }
}


size_t Script::getFuncCount() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getFuncCount();
    }

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->getFuncCount();
    }
#endif

    default: {
      return 0;
    }
  }
}


size_t Script::getObjectSlotCount() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getObjectSlotCount();
    }

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->getObjectSlotCount();
    }
#endif

    default: {
      return 0;
    }
  }
}


void Script::getExportVarList(size_t varListSize, void **varList) {
  switch (mStatus) {
#define DELEGATE(STATUS) \
    case ScriptStatus::STATUS:                           \
      m##STATUS->getExportVarList(varListSize, varList); \
      break;

#if USE_CACHE
    DELEGATE(Cached);
#endif

    DELEGATE(Compiled);
#undef DELEGATE

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}

void Script::getExportVarNameList(std::vector<std::string> &varList) {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getExportVarNameList(varList);
    }

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}


void Script::getExportFuncList(size_t funcListSize, void **funcList) {
  switch (mStatus) {
#define DELEGATE(STATUS) \
    case ScriptStatus::STATUS:                              \
      m##STATUS->getExportFuncList(funcListSize, funcList); \
      break;

#if USE_CACHE
    DELEGATE(Cached);
#endif

    DELEGATE(Compiled);
#undef DELEGATE

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}

void Script::getExportFuncNameList(std::vector<std::string> &funcList) {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getExportFuncNameList(funcList);
    }

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}


void Script::getPragmaList(size_t pragmaListSize,
                           char const **keyList,
                           char const **valueList) {
  switch (mStatus) {
#define DELEGATE(STATUS) \
    case ScriptStatus::STATUS:                                      \
      m##STATUS->getPragmaList(pragmaListSize, keyList, valueList); \
      break;

#if USE_CACHE
    DELEGATE(Cached);
#endif

    DELEGATE(Compiled);
#undef DELEGATE

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}


void Script::getFuncInfoList(size_t funcInfoListSize,
                             FuncInfo *funcInfoList) {
  switch (mStatus) {
#define DELEGATE(STATUS) \
    case ScriptStatus::STATUS:                                    \
      m##STATUS->getFuncInfoList(funcInfoListSize, funcInfoList); \
      break;

#if USE_CACHE
    DELEGATE(Cached);
#endif

    DELEGATE(Compiled);
#undef DELEGATE

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}


void Script::getObjectSlotList(size_t objectSlotListSize,
                               uint32_t *objectSlotList) {
  switch (mStatus) {
#define DELEGATE(STATUS)     \
    case ScriptStatus::STATUS:                                          \
      m##STATUS->getObjectSlotList(objectSlotListSize, objectSlotList); \
      break;

#if USE_CACHE
    DELEGATE(Cached);
#endif

    DELEGATE(Compiled);
#undef DELEGATE

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
    }
  }
}


#if USE_OLD_JIT
char *Script::getContext() {
  switch (mStatus) {

#if USE_CACHE
    case ScriptStatus::Cached: {
      return mCached->getContext();
    }
#endif

    case ScriptStatus::Compiled: {
      return mCompiled->getContext();
    }

    default: {
      mErrorCode = BCC_INVALID_OPERATION;
      return NULL;
    }
  }
}
#endif


int Script::registerSymbolCallback(BCCSymbolLookupFn pFn, void *pContext) {
  mpExtSymbolLookupFn = pFn;
  mpExtSymbolLookupFnContext = pContext;

  if (mStatus != ScriptStatus::Unknown) {
    mErrorCode = BCC_INVALID_OPERATION;
    LOGE("Invalid operation: %s\n", __func__);
    return 1;
  }
  return 0;
}

#if USE_MCJIT
size_t Script::getELFSize() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getELFSize();
    }

    default: {
      return 0;
    }
  }
}

const char *Script::getELF() const {
  switch (mStatus) {
    case ScriptStatus::Compiled: {
      return mCompiled->getELF();
    }

    default: {
      return NULL;
    }
  }
}
#endif

} // namespace bcc