typedef void (*AddHistogramSampleCallback)(void* histogram, int sample);
+// --- M e m o r y A l l o c a t i o n C a l l b a c k ---
+ enum ObjectSpace {
+ kObjectSpaceNewSpace = 1 << 0,
+ kObjectSpaceOldPointerSpace = 1 << 1,
+ kObjectSpaceOldDataSpace = 1 << 2,
+ kObjectSpaceCodeSpace = 1 << 3,
+ kObjectSpaceMapSpace = 1 << 4,
+ kObjectSpaceLoSpace = 1 << 5,
+
+ kObjectSpaceAll = kObjectSpaceNewSpace | kObjectSpaceOldPointerSpace |
+ kObjectSpaceOldDataSpace | kObjectSpaceCodeSpace | kObjectSpaceMapSpace |
+ kObjectSpaceLoSpace
+ };
+
+ enum AllocationAction {
+ kAllocationActionAllocate = 1 << 0,
+ kAllocationActionFree = 1 << 1,
+ kAllocationActionAll = kAllocationActionAllocate | kAllocationActionFree
+ };
+
+typedef void (*MemoryAllocationCallback)(ObjectSpace space,
+ AllocationAction action,
+ int size);
+
// --- F a i l e d A c c e s s C h e c k C a l l b a c k ---
typedef void (*FailedAccessCheckCallback)(Local<Object> target,
AccessType type,
static void SetGlobalGCEpilogueCallback(GCCallback);
/**
+ * Enables the host application to provide a mechanism to be notified
+ * and perform custom logging when V8 Allocates Executable Memory.
+ */
+ static void AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action);
+
+ /**
+ * This function removes callback which was installed by
+ * AddMemoryAllocationCallback function.
+ */
+ static void RemoveMemoryAllocationCallback(MemoryAllocationCallback callback);
+
+ /**
* Allows the host application to group objects together. If one
* object in the group is alive, all objects in the group are alive.
* After each garbage collection, object groups are removed. It is
int MemoryAllocator::size_ = 0;
int MemoryAllocator::size_executable_ = 0;
+List<MemoryAllocator::MemoryAllocationCallbackRegistration>
+ MemoryAllocator::memory_allocation_callbacks_;
+
VirtualMemory* MemoryAllocator::initial_chunk_ = NULL;
// 270 is an estimate based on the static default heap size of a pair of 256K
}
-void *executable_memory_histogram = NULL;
-
bool MemoryAllocator::Setup(int capacity) {
capacity_ = RoundUp(capacity, Page::kPageSize);
size_ = 0;
size_executable_ = 0;
- executable_memory_histogram =
- StatsTable::CreateHistogram("V8.ExecutableMemoryMax", 0, MB * 512, 50);
ChunkInfo info; // uninitialized element.
for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
chunks_.Add(info);
int alloced = static_cast<int>(*allocated);
size_ += alloced;
- if (executable == EXECUTABLE) {
- size_executable_ += alloced;
- static int size_executable_max_observed_ = 0;
- if (size_executable_max_observed_ < size_executable_) {
- size_executable_max_observed_ = size_executable_;
- StatsTable::AddHistogramSample(executable_memory_histogram,
- size_executable_);
- }
- }
+ if (executable == EXECUTABLE) size_executable_ += alloced;
#ifdef DEBUG
ZapBlock(reinterpret_cast<Address>(mem), alloced);
#endif
Counters::memory_allocated.Decrement(static_cast<int>(length));
size_ -= static_cast<int>(length);
if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length);
+
ASSERT(size_ >= 0);
}
+void MemoryAllocator::PerformAllocationCallback(ObjectSpace space,
+ AllocationAction action,
+ int size) {
+ for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
+ MemoryAllocationCallbackRegistration registration =
+ memory_allocation_callbacks_[i];
+ if ((registration.space & space) == space &&
+ (registration.action & action) == action)
+ registration.callback(space, action, static_cast<int>(size));
+ }
+}
+
+
+bool MemoryAllocator::MemoryAllocationCallbackRegistered(
+ MemoryAllocationCallback callback) {
+ for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
+ if (memory_allocation_callbacks_[i].callback == callback) return true;
+ }
+ return false;
+}
+
+
+void MemoryAllocator::AddMemoryAllocationCallback(
+ MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action) {
+ ASSERT(callback != NULL);
+ MemoryAllocationCallbackRegistration registration(callback, space, action);
+ ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback));
+ return memory_allocation_callbacks_.Add(registration);
+}
+
+
+void MemoryAllocator::RemoveMemoryAllocationCallback(
+ MemoryAllocationCallback callback) {
+ ASSERT(callback != NULL);
+ for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
+ if (memory_allocation_callbacks_[i].callback == callback) {
+ memory_allocation_callbacks_.Remove(i);
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {
ASSERT(initial_chunk_ == NULL);
int chunk_id = Pop();
chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner);
+ ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity());
+ PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size);
return InitializePagesInChunk(chunk_id, *allocated_pages, owner);
}
Counters::memory_allocated.Decrement(static_cast<int>(c.size()));
} else {
LOG(DeleteEvent("PagedChunk", c.address()));
+ ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner()->identity());
+ int size = c.size();
FreeRawMemory(c.address(), c.size(), c.executable());
+ PerformAllocationCallback(space, kAllocationActionFree, size);
}
c.init(NULL, 0, NULL);
Push(chunk_id);
LOG(DeleteEvent("LargeObjectChunk", mem));
return NULL;
}
+ ObjectSpace space =
+ (executable == EXECUTABLE) ? kObjectSpaceCodeSpace : kObjectSpaceLoSpace;
+ MemoryAllocator::PerformAllocationCallback(space,
+ kAllocationActionAllocate,
+ *chunk_size);
return reinterpret_cast<LargeObjectChunk*>(mem);
}
Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
Executability executable =
page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
+ ObjectSpace space = kObjectSpaceLoSpace;
+ if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
+ int size = chunk->size();
MemoryAllocator::FreeRawMemory(chunk->address(),
chunk->size(),
executable);
+ MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree,
+ size);
}
size_ = 0;
MarkCompactCollector::ReportDeleteIfNeeded(object);
size_ -= static_cast<int>(chunk_size);
page_count_--;
+ ObjectSpace space = kObjectSpaceLoSpace;
+ if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
MemoryAllocator::FreeRawMemory(chunk_address, chunk_size, executable);
+ MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree,
+ size_);
LOG(DeleteEvent("LargeObjectChunk", chunk_address));
}
}
static void FreeRawMemory(void* buf,
size_t length,
Executability executable);
+ static void PerformAllocationCallback(ObjectSpace space,
+ AllocationAction action,
+ int size);
+
+ static void AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action);
+ static void RemoveMemoryAllocationCallback(
+ MemoryAllocationCallback callback);
+ static bool MemoryAllocationCallbackRegistered(
+ MemoryAllocationCallback callback);
// Returns the maximum available bytes of heaps.
static int Available() { return capacity_ < size_ ? 0 : capacity_ - size_; }
// Allocated executable space size in bytes.
static int size_executable_;
+ struct MemoryAllocationCallbackRegistration {
+ MemoryAllocationCallbackRegistration(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action)
+ : callback(callback), space(space), action(action) {
+ }
+ MemoryAllocationCallback callback;
+ ObjectSpace space;
+ AllocationAction action;
+ };
+ // A List of callback that are triggered when memory is allocated or free'd
+ static List<MemoryAllocationCallbackRegistration>
+ memory_allocation_callbacks_;
+
// The initial chunk of virtual memory.
static VirtualMemory* initial_chunk_;