diff --git a/include/v8.h b/include/v8.h index d62d669697..b89c244ca2 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2359,6 +2359,30 @@ typedef void* (*CreateHistogramCallback)(const char* name, 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 target, AccessType type, @@ -2578,6 +2602,20 @@ class V8EXPORT V8 { */ 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. diff --git a/src/api.cc b/src/api.cc index e7a9e5c340..47105573fd 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3905,6 +3905,22 @@ void V8::RemoveGCEpilogueCallback(GCEpilogueCallback callback) { } +void V8::AddMemoryAllocationCallback(MemoryAllocationCallback callback, + ObjectSpace space, + AllocationAction action) { + if (IsDeadCheck("v8::V8::AddMemoryAllocationCallback()")) return; + i::MemoryAllocator::AddMemoryAllocationCallback(callback, + space, + action); +} + + +void V8::RemoveMemoryAllocationCallback(MemoryAllocationCallback callback) { + if (IsDeadCheck("v8::V8::RemoveMemoryAllocationCallback()")) return; + i::MemoryAllocator::RemoveMemoryAllocationCallback(callback); +} + + void V8::PauseProfiler() { #ifdef ENABLE_LOGGING_AND_PROFILING PauseProfilerEx(PROFILER_MODULE_CPU); diff --git a/src/d8.cc b/src/d8.cc index 7fd7925baa..5a1e63a763 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -486,7 +486,7 @@ void Shell::Initialize() { // Start the debugger agent if requested. if (i::FLAG_debugger_agent) { - v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port); + v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true); } // Start the in-process debugger if requested. diff --git a/src/spaces.cc b/src/spaces.cc index e734b938f2..b366f3923f 100644 --- a/src/spaces.cc +++ b/src/spaces.cc @@ -274,6 +274,9 @@ int MemoryAllocator::capacity_ = 0; int MemoryAllocator::size_ = 0; int MemoryAllocator::size_executable_ = 0; +List + 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 @@ -299,8 +302,6 @@ int MemoryAllocator::Pop() { } -void *executable_memory_histogram = NULL; - bool MemoryAllocator::Setup(int capacity) { capacity_ = RoundUp(capacity, Page::kPageSize); @@ -318,8 +319,6 @@ bool MemoryAllocator::Setup(int capacity) { 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); @@ -366,15 +365,7 @@ void* MemoryAllocator::AllocateRawMemory(const size_t requested, int alloced = static_cast(*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
(mem), alloced); #endif @@ -397,10 +388,56 @@ void MemoryAllocator::FreeRawMemory(void* mem, Counters::memory_allocated.Decrement(static_cast(length)); size_ -= static_cast(length); if (executable == EXECUTABLE) size_executable_ -= static_cast(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(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); @@ -458,6 +495,8 @@ Page* MemoryAllocator::AllocatePages(int requested_pages, int* allocated_pages, int chunk_id = Pop(); chunks_[chunk_id].init(static_cast
(chunk), chunk_size, owner); + ObjectSpace space = static_cast(1 << owner->identity()); + PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); return InitializePagesInChunk(chunk_id, *allocated_pages, owner); } @@ -616,7 +655,10 @@ void MemoryAllocator::DeleteChunk(int chunk_id) { Counters::memory_allocated.Decrement(static_cast(c.size())); } else { LOG(DeleteEvent("PagedChunk", c.address())); + ObjectSpace space = static_cast(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); @@ -2614,6 +2656,11 @@ LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes, LOG(DeleteEvent("LargeObjectChunk", mem)); return NULL; } + ObjectSpace space = + (executable == EXECUTABLE) ? kObjectSpaceCodeSpace : kObjectSpaceLoSpace; + MemoryAllocator::PerformAllocationCallback(space, + kAllocationActionAllocate, + *chunk_size); return reinterpret_cast(mem); } @@ -2651,9 +2698,14 @@ void LargeObjectSpace::TearDown() { 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; @@ -2867,7 +2919,11 @@ void LargeObjectSpace::FreeUnmarkedObjects() { MarkCompactCollector::ReportDeleteIfNeeded(object); size_ -= static_cast(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)); } } diff --git a/src/spaces.h b/src/spaces.h index 26bbfea646..c33ab2c598 100644 --- a/src/spaces.h +++ b/src/spaces.h @@ -567,6 +567,17 @@ class MemoryAllocator : public AllStatic { 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_; } @@ -643,6 +654,20 @@ class MemoryAllocator : public AllStatic { // 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 + memory_allocation_callbacks_; + // The initial chunk of virtual memory. static VirtualMemory* initial_chunk_;