Arquivos
hhvm/hphp/runtime/vm/instrumentation.cpp
T
Keith Adams 98483c74d6 Lift a lot of stuff out of HPHP::VM.
This is a partial step towards merging the HPHP::VM namespace
up into its parent. To keep it reviewable/mergeable I'm not doing
everything at once here, but most of the code I've touched seems
improved. I've drawn an invisible line around the jit, Unit and
its cohort (Class, Func, PreClass, etc.); we'll get back to them
soon.
2013-04-25 00:50:01 -07:00

335 linhas
10 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <runtime/vm/instrumentation.h>
#include <runtime/vm/unit.h>
#include <runtime/vm/runtime.h>
#include <runtime/base/execution_context.h>
namespace HPHP {
namespace VM {
///////////////////////////////////////////////////////////////////////////////
void Injection::execute() const {
if (m_builtin) {
assert(m_callback);
// Execute function in runtime
m_callback(m_arg);
return;
}
// Execute php code piece
TypedValue retval;
VarEnv *varEnv = nullptr;
ActRec *cfpSave = nullptr;
ObjectData *this_ = nullptr;
Class *cls = nullptr;
ActRec *fp = g_vmContext->getFP();
if (fp) {
if (!fp->hasVarEnv()) {
fp->m_varEnv = VarEnv::createLazyAttach(fp);
}
varEnv = fp->m_varEnv;
cfpSave = varEnv->getCfp();
if (fp->hasThis()) {
this_ = fp->getThis();
} else if (fp->hasClass()) {
cls = fp->getClass();
}
}
// Note: For now we don't merge analysis code's class and function.
// Later we might decide to do so
g_vmContext->invokeFunc(&retval, m_unit->getMain(curClass()),
Array::Create(), this_, cls, varEnv, nullptr, nullptr);
if (varEnv) {
varEnv->setCfp(cfpSave);
}
}
///////////////////////////////////////////////////////////////////////////////
static InjectionCache* s_injectionCache = nullptr;
class InjectionCacheHolder {
public:
InjectionCacheHolder() {
s_injectionCache = new InjectionCache();
}
~InjectionCacheHolder() {
delete s_injectionCache;
}
};
static InjectionCacheHolder s_injectionCacheHolder;
const Injection* InjectionCache::GetInjection(const StringData* code,
const StringData* desc) {
return s_injectionCache->getInjectionImpl(code, desc);
}
const Injection* InjectionCache::GetInjection(const std::string& code,
const std::string& desc) {
return s_injectionCache->getInjectionImpl(code, desc);
}
const Injection* InjectionCache::GetInjection(Injection::Callback callback,
void *arg,
const StringData* desc) {
return s_injectionCache->getInjectionImpl(callback, arg, desc);
}
const StringData* InjectionCache::GetStringData(const StringData* sd) {
return s_injectionCache->getStringData(sd);
}
void InjectionCache::ClearCache() {
s_injectionCache->clearCacheImpl();
}
const Injection* InjectionCache::getInjectionImpl(const StringData* code,
const StringData* desc) {
Unit* unit = getUnit(getStringData(code));
if (unit == nullptr) {
return nullptr;
}
Injection injection(unit, getStringData(desc));
const Injection* inj = getInjection(&injection);
return inj;
}
const Injection* InjectionCache::getInjectionImpl(const std::string& code,
const std::string& desc) {
StackStringData sdCode(code.c_str(), code.size(), AttachLiteral);
StackStringData sdDesc(desc.c_str(), desc.size(), AttachLiteral);
return getInjectionImpl(&sdCode, &sdDesc);
}
const Injection* InjectionCache::getInjectionImpl(Injection::Callback callback,
void *arg,
const StringData* desc) {
Injection injection(callback, arg, desc);
const Injection* inj = getInjection(&injection);
return inj;
}
void InjectionCache::clearCacheImpl() {
WriteLock lock(m_lock);
for (InjectionMap::iterator iter = m_injectionCache.begin();
iter != m_injectionCache.end(); ++iter) {
delete iter->first;
}
m_injectionCache.clear();
for (UnitMap::iterator iter = m_unitCache.begin();
iter != m_unitCache.end(); ++iter) {
delete iter->second;
}
m_unitCache.clear();
for (StringDataMap::iterator iter = m_sdCache.begin();
iter != m_sdCache.end(); ++iter) {
delete iter->first;
}
m_sdCache.clear();
}
const StringData* InjectionCache::getStringData(const StringData* sd) {
ReadLock lock(m_lock);
StringDataMap::const_accessor accFind;
if (m_sdCache.find(accFind, sd)) {
return accFind->first;
}
accFind.release(); // Release read lock
// Gap of lock
StringData* sdata = new StringData(sd->data(), sd->size(), CopyMalloc);
StringDataMap::accessor accInsert;
if (!m_sdCache.insert(accInsert, sdata)) {
// Same string inserted in gap of the lock
delete sdata;
}
return accInsert->first;
}
Unit* InjectionCache::getUnit(const StringData* code) {
ReadLock lock(m_lock);
// Note: caller needs to make sure the parameter code is not temporary
UnitMap::accessor acc;
if (m_unitCache.insert(acc, code)) {
Unit* unit = compile_string(code->data(), code->size());
// Here we save it even if unit == NULL, that at least saves us from
// compiling same illegal string
acc->second = unit;
}
return acc->second;
}
const Injection* InjectionCache::getInjection(const Injection* inj) {
ReadLock lock(m_lock);
InjectionMap::const_accessor accFind;
if (m_injectionCache.find(accFind, inj)) {
return accFind->first;
}
accFind.release();
Injection *injection = new Injection(*inj);
InjectionMap::accessor accInsert;
if (!m_injectionCache.insert(accInsert, injection)) {
delete injection;
}
return accInsert->first;
}
///////////////////////////////////////////////////////////////////////////////
InjectionTables::InjectionTables()
: m_int64Tables(InstHookTypeInt64Count), m_sdTables(InstHookTypeSDCount) {
for (int i = 0; i < InstHookTypeInt64Count; i++) {
m_int64Tables[i] = nullptr;
}
for (int i = 0; i < InstHookTypeSDCount; i++) {
m_sdTables[i] = nullptr;
}
}
InjectionTables::~InjectionTables() {
clear();
}
void InjectionTables::clear() {
for (int i = 0; i < InstHookTypeInt64Count; i++) {
setInt64Table(i, nullptr);
}
for (int i = 0; i < InstHookTypeSDCount; i++) {
setSDTable(i, nullptr);
}
}
InjectionTables* InjectionTables::clone() {
InjectionTables* newTables = new InjectionTables();
for (int i = 0; i < InstHookTypeInt64Count; i++) {
VM::InjectionTableInt64* table = m_int64Tables[i];
if (!table) {
newTables->m_int64Tables[i] = nullptr;
continue;
}
VM::InjectionTableInt64* newTable = new InjectionTableInt64();
newTable->insert(table->begin(), table->end());
newTables->m_int64Tables[i] = newTable;
}
for (int i = 0; i < InstHookTypeSDCount; i++) {
VM::InjectionTableSD* table = m_sdTables[i];
if (!table) {
newTables->m_sdTables[i] = nullptr;
continue;
}
VM::InjectionTableSD* newTable = new InjectionTableSD();
newTable->insert(table->begin(), table->end());
newTables->m_sdTables[i] = newTable;
}
return newTables;
}
void InjectionTables::setInt64Table(int hookType, InjectionTableInt64* table) {
if (m_int64Tables[hookType]) {
delete m_int64Tables[hookType];
}
m_int64Tables[hookType] = table;
}
void InjectionTables::setSDTable(int hookType, InjectionTableSD* table) {
if (m_sdTables[hookType]) {
delete m_sdTables[hookType];
}
m_sdTables[hookType] = table;
}
int InjectionTables::countInjections() {
int total = 0;
for (int i = 0; i < InstHookTypeInt64Count; i++) {
total += m_int64Tables[i]->size();
}
for (int i = 0; i < InstHookTypeSDCount; i++) {
total += m_sdTables[i]->size();
}
return total;
}
///////////////////////////////////////////////////////////////////////////////
static InjectionTables* s_globalInjTables = nullptr;
static ReadWriteMutex s_globalInjTableLock;
void InstHelpers::InstCustomStringCallback(const StringData* hook,
Injection::Callback callback,
void *arg, const StringData* desc) {
const Injection* inj = InjectionCache::GetInjection(callback, arg, desc);
assert(inj);
const StringData* hookCached = InjectionCache::GetStringData(hook);
if (!g_vmContext->m_injTables) {
g_vmContext->m_injTables = new InjectionTables();
}
if (!g_vmContext->m_injTables->getSDTable(InstHookTypeCustomEvt)) {
g_vmContext->m_injTables->setSDTable(InstHookTypeCustomEvt,
new InjectionTableSD());
}
InjectionTableSD* table =
g_vmContext->m_injTables->getSDTable(InstHookTypeCustomEvt);
(*table)[hookCached] = inj;
}
void InstHelpers::PushInstToGlobal() {
WriteLock lock(s_globalInjTableLock);
if (s_globalInjTables) {
delete s_globalInjTables;
s_globalInjTables = nullptr;
}
if (g_vmContext->m_injTables) {
s_globalInjTables = g_vmContext->m_injTables->clone();
}
}
void InstHelpers::PullInstFromGlobal() {
if (g_vmContext->m_injTables) {
delete g_vmContext->m_injTables;
g_vmContext->m_injTables = nullptr;
}
ReadLock lock(s_globalInjTableLock);
if (s_globalInjTables) {
g_vmContext->m_injTables = s_globalInjTables->clone();
}
}
int InstHelpers::CountGlobalInst() {
ReadLock lock(s_globalInjTableLock);
if (s_globalInjTables) {
return s_globalInjTables->countInjections();
}
return 0;
}
void InstHelpers::ClearGlobalInst() {
WriteLock lock(s_globalInjTableLock);
if (s_globalInjTables) {
delete s_globalInjTables;
s_globalInjTables = nullptr;
}
}
///////////////////////////////////////////////////////////////////////////////
} } // HPHP::VM