Arquivos
hhvm/hphp/runtime/base/memory/smart_allocator.h
T
ptarjan 0038b76a58 kill TAINTED code
While I was working on the TestCodeRun refactor I found two tests about Tainted code. I looked into it and coulnd't get HHVM to compile with TAINTED=1. Then I checked and none of the extension functions we exposed about tainting were used in WWW. Scratching my head I asked, @srenfro and @jdelong, who  thought it was dead. So I killed this zombie.
2013-04-12 12:04:04 -07:00

363 linhas
13 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. |
+----------------------------------------------------------------------+
*/
#ifndef __HPHP_SMART_ALLOCATOR_H__
#define __HPHP_SMART_ALLOCATOR_H__
#include <boost/noncopyable.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <util/base.h>
#include <util/thread_local.h>
#include <util/stack_trace.h>
#include <util/lock.h>
#include <runtime/base/types.h>
#include <runtime/base/util/countable.h>
#include <runtime/base/memory/memory_usage_stats.h>
#include <util/trace.h>
namespace HPHP {
#ifdef DEBUG_MEMORY_LEAK
#define DEBUGGING_SMART_ALLOCATOR 1
#endif
//#define DEBUGGING_SMART_ALLOCATOR 1
//#define SMART_ALLOCATOR_DEBUG_FREE
///////////////////////////////////////////////////////////////////////////////
/**
* If a class is using SmartAllocator, all "new" and "delete" should be done
* through these two macros in a form like this,
*
* MyClass *obj = NEW(MyClass)(...);
* DELETE(MyClass)(obj);
*
* Note that these various allocation functions should only be used
* for ObjectData-derived classes. (If you need other
* request-lifetime memory you need to do something else.)
*/
template<class T>
inline void smart_allocator_check_type() {
static_assert((boost::is_base_of<ObjectData,T>::value),
"Non-ObjectData allocated in smart heap");
}
#ifdef DEBUGGING_SMART_ALLOCATOR
#define NEW(T) new T
#define NEWOBJ(T) new T
#define NEWOBJSZ(T,SZ) new (malloc(SZ)) T
#define ALLOCOBJSZ(SZ) (malloc(SZ))
#define ALLOCOBJIDX(I) (malloc(object_alloc_index_to_size(I)))
#define DELETE(T) delete
#define DELETEOBJSZ(SZ) free
#define DELETEOBJ(NS,T,OBJ) delete OBJ
#define RELEASEOBJ(NS,T,OBJ) ::operator delete(OBJ)
#define SWEEPOBJ(T) delete this
#else
#define NEW(T) new (T::AllocatorType::getNoCheck()) T
#define NEWOBJ(T) new \
((smart_allocator_check_type<T>(), ThreadLocalSingleton \
<ObjectAllocator<ObjectSizeClass<sizeof(T)>::value> > \
::getNoCheck())) T
#define NEWOBJSZ(T,SZ) \
new ((smart_allocator_check_type<T>(), info->instanceSizeAllocator(SZ))) T
#define ALLOCOBJSZ(SZ) (ThreadInfo::s_threadInfo.getNoCheck()->\
instanceSizeAllocator(SZ)->alloc())
#define ALLOCOBJIDX(I) (ThreadInfo::s_threadInfo.getNoCheck()-> \
instanceIdxAllocator(I)->alloc())
#define DELETE(T) T::AllocatorType::getNoCheck()->release
#define DELETEOBJSZ(SZ) (ThreadInfo::s_threadInfo.getNoCheck()->\
instanceSizeAllocator(SZ)->release)
#define DELETEOBJ(NS,T,OBJ) delete OBJ
#define RELEASEOBJ(NS,T,OBJ) \
(ThreadLocalSingleton \
<ObjectAllocator<ObjectSizeClass<sizeof(T)>::value> > \
::getNoCheck())->release(OBJ)
#define SWEEPOBJ(T) this->~T()
#endif
///////////////////////////////////////////////////////////////////////////////
/**
* To use this allocator, simply add DECLARE_SMART_ALLOCATION macro to .h and
* add IMPLEMENT_SMART_ALLOCATION macro to .cpp. For example,
*
* class MyClass {
* DECLARE_SMART_ALLOCATION(MyClass);
* };
*
* IMPLEMENT_SMART_ALLOCATION(MyClass);
*/
typedef void (*AllocatorThreadLocalInit)(void);
std::set<AllocatorThreadLocalInit>& GetAllocatorInitList();
void InitAllocatorThreadLocal() ATTRIBUTE_COLD;
#define DECLARE_SMART_ALLOCATION(T) \
public: \
typedef \
ThreadLocalSingleton<SmartAllocator<T, SmartAllocatorImpl::T> > \
AllocatorType; \
static void *SmaAllocatorInitSetup; \
void release(); \
#define IMPLEMENT_SMART_ALLOCATION(T) \
void *T::SmaAllocatorInitSetup = \
SmartAllocatorInitSetup<T, SmartAllocatorImpl::T>(); \
void T::release() { \
DELETE(T)(this); \
} \
#define IMPLEMENT_SMART_ALLOCATION_HOT(T) \
void *T::SmaAllocatorInitSetup = \
SmartAllocatorInitSetup<T, SmartAllocatorImpl::T>(); \
HOT_FUNC void T::release() { \
DELETE(T)(this); \
} \
#define IMPLEMENT_SMART_ALLOCATION_CLS(C, T) \
void *C::T::SmaAllocatorInitSetup = \
SmartAllocatorInitSetup<C::T, SmartAllocatorImpl::T>(); \
void C::T::release() { \
DELETE(T)(this); \
} \
///////////////////////////////////////////////////////////////////////////////
#define SLAB_SIZE (2 << 20)
/**
* Just a simple free-list based memory allocator.
*/
class SmartAllocatorImpl : boost::noncopyable {
public:
enum Name {
TestAllocator = -1,
RefData,
StringData,
SharedMap,
Variant,
Bucket,
HphpArray,
ObjectData,
GlobalVariables,
TestGlobals
};
struct Iterator;
// Ensure we have room for freelist and _count tombstone
static const size_t MinItemSize = 16;
public:
SmartAllocatorImpl(Name name, int itemSize);
~SmartAllocatorImpl();
Name getAllocatorType() const { return m_name; }
int getItemSize() const { return m_itemSize;}
static size_t itemSizeRoundup(size_t n) {
return n >= MinItemSize ? n : MinItemSize;
}
/**
* Allocation/deallocation of object memory.
*/
void* alloc() { return alloc(m_itemSize); }
void* alloc(size_t size);
void dealloc(void *obj) {
TRACE(1, "dealloc %p\n", obj);
assert(memset(obj, kSmartFreeFill, m_itemSize));
m_free.push(obj);
MemoryManager::TheMemoryManager()->getStats().usage -= m_itemSize;
}
void clear() { m_free.clear(); }
/*
* Returns whether the given pointer points into this smart
* allocator (regardless of whether it is already freed).
*/
bool isFromThisAllocator(void*) const { return false; }
// keep these frequently used fields together.
private:
TRACE_SET_MOD(smartalloc);
GarbageList m_free;
const int m_itemSize;
const Name m_name;
};
/*
* Object for iterating over all unfreed objects in smart allocator.
*
* It is legal to deallocate the currently pointed to element during
* iteration (and will not affect the iteration state). Other changes
* to the allocator during iteration do not have guaranteed behavior.
*
* Nop'd out but still here to keep cycle-gc compiling.
*/
struct SmartAllocatorImpl::Iterator : private boost::noncopyable {
explicit Iterator(const SmartAllocatorImpl*);
void* current() const; // returns 0 when done
void next();
};
///////////////////////////////////////////////////////////////////////////////
// This allocator is for known and fixed sized classes, like StringData or
// ArrayData.
template<typename T, SmartAllocatorImpl::Name TNameEnum>
class SmartAllocator : public SmartAllocatorImpl {
public:
/**
* Specify how many items to allocate a time. The more, the less number of
* times to grow the memory, but the higher chance of increasing memory
* footprint.
*/
SmartAllocator() : SmartAllocatorImpl(TNameEnum, sizeof(T)) {
static_assert(sizeof(T) <= SLAB_SIZE, "slab too small");
}
void release(T *p) {
if (p) {
p->~T();
dealloc(p);
}
}
static void Create(void* storage) {
new (storage) SmartAllocator<T, TNameEnum>();
}
static void Delete(SmartAllocator *p) {
p->~SmartAllocator();
}
static void OnThreadExit(SmartAllocator *p) {
p->~SmartAllocator();
}
};
template<typename T, SmartAllocatorImpl::Name TNameEnum>
void *SmartAllocatorInitSetup() {
ThreadLocalSingleton<SmartAllocator<T, TNameEnum> > tls;
GetAllocatorInitList().insert((AllocatorThreadLocalInit)(tls.getCheck));
return (void*)tls.getNoCheck;
}
///////////////////////////////////////////////////////////////////////////////
// This allocator is for unknown but fixed sized classes, like ObjectData.
#define DECLARE_OBJECT_ALLOCATION_NO_SWEEP(T) \
public: \
/* static void *ObjAllocatorInitSetup; */ \
inline ALWAYS_INLINE void operator delete(void *p) { \
if (T::IsResourceClass) { \
RELEASEOBJ(NS, T, p); \
return; \
} \
HPHP::VM::Instance* this_ = (HPHP::VM::Instance*)p; \
HPHP::VM::Class* cls = this_->getVMClass(); \
size_t nProps = cls->numDeclProperties(); \
size_t builtinPropSize = cls->builtinPropSize(); \
TypedValue* propVec = \
(TypedValue *)((uintptr_t)this_ + sizeof(ObjectData) + \
builtinPropSize); \
for (unsigned i = 0; i < nProps; ++i) { \
TypedValue* prop = &propVec[i]; \
tvRefcountedDecRef(prop); \
} \
DELETEOBJSZ(HPHP::VM::Instance::sizeForNProps(nProps) + \
builtinPropSize)(this_); \
}
#define DECLARE_OBJECT_ALLOCATION(T) \
DECLARE_OBJECT_ALLOCATION_NO_SWEEP(T) \
virtual void sweep();
#define IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP_CLS(NS,T) \
/* void *NS::T::ObjAllocatorInitSetup = \
ObjectAllocatorInitSetup<NS::T>(); */
#define IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(T) \
IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP_CLS(HPHP,T)
#define IMPLEMENT_OBJECT_ALLOCATION_CLS(NS,T) \
IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP_CLS(NS,T); \
void NS::T::sweep() { \
SWEEPOBJ(T); \
}
#define IMPLEMENT_OBJECT_ALLOCATION(T) IMPLEMENT_OBJECT_ALLOCATION_CLS(HPHP,T)
class ObjectAllocatorBase : public SmartAllocatorImpl {
public:
ObjectAllocatorBase(int itemSize);
void release(void *p) {
if (p) {
dealloc(p);
}
}
};
template<int S>
class ObjectAllocator : public ObjectAllocatorBase {
public:
static void Create(void* storage) {
new (storage) ObjectAllocator<S>();
static_assert(unsigned(S) <= SLAB_SIZE, "slab too small");
}
static void Delete(ObjectAllocator *p) {
p->~ObjectAllocator();
}
static void OnThreadExit(ObjectAllocator *p) {
p->~ObjectAllocator();
}
ObjectAllocator() : ObjectAllocatorBase(S) { }
};
///////////////////////////////////////////////////////////////////////////////
}
template<typename T, HPHP::SmartAllocatorImpl::Name TNameEnum>
inline void *operator new(size_t sizeT,
HPHP::SmartAllocator<T, TNameEnum> *a) {
assert(sizeT == sizeof(T));
return a->alloc(HPHP::SmartAllocatorImpl::itemSizeRoundup(sizeof(T)));
}
inline void *operator new(size_t sizeT, HPHP::ObjectAllocatorBase *a) {
assert(sizeT <= size_t(a->getItemSize()));
return a->alloc();
}
template<typename T, HPHP::SmartAllocatorImpl::Name TNameEnum>
inline void operator delete
(void *p, HPHP::SmartAllocator<T, TNameEnum> *a) {
assert(p);
a->dealloc((T*)p);
}
inline void operator delete(void *p , HPHP::ObjectAllocatorBase *a) {
assert(p);
a->dealloc(p);
}
///////////////////////////////////////////////////////////////////////////////
#endif // __HPHP_SMART_ALLOCATOR_H__