Commit 5b343483 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

Remove remaining infrastructure for FunctionEntryHook

Bug: v8:8503, v8:7777
Change-Id: Iadf515cf4735d1046c7c14f44c02d5d5f95df87a
Reviewed-on: https://chromium-review.googlesource.com/c/1356512Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57969}
parent c2aaf0a6
......@@ -6807,21 +6807,6 @@ class V8_EXPORT HeapCodeStatistics {
class RetainedObjectInfo;
/**
* FunctionEntryHook is the type of the profile entry hook called at entry to
* any generated function when function-level profiling is enabled.
*
* \param function the address of the function that's being entered.
* \param return_addr_location points to a location on stack where the machine
* return address resides. This can be used to identify the caller of
* \p function, and/or modified to divert execution when \p function exits.
*
* \note the entry hook must not cause garbage collection.
*/
typedef void (*FunctionEntryHook)(uintptr_t function,
uintptr_t return_addr_location);
/**
* A JIT code event is issued each time code is added, moved or removed.
*
......@@ -7121,8 +7106,7 @@ class V8_EXPORT Isolate {
*/
struct CreateParams {
CreateParams()
: entry_hook(nullptr),
code_event_handler(nullptr),
: code_event_handler(nullptr),
snapshot_blob(nullptr),
counter_lookup_callback(nullptr),
create_histogram_callback(nullptr),
......@@ -7132,17 +7116,6 @@ class V8_EXPORT Isolate {
allow_atomics_wait(true),
only_terminate_in_safe_scope(false) {}
/**
* The optional 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 entry_hook is invoked at the very start of each
* generated function.
* An entry_hook can only be provided in no-snapshot builds; in snapshot
* builds it must be nullptr.
* TODO(v8:8503): Remove entry_hook.
*/
FunctionEntryHook entry_hook;
/**
* Allows the host application to provide the address of a function that is
* notified each time code is added, moved or removed.
......
......@@ -71,12 +71,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {r1};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// r0 : number of arguments
......
......@@ -71,13 +71,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// x1 function the function to call
Register registers[] = {x1};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// x1: target
......
......@@ -72,12 +72,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {edi};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// eax : number of arguments
......
......@@ -38,7 +38,6 @@ namespace internal {
V(TypeConversionStackParameter) \
V(Typeof) \
V(AsyncFunctionStackParameter) \
V(CallFunction) \
V(CallVarargs) \
V(CallForwardVarargs) \
V(CallWithSpread) \
......@@ -809,13 +808,6 @@ class ConstructStubDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(ConstructStubDescriptor, CallInterfaceDescriptor)
};
class CallFunctionDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kTarget)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged())
DECLARE_DESCRIPTOR(CallFunctionDescriptor, CallInterfaceDescriptor)
};
class AbortDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS_NO_CONTEXT(kMessageOrMessageId)
......
......@@ -71,12 +71,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {a1};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// a1: target
......
......@@ -71,12 +71,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {a1};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// a1: target
......
......@@ -71,12 +71,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {r4};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// r3 : number of arguments
......
......@@ -70,12 +70,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {r3};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// r2 : number of arguments
......
......@@ -73,12 +73,6 @@ void TypeofDescriptor::InitializePlatformSpecific(
// static
const Register TypeConversionDescriptor::ArgumentRegister() { return rax; }
void CallFunctionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {rdi};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void CallTrampolineDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// rax : number of arguments
......
......@@ -175,13 +175,6 @@
'test-api/Float64Array': [SKIP],
}], # 'arch == arm64 and (mode == debug or dcheck_always_on) and simulator_run'
##############################################################################
['arch == ia32', {
# Requires call to non-isolate-independent code target, unsupported by
# MacroAssembler::Call() on ia32.
'test-api/SetFunctionEntryHook': [SKIP],
}], # 'no_snap == False'
##############################################################################
['variant == nooptimization and (arch == arm or arch == arm64) and simulator_run', {
# Slow tests: https://crbug.com/v8/7783
......@@ -241,10 +234,6 @@
# https://crbug.com/v8/7763
'test-lockers/ExtensionsRegistration': [SKIP],
}], # 'no_snap == True'
['no_snap == False', {
# FunctionEntryHooks require bootstrapping from scratch.
'test-api/SetFunctionEntryHook': [SKIP],
}], # 'no_snap == False'
##############################################################################
# TODO(machenbach): Fix application of '*'. Nosnap windows needs a separate
......
......@@ -14519,358 +14519,6 @@ THREADED_TEST(NestedHandleScopeAndContexts) {
env->Exit();
}
struct SymbolInfo {
size_t id;
size_t size;
std::string name;
};
class SetFunctionEntryHookTest {
public:
SetFunctionEntryHookTest() {
CHECK_NULL(instance_);
instance_ = this;
}
~SetFunctionEntryHookTest() {
CHECK(instance_ == this);
instance_ = nullptr;
}
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_NOT_NULL(instance_);
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_NOT_NULL(instance_);
instance_->OnEntryHook(function, return_addr_location);
}
static void RuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_NOT_NULL(instance_);
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.
int 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*>, int> InvocationMap;
SymbolMap symbols_;
SymbolLocationMap symbol_locations_;
InvocationMap invocations_;
i::Isolate* isolate_ = nullptr;
static SetFunctionEntryHookTest* instance_;
};
SetFunctionEntryHookTest* SetFunctionEntryHookTest::instance_ = nullptr;
// 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_NOT_NULL(event->code_start);
CHECK_NE(0, static_cast<int>(event->code_len));
CHECK_NOT_NULL(event->name.str);
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);
}
}
break;
default:
break;
}
}
void SetFunctionEntryHookTest::OnEntryHook(
uintptr_t function, uintptr_t return_addr_location) {
// Get the function's code object.
i::Code function_code = isolate_->heap()->GcSafeFindCodeForInnerPointer(
static_cast<i::Address>(function));
CHECK(!function_code.is_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(static_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->raw_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->raw_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 nullptr;
--it;
size_t offs = addr - it->first;
if (offs < it->second->size)
return it->second;
return nullptr;
}
int SetFunctionEntryHookTest::CountInvocations(
const char* caller_name, const char* function_name) {
InvocationMap::iterator it(invocations_.begin());
int 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 != nullptr) {
if (function->name.find(function_name) == std::string::npos)
continue;
}
// Filter out non-matching callers.
if (caller_name != nullptr) {
if (caller == nullptr) 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::HandleScope outer(isolate);
v8::Local<Context> env = Context::New(isolate);
env->Enter();
Local<ObjectTemplate> t = ObjectTemplate::New(isolate);
t->Set(v8_str("asdf"), v8::FunctionTemplate::New(isolate, RuntimeCallback));
CHECK(env->Global()
->Set(env, v8_str("obj"), t->NewInstance(env).ToLocalChecked())
.FromJust());
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()";
CompileRun(script);
bar_func_ = i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(
*env->Global()->Get(env, v8_str("bar")).ToLocalChecked()));
CHECK(!bar_func_.is_null());
foo_func_ = i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(
*env->Global()->Get(env, v8_str("foo")).ToLocalChecked()));
CHECK(!foo_func_.is_null());
v8::Local<v8::Value> value = CompileRun("bar();");
CHECK(value->IsNumber());
CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value());
// Test the optimized codegen path.
value = CompileRun("%OptimizeFunctionOnNextCall(foo);"
"bar();");
CHECK(value->IsNumber());
CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value());
env->Exit();
}
void SetFunctionEntryHookTest::RunTest() {
// Work in a new isolate throughout.
v8::Isolate::CreateParams create_params;
create_params.entry_hook = EntryHook;
create_params.code_event_handler = JitEvent;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::Allocate();
isolate_ = reinterpret_cast<i::Isolate*>(isolate);
v8::Isolate::Initialize(isolate, create_params);
{
v8::Isolate::Scope scope(isolate);
RunLoopInNewEnv(isolate);
// Check the expected invocation counts.
if (i::FLAG_always_opt) {
CHECK_EQ(2, CountInvocations(nullptr, "bar"));
CHECK_EQ(200, CountInvocations("bar", "foo"));
CHECK_EQ(200, CountInvocations(nullptr, "foo"));
} else if (i::FLAG_opt) {
// For ignition we don't see the actual functions being called, instead
// we see the InterpreterEntryTrampoline at least 102 times
// (100 unoptimized calls to foo, and 2 calls to bar).
CHECK_LE(102, CountInvocations(nullptr, "InterpreterEntryTrampoline"));
// We should also see the calls to the optimized function foo.
CHECK_EQ(100, CountInvocations(nullptr, "foo"));
} else {
// For ignition without an optimizing compiler, we should only see the
// InterpreterEntryTrampoline.
// (200 unoptimized calls to foo, and 2 calls to bar).
CHECK_LE(202, CountInvocations(nullptr, "InterpreterEntryTrampoline"));
}
// Verify that we have an entry hook on some specific stubs and builtins.
CHECK_NE(0, CountInvocations(
nullptr,
"CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit"));
CHECK_NE(0, CountInvocations(
nullptr,
"CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit"));
CHECK_NE(0, CountInvocations(nullptr, "JSEntryStub"));
CHECK_NE(0, CountInvocations(nullptr, "JSEntryTrampoline"));
}
isolate->Dispose();
Reset();
// Make sure a second isolate is unaffected by the previous entry hook.
create_params = v8::Isolate::CreateParams();
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
isolate = v8::Isolate::Allocate();
isolate_ = reinterpret_cast<i::Isolate*>(isolate);
v8::Isolate::Initialize(isolate, create_params);
{
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, static_cast<int>(invocations_.size()));
}
isolate->Dispose();
}
UNINITIALIZED_TEST(SetFunctionEntryHook) {
// FunctionEntryHook does not work well with experimental natives.
// Experimental natives are compiled during snapshot deserialization.
// This test breaks because InstallGetter (function from snapshot that
// only gets called from experimental natives) is compiled with entry hooks.
i::FLAG_allow_natives_syntax = true;
i::FLAG_turbo_inlining = false;
SetFunctionEntryHookTest test;
test.RunTest();
}
static v8::base::HashMap* code_map = nullptr;
static v8::base::HashMap* jitcode_line_info = nullptr;
static int saw_bar = 0;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment