Add custom allocator for precise marking and add more tools

* CustomAllocator : Supports precise marking for specified objects.
                    (also supports iteration of specific typed objects)
* HeapVisualizer : Track and visualize heap usage info.
* GCLeakChecker : Shows where the false reference came from.
                  (Developer should specify the location of false reference)
* Add support for incremental build of bdwgc
This commit is contained in:
Eunji Jeong 2016-12-16 17:14:01 +09:00
commit 1a56fe4e48
22 changed files with 1056 additions and 475 deletions

2
.gitignore vendored
View file

@ -56,4 +56,4 @@ android/obj
android/libs
#test/chakracore/chakracorelog.verbose.txt
*.gen.txt
bdwgc_log
bdwgcUsage.dat

View file

@ -195,11 +195,12 @@ endif
SRC=
SRC += $(foreach dir, src , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/heap , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/interpreter , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/parser , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/parser/ast , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/parser/esprima_cpp , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/runtime , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/interpreter , $(wildcard $(dir)/*.cpp))
SRC += $(foreach dir, src/util , $(wildcard $(dir)/*.cpp))
ifeq ($(OUTPUT), bin)
SRC += $(foreach dir, src/shell , $(wildcard $(dir)/*.cpp))

View file

@ -7,6 +7,11 @@ if [ ! -f /proc/cpuinfo ]; then
fi
NUMPROC=$(grep 'processor' /proc/cpuinfo | wc -l)
INCREMENTAL=false
if [[ $1 == incremental ]]; then
INCREMENTAL=true
fi
#LTO=true
LTO=false
#COMPILER_VERSION_MAJOR=4.9
@ -18,10 +23,12 @@ COMPILER_VERSION_MINOR=4.6.4
# GC build
###########################################################
cd third_party/bdwgc/
autoreconf -vif
automake --add-missing
#./autogen.sh
#make distclean
if [[ $INCREMENTAL == false ]]; then
autoreconf -vif
automake --add-missing
#./autogen.sh
#make distclean
fi
# Common flags --------------------------------------------
@ -70,10 +77,20 @@ function build_gc_for_linux() {
for mode in debug release; do
for libtype in shared static; do
echo =========================================================================
echo Building bdwgc for $host $arch $mode $libtype
if ([ "$ARCH" != "" ] && [ "$ARCH" != $arch ]) ||
([ "$MODE" != "" ] && [ "$MODE" != $mode ]) ||
([ "$LIBTYPE" != "" ] && [ "$LIBTYPE" != $libtype ]); then
echo Skipping bdwgc build for $host $arch $mode $libtype
continue
else
echo Building bdwgc for $host $arch $mode $libtype
fi
BUILDDIR=out/$host/$arch/$mode.$libtype
rm -rf $BUILDDIR
if [[ $INCREMENTAL == false ]]; then
rm -rf $BUILDDIR
fi
mkdir -p $BUILDDIR
cd $BUILDDIR
@ -86,7 +103,9 @@ function build_gc_for_linux() {
CFLAGS="$CFLAGS_COMMON ${!CFLAGS_HOST} ${!CFLAGS_ARCH} ${!CFLAGS_MODE} ${!CFLAGS_LIBTYPE}"
LDFLAGS="$LDFLAGS_COMMON ${!LDFLAGS_HOST} ${!LDFLAGS_ARCH} ${!LDFLAGS_MODE} ${!LDFLAGS_LIBTYPE}"
../../../../configure $GCCONFFLAGS CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS $CFLAGS" > /dev/null
if [[ $INCREMENTAL == false ]]; then
../../../../configure $GCCONFFLAGS CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS $CFLAGS" > /dev/null
fi
make -j$NUMPROC > /dev/null
echo Building bdwgc for $host $arch $mode $libtype done
@ -215,6 +234,7 @@ if [[ $1 == tizen_obs_arm ]]; then
build_gc_for_tizen_obs arm $2
elif [[ $1 == tizen_obs_i386 ]]; then
build_gc_for_tizen_obs i386 $2
else # full build
build_gc_for_linux

View file

@ -26,337 +26,8 @@
#include <unicode/locid.h>
#include <unicode/uchar.h>
// #define PROFILE_MASSIF
#ifndef PROFILE_MASSIF
#include <gc_allocator.h>
#include <gc_cpp.h>
#include <unicode/locid.h>
#include <unicode/datefmt.h>
#else
#include <gc.h>
void* GC_malloc_hook(size_t siz);
void* GC_malloc_atomic_hook(size_t siz);
void GC_free_hook(void* address);
#undef GC_MALLOC
#define GC_MALLOC(X) GC_malloc_hook(X)
#undef GC_MALLOC_ATOMIC
#define GC_MALLOC_ATOMIC(X) GC_malloc_atomic_hook(X)
#undef GC_FREE
#define GC_FREE(X) GC_free_hook(X)
#undef GC_REGISTER_FINALIZER_NO_ORDER
#define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) GC_register_finalizer_no_order(p, f, d, of, od)
#include <gc_allocator.h>
#include <gc_cpp.h>
#endif
namespace Escargot {
template <class GC_Tp>
class gc_malloc_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_allocator<GC_Tp1> other;
};
gc_malloc_allocator() throw() {}
gc_malloc_allocator(const gc_malloc_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_allocator(const gc_malloc_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
return (GC_Tp*)GC_MALLOC(sizeof(GC_Tp) * GC_n);
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_allocator<GC_T1>&, const gc_malloc_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_allocator<GC_T1>&, const gc_malloc_allocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_pointer_free_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_pointer_free_allocator<GC_Tp1> other;
};
gc_malloc_pointer_free_allocator() throw() {}
gc_malloc_pointer_free_allocator(const gc_malloc_pointer_free_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_pointer_free_allocator(const gc_malloc_pointer_free_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_pointer_free_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
return (GC_Tp*)GC_MALLOC_ATOMIC(sizeof(GC_Tp) * GC_n);
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_pointer_free_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_pointer_free_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_pointer_free_allocator<GC_T1>&, const gc_malloc_pointer_free_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_pointer_free_allocator<GC_T1>&, const gc_malloc_pointer_free_allocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_ignore_off_page_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_ignore_off_page_allocator<GC_Tp1> other;
};
gc_malloc_ignore_off_page_allocator() throw() {}
gc_malloc_ignore_off_page_allocator(const gc_malloc_ignore_off_page_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_ignore_off_page_allocator(const gc_malloc_ignore_off_page_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_ignore_off_page_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
if (sizeof(GC_Tp) * GC_n > 1024) {
return (GC_Tp*)GC_MALLOC_IGNORE_OFF_PAGE(sizeof(GC_Tp) * GC_n);
} else {
return (GC_Tp*)GC_MALLOC(sizeof(GC_Tp) * GC_n);
}
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_ignore_off_page_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_ignore_off_page_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_ignore_off_page_allocator<GC_T1>&, const gc_malloc_ignore_off_page_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_ignore_off_page_allocator<GC_T1>&, const gc_malloc_ignore_off_page_allocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_atomic_ignore_off_page_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_atomic_ignore_off_page_allocator<GC_Tp1> other;
};
gc_malloc_atomic_ignore_off_page_allocator() throw() {}
gc_malloc_atomic_ignore_off_page_allocator(const gc_malloc_atomic_ignore_off_page_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_atomic_ignore_off_page_allocator(const gc_malloc_atomic_ignore_off_page_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_atomic_ignore_off_page_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
if (sizeof(GC_Tp) * GC_n > 1024) {
return (GC_Tp*)GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sizeof(GC_Tp) * GC_n);
} else {
return (GC_Tp*)GC_MALLOC_ATOMIC(sizeof(GC_Tp) * GC_n);
}
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_atomic_ignore_off_page_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_atomic_ignore_off_page_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_atomic_ignore_off_page_allocator<GC_T1>&, const gc_malloc_atomic_ignore_off_page_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_atomic_ignore_off_page_allocator<GC_T1>&, const gc_malloc_atomic_ignore_off_page_allocator<GC_T2>&)
{
return false;
}
}
/* COMPILER() - the compiler being used to build the project */
#define COMPILER(FEATURE) (defined COMPILER_##FEATURE && COMPILER_##FEATURE)
@ -531,6 +202,7 @@ inline bool operator!=(const gc_malloc_atomic_ignore_off_page_allocator<GC_T1>&,
#define ALLOCA(bytes, typenameWithoutPointer, ec) (typenameWithoutPointer*)alloca(bytes)
#include "heap/Heap.h"
#include "CheckedArithmetic.h"
#include "runtime/String.h"

213
src/heap/Allocator.cpp Normal file
View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd
*
* 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 "Escargot.h"
#include "Allocator.h"
#include "runtime/Value.h"
#include "runtime/ArrayObject.h"
#ifdef PROFILE_MASSIF
std::unordered_map<void*, void*> g_addressTable;
std::vector<void*> g_freeList;
void unregisterGCAddress(void* address)
{
// ASSERT(g_addressTable.find(address) != g_addressTable.end());
if (g_addressTable.find(address) != g_addressTable.end()) {
auto iter = g_addressTable.find(address);
free(iter->second);
g_addressTable.erase(iter);
}
}
void registerGCAddress(void* address, size_t siz)
{
if (g_addressTable.find(address) != g_addressTable.end()) {
unregisterGCAddress(address);
}
g_addressTable[address] = malloc(siz);
}
void* GC_malloc_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc(siz);
#else
ptr = GC_malloc(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_malloc_atomic_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc_atomic(siz);
#else
ptr = GC_malloc_atomic(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_generic_malloc_ignore_off_page_hook(size_t siz, int kind)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_generic_malloc(siz, kind);
#else
ptr = GC_generic_malloc(siz, kind);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_malloc_ignore_off_page_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc_ignore_off_page(siz);
#else
ptr = GC_malloc_ignore_off_page(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_malloc_atomic_ignore_off_page_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc_atomic_ignore_off_page_hook(siz);
#else
ptr = GC_malloc_atomic_ignore_off_page_hook(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void GC_free_hook(void* address)
{
#ifdef NDEBUG
GC_free(address);
#else
GC_free(address);
#endif
unregisterGCAddress(address);
}
#endif
namespace Escargot {
static int s_gcKinds[HeapObjectKind::NumberOfKind];
template<GC_get_next_pointer_proc proc>
static struct GC_ms_entry* markAndPushCustom(GC_word* addr,
struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit,
GC_word env) {
return GC_mark_and_push_custom(addr, mark_stack_ptr, mark_stack_limit, proc);
}
static GC_word* getNextValidValue(GC_word** iterator) {
Value* current = (*(Value**)iterator)++;
if (*current && current->isPointerValue())
return (GC_word*)current->asPointerValue();
else
return NULL;
}
void initializeCustomAllocators() {
s_gcKinds[HeapObjectKind::ValueVectorKind] =
GC_new_kind(GC_new_free_list(),
GC_MAKE_PROC(GC_new_proc(markAndPushCustom<getNextValidValue>), 0),
FALSE,
TRUE);
s_gcKinds[HeapObjectKind::ArrayObjectKind] =
GC_new_kind(GC_new_free_list(),
0 | GC_DS_LENGTH,
TRUE,
TRUE);
#ifdef PROFILE_MASSIF
GC_is_valid_displacement_print_proc = [](void* ptr) {
g_freeList.push_back(ptr);
};
GC_set_on_collection_event([](GC_EventType evtType) {
if (GC_EVENT_PRE_START_WORLD == evtType) {
auto iter = g_addressTable.begin();
while (iter != g_addressTable.end()) {
GC_is_valid_displacement(iter->first);
iter++;
}
for (unsigned i = 0; i < g_freeList.size(); i++) {
unregisterGCAddress(g_freeList[i]);
}
g_freeList.clear();
}
});
#endif
}
void iterateSpecificKindOfObject(ExecutionState& state, HeapObjectKind kind, HeapObjectIteratorCallback callback) {
struct HeapObjectIteratorData {
int kind;
ExecutionState& state;
HeapObjectIteratorCallback callback;
};
HeapObjectIteratorData data { s_gcKinds[kind], state, callback };
GC_gcollect(); // Update mark status
GC_enumerate_reachable_objects_inner([](void* obj, size_t bytes, void* cd) {
size_t size;
int kind = GC_get_kind_and_size(obj, &size);
ASSERT(size == bytes);
HeapObjectIteratorData* data = (HeapObjectIteratorData*) cd;
if (kind == data->kind) {
data->callback(data->state, obj);
}
}, (void*)(&data));
}
template <>
Value* CustomAllocator<Value>::allocate(size_type GC_n, const void*) {
// Un-comment this to use default allocator
// return (Value*)GC_MALLOC_IGNORE_OFF_PAGE(sizeof(Value) * GC_n);
int kind = s_gcKinds[HeapObjectKind::ValueVectorKind];
return (Value*)GC_GENERIC_MALLOC_IGNORE_OFF_PAGE(sizeof(Value) * GC_n, kind);
}
template <>
ArrayObject* CustomAllocator<ArrayObject>::allocate(size_type GC_n, const void*) {
// Un-comment this to use default allocator
// return (ArrayObject*)GC_MALLOC(sizeof(ArrayObject));
ASSERT(GC_n == 1);
int kind = s_gcKinds[HeapObjectKind::ArrayObjectKind];
return (ArrayObject*)GC_GENERIC_MALLOC(sizeof(ArrayObject), kind);
}
}

400
src/heap/Allocator.h Normal file
View file

@ -0,0 +1,400 @@
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd
*
* 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.
*/
#ifndef __Allocator__
#define __Allocator__
namespace Escargot {
class ExecutionState;
enum HeapObjectKind {
ValueVectorKind = 0,
ArrayObjectKind,
NumberOfKind,
};
void initializeCustomAllocators();
typedef std::function<void(ExecutionState& state, void* obj)> HeapObjectIteratorCallback;
/*
* This Function will iterate over every objects of given kind.
* It has to call GC_gcollect() since it needs precise mark status.
* So don't call this in performance-critical path.
*/
void iterateSpecificKindOfObject(ExecutionState& state, HeapObjectKind kind, HeapObjectIteratorCallback callback);
template <class GC_Tp>
class CustomAllocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1> struct rebind {
typedef CustomAllocator<GC_Tp1> other;
};
CustomAllocator() throw() { }
CustomAllocator(const CustomAllocator&) throw() { }
# if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1> CustomAllocator
(const CustomAllocator<GC_Tp1>&) throw() { }
# endif
~CustomAllocator() throw() { }
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0);
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n)
{ GC_FREE(__p); }
size_type max_size() const throw()
{ return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template<>
class CustomAllocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1> struct rebind {
typedef CustomAllocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const CustomAllocator<GC_T1>&, const CustomAllocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const CustomAllocator<GC_T1>&, const CustomAllocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_allocator<GC_Tp1> other;
};
gc_malloc_allocator() throw() {}
gc_malloc_allocator(const gc_malloc_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_allocator(const gc_malloc_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
return (GC_Tp*)GC_MALLOC(sizeof(GC_Tp) * GC_n);
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_allocator<GC_T1>&, const gc_malloc_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_allocator<GC_T1>&, const gc_malloc_allocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_pointer_free_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_pointer_free_allocator<GC_Tp1> other;
};
gc_malloc_pointer_free_allocator() throw() {}
gc_malloc_pointer_free_allocator(const gc_malloc_pointer_free_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_pointer_free_allocator(const gc_malloc_pointer_free_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_pointer_free_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
return (GC_Tp*)GC_MALLOC_ATOMIC(sizeof(GC_Tp) * GC_n);
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_pointer_free_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_pointer_free_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_pointer_free_allocator<GC_T1>&, const gc_malloc_pointer_free_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_pointer_free_allocator<GC_T1>&, const gc_malloc_pointer_free_allocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_ignore_off_page_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_ignore_off_page_allocator<GC_Tp1> other;
};
gc_malloc_ignore_off_page_allocator() throw() {}
gc_malloc_ignore_off_page_allocator(const gc_malloc_ignore_off_page_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_ignore_off_page_allocator(const gc_malloc_ignore_off_page_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_ignore_off_page_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
if (sizeof(GC_Tp) * GC_n > 1024) {
return (GC_Tp*)GC_MALLOC_IGNORE_OFF_PAGE(sizeof(GC_Tp) * GC_n);
} else {
return (GC_Tp*)GC_MALLOC(sizeof(GC_Tp) * GC_n);
}
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_ignore_off_page_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_ignore_off_page_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_ignore_off_page_allocator<GC_T1>&, const gc_malloc_ignore_off_page_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_ignore_off_page_allocator<GC_T1>&, const gc_malloc_ignore_off_page_allocator<GC_T2>&)
{
return false;
}
template <class GC_Tp>
class gc_malloc_atomic_ignore_off_page_allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef GC_Tp* pointer;
typedef const GC_Tp* const_pointer;
typedef GC_Tp& reference;
typedef const GC_Tp& const_reference;
typedef GC_Tp value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_atomic_ignore_off_page_allocator<GC_Tp1> other;
};
gc_malloc_atomic_ignore_off_page_allocator() throw() {}
gc_malloc_atomic_ignore_off_page_allocator(const gc_malloc_atomic_ignore_off_page_allocator&) throw() {}
#if !(GC_NO_MEMBER_TEMPLATES || 0 < _MSC_VER && _MSC_VER <= 1200)
// MSVC++ 6.0 do not support member templates
template <class GC_Tp1>
gc_malloc_atomic_ignore_off_page_allocator(const gc_malloc_atomic_ignore_off_page_allocator<GC_Tp1>&) throw() {}
#endif
~gc_malloc_atomic_ignore_off_page_allocator() throw()
{
}
pointer address(reference GC_x) const { return &GC_x; }
const_pointer address(const_reference GC_x) const { return &GC_x; }
// GC_n is permitted to be 0. The C++ standard says nothing about what
// the return value is when GC_n == 0.
GC_Tp* allocate(size_type GC_n, const void* = 0)
{
if (sizeof(GC_Tp) * GC_n > 1024) {
return (GC_Tp*)GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sizeof(GC_Tp) * GC_n);
} else {
return (GC_Tp*)GC_MALLOC_ATOMIC(sizeof(GC_Tp) * GC_n);
}
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n) { GC_FREE(__p); }
size_type max_size() const throw() { return size_t(-1) / sizeof(GC_Tp); }
void construct(pointer __p, const GC_Tp& __val) { new (__p) GC_Tp(__val); }
void destroy(pointer __p) { __p->~GC_Tp(); }
};
template <>
class gc_malloc_atomic_ignore_off_page_allocator<void> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class GC_Tp1>
struct rebind {
typedef gc_malloc_atomic_ignore_off_page_allocator<GC_Tp1> other;
};
};
template <class GC_T1, class GC_T2>
inline bool operator==(const gc_malloc_atomic_ignore_off_page_allocator<GC_T1>&, const gc_malloc_atomic_ignore_off_page_allocator<GC_T2>&)
{
return true;
}
template <class GC_T1, class GC_T2>
inline bool operator!=(const gc_malloc_atomic_ignore_off_page_allocator<GC_T1>&, const gc_malloc_atomic_ignore_off_page_allocator<GC_T2>&)
{
return false;
}
}
#endif

30
src/heap/Heap.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "Escargot.h"
#include "Heap.h"
#include "HeapProfiler.h"
namespace Escargot {
void Heap::initialize()
{
GC_set_free_space_divisor(24);
GC_set_force_unmap_on_gcollect(1);
// GC_set_full_freq(1);
// GC_set_time_limit(GC_TIME_UNLIMITED);
initializeCustomAllocators();
#ifdef PROFILE_BDWGC
Escargot::HeapUsageVisualizer::initialize();
#endif
}
void Heap::finalize()
{
for (size_t i = 0; i < 5; i++) {
GC_gcollect_and_unmap();
}
}
}

56
src/heap/Heap.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef __Heap__
#define __Heap__
namespace Escargot {
class Heap {
public:
static void initialize();
static void finalize();
};
}
//#define PROFILE_MASSIF
#ifdef PROFILE_MASSIF
#ifndef ESCARGOT_SHELL
#error `PROFILE_MASSIF` can only be enabled in escargot shell
#endif
#include <gc.h>
void* GC_malloc_hook(size_t siz);
void* GC_malloc_atomic_hook(size_t siz);
void* GC_generic_malloc_ignore_off_page_hook(size_t siz, int kind);
void GC_free_hook(void* address);
#undef GC_MALLOC
#define GC_MALLOC(X) GC_malloc_hook(X)
#undef GC_MALLOC_ATOMIC
#define GC_MALLOC_ATOMIC(X) GC_malloc_atomic_hook(X)
#undef GC_GENERIC_MALLOC_IGNORE_OFF_PAGE
#define GC_GENERIC_MALLOC_IGNORE_OFF_PAGE(siz, kind) GC_generic_malloc_ignore_off_page_hook(siz, kind)
#undef GC_FREE
#define GC_FREE(X) GC_free_hook(X)
#undef GC_REGISTER_FINALIZER_NO_ORDER
#define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) GC_register_finalizer_no_order(p, f, d, of, od)
#endif
#include <gc_allocator.h>
#include <gc_cpp.h>
#include <gc_mark.h>
#ifdef GC_DEBUG
#include <gc_backptr.h>
#endif
#include "Allocator.h"
#endif

121
src/heap/HeapProfiler.cpp Normal file
View file

@ -0,0 +1,121 @@
#include "Escargot.h"
#include "HeapProfiler.h"
#include "heap/Allocator.h"
#include "runtime/ErrorObject.h"
namespace Escargot {
#ifdef PROFILE_BDWGC
std::string HeapUsageVisualizer::m_outputFile = "bdwgcUsage.dat";
size_t GCLeakChecker::m_totalFreed = 0;
std::vector<GCLeakChecker::LeakCheckedAddr> GCLeakChecker::m_leakCheckedAddrs;
static std::string s_gcLogPhaseName = "initial phase";
void HeapUsageVisualizer::initialize()
{
remove(HeapUsageVisualizer::m_outputFile.c_str());
FILE* fp = fopen(HeapUsageVisualizer::m_outputFile.c_str(), "a");
if (fp) {
fprintf(fp, "GC_no PeakRSS TotalHeap Marked # Phase\n");
fclose(fp);
}
GC_set_on_collection_event([](GC_EventType evtType) {
if (GC_EVENT_RECLAIM_START == evtType) {
GC_dump_for_graph(HeapUsageVisualizer::m_outputFile.c_str(),
s_gcLogPhaseName.c_str());
}
});
}
void GCLeakChecker::registerAddress(void* ptr, std::string description)
{
RELEASE_ASSERT(ptr);
m_leakCheckedAddrs.push_back(LeakCheckedAddr { ptr, description, false });
printf("GCLeakChecker::registerAddress %p (%zu - %zu = %zu)\n", ptr,
m_leakCheckedAddrs.size(), m_totalFreed, m_leakCheckedAddrs.size() - m_totalFreed);
GC_REGISTER_FINALIZER_NO_ORDER(ptr, [] (void* obj, void* cd) {
GCLeakChecker::unregisterAddress(obj);
}, NULL, NULL, NULL);
}
void GCLeakChecker::unregisterAddress(void* ptr)
{
RELEASE_ASSERT(ptr);
m_totalFreed++;
printf("GCLeakChecker::unregisterAddress %p (%zu - %zu = %zu)\n", ptr,
m_leakCheckedAddrs.size(), m_totalFreed, m_leakCheckedAddrs.size() - m_totalFreed);
for (auto& it: m_leakCheckedAddrs) {
if (it.ptr == ptr) {
it.deallocated = true;
return;
}
}
RELEASE_ASSERT_NOT_REACHED();
}
void GCLeakChecker::dumpBackTrace(ExecutionState& state, const char* phase)
{
if (phase)
s_gcLogPhaseName = phase;
#ifdef GC_DEBUG
auto stream = stderr;
fprintf(stderr, "GCLeakChecker::dumpBackTrace %s start >>>>>>>>>>\n", s_gcLogPhaseName.c_str());
for (const auto& it: m_leakCheckedAddrs) {
if (it.deallocated) {
fprintf(stderr, "%s (%p) deallocated\n", it.description.c_str(), it.ptr);
} else {
fprintf(stderr, "Backtrace of %s (%p):\n", it.description.c_str(), it.ptr);
GC_print_backtrace(it.ptr);
}
}
fprintf(stderr, "GCLeakChecker::dumpBackTrace %s end <<<<<<<<<<\n", s_gcLogPhaseName.c_str());
#else
fprintf(stderr, "Cannot print the backtrace of leaked address.\n");
fprintf(stderr, "Please re-configure bdwgc with `--enable-gc-debug`, ");
fprintf(stderr, "and re-build escargot with `-DGC_DEBUG`.\n");
#endif
}
/* Usage in JS: registerLeakCheck( object: PointerValue, description: String ); */
Value builtinRegisterLeakCheck(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
{
if (!argv[0].isPointerValue())
state.throwException(new ErrorObject(state, new ASCIIString("builtinRegisterLeakCheck should get pointer-type argument")));
PointerValue* ptr = argv[0].asPointerValue();
std::string description = argv[1].toString(state)->toUTF8StringData().data();
RELEASE_ASSERT(ptr);
GCLeakChecker::registerAddress(ptr, description);
return Value();
}
/* Usage in JS: dumpBackTrace( phase: String ); */
Value builtinDumpBackTrace(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
{
GCLeakChecker::dumpBackTrace(state, argv[0].toString(state)->toUTF8StringData().data());
return Value();
}
/* Usage in JS: setPhaseName( phase: String ); */
Value builtinSetGCPhaseName(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
{
s_gcLogPhaseName = argv[0].toString(state)->toUTF8StringData().data();
return Value();
}
#endif // PROFILE_BDWGC
}

45
src/heap/HeapProfiler.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef __HeapProfiler__
#define __HeapProfiler__
//#define PROFILE_BDWGC
#ifdef PROFILE_BDWGC
#include "runtime/Value.h"
namespace Escargot {
class ExecutionState;
class HeapUsageVisualizer {
public:
static void initialize();
private:
static std::string m_outputFile;
};
class GCLeakChecker {
public:
static void registerAddress(void* ptr, std::string description);
static void dumpBackTrace(ExecutionState& state, const char* phase = NULL);
private:
static void unregisterAddress(void* ptr);
struct LeakCheckedAddr {
void* ptr;
std::string description;
bool deallocated;
};
static size_t m_totalFreed;
static std::vector<LeakCheckedAddr> m_leakCheckedAddrs;
};
Value builtinRegisterLeakCheck(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression);
Value builtinDumpBackTrace(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression);
Value builtinSetGCPhaseName(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression);
}
#endif // PROFILE_BDWGC
#endif // __HeapProfiler__

View file

@ -1147,7 +1147,7 @@ struct EnumerateObjectData : public gc {
ObjectStructureChain m_hiddenClassChain;
Object* m_object;
size_t m_idx;
Vector<Value, gc_malloc_ignore_off_page_allocator<Value>> m_keys;
ValueVector m_keys;
};

View file

@ -90,4 +90,15 @@ void ArrayObject::sort(ExecutionState& state, std::function<bool(const Value& a,
}
Object::sort(state, comp);
}
void* ArrayObject::operator new(size_t size)
{
return CustomAllocator<ArrayObject>().allocate(1);
}
void ArrayObject::iterateArrays(ExecutionState& state, HeapObjectIteratorCallback callback)
{
iterateSpecificKindOfObject(state, HeapObjectKind::ArrayObjectKind, callback);
}
}

View file

@ -20,7 +20,6 @@ public:
return true;
}
uint32_t getLength(ExecutionState& state)
{
if (LIKELY(isPlainObject())) {
@ -50,6 +49,12 @@ public:
}
virtual void sort(ExecutionState& state, std::function<bool(const Value& a, const Value& b)> comp);
// Use custom allocator for Array object (for Badtime)
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
static void iterateArrays(ExecutionState& state, HeapObjectIteratorCallback callback);
protected:
Value getLengthSlowCase(ExecutionState& state);
bool setLengthSlowCase(ExecutionState& state, const Value& value);
@ -156,7 +161,7 @@ protected:
return false;
}
Vector<Value, gc_malloc_ignore_off_page_allocator<Value>> m_fastModeData;
ValueVector m_fastModeData;
};
}

View file

@ -6,6 +6,7 @@
#include "NumberObject.h"
#include "parser/ScriptParser.h"
#include "parser/esprima_cpp/esprima.h"
#include "heap/HeapProfiler.h"
namespace Escargot {
@ -202,6 +203,24 @@ void GlobalObject::installOthers(ExecutionState& state)
NativeFunctionInfo(state.context()->staticStrings().gc, builtinGc, 0, nullptr, NativeFunctionInfo::Strict), false),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::AllPresent)));
#ifdef PROFILE_BDWGC
AtomicString dumpBackTrace(state, "dumpBackTrace");
defineOwnProperty(state, ObjectPropertyName(dumpBackTrace),
ObjectPropertyDescriptor(new FunctionObject(state,
NativeFunctionInfo(dumpBackTrace, builtinDumpBackTrace, 1, nullptr, NativeFunctionInfo::Strict), false),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::AllPresent)));
AtomicString registerLeakCheck(state, "registerLeakCheck");
defineOwnProperty(state, ObjectPropertyName(registerLeakCheck),
ObjectPropertyDescriptor(new FunctionObject(state,
NativeFunctionInfo(registerLeakCheck, builtinRegisterLeakCheck, 2, nullptr, NativeFunctionInfo::Strict), false),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::AllPresent)));
AtomicString setPhaseName(state, "setPhaseName");
defineOwnProperty(state, ObjectPropertyName(setPhaseName),
ObjectPropertyDescriptor(new FunctionObject(state,
NativeFunctionInfo(setPhaseName, builtinSetGCPhaseName, 1, nullptr, NativeFunctionInfo::Strict), false),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::AllPresent)));
#endif
m_stringProxyObject = new StringObject(state);
m_numberProxyObject = new NumberObject(state);
}

