Revision: 12401
Author: [email protected]
Date: Tue Aug 28 07:43:28 2012
Log: 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 <[email protected]>.
http://code.google.com/p/v8/source/detail?r=12401
Modified:
/branches/bleeding_edge/include/v8.h
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/compiler.cc
/branches/bleeding_edge/src/cpu-profiler.h
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/isolate.cc
/branches/bleeding_edge/src/log.cc
/branches/bleeding_edge/src/log.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/serialize.cc
/branches/bleeding_edge/test/cctest/test-api.cc
=======================================
--- /branches/bleeding_edge/include/v8.h Mon Aug 27 12:47:02 2012
+++ /branches/bleeding_edge/include/v8.h Tue Aug 28 07:43:28 2012
@@ -2944,6 +2944,57 @@
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 @@
*/
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
=======================================
--- /branches/bleeding_edge/src/api.cc Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/api.cc Tue Aug 28 07:43:28 2012
@@ -4261,6 +4261,15 @@
bool v8::V8::SetFunctionEntryHook(FunctionEntryHook entry_hook) {
return i::ProfileEntryHookStub::SetFunctionEntryHook(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() {
=======================================
--- /branches/bleeding_edge/src/compiler.cc Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/compiler.cc Tue Aug 28 07:43:28 2012
@@ -1019,7 +1019,7 @@
// 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();
=======================================
--- /branches/bleeding_edge/src/cpu-profiler.h Mon Aug 27 12:47:02 2012
+++ /branches/bleeding_edge/src/cpu-profiler.h Tue Aug 28 07:43:28 2012
@@ -188,7 +188,7 @@
#define PROFILE(isolate, Call) \
- LOG(isolate, Call); \
+ LOG_CODE_EVENT(isolate, Call); \
do { \
if (v8::internal::CpuProfiler::is_profiling(isolate)) { \
v8::internal::CpuProfiler::Call; \
=======================================
--- /branches/bleeding_edge/src/heap.cc Tue Aug 28 07:20:50 2012
+++ /branches/bleeding_edge/src/heap.cc Tue Aug 28 07:43:28 2012
@@ -1741,7 +1741,7 @@
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(
=======================================
--- /branches/bleeding_edge/src/isolate.cc Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/isolate.cc Tue Aug 28 07:43:28 2012
@@ -1908,7 +1908,8 @@
// 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());
=======================================
--- /branches/bleeding_edge/src/log.cc Mon Aug 27 12:47:02 2012
+++ /branches/bleeding_edge/src/log.cc Tue Aug 28 07:43:28 2012
@@ -526,6 +526,7 @@
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),
@@ -539,6 +540,52 @@
delete name_buffer_;
delete log_;
}
+
+
+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,
@@ -864,13 +911,17 @@
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,
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 @@
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 @@
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 @@
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, 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::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::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::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 @@
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";
@@ -1674,6 +1747,18 @@
return true;
}
+
+
+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() {
=======================================
--- /branches/bleeding_edge/src/log.h Mon Aug 27 12:47:02 2012
+++ /branches/bleeding_edge/src/log.h Tue Aug 28 07:43:28 2012
@@ -86,6 +86,15 @@
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 @@
// 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();
@@ -273,6 +286,10 @@
bool is_logging() {
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
@@ -312,6 +329,11 @@
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 @@
// '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_;
=======================================
--- /branches/bleeding_edge/src/runtime.cc Tue Aug 28 04:25:08 2012
+++ /branches/bleeding_edge/src/runtime.cc Tue Aug 28 07:43:28 2012
@@ -2269,7 +2269,8 @@
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()));
}
=======================================
--- /branches/bleeding_edge/src/serialize.cc Mon Aug 27 12:47:02 2012
+++ /branches/bleeding_edge/src/serialize.cc Tue Aug 28 07:43:28 2012
@@ -679,29 +679,35 @@
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 @@
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());
}
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Mon Aug 27 12:47:02 2012
+++ /branches/bleeding_edge/test/cctest/test-api.cc Tue Aug 28 07:43:28 2012
@@ -10981,6 +10981,8 @@
++foo_count;
// TODO(siggi): Verify return_addr_location.
+ // This can be done by capturing JitCodeEvents, but requires an
ordered
+ // collection.
}
@@ -11064,6 +11066,194 @@
CHECK_EQ(0u, bar_count);
CHECK_EQ(0u, foo_count);
}
+
+
+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); }
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev