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:
parent
2b1e4560e7
commit
d3e457303e
38
include/v8.h
38
include/v8.h
@ -2359,6 +2359,30 @@ typedef void* (*CreateHistogramCallback)(const char* name,
|
|||||||
|
|
||||||
typedef void (*AddHistogramSampleCallback)(void* histogram, int sample);
|
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 ---
|
// --- 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,
|
typedef void (*FailedAccessCheckCallback)(Local<Object> target,
|
||||||
AccessType type,
|
AccessType type,
|
||||||
@ -2578,6 +2602,20 @@ class V8EXPORT V8 {
|
|||||||
*/
|
*/
|
||||||
static void SetGlobalGCEpilogueCallback(GCCallback);
|
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
|
* Allows the host application to group objects together. If one
|
||||||
* object in the group is alive, all objects in the group are alive.
|
* object in the group is alive, all objects in the group are alive.
|
||||||
|
16
src/api.cc
16
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() {
|
void V8::PauseProfiler() {
|
||||||
#ifdef ENABLE_LOGGING_AND_PROFILING
|
#ifdef ENABLE_LOGGING_AND_PROFILING
|
||||||
PauseProfilerEx(PROFILER_MODULE_CPU);
|
PauseProfilerEx(PROFILER_MODULE_CPU);
|
||||||
|
@ -486,7 +486,7 @@ void Shell::Initialize() {
|
|||||||
|
|
||||||
// Start the debugger agent if requested.
|
// Start the debugger agent if requested.
|
||||||
if (i::FLAG_debugger_agent) {
|
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.
|
// Start the in-process debugger if requested.
|
||||||
|
@ -274,6 +274,9 @@ int MemoryAllocator::capacity_ = 0;
|
|||||||
int MemoryAllocator::size_ = 0;
|
int MemoryAllocator::size_ = 0;
|
||||||
int MemoryAllocator::size_executable_ = 0;
|
int MemoryAllocator::size_executable_ = 0;
|
||||||
|
|
||||||
|
List<MemoryAllocator::MemoryAllocationCallbackRegistration>
|
||||||
|
MemoryAllocator::memory_allocation_callbacks_;
|
||||||
|
|
||||||
VirtualMemory* MemoryAllocator::initial_chunk_ = NULL;
|
VirtualMemory* MemoryAllocator::initial_chunk_ = NULL;
|
||||||
|
|
||||||
// 270 is an estimate based on the static default heap size of a pair of 256K
|
// 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) {
|
bool MemoryAllocator::Setup(int capacity) {
|
||||||
capacity_ = RoundUp(capacity, Page::kPageSize);
|
capacity_ = RoundUp(capacity, Page::kPageSize);
|
||||||
|
|
||||||
@ -318,8 +319,6 @@ bool MemoryAllocator::Setup(int capacity) {
|
|||||||
|
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
size_executable_ = 0;
|
size_executable_ = 0;
|
||||||
executable_memory_histogram =
|
|
||||||
StatsTable::CreateHistogram("V8.ExecutableMemoryMax", 0, MB * 512, 50);
|
|
||||||
ChunkInfo info; // uninitialized element.
|
ChunkInfo info; // uninitialized element.
|
||||||
for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
|
for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
|
||||||
chunks_.Add(info);
|
chunks_.Add(info);
|
||||||
@ -366,15 +365,7 @@ void* MemoryAllocator::AllocateRawMemory(const size_t requested,
|
|||||||
int alloced = static_cast<int>(*allocated);
|
int alloced = static_cast<int>(*allocated);
|
||||||
size_ += alloced;
|
size_ += alloced;
|
||||||
|
|
||||||
if (executable == EXECUTABLE) {
|
if (executable == EXECUTABLE) size_executable_ += alloced;
|
||||||
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_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
ZapBlock(reinterpret_cast<Address>(mem), alloced);
|
ZapBlock(reinterpret_cast<Address>(mem), alloced);
|
||||||
#endif
|
#endif
|
||||||
@ -397,10 +388,56 @@ void MemoryAllocator::FreeRawMemory(void* mem,
|
|||||||
Counters::memory_allocated.Decrement(static_cast<int>(length));
|
Counters::memory_allocated.Decrement(static_cast<int>(length));
|
||||||
size_ -= static_cast<int>(length);
|
size_ -= static_cast<int>(length);
|
||||||
if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length);
|
if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length);
|
||||||
|
|
||||||
ASSERT(size_ >= 0);
|
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) {
|
void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {
|
||||||
ASSERT(initial_chunk_ == NULL);
|
ASSERT(initial_chunk_ == NULL);
|
||||||
|
|
||||||
@ -458,6 +495,8 @@ Page* MemoryAllocator::AllocatePages(int requested_pages, int* allocated_pages,
|
|||||||
int chunk_id = Pop();
|
int chunk_id = Pop();
|
||||||
chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner);
|
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);
|
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()));
|
Counters::memory_allocated.Decrement(static_cast<int>(c.size()));
|
||||||
} else {
|
} else {
|
||||||
LOG(DeleteEvent("PagedChunk", c.address()));
|
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());
|
FreeRawMemory(c.address(), c.size(), c.executable());
|
||||||
|
PerformAllocationCallback(space, kAllocationActionFree, size);
|
||||||
}
|
}
|
||||||
c.init(NULL, 0, NULL);
|
c.init(NULL, 0, NULL);
|
||||||
Push(chunk_id);
|
Push(chunk_id);
|
||||||
@ -2614,6 +2656,11 @@ LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes,
|
|||||||
LOG(DeleteEvent("LargeObjectChunk", mem));
|
LOG(DeleteEvent("LargeObjectChunk", mem));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
ObjectSpace space =
|
||||||
|
(executable == EXECUTABLE) ? kObjectSpaceCodeSpace : kObjectSpaceLoSpace;
|
||||||
|
MemoryAllocator::PerformAllocationCallback(space,
|
||||||
|
kAllocationActionAllocate,
|
||||||
|
*chunk_size);
|
||||||
return reinterpret_cast<LargeObjectChunk*>(mem);
|
return reinterpret_cast<LargeObjectChunk*>(mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2651,9 +2698,14 @@ void LargeObjectSpace::TearDown() {
|
|||||||
Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
|
Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
|
||||||
Executability executable =
|
Executability executable =
|
||||||
page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
|
page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
|
||||||
|
ObjectSpace space = kObjectSpaceLoSpace;
|
||||||
|
if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
|
||||||
|
int size = chunk->size();
|
||||||
MemoryAllocator::FreeRawMemory(chunk->address(),
|
MemoryAllocator::FreeRawMemory(chunk->address(),
|
||||||
chunk->size(),
|
chunk->size(),
|
||||||
executable);
|
executable);
|
||||||
|
MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree,
|
||||||
|
size);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
@ -2867,7 +2919,11 @@ void LargeObjectSpace::FreeUnmarkedObjects() {
|
|||||||
MarkCompactCollector::ReportDeleteIfNeeded(object);
|
MarkCompactCollector::ReportDeleteIfNeeded(object);
|
||||||
size_ -= static_cast<int>(chunk_size);
|
size_ -= static_cast<int>(chunk_size);
|
||||||
page_count_--;
|
page_count_--;
|
||||||
|
ObjectSpace space = kObjectSpaceLoSpace;
|
||||||
|
if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
|
||||||
MemoryAllocator::FreeRawMemory(chunk_address, chunk_size, executable);
|
MemoryAllocator::FreeRawMemory(chunk_address, chunk_size, executable);
|
||||||
|
MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree,
|
||||||
|
size_);
|
||||||
LOG(DeleteEvent("LargeObjectChunk", chunk_address));
|
LOG(DeleteEvent("LargeObjectChunk", chunk_address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/spaces.h
25
src/spaces.h
@ -567,6 +567,17 @@ class MemoryAllocator : public AllStatic {
|
|||||||
static void FreeRawMemory(void* buf,
|
static void FreeRawMemory(void* buf,
|
||||||
size_t length,
|
size_t length,
|
||||||
Executability executable);
|
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.
|
// Returns the maximum available bytes of heaps.
|
||||||
static int Available() { return capacity_ < size_ ? 0 : capacity_ - size_; }
|
static int Available() { return capacity_ < size_ ? 0 : capacity_ - size_; }
|
||||||
@ -643,6 +654,20 @@ class MemoryAllocator : public AllStatic {
|
|||||||
// Allocated executable space size in bytes.
|
// Allocated executable space size in bytes.
|
||||||
static int size_executable_;
|
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.
|
// The initial chunk of virtual memory.
|
||||||
static VirtualMemory* initial_chunk_;
|
static VirtualMemory* initial_chunk_;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user