Add API callback for tracking allocated memory

Allows the host application to provide a mechanism for notification and custom logging through a callback (set through the API V8::AddMemoryAllocationCallback and removed through V8::RemoveMemoryAllocationCallback), when V8 allocates ro frees memory. 

This replaces the current histogram data "V8.ExecutableMemoryMax" by allowing usage data to be logged by the application as required rather than always through V8. 

BUG=http://crbug.com/54222
TEST=Set the callback function with the API and cause V8 to allocate memory.

Patch by Paul Mehta <pmehta@chromium.org>

Review URL: http://codereview.chromium.org/3329006


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5403 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2010-09-03 11:31:58 +00:00
parent 2b1e4560e7
commit d3e457303e
5 changed files with 149 additions and 14 deletions

View File

@ -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<Object> 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.

View File

@ -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);

View File

@ -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.

View File

@ -274,6 +274,9 @@ int MemoryAllocator::capacity_ = 0;
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
@ -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<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
@ -397,10 +388,56 @@ void MemoryAllocator::FreeRawMemory(void* mem,
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);
@ -458,6 +495,8 @@ Page* MemoryAllocator::AllocatePages(int requested_pages, int* allocated_pages,
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);
}
@ -616,7 +655,10 @@ void MemoryAllocator::DeleteChunk(int chunk_id) {
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);
@ -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<LargeObjectChunk*>(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<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));
}
}

View File

@ -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<MemoryAllocationCallbackRegistration>
memory_allocation_callbacks_;
// The initial chunk of virtual memory.
static VirtualMemory* initial_chunk_;