blob: cd0243723b52b2b803120c463c6fe0b5263926a2 [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/win32-headers.h"
#include "src/init/v8.h"
#include "test/cctest/cctest.h"
#if defined(V8_OS_WIN_X64)
#define CONTEXT_PC(context) (context.Rip)
#elif defined(V8_OS_WIN_ARM64)
#define CONTEXT_PC(context) (context.Pc)
#endif
class UnwindingWin64Callbacks {
public:
UnwindingWin64Callbacks() = default;
static void Getter(v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
// Expects to find at least 15 stack frames in the call stack.
// The stack walking should fail on stack frames for builtin functions if
// stack unwinding data has not been correctly registered.
int stack_frames = CountCallStackFrames(15);
CHECK_GE(stack_frames, 15);
}
static void Setter(v8::Local<v8::String> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {}
private:
// Windows-specific code to walk the stack starting from the current
// instruction pointer.
static int CountCallStackFrames(int max_frames) {
CONTEXT context_record;
::RtlCaptureContext(&context_record);
int iframe = 0;
while (++iframe < max_frames) {
uint64_t image_base;
PRUNTIME_FUNCTION function_entry = ::RtlLookupFunctionEntry(
CONTEXT_PC(context_record), &image_base, nullptr);
if (!function_entry) break;
void* handler_data;
uint64_t establisher_frame;
::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base,
CONTEXT_PC(context_record), function_entry,
&context_record, &handler_data, &establisher_frame,
NULL);
}
return iframe;
}
};
// Verifies that stack unwinding data has been correctly registered on Win64.
UNINITIALIZED_TEST(StackUnwindingWin64) {
#ifdef V8_WIN64_UNWINDING_INFO
static const char* unwinding_win64_test_source =
"function start(count) {\n"
" for (var i = 0; i < count; i++) {\n"
" var o = instance.foo;\n"
" instance.foo = o + 1;\n"
" }\n"
"};\n"
"%PrepareFunctionForOptimization(start);\n";
// This test may fail on Windows 7
if (!::IsWindows8OrGreater()) {
return;
}
i::FLAG_allow_natives_syntax = true;
i::FLAG_win64_unwinding_info = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
isolate->Enter();
{
v8::HandleScope scope(isolate);
LocalContext env(isolate);
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::ObjectTemplate> instance_template =
func_template->InstanceTemplate();
UnwindingWin64Callbacks accessors;
v8::Local<v8::External> data = v8::External::New(isolate, &accessors);
instance_template->SetAccessor(v8_str("foo"),
&UnwindingWin64Callbacks::Getter,
&UnwindingWin64Callbacks::Setter, data);
v8::Local<v8::Function> func =
func_template->GetFunction(env.local()).ToLocalChecked();
v8::Local<v8::Object> instance =
func->NewInstance(env.local()).ToLocalChecked();
env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
CompileRun(unwinding_win64_test_source);
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
env->Global()->Get(env.local(), v8_str("start")).ToLocalChecked());
CompileRun("start(1); %OptimizeFunctionOnNextCall(start);");
int32_t repeat_count = 100;
v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
function->Call(env.local(), env.local()->Global(), arraysize(args), args)
.ToLocalChecked();
}
isolate->Exit();
isolate->Dispose();
#endif // V8_WIN64_UNWINDING_INFO
}
#undef CONTEXT_PC