From 475131b2b9155064d5746724c1176208c109cfe9 Mon Sep 17 00:00:00 2001 From: "ulan@chromium.org" Date: Thu, 27 Jun 2013 15:31:06 +0000 Subject: [PATCH] Revert r15361 "Improved function entry hook coverage" because of ARM build error. R=siggi@chromium.org BUG= Review URL: https://chromiumcodereview.appspot.com/18062006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15365 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 13 +- src/api.cc | 38 +--- src/arm/builtins-arm.cc | 1 - src/arm/code-stubs-arm.cc | 37 ++-- src/bootstrapper.cc | 9 +- src/code-stubs.cc | 19 +- src/code-stubs.h | 10 + src/frames-inl.h | 11 -- src/frames.cc | 21 +- src/frames.h | 6 - src/ia32/builtins-ia32.cc | 2 - src/ia32/code-stubs-ia32.cc | 24 +-- src/ic.cc | 2 +- src/isolate.cc | 13 -- src/isolate.h | 13 +- src/v8.h | 2 - src/x64/builtins-x64.cc | 2 - src/x64/code-stubs-x64.cc | 66 ++++--- test/cctest/test-api.cc | 374 ++++++++---------------------------- 19 files changed, 191 insertions(+), 472 deletions(-) mode change 100644 => 100755 test/cctest/test-api.cc diff --git a/include/v8.h b/include/v8.h index cf01684729..2ccab5e383 100644 --- a/include/v8.h +++ b/include/v8.h @@ -4476,26 +4476,19 @@ class V8EXPORT V8 { static void SetReturnAddressLocationResolver( 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 diff --git a/src/api.cc b/src/api.cc index edffca18f4..014d82ee4f 100644 --- a/src/api.cc +++ b/src/api.cc @@ -300,13 +300,8 @@ static inline bool EmptyCheck(const char* location, const v8::Data* obj) { // --- 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); } @@ -318,7 +313,7 @@ static inline bool EnsureInitializedForIsolate(i::Isolate* isolate, 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 @@ -5216,7 +5211,7 @@ bool v8::V8::Initialize() { if (isolate != NULL && isolate->IsInitialized()) { return true; } - return InitializeHelper(isolate); + return InitializeHelper(); } @@ -5232,30 +5227,7 @@ void v8::V8::SetReturnAddressLocationResolver( 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(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); } diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 6b3caf3f0e..2641975255 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -717,7 +717,6 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // 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()); diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index e6292cd7da..9cdaa12083 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3181,8 +3181,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { // 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 @@ -3273,8 +3271,6 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { 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 @@ -7075,9 +7071,8 @@ void StubFailureTrampolineStub::Generate(MacroAssembler* masm) { 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); @@ -7091,21 +7086,9 @@ void ProfileEntryHookStub::Generate(MacroAssembler* masm) { 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)); @@ -7123,12 +7106,14 @@ void ProfileEntryHookStub::Generate(MacroAssembler* masm) { } #if defined(V8_HOST_ARCH_ARM) - __ mov(ip, FUNCTION_ADDR(masm->isolate()->function_entry_hook()), - RelocInfo::NONE)); + __ mov(ip, Operand(reinterpret_cast(&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
( + reinterpret_cast(EntryHookTrampoline)); + ApiFunction dispatcher(trampoline_address); __ mov(ip, Operand(ExternalReference(&dispatcher, ExternalReference::BUILTIN_CALL, masm->isolate()))); @@ -7140,8 +7125,8 @@ void ProfileEntryHookStub::Generate(MacroAssembler* masm) { __ mov(sp, r5); } - // Also pop pc to get Ret(0). - __ ldm(ia_w, sp, kSavedRegs | pc.bit()); + __ Pop(lr, r5, r1); + __ Ret(); } diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 7af44cb141..53dca036b9 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2575,14 +2575,7 @@ Genesis::Genesis(Isolate* isolate, 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(); - } - + native_context_ = Snapshot::NewContextFromSnapshot(); if (!native_context().is_null()) { AddToWeakNativeContextList(*native_context()); isolate->set_context(*native_context()); diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 2d94ec978e..5bec7e05f9 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -757,11 +757,24 @@ void StubFailureTrampolineStub::GenerateAheadOfTime(Isolate* isolate) { } +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; } diff --git a/src/code-stubs.h b/src/code-stubs.h index dab3688bd9..6b6ba79b44 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -2135,6 +2135,13 @@ class ProfileEntryHookStub : public PlatformCodeStub { // 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); @@ -2144,6 +2151,9 @@ class ProfileEntryHookStub : public PlatformCodeStub { void Generate(MacroAssembler* masm); + // The current function entry hook. + static FunctionEntryHook entry_hook_; + DISALLOW_COPY_AND_ASSIGN(ProfileEntryHookStub); }; diff --git a/src/frames-inl.h b/src/frames-inl.h index 8d10645d1d..bd652da5c4 100644 --- a/src/frames-inl.h +++ b/src/frames-inl.h @@ -136,17 +136,6 @@ inline Code* StackFrame::GetContainingCode(Isolate* isolate, Address pc) { } -inline Address* StackFrame::ResolveReturnAddressLocation(Address* pc_address) { - if (return_address_location_resolver_ == NULL) { - return pc_address; - } else { - return reinterpret_cast( - return_address_location_resolver_( - reinterpret_cast(pc_address))); - } -} - - inline EntryFrame::EntryFrame(StackFrameIteratorBase* iterator) : StackFrame(iterator) { } diff --git a/src/frames.cc b/src/frames.cc index edd5ddde40..0ca6991488 100644 --- a/src/frames.cc +++ b/src/frames.cc @@ -43,8 +43,19 @@ namespace v8 { 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( + return_address_location_resolver( + reinterpret_cast(pc_address))); + } +} // Iterator that supports traversing the stack handlers of a @@ -228,7 +239,7 @@ SafeStackFrameIterator::SafeStackFrameIterator( ASSERT(fp != NULL); state.fp = fp; state.sp = sp; - state.pc_address = StackFrame::ResolveReturnAddressLocation( + state.pc_address = ResolveReturnAddressLocation( reinterpret_cast(StandardFrame::ComputePCAddress(fp))); type = StackFrame::ComputeType(this, &state); } else { @@ -378,8 +389,8 @@ void StackFrame::IteratePc(ObjectVisitor* v, 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; } diff --git a/src/frames.h b/src/frames.h index 9ca218ac52..19e4d609b5 100644 --- a/src/frames.h +++ b/src/frames.h @@ -297,10 +297,6 @@ class StackFrame BASE_EMBEDDED { 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, @@ -336,8 +332,6 @@ class StackFrame BASE_EMBEDDED { 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; diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 93400ae79a..c3490a3125 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -447,8 +447,6 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { 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)); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 045ed82650..83b753b591 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -5077,8 +5077,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { // 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). @@ -5152,8 +5150,6 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { Label invoke, handler_entry, exit; Label not_outermost_js, not_outermost_js_2; - ProfileEntryHookStub::MaybeCallEntryHook(masm); - // Set up frame. __ push(ebp); __ mov(ebp, esp); @@ -7695,11 +7691,7 @@ void StubFailureTrampolineStub::Generate(MacroAssembler* masm) { 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); } @@ -7707,11 +7699,9 @@ void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { 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)); @@ -7724,16 +7714,12 @@ void ProfileEntryHookStub::Generate(MacroAssembler* masm) { __ 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(&entry_hook_); + __ call(Operand(hook_location, RelocInfo::NONE32)); __ add(esp, Immediate(2 * kPointerSize)); // Restore ecx. - __ pop(edx); __ pop(ecx); - __ pop(eax); - __ ret(0); } diff --git a/src/ic.cc b/src/ic.cc index ff3a94d18c..39955610b5 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -144,7 +144,7 @@ IC::IC(FrameDepth depth, Isolate* isolate) : isolate_(isolate) { ASSERT(fp == frame->fp() && pc_address == frame->pc_address()); #endif fp_ = fp; - pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address); + pc_address_ = pc_address; } diff --git a/src/isolate.cc b/src/isolate.cc index 2383399f7f..476e405249 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -1753,10 +1753,8 @@ Isolate::Isolate() 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), @@ -2079,14 +2077,6 @@ bool Isolate::Init(Deserializer* des) { 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; @@ -2278,9 +2268,6 @@ bool Isolate::Init(Deserializer* des) { sweeper_thread_[i]->Start(); } } - - initialized_from_snapshot_ = (des != NULL); - return true; } diff --git a/src/isolate.h b/src/isolate.h index 981f81ce38..e6f41691d0 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -547,7 +547,7 @@ class Isolate { } 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; } @@ -1050,8 +1050,6 @@ class Isolate { 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_; } @@ -1111,11 +1109,6 @@ class Isolate { 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(); @@ -1295,9 +1288,6 @@ class 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_; @@ -1321,7 +1311,6 @@ class Isolate { #endif CpuProfiler* cpu_profiler_; HeapProfiler* heap_profiler_; - FunctionEntryHook function_entry_hook_; #define GLOBAL_BACKING_STORE(type, name, initialvalue) \ type name##_; diff --git a/src/v8.h b/src/v8.h index 52fb98a561..b16374f338 100644 --- a/src/v8.h +++ b/src/v8.h @@ -101,8 +101,6 @@ class V8 : public AllStatic { // 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 diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 9376cc787e..89ae74d7e7 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -456,8 +456,6 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { - ProfileEntryHookStub::MaybeCallEntryHook(masm); - // Expects five C++ function parameters. // - Address entry (ignored) // - JSFunction* function ( diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 1cac35ccc9..897f0080d4 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -4129,8 +4129,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { // 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); @@ -4211,8 +4209,6 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { 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. @@ -6667,11 +6663,7 @@ void StubFailureTrampolineStub::Generate(MacroAssembler* masm) { 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); } @@ -6679,25 +6671,45 @@ void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { 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); @@ -6706,9 +6718,13 @@ void ProfileEntryHookStub::Generate(MacroAssembler* 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(); } diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc old mode 100644 new mode 100755 index d85e573409..b95f292b6e --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -31,8 +31,6 @@ #include // kill #include // getpid #endif // WIN32 -#include -#include #include "v8.h" @@ -12265,268 +12263,68 @@ THREADED_TEST(NestedHandleScopeAndContexts) { } -static bool MatchPointers(void* key1, void* key2) { - return key1 == key2; -} +static i::Handle* foo_ptr = NULL; +static int foo_entry_count = 0; +static i::Handle* bar_ptr = NULL; +static int bar_entry_count = 0; +static int bar_caller_count = 0; -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& 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 foo_func_; - i::Handle bar_func_; - - typedef std::map SymbolMap; - typedef std::map SymbolLocationMap; - typedef std::map, 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); - } -} - - -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(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(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(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(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(function)); - CHECK(function_code != NULL); + CHECK(code != NULL); - // Then try and look up the caller's code object. - i::Address caller = *reinterpret_cast(return_addr_location); + if (bar_ptr != NULL && code == (*bar_ptr)->code()) + ++bar_entry_count; - // Count the invocation. - SymbolInfo* caller_symbol = FindSymbolForAddr(caller); - SymbolInfo* function_symbol = - FindSymbolForAddr(reinterpret_cast(function)); - ++invocations_[std::make_pair(caller_symbol, function_symbol)]; + if (foo_ptr != NULL && code == (*foo_ptr)->code()) + ++foo_entry_count; - 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()); - } + // Let's check whether bar is the caller. + if (bar_ptr != NULL) { + const v8::internal::byte* caller = + *reinterpret_cast(return_addr_location); - 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()); - } -} - - -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; -} - - -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 env = Context::New(isolate); env->Enter(); - Local 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::cast( + i::Handle bar = + i::Handle::cast( v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar")))); - ASSERT(!bar_func_.is_null()); + ASSERT(*bar); - foo_func_ = + i::Handle foo = i::Handle::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 value = CompileRun("bar();"); CHECK(value->IsNumber()); @@ -12541,55 +12339,6 @@ void SetFunctionEntryHookTest::RunLoopInNewEnv(v8::Isolate* isolate) { 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. @@ -12602,8 +12351,42 @@ TEST(SetFunctionEntryHook) { 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); } @@ -12745,6 +12528,11 @@ static void event_handler(const v8::JitCodeEvent* event) { } +static bool MatchPointers(void* key1, void* key2) { + return key1 == key2; +} + + TEST(SetJitCodeEventHandler) { i::FLAG_stress_compaction = true; i::FLAG_incremental_marking = false;