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
This commit is contained in:
parent
a32277c4ba
commit
475131b2b9
13
include/v8.h
13
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
|
||||
|
38
src/api.cc
38
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<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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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<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())));
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<Context>();
|
||||
}
|
||||
|
||||
if (!native_context().is_null()) {
|
||||
AddToWeakNativeContextList(*native_context());
|
||||
isolate->set_context(*native_context());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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<Address*>(
|
||||
return_address_location_resolver_(
|
||||
reinterpret_cast<uintptr_t>(pc_address)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline EntryFrame::EntryFrame(StackFrameIteratorBase* iterator)
|
||||
: StackFrame(iterator) {
|
||||
}
|
||||
|
@ -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<Address*>(
|
||||
return_address_location_resolver(
|
||||
reinterpret_cast<uintptr_t>(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<Address*>(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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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<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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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##_;
|
||||
|
2
src/v8.h
2
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
|
||||
|
@ -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 (
|
||||
|
@ -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();
|
||||
}
|
||||
|
372
test/cctest/test-api.cc
Normal file → Executable file
372
test/cctest/test-api.cc
Normal file → Executable file
@ -31,8 +31,6 @@
|
||||
#include <signal.h> // kill
|
||||
#include <unistd.h> // getpid
|
||||
#endif // WIN32
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
@ -12265,268 +12263,68 @@ THREADED_TEST(NestedHandleScopeAndContexts) {
|
||||
}
|
||||
|
||||
|
||||
static bool MatchPointers(void* key1, void* key2) {
|
||||
return key1 == key2;
|
||||
}
|
||||
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;
|
||||
|
||||
|
||||
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,
|
||||
static void entry_hook(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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(
|
||||
i::Code* code = i::Code::GetCodeFromTargetAddress(
|
||||
reinterpret_cast<i::Address>(function));
|
||||
CHECK(function_code != NULL);
|
||||
CHECK(code != NULL);
|
||||
|
||||
// Then try and look up the caller's code object.
|
||||
i::Address caller = *reinterpret_cast<i::Address*>(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<i::Address>(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<v8::internal::byte**>(return_addr_location);
|
||||
|
||||
if ((*bar_ptr)->code()->instruction_start() <= caller &&
|
||||
(*bar_ptr)->code()->instruction_end() > caller) {
|
||||
++bar_caller_count;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
static void RunLoopInNewEnv() {
|
||||
bar_ptr = NULL;
|
||||
foo_ptr = NULL;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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());
|
||||
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user