View file

@ -236,7 +236,7 @@ private:
#include "runtime/ValueInlines.h"
namespace Escargot {
typedef Vector<Value, gc_malloc_ignore_off_page_allocator<Value>> ValueVector;
typedef Vector<Value, CustomAllocator<Value>> ValueVector;
}
#endif

View file

@ -31,92 +31,6 @@ void __attribute__((optimize("O0"))) fillStack(size_t siz)
}
#endif
#ifdef PROFILE_MASSIF
std::unordered_map<void*, void*> g_addressTable;
std::vector<void*> g_freeList;
void unregisterGCAddress(void* address)
{
// ASSERT(g_addressTable.find(address) != g_addressTable.end());
if (g_addressTable.find(address) != g_addressTable.end()) {
auto iter = g_addressTable.find(address);
free(iter->second);
g_addressTable.erase(iter);
}
}
void registerGCAddress(void* address, size_t siz)
{
if (g_addressTable.find(address) != g_addressTable.end()) {
unregisterGCAddress(address);
}
g_addressTable[address] = malloc(siz);
}
void* GC_malloc_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc(siz);
#else
ptr = GC_malloc(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_malloc_atomic_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc_atomic(siz);
#else
ptr = GC_malloc_atomic(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_malloc_ignore_off_page_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc_ignore_off_page(siz);
#else
ptr = GC_malloc_ignore_off_page(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void* GC_malloc_atomic_ignore_off_page_hook(size_t siz)
{
void* ptr;
#ifdef NDEBUG
ptr = GC_malloc_atomic_ignore_off_page_hook(siz);
#else
ptr = GC_malloc_atomic_ignore_off_page_hook(siz);
#endif
registerGCAddress(ptr, siz);
return ptr;
}
void GC_free_hook(void* address)
{
#ifdef NDEBUG
GC_free(address);
#else
GC_free(address);
#endif
unregisterGCAddress(address);
}
#endif
#ifdef PROFILE_BDWGC
const char* bdwgc_log_filename = "bdwgc_log";
const char* bdwgc_log_phase = "initial phase";
#endif
void eval(Escargot::Context* context, Escargot::String* str, Escargot::String* fileName, bool shouldPrintScriptResult)
{
auto result = context->scriptParser().parse(str, fileName);
@ -154,43 +68,9 @@ int main(int argc, char* argv[])
GC_gcollect();
GC_gcollect();
*/
GC_set_free_space_divisor(24);
GC_set_force_unmap_on_gcollect(1);
// GC_set_full_freq(1);
// GC_set_time_limit(GC_TIME_UNLIMITED);
#ifdef PROFILE_MASSIF
GC_is_valid_displacement_print_proc = [](void* ptr) {
g_freeList.push_back(ptr);
};
GC_set_on_collection_event([](GC_EventType evtType) {
if (GC_EVENT_PRE_START_WORLD == evtType) {
auto iter = g_addressTable.begin();
while (iter != g_addressTable.end()) {
GC_is_valid_displacement(iter->first);
iter++;
}
for (unsigned i = 0; i < g_freeList.size(); i++) {
unregisterGCAddress(g_freeList[i]);
}
Escargot::Heap::initialize();
g_freeList.clear();
}
});
#endif
#ifdef PROFILE_BDWGC
remove(bdwgc_log_filename);
FILE* fp = fopen(bdwgc_log_filename, "a");
if (fp) {
fprintf(fp, "GC_no PeakRSS TotalHeap Marked # Phase\n");
fclose(fp);
}
GC_set_on_collection_event([](GC_EventType evtType) {
if (GC_EVENT_RECLAIM_START == evtType) {
GC_dump_for_graph(bdwgc_log_filename, bdwgc_log_phase);
}
});
#endif
#ifndef NDEBUG
setbuf(stdout, NULL);
setbuf(stderr, NULL);
@ -234,12 +114,9 @@ int main(int argc, char* argv[])
delete context;
delete instance;
GC_gcollect_and_unmap();
GC_gcollect_and_unmap();
GC_gcollect_and_unmap();
GC_gcollect_and_unmap();
GC_gcollect_and_unmap();
GC_gcollect_and_unmap();
Escargot::Heap::finalize();
return 0;
}
@ -279,3 +156,19 @@ int main(int argc, char* argv[])
}
}
*/
/* Custom allocator & Array iterator test
#include "runtime/ArrayObject.h"
size_t counter = 0;
Escargot::HeapObjectIteratorCallback callback =
[&counter](Escargot::ExecutionState& state, void* obj) {
Escargot::ArrayObject* arr = (Escargot::ArrayObject*) obj;
printf("ArrayObject %p with length %zu\n", obj, arr->getLength(state));
counter++;
};
Escargot::ExecutionState state(context);
Escargot::ArrayObject::iterateArrays(state, callback);
printf("Array total count %zu\n", counter);
*/

View file

@ -189,6 +189,11 @@
void *base;
#ifdef ESCARGOT
ptr_t object_start;
if (GC_base(current) == 0) {
GC_err_printf("GC_base(%p) == 0\n", current);
return;
}
#endif
GC_print_heap_obj(GC_base(current));
@ -585,7 +590,11 @@ GC_API GC_ATTR_MALLOC void * GC_CALL
return (GC_store_debug_info(result, (word)lb, s, i));
}
#ifdef ESCARGOT // To expose API
GC_API GC_ATTR_MALLOC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
#else
STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
#endif
{
void * result = GC_generic_malloc(lb + DEBUG_BYTES, knd);
@ -966,6 +975,28 @@ GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
return(result);
}
#ifdef ESCARGOT // To expose API
GC_API GC_ATTR_MALLOC void * GC_CALL
GC_debug_generic_malloc_ignore_off_page(size_t lb, int k, GC_EXTRA_PARAMS)
{
void * result = GC_generic_malloc_inner_ignore_off_page(
lb + DEBUG_BYTES, k);
if (result == 0) {
GC_err_printf("GC internal allocation (%lu bytes) returning NULL (%s:%d)\n",
(unsigned long) lb, s, i);
return(0);
}
if (!GC_debugging_started) {
GC_start_debugging_inner();
}
ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
return (GC_store_debug_info(result, (word)lb, s, i));
}
#endif
GC_API GC_ATTR_MALLOC void * GC_CALL
GC_debug_generic_or_special_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
{

View file

@ -204,6 +204,32 @@ GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
/* first page of the resulting object */
/* are ignored. */
#ifdef ESCARGOT // To expose API
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
GC_debug_generic_malloc(
size_t /* lb */,
int /* knd */,
GC_EXTRA_PARAMS);
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
GC_debug_generic_malloc_ignore_off_page(
size_t /* lb */, int /* knd */,
GC_EXTRA_PARAMS);
#ifndef GC_GENERIC_MALLOC
#ifdef GC_DEBUG
# define GC_GENERIC_MALLOC(sz, knd) \
GC_debug_generic_malloc(sz, knd, GC_EXTRAS)
# define GC_GENERIC_MALLOC_IGNORE_OFF_PAGE(sz, knd) \
GC_debug_generic_malloc_ignore_off_page(sz, knd, GC_EXTRAS)
#else /* GC_DEBUG */
# define GC_GENERIC_MALLOC(sz, knd) \
GC_generic_malloc(sz, knd)
# define GC_GENERIC_MALLOC_IGNORE_OFF_PAGE(sz, knd) \
GC_generic_malloc_ignore_off_page(sz, knd)
#endif /* GC_DEBUG */
#endif
#endif
/* Same as above but primary for allocating an object of the same kind */
/* as an existing one (kind obtained by GC_get_kind_and_size). */
/* Not suitable for GCJ and typed-malloc kinds. */
@ -272,6 +298,19 @@ GC_API int GC_CALL GC_is_marked(const void *) GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_clear_mark_bit(const void *) GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_set_mark_bit(const void *) GC_ATTR_NONNULL(1);
#ifdef ESCARGOT
/* To use mark function, we should choose between:
* 1. include private header or 2. do some work inside bdwgc
* Currently second choice is adopted,
* but it can be changed in future for better performance.
*/
typedef GC_word* (GC_get_next_pointer_proc)(GC_word** iterator);
GC_API struct GC_ms_entry* GC_mark_and_push_custom(GC_word* addr,
struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit,
const GC_get_next_pointer_proc proc);
#endif
/* Push everything in the given range onto the mark stack. */
/* (GC_push_conditional pushes either all or only dirty pages depending */
/* on the third argument.) GC_push_all_eager also ensures that stack */

View file

@ -869,6 +869,29 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
return mark_stack_top;
}
#ifdef ESCARGOT
GC_API mse * GC_mark_and_push_custom(GC_word *addr, mse *mark_stack_ptr, mse *mark_stack_limit,
const GC_get_next_pointer_proc proc) {
DECLARE_HDR_CACHE;
INIT_HDR_CACHE;
const char* start = GC_USR_PTR_FROM_BASE(addr);
const char* end = ((char*)addr) + GC_size(addr) - 8;
GC_word* iterator = (GC_word*)start;
while (TRUE) {
GC_word* points = proc(&iterator);
if (points) {
PUSH_CONTENTS((ptr_t)points, mark_stack_ptr, mark_stack_limit, iterator, exit);
}
if (iterator >= (GC_word*)end)
break;
}
return (mark_stack_ptr);
}
#endif
#ifdef PARALLEL_MARK
STATIC GC_bool GC_help_wanted = FALSE; /* Protected by mark lock */

