Add a new API V8::SetJitCodeEventHandler to push code name and location to users such as profilers.
BUG=None TEST=Included in CL. Committed: https://code.google.com/p/v8/source/detail?r=12389 Review URL: https://chromiumcodereview.appspot.com/10795074 Patch from Sigurður Ásgeirsson <siggi@chromium.org>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12401 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
78037d0a4e
commit
f9d4856289
74
include/v8.h
74
include/v8.h
@ -2944,6 +2944,57 @@ typedef void (*FunctionEntryHook)(uintptr_t function,
|
||||
uintptr_t return_addr_location);
|
||||
|
||||
|
||||
/**
|
||||
* A JIT code event is issued each time code is added, moved or removed.
|
||||
*
|
||||
* \note removal events are not currently issued.
|
||||
*/
|
||||
struct JitCodeEvent {
|
||||
enum EventType {
|
||||
CODE_ADDED,
|
||||
CODE_MOVED,
|
||||
CODE_REMOVED
|
||||
};
|
||||
|
||||
// Type of event.
|
||||
EventType type;
|
||||
// Start of the instructions.
|
||||
void* code_start;
|
||||
// Size of the instructions.
|
||||
size_t code_len;
|
||||
|
||||
union {
|
||||
// Only valid for CODE_ADDED.
|
||||
struct {
|
||||
// Name of the object associated with the code, note that the string is
|
||||
// not zero-terminated.
|
||||
const char* str;
|
||||
// Number of chars in str.
|
||||
size_t len;
|
||||
} name;
|
||||
// New location of instructions. Only valid for CODE_MOVED.
|
||||
void* new_code_start;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Option flags passed to the SetJitCodeEventHandler function.
|
||||
*/
|
||||
enum JitCodeEventOptions {
|
||||
kJitCodeEventDefault = 0,
|
||||
// Generate callbacks for already existent code.
|
||||
kJitCodeEventEnumExisting = 1
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback function passed to SetJitCodeEventHandler.
|
||||
*
|
||||
* \param event code add, move or removal event.
|
||||
*/
|
||||
typedef void (*JitCodeEventHandler)(const JitCodeEvent* event);
|
||||
|
||||
|
||||
/**
|
||||
* Interface for iterating though all external resources in the heap.
|
||||
*/
|
||||
@ -3218,6 +3269,29 @@ class V8EXPORT V8 {
|
||||
*/
|
||||
static bool SetFunctionEntryHook(FunctionEntryHook entry_hook);
|
||||
|
||||
/**
|
||||
* Allows the host application to provide the address of a function that is
|
||||
* notified each time code is added, moved or removed.
|
||||
*
|
||||
* \param options options for the JIT code event handler.
|
||||
* \param event_handler the JIT code event handler, which will be invoked
|
||||
* each time code is added, moved or removed.
|
||||
* \note \p event_handler won't get notified of existent code.
|
||||
* \note since code removal notifications are not currently issued, the
|
||||
* \p event_handler may get notifications of code that overlaps earlier
|
||||
* code notifications. This happens when code areas are reused, and the
|
||||
* earlier overlapping code areas should therefore be discarded.
|
||||
* \note the events passed to \p event_handler and the strings they point to
|
||||
* are not guaranteed to live past each call. The \p event_handler must
|
||||
* copy strings and other parameters it needs to keep around.
|
||||
* \note the set of events declared in JitCodeEvent::EventType is expected to
|
||||
* grow over time, and the JitCodeEvent structure is expected to accrue
|
||||
* new members. The \p event_handler function must ignore event codes
|
||||
* it does not recognize to maintain future compatibility.
|
||||
*/
|
||||
static void SetJitCodeEventHandler(JitCodeEventOptions options,
|
||||
JitCodeEventHandler event_handler);
|
||||
|
||||
/**
|
||||
* Adjusts the amount of registered external memory. Used to give
|
||||
* V8 an indication of the amount of externally allocated memory
|
||||
|
@ -4263,6 +4263,15 @@ bool v8::V8::SetFunctionEntryHook(FunctionEntryHook entry_hook) {
|
||||
}
|
||||
|
||||
|
||||
void v8::V8::SetJitCodeEventHandler(
|
||||
JitCodeEventOptions options, JitCodeEventHandler event_handler) {
|
||||
i::Isolate* isolate = i::Isolate::Current();
|
||||
// Ensure that logging is initialized for our isolate.
|
||||
isolate->InitializeLoggingAndCounters();
|
||||
isolate->logger()->SetCodeEventHandler(options, event_handler);
|
||||
}
|
||||
|
||||
|
||||
bool v8::V8::Dispose() {
|
||||
i::Isolate* isolate = i::Isolate::Current();
|
||||
if (!ApiCheck(isolate != NULL && isolate->IsDefaultIsolate(),
|
||||
|
@ -1019,7 +1019,7 @@ void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
|
||||
// Log the code generation. If source information is available include
|
||||
// script name and line number. Check explicitly whether logging is
|
||||
// enabled as finding the line number is not free.
|
||||
if (info->isolate()->logger()->is_logging() ||
|
||||
if (info->isolate()->logger()->is_logging_code_events() ||
|
||||
CpuProfiler::is_profiling(info->isolate())) {
|
||||
Handle<Script> script = info->script();
|
||||
Handle<Code> code = info->code();
|
||||
|
@ -188,7 +188,7 @@ class ProfilerEventsProcessor : public Thread {
|
||||
|
||||
|
||||
#define PROFILE(isolate, Call) \
|
||||
LOG(isolate, Call); \
|
||||
LOG_CODE_EVENT(isolate, Call); \
|
||||
do { \
|
||||
if (v8::internal::CpuProfiler::is_profiling(isolate)) { \
|
||||
v8::internal::CpuProfiler::Call; \
|
||||
|
@ -1741,7 +1741,7 @@ class ScavengingVisitor : public StaticVisitorBase {
|
||||
RecordCopiedObject(heap, target);
|
||||
HEAP_PROFILE(heap, ObjectMoveEvent(source->address(), target->address()));
|
||||
Isolate* isolate = heap->isolate();
|
||||
if (isolate->logger()->is_logging() ||
|
||||
if (isolate->logger()->is_logging_code_events() ||
|
||||
CpuProfiler::is_profiling(isolate)) {
|
||||
if (target->IsSharedFunctionInfo()) {
|
||||
PROFILE(isolate, SharedFunctionInfoMoveEvent(
|
||||
|
@ -1908,7 +1908,8 @@ bool Isolate::Init(Deserializer* des) {
|
||||
|
||||
// If we are deserializing, log non-function code objects and compiled
|
||||
// functions found in the snapshot.
|
||||
if (create_heap_objects && (FLAG_log_code || FLAG_ll_prof)) {
|
||||
if (create_heap_objects &&
|
||||
(FLAG_log_code || FLAG_ll_prof || logger_->is_logging_code_events())) {
|
||||
HandleScope scope;
|
||||
LOG(this, LogCodeObjects());
|
||||
LOG(this, LogCompiledFunctions());
|
||||
|
111
src/log.cc
111
src/log.cc
@ -526,6 +526,7 @@ Logger::Logger()
|
||||
name_buffer_(new NameBuffer),
|
||||
address_to_name_map_(NULL),
|
||||
is_initialized_(false),
|
||||
code_event_handler_(NULL),
|
||||
last_address_(NULL),
|
||||
prev_sp_(NULL),
|
||||
prev_function_(NULL),
|
||||
@ -541,6 +542,52 @@ Logger::~Logger() {
|
||||
}
|
||||
|
||||
|
||||
void Logger::IssueCodeAddedEvent(Code* code,
|
||||
const char* name,
|
||||
size_t name_len) {
|
||||
JitCodeEvent event;
|
||||
event.type = JitCodeEvent::CODE_ADDED;
|
||||
event.code_start = code->instruction_start();
|
||||
event.code_len = code->instruction_size();
|
||||
event.name.str = name;
|
||||
event.name.len = name_len;
|
||||
|
||||
code_event_handler_(&event);
|
||||
}
|
||||
|
||||
|
||||
void Logger::IssueCodeMovedEvent(Address from, Address to) {
|
||||
Code* from_code = Code::cast(HeapObject::FromAddress(from));
|
||||
|
||||
JitCodeEvent event;
|
||||
event.type = JitCodeEvent::CODE_MOVED;
|
||||
event.code_start = from_code->instruction_start();
|
||||
event.code_len = from_code->instruction_size();
|
||||
|
||||
// Calculate the header size.
|
||||
const size_t header_size =
|
||||
from_code->instruction_start() - reinterpret_cast<byte*>(from_code);
|
||||
|
||||
// Calculate the new start address of the instructions.
|
||||
event.new_code_start =
|
||||
reinterpret_cast<byte*>(HeapObject::FromAddress(to)) + header_size;
|
||||
|
||||
code_event_handler_(&event);
|
||||
}
|
||||
|
||||
|
||||
void Logger::IssueCodeRemovedEvent(Address from) {
|
||||
Code* from_code = Code::cast(HeapObject::FromAddress(from));
|
||||
|
||||
JitCodeEvent event;
|
||||
event.type = JitCodeEvent::CODE_REMOVED;
|
||||
event.code_start = from_code->instruction_start();
|
||||
event.code_len = from_code->instruction_size();
|
||||
|
||||
code_event_handler_(&event);
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_EVENT(ignore1, name) name,
|
||||
static const char* const kLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
|
||||
LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)
|
||||
@ -864,13 +911,17 @@ void Logger::SetterCallbackEvent(String* name, Address entry_point) {
|
||||
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
Code* code,
|
||||
const char* comment) {
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled()) {
|
||||
if (!is_logging_code_events()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
|
||||
name_buffer_->Reset();
|
||||
name_buffer_->AppendBytes(kLogEventsNames[tag]);
|
||||
name_buffer_->AppendByte(':');
|
||||
name_buffer_->AppendBytes(comment);
|
||||
}
|
||||
if (code_event_handler_ != NULL) {
|
||||
IssueCodeAddedEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) {
|
||||
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
@ -899,13 +950,17 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
Code* code,
|
||||
String* name) {
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled()) {
|
||||
if (!is_logging_code_events()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
|
||||
name_buffer_->Reset();
|
||||
name_buffer_->AppendBytes(kLogEventsNames[tag]);
|
||||
name_buffer_->AppendByte(':');
|
||||
name_buffer_->AppendString(name);
|
||||
}
|
||||
if (code_event_handler_ != NULL) {
|
||||
IssueCodeAddedEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) {
|
||||
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
@ -940,14 +995,18 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
Code* code,
|
||||
SharedFunctionInfo* shared,
|
||||
String* name) {
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled()) {
|
||||
if (!is_logging_code_events()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
|
||||
name_buffer_->Reset();
|
||||
name_buffer_->AppendBytes(kLogEventsNames[tag]);
|
||||
name_buffer_->AppendByte(':');
|
||||
name_buffer_->AppendBytes(ComputeMarker(code));
|
||||
name_buffer_->AppendString(name);
|
||||
}
|
||||
if (code_event_handler_ != NULL) {
|
||||
IssueCodeAddedEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) {
|
||||
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
@ -981,8 +1040,8 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
Code* code,
|
||||
SharedFunctionInfo* shared,
|
||||
String* source, int line) {
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled()) {
|
||||
if (!is_logging_code_events()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
|
||||
name_buffer_->Reset();
|
||||
name_buffer_->AppendBytes(kLogEventsNames[tag]);
|
||||
name_buffer_->AppendByte(':');
|
||||
@ -993,6 +1052,10 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
name_buffer_->AppendByte(':');
|
||||
name_buffer_->AppendInt(line);
|
||||
}
|
||||
if (code_event_handler_ != NULL) {
|
||||
IssueCodeAddedEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) {
|
||||
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
@ -1022,13 +1085,17 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
|
||||
|
||||
void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled()) {
|
||||
if (!is_logging_code_events()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
|
||||
name_buffer_->Reset();
|
||||
name_buffer_->AppendBytes(kLogEventsNames[tag]);
|
||||
name_buffer_->AppendByte(':');
|
||||
name_buffer_->AppendInt(args_count);
|
||||
}
|
||||
if (code_event_handler_ != NULL) {
|
||||
IssueCodeAddedEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) {
|
||||
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
@ -1055,13 +1122,17 @@ void Logger::CodeMovingGCEvent() {
|
||||
|
||||
|
||||
void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled()) {
|
||||
if (!is_logging_code_events()) return;
|
||||
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
|
||||
name_buffer_->Reset();
|
||||
name_buffer_->AppendBytes(kLogEventsNames[REG_EXP_TAG]);
|
||||
name_buffer_->AppendByte(':');
|
||||
name_buffer_->AppendString(source);
|
||||
}
|
||||
if (code_event_handler_ != NULL) {
|
||||
IssueCodeAddedEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) {
|
||||
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
|
||||
}
|
||||
@ -1083,6 +1154,7 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
|
||||
|
||||
|
||||
void Logger::CodeMoveEvent(Address from, Address to) {
|
||||
if (code_event_handler_ != NULL) IssueCodeMovedEvent(from, to);
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) LowLevelCodeMoveEvent(from, to);
|
||||
if (Serializer::enabled() && address_to_name_map_ != NULL) {
|
||||
@ -1093,6 +1165,7 @@ void Logger::CodeMoveEvent(Address from, Address to) {
|
||||
|
||||
|
||||
void Logger::CodeDeleteEvent(Address from) {
|
||||
if (code_event_handler_ != NULL) IssueCodeRemovedEvent(from);
|
||||
if (!log_->IsEnabled()) return;
|
||||
if (FLAG_ll_prof) LowLevelCodeDeleteEvent(from);
|
||||
if (Serializer::enabled() && address_to_name_map_ != NULL) {
|
||||
@ -1392,7 +1465,7 @@ static int EnumerateCompiledFunctions(Handle<SharedFunctionInfo>* sfis,
|
||||
|
||||
|
||||
void Logger::LogCodeObject(Object* object) {
|
||||
if (FLAG_log_code || FLAG_ll_prof) {
|
||||
if (FLAG_log_code || FLAG_ll_prof || is_logging_code_events()) {
|
||||
Code* code_object = Code::cast(object);
|
||||
LogEventsAndTags tag = Logger::STUB_TAG;
|
||||
const char* description = "Unknown code from the snapshot";
|
||||
@ -1676,6 +1749,18 @@ bool Logger::SetUp() {
|
||||
}
|
||||
|
||||
|
||||
void Logger::SetCodeEventHandler(uint32_t options,
|
||||
JitCodeEventHandler event_handler) {
|
||||
code_event_handler_ = event_handler;
|
||||
|
||||
if (code_event_handler_ != NULL && (options & kJitCodeEventEnumExisting)) {
|
||||
HandleScope scope;
|
||||
LogCodeObjects();
|
||||
LogCompiledFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sampler* Logger::sampler() {
|
||||
return ticker_;
|
||||
}
|
||||
|
25
src/log.h
25
src/log.h
@ -86,6 +86,15 @@ class Ticker;
|
||||
logger->Call; \
|
||||
} while (false)
|
||||
|
||||
#define LOG_CODE_EVENT(isolate, Call) \
|
||||
do { \
|
||||
v8::internal::Logger* logger = \
|
||||
(isolate)->logger(); \
|
||||
if (logger->is_logging_code_events()) \
|
||||
logger->Call; \
|
||||
} while (false)
|
||||
|
||||
|
||||
#define LOG_EVENTS_AND_TAGS_LIST(V) \
|
||||
V(CODE_CREATION_EVENT, "code-creation") \
|
||||
V(CODE_MOVE_EVENT, "code-move") \
|
||||
@ -151,6 +160,10 @@ class Logger {
|
||||
// Acquires resources for logging if the right flags are set.
|
||||
bool SetUp();
|
||||
|
||||
// Sets the current code event handler.
|
||||
void SetCodeEventHandler(uint32_t options,
|
||||
JitCodeEventHandler event_handler);
|
||||
|
||||
void EnsureTickerStarted();
|
||||
void EnsureTickerStopped();
|
||||
|
||||
@ -274,6 +287,10 @@ class Logger {
|
||||
return logging_nesting_ > 0;
|
||||
}
|
||||
|
||||
bool is_logging_code_events() {
|
||||
return is_logging() || code_event_handler_ != NULL;
|
||||
}
|
||||
|
||||
// Pause/Resume collection of profiling data.
|
||||
// When data collection is paused, CPU Tick events are discarded until
|
||||
// data collection is Resumed.
|
||||
@ -312,6 +329,11 @@ class Logger {
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
// Issue code notifications.
|
||||
void IssueCodeAddedEvent(Code* code, const char* name, size_t name_len);
|
||||
void IssueCodeMovedEvent(Address from, Address to);
|
||||
void IssueCodeRemovedEvent(Address from);
|
||||
|
||||
// Emits the profiler's first message.
|
||||
void ProfilerBeginEvent();
|
||||
|
||||
@ -413,6 +435,9 @@ class Logger {
|
||||
// 'true' between SetUp() and TearDown().
|
||||
bool is_initialized_;
|
||||
|
||||
// The code event handler - if any.
|
||||
JitCodeEventHandler code_event_handler_;
|
||||
|
||||
// Support for 'incremental addresses' in compressed logs:
|
||||
// LogMessageBuilder::AppendAddress(Address addr)
|
||||
Address last_address_;
|
||||
|
@ -2269,7 +2269,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
|
||||
target->set_literals(*literals);
|
||||
target->set_next_function_link(isolate->heap()->undefined_value());
|
||||
|
||||
if (isolate->logger()->is_logging() || CpuProfiler::is_profiling(isolate)) {
|
||||
if (isolate->logger()->is_logging_code_events() ||
|
||||
CpuProfiler::is_profiling(isolate)) {
|
||||
isolate->logger()->LogExistingFunction(
|
||||
source_shared, Handle<Code>(source_shared->code()));
|
||||
}
|
||||
|
@ -679,29 +679,35 @@ HeapObject* Deserializer::GetAddressFromStart(int space) {
|
||||
void Deserializer::Deserialize() {
|
||||
isolate_ = Isolate::Current();
|
||||
ASSERT(isolate_ != NULL);
|
||||
// Don't GC while deserializing - just expand the heap.
|
||||
AlwaysAllocateScope always_allocate;
|
||||
// Don't use the free lists while deserializing.
|
||||
LinearAllocationScope allocate_linearly;
|
||||
// No active threads.
|
||||
ASSERT_EQ(NULL, isolate_->thread_manager()->FirstThreadStateInUse());
|
||||
// No active handles.
|
||||
ASSERT(isolate_->handle_scope_implementer()->blocks()->is_empty());
|
||||
ASSERT_EQ(NULL, external_reference_decoder_);
|
||||
external_reference_decoder_ = new ExternalReferenceDecoder();
|
||||
isolate_->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
|
||||
isolate_->heap()->IterateWeakRoots(this, VISIT_ALL);
|
||||
{
|
||||
// Don't GC while deserializing - just expand the heap.
|
||||
AlwaysAllocateScope always_allocate;
|
||||
// Don't use the free lists while deserializing.
|
||||
LinearAllocationScope allocate_linearly;
|
||||
// No active threads.
|
||||
ASSERT_EQ(NULL, isolate_->thread_manager()->FirstThreadStateInUse());
|
||||
// No active handles.
|
||||
ASSERT(isolate_->handle_scope_implementer()->blocks()->is_empty());
|
||||
ASSERT_EQ(NULL, external_reference_decoder_);
|
||||
external_reference_decoder_ = new ExternalReferenceDecoder();
|
||||
isolate_->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
|
||||
isolate_->heap()->IterateWeakRoots(this, VISIT_ALL);
|
||||
|
||||
isolate_->heap()->set_native_contexts_list(
|
||||
isolate_->heap()->undefined_value());
|
||||
isolate_->heap()->set_native_contexts_list(
|
||||
isolate_->heap()->undefined_value());
|
||||
|
||||
// Update data pointers to the external strings containing natives sources.
|
||||
for (int i = 0; i < Natives::GetBuiltinsCount(); i++) {
|
||||
Object* source = isolate_->heap()->natives_source_cache()->get(i);
|
||||
if (!source->IsUndefined()) {
|
||||
ExternalAsciiString::cast(source)->update_data_cache();
|
||||
// Update data pointers to the external strings containing natives sources.
|
||||
for (int i = 0; i < Natives::GetBuiltinsCount(); i++) {
|
||||
Object* source = isolate_->heap()->natives_source_cache()->get(i);
|
||||
if (!source->IsUndefined()) {
|
||||
ExternalAsciiString::cast(source)->update_data_cache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue code events for newly deserialized code objects.
|
||||
LOG_CODE_EVENT(isolate_, LogCodeObjects());
|
||||
LOG_CODE_EVENT(isolate_, LogCompiledFunctions());
|
||||
}
|
||||
|
||||
|
||||
@ -714,7 +720,17 @@ void Deserializer::DeserializePartial(Object** root) {
|
||||
if (external_reference_decoder_ == NULL) {
|
||||
external_reference_decoder_ = new ExternalReferenceDecoder();
|
||||
}
|
||||
|
||||
// Keep track of the code space start and end pointers in case new
|
||||
// code objects were unserialized
|
||||
OldSpace* code_space = isolate_->heap()->code_space();
|
||||
Address start_address = code_space->top();
|
||||
VisitPointer(root);
|
||||
|
||||
// There's no code deserialized here. If this assert fires
|
||||
// then that's changed and logging should be added to notify
|
||||
// the profiler et al of the new code.
|
||||
CHECK_EQ(start_address, code_space->top());
|
||||
}
|
||||
|
||||
|
||||
|
@ -10981,6 +10981,8 @@ static void entry_hook(uintptr_t function,
|
||||
++foo_count;
|
||||
|
||||
// TODO(siggi): Verify return_addr_location.
|
||||
// This can be done by capturing JitCodeEvents, but requires an ordered
|
||||
// collection.
|
||||
}
|
||||
|
||||
|
||||
@ -11066,6 +11068,194 @@ TEST(SetFunctionEntryHook) {
|
||||
}
|
||||
|
||||
|
||||
static i::HashMap* code_map = NULL;
|
||||
static int saw_bar = 0;
|
||||
static int move_events = 0;
|
||||
|
||||
|
||||
static bool FunctionNameIs(const char* expected,
|
||||
const v8::JitCodeEvent* event) {
|
||||
// Log lines for functions are of the general form:
|
||||
// "LazyCompile:<type><function_name>", where the type is one of
|
||||
// "*", "~" or "".
|
||||
static const char kPreamble[] = "LazyCompile:";
|
||||
static size_t kPreambleLen = sizeof(kPreamble) - 1;
|
||||
|
||||
if (event->name.len < sizeof(kPreamble) - 1 ||
|
||||
strncmp(kPreamble, event->name.str, kPreambleLen) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* tail = event->name.str + kPreambleLen;
|
||||
size_t tail_len = event->name.len - kPreambleLen;
|
||||
size_t expected_len = strlen(expected);
|
||||
if (tail_len == expected_len + 1) {
|
||||
if (*tail == '*' || *tail == '~') {
|
||||
--tail_len;
|
||||
++tail;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tail_len != expected_len)
|
||||
return false;
|
||||
|
||||
return strncmp(tail, expected, expected_len) == 0;
|
||||
}
|
||||
|
||||
|
||||
static void event_handler(const v8::JitCodeEvent* event) {
|
||||
CHECK(event != NULL);
|
||||
CHECK(code_map != NULL);
|
||||
|
||||
switch (event->type) {
|
||||
case v8::JitCodeEvent::CODE_ADDED: {
|
||||
CHECK(event->code_start != NULL);
|
||||
CHECK_NE(0, static_cast<int>(event->code_len));
|
||||
CHECK(event->name.str != NULL);
|
||||
i::HashMap::Entry* entry =
|
||||
code_map->Lookup(event->code_start,
|
||||
i::ComputePointerHash(event->code_start),
|
||||
true);
|
||||
entry->value = reinterpret_cast<void*>(event->code_len);
|
||||
|
||||
if (FunctionNameIs("bar", event)) {
|
||||
++saw_bar;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case v8::JitCodeEvent::CODE_MOVED: {
|
||||
++move_events;
|
||||
|
||||
uint32_t hash = i::ComputePointerHash(event->code_start);
|
||||
// We should never see code move that we haven't seen before.
|
||||
i::HashMap::Entry* entry =
|
||||
code_map->Lookup(event->code_start, hash, false);
|
||||
CHECK(entry != NULL);
|
||||
CHECK_EQ(reinterpret_cast<void*>(event->code_len), entry->value);
|
||||
code_map->Remove(event->code_start, hash);
|
||||
|
||||
entry = code_map->Lookup(event->new_code_start,
|
||||
i::ComputePointerHash(event->new_code_start),
|
||||
true);
|
||||
CHECK(entry != NULL);
|
||||
entry->value = reinterpret_cast<void*>(event->code_len);
|
||||
}
|
||||
break;
|
||||
|
||||
case v8::JitCodeEvent::CODE_REMOVED:
|
||||
// Object/code removal events are currently not dispatched from the GC.
|
||||
CHECK(false);
|
||||
break;
|
||||
default:
|
||||
// Impossible event.
|
||||
CHECK(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implemented in the test-alloc.cc test suite.
|
||||
void SimulateFullSpace(i::PagedSpace* space);
|
||||
|
||||
|
||||
static bool MatchPointers(void* key1, void* key2) {
|
||||
return key1 == key2;
|
||||
}
|
||||
|
||||
|
||||
TEST(SetJitCodeEventHandler) {
|
||||
const char* script =
|
||||
"function bar() {"
|
||||
" var sum = 0;"
|
||||
" for (i = 0; i < 100; ++i)"
|
||||
" sum = foo(i);"
|
||||
" return sum;"
|
||||
"}"
|
||||
"function foo(i) { return i * i; };"
|
||||
"bar();";
|
||||
|
||||
// Run this test in a new isolate to make sure we don't
|
||||
// have remnants of state from other code.
|
||||
v8::Isolate* isolate = v8::Isolate::New();
|
||||
isolate->Enter();
|
||||
|
||||
{
|
||||
i::HashMap code(MatchPointers);
|
||||
code_map = &code;
|
||||
|
||||
saw_bar = 0;
|
||||
move_events = 0;
|
||||
|
||||
i::FLAG_stress_compaction = true;
|
||||
V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, event_handler);
|
||||
|
||||
v8::HandleScope scope;
|
||||
// Generate new code objects sparsely distributed across several
|
||||
// different fragmented code-space pages.
|
||||
const int kIterations = 10;
|
||||
for (int i = 0; i < kIterations; ++i) {
|
||||
LocalContext env;
|
||||
|
||||
v8::Handle<v8::Script> compiled_script;
|
||||
{
|
||||
i::AlwaysAllocateScope always_allocate;
|
||||
SimulateFullSpace(HEAP->code_space());
|
||||
compiled_script = v8_compile(script);
|
||||
}
|
||||
compiled_script->Run();
|
||||
|
||||
// Clear the compilation cache to get more wastage.
|
||||
ISOLATE->compilation_cache()->Clear();
|
||||
}
|
||||
|
||||
// Force code movement.
|
||||
HEAP->CollectAllAvailableGarbage("TestSetJitCodeEventHandler");
|
||||
|
||||
CHECK_LE(kIterations, saw_bar);
|
||||
CHECK_NE(0, move_events);
|
||||
|
||||
code_map = NULL;
|
||||
V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
|
||||
}
|
||||
|
||||
isolate->Exit();
|
||||
isolate->Dispose();
|
||||
|
||||
// Do this in a new isolate.
|
||||
isolate = v8::Isolate::New();
|
||||
isolate->Enter();
|
||||
|
||||
// Verify that we get callbacks for existing code objects when we
|
||||
// request enumeration of existing code.
|
||||
{
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
CompileRun(script);
|
||||
|
||||
// Now get code through initial iteration.
|
||||
i::HashMap code(MatchPointers);
|
||||
code_map = &code;
|
||||
|
||||
V8::SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting, event_handler);
|
||||
V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
|
||||
|
||||
code_map = NULL;
|
||||
|
||||
// We expect that we got some events. Note that if we could get code removal
|
||||
// notifications, we could compare two collections, one created by listening
|
||||
// from the time of creation of an isolate, and the other by subscribing
|
||||
// with EnumExisting.
|
||||
CHECK_NE(0, code.occupancy());
|
||||
}
|
||||
|
||||
isolate->Exit();
|
||||
isolate->Dispose();
|
||||
}
|
||||
|
||||
|
||||
static int64_t cast(intptr_t x) { return static_cast<int64_t>(x); }
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user