ReturnAddressLocationResolver return_address_resolver);
/**
- * Deprecated, use the variant with the Isolate parameter below instead.
- */
- V8_DEPRECATED(static bool SetFunctionEntryHook(FunctionEntryHook entry_hook));
-
- /**
* Allows the host application to provide the address of a function that's
* invoked on entry to every V8-generated function.
* Note that \p entry_hook is invoked at the very start of each
* generated function.
*
- * \param isolate the isolate to operate on.
* \param entry_hook a function that will be invoked on entry to every
* V8-generated function.
* \returns true on success on supported platforms, false on failure.
- * \note Setting an entry hook can only be done very early in an isolates
- * lifetime, and once set, the entry hook cannot be revoked.
+ * \note Setting a new entry hook function when one is already active will
+ * fail.
*/
- static bool SetFunctionEntryHook(Isolate* isolate,
- FunctionEntryHook entry_hook);
+ static bool SetFunctionEntryHook(FunctionEntryHook entry_hook);
/**
* Allows the host application to provide the address of a function that is
// --- S t a t i c s ---
-static bool InitializeHelper(i::Isolate* isolate) {
- // If the isolate has a function entry hook, it needs to re-build all its
- // code stubs with entry hooks embedded, so let's deserialize a snapshot.
- if (isolate == NULL || isolate->function_entry_hook() == NULL) {
- if (i::Snapshot::Initialize())
- return true;
- }
+static bool InitializeHelper() {
+ if (i::Snapshot::Initialize()) return true;
return i::V8::Initialize(NULL);
}
if (isolate->IsInitialized()) return true;
}
ASSERT(isolate == i::Isolate::Current());
- return ApiCheck(InitializeHelper(isolate), location, "Error initializing V8");
+ return ApiCheck(InitializeHelper(), location, "Error initializing V8");
}
// Some initializing API functions are called early and may be
if (isolate != NULL && isolate->IsInitialized()) {
return true;
}
- return InitializeHelper(isolate);
+ return InitializeHelper();
}
bool v8::V8::SetFunctionEntryHook(FunctionEntryHook entry_hook) {
- return SetFunctionEntryHook(Isolate::GetCurrent(), entry_hook);
-}
-
-
-bool v8::V8::SetFunctionEntryHook(Isolate* ext_isolate,
- FunctionEntryHook entry_hook) {
- ASSERT(ext_isolate != NULL);
- ASSERT(entry_hook != NULL);
-
- i::Isolate* isolate = reinterpret_cast<i::Isolate*>(ext_isolate);
-
- // The entry hook can only be set before the Isolate is initialized, as
- // otherwise the Isolate's code stubs generated at initialization won't
- // contain entry hooks.
- if (isolate->IsInitialized())
- return false;
-
- // Setting an entry hook is a one-way operation, once set, it cannot be
- // changed or unset.
- if (isolate->function_entry_hook() != NULL)
- return false;
-
- isolate->set_function_entry_hook(entry_hook);
- return true;
+ return i::ProfileEntryHookStub::SetFunctionEntryHook(entry_hook);
}
// r3: argc
// r4: argv
// r5-r7, cp may be clobbered
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
// Clear the context before we push it when entering the internal frame.
__ mov(cp, Operand::Zero());
// sp: stack pointer (restored as callee's sp after C call)
// cp: current context (C callee-saved)
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// Result returned in r0 or r0+r1 by default.
// NOTE: Invocations of builtins may return failure objects
Label invoke, handler_entry, exit;
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// Called from C, so do not pop argc and args on exit (preserve sp)
// No need to save register-passed args
// Save callee-saved registers (incl. cp and fp), sp, and lr
void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
- if (masm->isolate()->function_entry_hook() != NULL) {
+ if (entry_hook_ != NULL) {
PredictableCodeSizeScope predictable(masm, 4 * Assembler::kInstrSize);
- AllowStubCallsScope allow_stub_calls(masm, true);
ProfileEntryHookStub stub;
__ push(lr);
__ CallStub(&stub);
const int32_t kReturnAddressDistanceFromFunctionStart =
3 * Assembler::kInstrSize;
- // This should contain all kCallerSaved registers.
- const RegList kSavedRegs =
- 1 << 0 | // r0
- 1 << 1 | // r1
- 1 << 2 | // r2
- 1 << 3 | // r3
- 1 << 5 | // r5
- 1 << 9; // r9
- // We also save lr, so the count here is one higher than the mask indicates.
- const int32_t kNumSavedRegs = 7;
-
- ASSERT((kCallerSaved & kSavedRegs) == kCallerSaved);
-
- // Save all caller-save registers as this may be called from anywhere.
- __ stm(db_w, sp, kSavedRegs | lr.bit());
+ // Save live volatile registers.
+ __ Push(lr, r5, r1);
+ const int32_t kNumSavedRegs = 3;
// Compute the function's address for the first argument.
__ sub(r0, lr, Operand(kReturnAddressDistanceFromFunctionStart));
}
#if defined(V8_HOST_ARCH_ARM)
- __ mov(ip, FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
- RelocInfo::NONE));
+ __ mov(ip, Operand(reinterpret_cast<int32_t>(&entry_hook_)));
+ __ ldr(ip, MemOperand(ip));
#else
// Under the simulator we need to indirect the entry hook through a
// trampoline function at a known address.
- ApiFunction dispatcher(FUNCTION_ADDR(EntryHookTrampoline));
+ Address trampoline_address = reinterpret_cast<Address>(
+ reinterpret_cast<intptr_t>(EntryHookTrampoline));
+ ApiFunction dispatcher(trampoline_address);
__ mov(ip, Operand(ExternalReference(&dispatcher,
ExternalReference::BUILTIN_CALL,
masm->isolate())));
__ mov(sp, r5);
}
- // Also pop pc to get Ret(0).
- __ ldm(ia_w, sp, kSavedRegs | pc.bit());
+ __ Pop(lr, r5, r1);
+ __ Ret();
}
StackLimitCheck check(isolate);
if (check.HasOverflowed()) return;
- // We can only de-serialize a context if the isolate was initialized from
- // a snapshot. Otherwise we have to build the context from scratch.
- if (isolate->initialized_from_snapshot()) {
- native_context_ = Snapshot::NewContextFromSnapshot();
- } else {
- native_context_ = Handle<Context>();
- }
-
+ native_context_ = Snapshot::NewContextFromSnapshot();
if (!native_context().is_null()) {
AddToWeakNativeContextList(*native_context());
isolate->set_context(*native_context());
}
+FunctionEntryHook ProfileEntryHookStub::entry_hook_ = NULL;
+
+
void ProfileEntryHookStub::EntryHookTrampoline(intptr_t function,
intptr_t stack_pointer) {
- FunctionEntryHook entry_hook = Isolate::Current()->function_entry_hook();
- ASSERT(entry_hook != NULL);
- entry_hook(function, stack_pointer);
+ if (entry_hook_ != NULL)
+ entry_hook_(function, stack_pointer);
+}
+
+
+bool ProfileEntryHookStub::SetFunctionEntryHook(FunctionEntryHook entry_hook) {
+ // We don't allow setting a new entry hook over one that's
+ // already active, as the hooks won't stack.
+ if (entry_hook != 0 && entry_hook_ != 0)
+ return false;
+
+ entry_hook_ = entry_hook;
+ return true;
}
// Generates a call to the entry hook if it's enabled.
static void MaybeCallEntryHook(MacroAssembler* masm);
+ // Sets or unsets the entry hook function. Returns true on success,
+ // false on an attempt to replace a non-NULL entry hook with another
+ // non-NULL hook.
+ static bool SetFunctionEntryHook(FunctionEntryHook entry_hook);
+
+ static bool HasEntryHook() { return entry_hook_ != NULL; }
+
private:
static void EntryHookTrampoline(intptr_t function,
intptr_t stack_pointer);
void Generate(MacroAssembler* masm);
+ // The current function entry hook.
+ static FunctionEntryHook entry_hook_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileEntryHookStub);
};
}
-inline Address* StackFrame::ResolveReturnAddressLocation(Address* pc_address) {
- if (return_address_location_resolver_ == NULL) {
- return pc_address;
- } else {
- return reinterpret_cast<Address*>(
- return_address_location_resolver_(
- reinterpret_cast<uintptr_t>(pc_address)));
- }
-}
-
-
inline EntryFrame::EntryFrame(StackFrameIteratorBase* iterator)
: StackFrame(iterator) {
}
namespace internal {
-ReturnAddressLocationResolver
- StackFrame::return_address_location_resolver_ = NULL;
+static ReturnAddressLocationResolver return_address_location_resolver = NULL;
+
+
+// Resolves pc_address through the resolution address function if one is set.
+static inline Address* ResolveReturnAddressLocation(Address* pc_address) {
+ if (return_address_location_resolver == NULL) {
+ return pc_address;
+ } else {
+ return reinterpret_cast<Address*>(
+ return_address_location_resolver(
+ reinterpret_cast<uintptr_t>(pc_address)));
+ }
+}
// Iterator that supports traversing the stack handlers of a
ASSERT(fp != NULL);
state.fp = fp;
state.sp = sp;
- state.pc_address = StackFrame::ResolveReturnAddressLocation(
+ state.pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp)));
type = StackFrame::ComputeType(this, &state);
} else {
void StackFrame::SetReturnAddressLocationResolver(
ReturnAddressLocationResolver resolver) {
- ASSERT(return_address_location_resolver_ == NULL);
- return_address_location_resolver_ = resolver;
+ ASSERT(return_address_location_resolver == NULL);
+ return_address_location_resolver = resolver;
}
static void SetReturnAddressLocationResolver(
ReturnAddressLocationResolver resolver);
- // Resolves pc_address through the resolution address function if one is set.
- static inline Address* ResolveReturnAddressLocation(Address* pc_address);
-
-
// Printing support.
enum PrintMode { OVERVIEW, DETAILS };
virtual void Print(StringStream* accumulator,
Isolate* isolate_;
State state_;
- static ReturnAddressLocationResolver return_address_location_resolver_;
-
// Fill in the state of the calling frame.
virtual void ComputeCallerState(State* state) const = 0;
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// Clear the context before we push it when entering the internal frame.
__ Set(esi, Immediate(0));
// esi: current context (C callee-saved)
// edi: JS function of the caller (C callee-saved)
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// NOTE: Invocations of builtins may return failure objects instead
// of a proper result. The builtin entry handles this by performing
// a garbage collection and retrying the builtin (twice).
Label invoke, handler_entry, exit;
Label not_outermost_js, not_outermost_js_2;
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// Set up frame.
__ push(ebp);
__ mov(ebp, esp);
void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
- if (masm->isolate()->function_entry_hook() != NULL) {
- // It's always safe to call the entry hook stub, as the hook itself
- // is not allowed to call back to V8.
- AllowStubCallsScope allow_stub_calls(masm, true);
-
+ if (entry_hook_ != NULL) {
ProfileEntryHookStub stub;
masm->CallStub(&stub);
}
void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
- // Save volatile registers.
- const int kNumSavedRegisters = 3;
- __ push(eax);
+ // Ecx is the only volatile register we must save.
+ const int kNumSavedRegisters = 1;
__ push(ecx);
- __ push(edx);
// Calculate and push the original stack pointer.
__ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
__ push(eax);
// Call the entry hook.
- ASSERT(masm->isolate()->function_entry_hook() != NULL);
- __ call(FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
- RelocInfo::RUNTIME_ENTRY);
+ int32_t hook_location = reinterpret_cast<int32_t>(&entry_hook_);
+ __ call(Operand(hook_location, RelocInfo::NONE32));
__ add(esp, Immediate(2 * kPointerSize));
// Restore ecx.
- __ pop(edx);
__ pop(ecx);
- __ pop(eax);
-
__ ret(0);
}
ASSERT(fp == frame->fp() && pc_address == frame->pc_address());
#endif
fp_ = fp;
- pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address);
+ pc_address_ = pc_address;
}
date_cache_(NULL),
code_stub_interface_descriptors_(NULL),
context_exit_happened_(false),
- initialized_from_snapshot_(false),
cpu_profiler_(NULL),
heap_profiler_(NULL),
- function_entry_hook_(NULL),
deferred_handles_head_(NULL),
optimizing_compiler_thread_(this),
marking_thread_(NULL),
ASSERT(Isolate::Current() == this);
TRACE_ISOLATE(init);
- if (function_entry_hook() != NULL) {
- // When function entry hooking is in effect, we have to create the code
- // stubs from scratch to get entry hooks, rather than loading the previously
- // generated stubs from disk.
- // If this assert fires, the initialization path has regressed.
- ASSERT(des == NULL);
- }
-
// The initialization process does not handle memory exhaustion.
DisallowAllocationFailure disallow_allocation_failure;
sweeper_thread_[i]->Start();
}
}
-
- initialized_from_snapshot_ = (des != NULL);
-
return true;
}
}
Context** context_address() { return &thread_local_top_.context_; }
- SaveContext* save_context() { return thread_local_top_.save_context_; }
+ SaveContext* save_context() {return thread_local_top_.save_context_; }
void set_save_context(SaveContext* save) {
thread_local_top_.save_context_ = save;
}
context_exit_happened_ = context_exit_happened;
}
- bool initialized_from_snapshot() { return initialized_from_snapshot_; }
-
double time_millis_since_init() {
return OS::TimeCurrentMillis() - time_millis_at_init_;
}
HStatistics* GetHStatistics();
HTracer* GetHTracer();
- FunctionEntryHook function_entry_hook() { return function_entry_hook_; }
- void set_function_entry_hook(FunctionEntryHook function_entry_hook) {
- function_entry_hook_ = function_entry_hook;
- }
-
private:
Isolate();
// that a context was recently exited.
bool context_exit_happened_;
- // True if this isolate was initialized from a snapshot.
- bool initialized_from_snapshot_;
-
// Time stamp at initialization.
double time_millis_at_init_;
#endif
CpuProfiler* cpu_profiler_;
HeapProfiler* heap_profiler_;
- FunctionEntryHook function_entry_hook_;
#define GLOBAL_BACKING_STORE(type, name, initialvalue) \
type name##_;
// Support for return-address rewriting profilers.
static void SetReturnAddressLocationResolver(
ReturnAddressLocationResolver resolver);
- // Support for entry hooking JITed code.
- static void SetFunctionEntryHook(FunctionEntryHook entry_hook);
// Random number generation support. Not cryptographically safe.
static uint32_t Random(Context* context);
// We use random numbers internally in memory allocation and in the
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// Expects five C++ function parameters.
// - Address entry (ignored)
// - JSFunction* function (
// this by performing a garbage collection and retrying the
// builtin once.
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
// Enter the exit frame that transitions from JavaScript to C++.
#ifdef _WIN64
int arg_stack_space = (result_size_ < 2 ? 2 : 4);
Label invoke, handler_entry, exit;
Label not_outermost_js, not_outermost_js_2;
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
{ // NOLINT. Scope block confuses linter.
MacroAssembler::NoRootArrayScope uninitialized_root_register(masm);
// Set up frame.
void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
- if (masm->isolate()->function_entry_hook() != NULL) {
- // It's always safe to call the entry hook stub, as the hook itself
- // is not allowed to call back to V8.
- AllowStubCallsScope allow_stub_calls(masm, true);
-
+ if (entry_hook_ != NULL) {
ProfileEntryHookStub stub;
masm->CallStub(&stub);
}
void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
- // This stub can be called from essentially anywhere, so it needs to save
- // all volatile and callee-save registers.
- const size_t kNumSavedRegisters = 2;
- __ push(arg_reg_1);
- __ push(arg_reg_2);
+ // Save volatile registers.
+ // Live registers at this point are the same as at the start of any
+ // JS function:
+ // o rdi: the JS function object being called (i.e. ourselves)
+ // o rsi: our context
+ // o rbp: our caller's frame pointer
+ // o rsp: stack pointer (pointing to return address)
+ // o rcx: rcx is zero for method calls and non-zero for function calls.
+#ifdef _WIN64
+ const int kNumSavedRegisters = 1;
+
+ __ push(rcx);
+#else
+ const int kNumSavedRegisters = 3;
+
+ __ push(rcx);
+ __ push(rdi);
+ __ push(rsi);
+#endif
// Calculate the original stack pointer and store it in the second arg.
- __ lea(arg_reg_2, Operand(rsp, (kNumSavedRegisters + 1) * kPointerSize));
+#ifdef _WIN64
+ __ lea(rdx, Operand(rsp, (kNumSavedRegisters + 1) * kPointerSize));
+#else
+ __ lea(rsi, Operand(rsp, (kNumSavedRegisters + 1) * kPointerSize));
+#endif
// Calculate the function address to the first arg.
- __ movq(arg_reg_1, Operand(rsp, kNumSavedRegisters * kPointerSize));
- __ subq(arg_reg_1, Immediate(Assembler::kShortCallInstructionLength));
-
- // Save the remainder of the volatile registers.
- masm->PushCallerSaved(kSaveFPRegs, arg_reg_1, arg_reg_2);
+#ifdef _WIN64
+ __ movq(rcx, Operand(rsp, kNumSavedRegisters * kPointerSize));
+ __ subq(rcx, Immediate(Assembler::kShortCallInstructionLength));
+#else
+ __ movq(rdi, Operand(rsp, kNumSavedRegisters * kPointerSize));
+ __ subq(rdi, Immediate(Assembler::kShortCallInstructionLength));
+#endif
// Call the entry hook function.
- __ movq(rax, FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
- RelocInfo::NONE64);
+ __ movq(rax, &entry_hook_, RelocInfo::NONE64);
+ __ movq(rax, Operand(rax, 0));
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(rax, kArgumentCount);
// Restore volatile regs.
- masm->PopCallerSaved(kSaveFPRegs, arg_reg_1, arg_reg_2);
- __ pop(arg_reg_2);
- __ pop(arg_reg_1);
+#ifdef _WIN64
+ __ pop(rcx);
+#else
+ __ pop(rsi);
+ __ pop(rdi);
+ __ pop(rcx);
+#endif
__ Ret();
}
#include <signal.h> // kill
#include <unistd.h> // getpid
#endif // WIN32
-#include <string>
-#include <map>
#include "v8.h"
}
-static bool MatchPointers(void* key1, void* key2) {
- return key1 == key2;
-}
-
-
-struct SymbolInfo {
- size_t id;
- size_t size;
- std::string name;
-};
-
-
-class SetFunctionEntryHookTest {
- public:
- SetFunctionEntryHookTest() {
- CHECK(instance_ == NULL);
- instance_ = this;
- }
- ~SetFunctionEntryHookTest() {
- CHECK(instance_ == this);
- instance_ = NULL;
- }
- void Reset() {
- symbols_.clear();
- symbol_locations_.clear();
- invocations_.clear();
- }
- void RunTest();
- void OnJitEvent(const v8::JitCodeEvent* event);
- static void JitEvent(const v8::JitCodeEvent* event) {
- CHECK(instance_ != NULL);
- instance_->OnJitEvent(event);
- }
-
- void OnEntryHook(uintptr_t function,
- uintptr_t return_addr_location);
- static void EntryHook(uintptr_t function,
- uintptr_t return_addr_location) {
- CHECK(instance_ != NULL);
- instance_->OnEntryHook(function, return_addr_location);
- }
-
- static void RuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
- CHECK(instance_ != NULL);
- args.GetReturnValue().Set(v8_num(42));
- }
- void RunLoopInNewEnv(v8::Isolate* isolate);
-
- // Records addr as location of symbol.
- void InsertSymbolAt(i::Address addr, SymbolInfo* symbol);
-
- // Finds the symbol containing addr
- SymbolInfo* FindSymbolForAddr(i::Address addr);
- // Returns the number of invocations where the caller name contains
- // \p caller_name and the function name contains \p function_name.
- size_t CountInvocations(const char* caller_name,
- const char* function_name);
-
- i::Handle<i::JSFunction> foo_func_;
- i::Handle<i::JSFunction> bar_func_;
-
- typedef std::map<size_t, SymbolInfo> SymbolMap;
- typedef std::map<i::Address, SymbolInfo*> SymbolLocationMap;
- typedef std::map<std::pair<SymbolInfo*, SymbolInfo*>, size_t> InvocationMap;
- SymbolMap symbols_;
- SymbolLocationMap symbol_locations_;
- InvocationMap invocations_;
-
- static SetFunctionEntryHookTest* instance_;
-};
-SetFunctionEntryHookTest* SetFunctionEntryHookTest::instance_ = NULL;
-
-
-// Returns true if addr is in the range [start, start+len).
-static bool Overlaps(i::Address start, size_t len, i::Address addr) {
- if (start <= addr && start + len > addr)
- return true;
-
- return false;
-}
-
-void SetFunctionEntryHookTest::InsertSymbolAt(i::Address addr,
- SymbolInfo* symbol) {
- // Insert the symbol at the new location.
- SymbolLocationMap::iterator it =
- symbol_locations_.insert(std::make_pair(addr, symbol)).first;
- // Now erase symbols to the left and right that overlap this one.
- while (it != symbol_locations_.begin()) {
- SymbolLocationMap::iterator left = it;
- --left;
- if (!Overlaps(left->first, left->second->size, addr))
- break;
- symbol_locations_.erase(left);
- }
-
- // Now erase symbols to the left and right that overlap this one.
- while (true) {
- SymbolLocationMap::iterator right = it;
- ++right;
- if (right == symbol_locations_.end())
- break;
- if (!Overlaps(addr, symbol->size, right->first))
- break;
- symbol_locations_.erase(right);
- }
-}
-
+static i::Handle<i::JSFunction>* foo_ptr = NULL;
+static int foo_entry_count = 0;
+static i::Handle<i::JSFunction>* bar_ptr = NULL;
+static int bar_entry_count = 0;
+static int bar_caller_count = 0;
-void SetFunctionEntryHookTest::OnJitEvent(const v8::JitCodeEvent* event) {
- 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);
- size_t symbol_id = symbols_.size();
-
- // Record the new symbol.
- SymbolInfo& info = symbols_[symbol_id];
- info.id = symbol_id;
- info.size = event->code_len;
- info.name.assign(event->name.str, event->name.str + event->name.len);
-
- // And record it's location.
- InsertSymbolAt(reinterpret_cast<i::Address>(event->code_start), &info);
- }
- break;
-
- case v8::JitCodeEvent::CODE_MOVED: {
- // We would like to never see code move that we haven't seen before,
- // but the code creation event does not happen until the line endings
- // have been calculated (this is so that we can report the line in the
- // script at which the function source is found, see
- // Compiler::RecordFunctionCompilation) and the line endings
- // calculations can cause a GC, which can move the newly created code
- // before its existence can be logged.
- SymbolLocationMap::iterator it(
- symbol_locations_.find(
- reinterpret_cast<i::Address>(event->code_start)));
- if (it != symbol_locations_.end()) {
- // Found a symbol at this location, move it.
- SymbolInfo* info = it->second;
- symbol_locations_.erase(it);
- InsertSymbolAt(reinterpret_cast<i::Address>(event->new_code_start),
- info);
- }
- }
- default:
- break;
- }
-}
-void SetFunctionEntryHookTest::OnEntryHook(
- uintptr_t function, uintptr_t return_addr_location) {
- // Get the function's code object.
- i::Code* function_code = i::Code::GetCodeFromTargetAddress(
+static void entry_hook(uintptr_t function,
+ uintptr_t return_addr_location) {
+ i::Code* code = i::Code::GetCodeFromTargetAddress(
reinterpret_cast<i::Address>(function));
- CHECK(function_code != NULL);
-
- // Then try and look up the caller's code object.
- i::Address caller = *reinterpret_cast<i::Address*>(return_addr_location);
-
- // Count the invocation.
- SymbolInfo* caller_symbol = FindSymbolForAddr(caller);
- SymbolInfo* function_symbol =
- FindSymbolForAddr(reinterpret_cast<i::Address>(function));
- ++invocations_[std::make_pair(caller_symbol, function_symbol)];
-
- if (!bar_func_.is_null() && function_code == bar_func_->code()) {
- // Check that we have a symbol for the "bar" function at the right location.
- SymbolLocationMap::iterator it(
- symbol_locations_.find(function_code->instruction_start()));
- CHECK(it != symbol_locations_.end());
- }
-
- if (!foo_func_.is_null() && function_code == foo_func_->code()) {
- // Check that we have a symbol for "foo" at the right location.
- SymbolLocationMap::iterator it(
- symbol_locations_.find(function_code->instruction_start()));
- CHECK(it != symbol_locations_.end());
- }
-}
+ CHECK(code != NULL);
+ if (bar_ptr != NULL && code == (*bar_ptr)->code())
+ ++bar_entry_count;
-SymbolInfo* SetFunctionEntryHookTest::FindSymbolForAddr(i::Address addr) {
- SymbolLocationMap::iterator it(symbol_locations_.lower_bound(addr));
- // Do we have a direct hit on a symbol?
- if (it != symbol_locations_.end()) {
- if (it->first == addr)
- return it->second;
- }
-
- // If not a direct hit, it'll have to be the previous symbol.
- if (it == symbol_locations_.begin())
- return NULL;
-
- --it;
- size_t offs = addr - it->first;
- if (offs < it->second->size)
- return it->second;
-
- return NULL;
-}
+ if (foo_ptr != NULL && code == (*foo_ptr)->code())
+ ++foo_entry_count;
+ // Let's check whether bar is the caller.
+ if (bar_ptr != NULL) {
+ const v8::internal::byte* caller =
+ *reinterpret_cast<v8::internal::byte**>(return_addr_location);
-size_t SetFunctionEntryHookTest::CountInvocations(
- const char* caller_name, const char* function_name) {
- InvocationMap::iterator it(invocations_.begin());
- size_t invocations = 0;
- for (; it != invocations_.end(); ++it) {
- SymbolInfo* caller = it->first.first;
- SymbolInfo* function = it->first.second;
-
- // Filter out non-matching functions.
- if (function_name != NULL) {
- if (function->name.find(function_name) == std::string::npos)
- continue;
+ if ((*bar_ptr)->code()->instruction_start() <= caller &&
+ (*bar_ptr)->code()->instruction_end() > caller) {
+ ++bar_caller_count;
}
-
- // Filter out non-matching callers.
- if (caller_name != NULL) {
- if (caller == NULL)
- continue;
- if (caller->name.find(caller_name) == std::string::npos)
- continue;
- }
-
- // It matches add the invocation count to the tally.
- invocations += it->second;
}
-
- return invocations;
}
-void SetFunctionEntryHookTest::RunLoopInNewEnv(v8::Isolate* isolate) {
+static void RunLoopInNewEnv() {
+ bar_ptr = NULL;
+ foo_ptr = NULL;
+
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope outer(isolate);
v8::Local<Context> env = Context::New(isolate);
env->Enter();
- Local<ObjectTemplate> t = ObjectTemplate::New();
- t->Set(v8_str("asdf"), v8::FunctionTemplate::New(RuntimeCallback));
- env->Global()->Set(v8_str("obj"), t->NewInstance());
-
const char* script =
- "function bar() {\n"
- " var sum = 0;\n"
- " for (i = 0; i < 100; ++i)\n"
- " sum = foo(i);\n"
- " return sum;\n"
- "}\n"
- "function foo(i) { return i * i; }\n"
- "// Invoke on the runtime function.\n"
- "obj.asdf()";
+ "function bar() {"
+ " var sum = 0;"
+ " for (i = 0; i < 100; ++i)"
+ " sum = foo(i);"
+ " return sum;"
+ "}"
+ "function foo(i) { return i * i; }";
CompileRun(script);
- bar_func_ = i::Handle<i::JSFunction>::cast(
+ i::Handle<i::JSFunction> bar =
+ i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar"))));
- ASSERT(!bar_func_.is_null());
+ ASSERT(*bar);
- foo_func_ =
+ i::Handle<i::JSFunction> foo =
i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo"))));
- ASSERT(!foo_func_.is_null());
+ ASSERT(*foo);
+
+ bar_ptr = &bar;
+ foo_ptr = &foo;
v8::Handle<v8::Value> value = CompileRun("bar();");
CHECK(value->IsNumber());
env->Exit();
}
-void SetFunctionEntryHookTest::RunTest() {
- // Work in a new isolate throughout.
- v8::Isolate* isolate = v8::Isolate::New();
-
- // Test setting the entry hook on the new isolate.
- CHECK(v8::V8::SetFunctionEntryHook(isolate, EntryHook));
-
- // Replacing the hook, once set should fail.
- CHECK_EQ(false, v8::V8::SetFunctionEntryHook(isolate, EntryHook));
-
- {
- v8::Isolate::Scope scope(isolate);
-
- v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, JitEvent);
-
- RunLoopInNewEnv(isolate);
-
- // Check the exepected invocation counts.
- CHECK_EQ(2, CountInvocations(NULL, "bar"));
- CHECK_EQ(200, CountInvocations("bar", "foo"));
- CHECK_EQ(200, CountInvocations(NULL, "foo"));
-
- // Verify that we have an entry hook on some specific stubs.
- CHECK_NE(0, CountInvocations(NULL, "CEntryStub"));
- CHECK_NE(0, CountInvocations(NULL, "JSEntryStub"));
- CHECK_NE(0, CountInvocations(NULL, "JSEntryTrampoline"));
- }
- isolate->Dispose();
-
- Reset();
-
- // Make sure a second isolate is unaffected by the previous entry hook.
- isolate = v8::Isolate::New();
- {
- v8::Isolate::Scope scope(isolate);
-
- // Reset the entry count to zero and set the entry hook.
- RunLoopInNewEnv(isolate);
-
- // We should record no invocations in this isolate.
- CHECK_EQ(0, invocations_.size());
- }
- // Since the isolate has been used, we shouldn't be able to set an entry
- // hook anymore.
- CHECK_EQ(false, v8::V8::SetFunctionEntryHook(isolate, EntryHook));
-
- isolate->Dispose();
-}
-
TEST(SetFunctionEntryHook) {
// FunctionEntryHook does not work well with experimental natives.
i::FLAG_allow_natives_syntax = true;
i::FLAG_use_inlining = false;
- SetFunctionEntryHookTest test;
- test.RunTest();
+ // Test setting and resetting the entry hook.
+ // Nulling it should always succeed.
+ CHECK(v8::V8::SetFunctionEntryHook(NULL));
+
+ CHECK(v8::V8::SetFunctionEntryHook(entry_hook));
+ // Setting a hook while one's active should fail.
+ CHECK_EQ(false, v8::V8::SetFunctionEntryHook(entry_hook));
+
+ CHECK(v8::V8::SetFunctionEntryHook(NULL));
+
+ // Reset the entry count to zero and set the entry hook.
+ bar_entry_count = 0;
+ bar_caller_count = 0;
+ foo_entry_count = 0;
+ CHECK(v8::V8::SetFunctionEntryHook(entry_hook));
+ RunLoopInNewEnv();
+
+ CHECK_EQ(2, bar_entry_count);
+ CHECK_EQ(200, bar_caller_count);
+ CHECK_EQ(200, foo_entry_count);
+
+ // Clear the entry hook and count.
+ bar_entry_count = 0;
+ bar_caller_count = 0;
+ foo_entry_count = 0;
+ v8::V8::SetFunctionEntryHook(NULL);
+
+ // Clear the compilation cache to make sure we don't reuse the
+ // functions from the previous invocation.
+ v8::internal::Isolate::Current()->compilation_cache()->Clear();
+
+ // Verify that entry hooking is now disabled.
+ RunLoopInNewEnv();
+ CHECK_EQ(0u, bar_entry_count);
+ CHECK_EQ(0u, bar_caller_count);
+ CHECK_EQ(0u, foo_entry_count);
}
}
+static bool MatchPointers(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+
TEST(SetJitCodeEventHandler) {
i::FLAG_stress_compaction = true;
i::FLAG_incremental_marking = false;