View file

@ -1311,8 +1311,10 @@ GC_API void GC_CALL GC_init(void)
# endif
RESTORE_CANCEL(cancel_state);
#ifdef ESCARGOT
/* These variables are not initialized properly */
GC_heapsize = 0;
GC_unmapped_bytes = 0;
GC_debug_header_size = 0;
#endif
}

View file

@ -698,15 +698,15 @@ GC_API void GC_CALL GC_dump_for_graph(const char* log_file_name,
getrusage(RUSAGE_SELF, &ru);
size_t peak_rss = ru.ru_maxrss;
GC_printf("[%d] %s : PeakRSS %zu KB, TotalHeap %lu KB, MarkedHeap %lu KB\n",
GC_get_gc_no(), phase_name, peak_rss,
GC_printf("[%lu] %s : PeakRSS %zu KB, TotalHeap %lu KB, MarkedHeap %lu KB\n",
(unsigned long) GC_get_gc_no(), phase_name, peak_rss,
(unsigned long) pstats.total_bytes / 1024,
(unsigned long) pstats.marked_bytes / 1024);
FILE* fp = fopen(log_file_name, "a");
if (fp) {
fprintf(fp, "%5d %9lu %9lu %9zu # %s\n",
GC_get_gc_no(),
fprintf(fp, "%5lu %9zu %9lu %9lu # %s\n",
(unsigned long) GC_get_gc_no(),
peak_rss,
(unsigned long) pstats.total_bytes / 1024,
(unsigned long) pstats.marked_bytes / 1024,

View file

@ -17,7 +17,7 @@
import subprocess
import os
BDWGC_LOGFILE="bdwgc_log"
BDWGC_LOGFILE="bdwgcUsage.dat"
GNUPLOT_DISPLAY_STYLE="lines"
def make_plot_subcmd(col, name):
@ -26,7 +26,7 @@ def make_plot_subcmd(col, name):
def draw_bdwgc_plot():
if not os.path.exists(BDWGC_LOGFILE):
print 'Cannot draw plot! No input file %s' % BDWGC_LOGFILE
print 'Re-build escargot with `-DPROFILE_BDWGC`'
print 'Please re-build escargot with `-DPROFILE_BDWGC` and run again.'
exit(1)
plot_cmd = 'plot '