blob: b1306a824fae7e6f23f64d2007293de539d03c61 [file] [log] [blame]
/*
* 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"
#include "bcinfo/BitcodeWrapper.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 "GDBJITRegistrar.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 <unistd.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;
}
bcinfo::BitcodeWrapper wrapper(bitcode, bitcodeSize);
mCompilerVersion = wrapper.getCompilerVersion();
mOptimizationLevel = wrapper.getOptimizationLevel();
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;
}
int status = -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) {
status = 0;
}
}
#endif
if (status == -1) {
status = internalCompile(false);
if (status != 0) {
LOGE("LLVM error message: %s\n", getCompilerErrorMessage());
}
}
// FIXME: Registration can be conditional on the presence of debug metadata
if (status == 0) {
registerObjectWithGDB(getELF(), getELFSize()); // thread-safe registration
}
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();
}
#if USE_CACHE
case ScriptStatus::Cached: {
return mCached->getELFSize();
}
#endif
default: {
return 0;
}
}
}
const char *Script::getELF() const {
switch (mStatus) {
case ScriptStatus::Compiled: {
return mCompiled->getELF();
}
#if USE_CACHE
case ScriptStatus::Cached: {
return mCached->getELF();
}
#endif
default: {
return NULL;
}
}
}
#endif
} // namespace bcc