| Index: test/cctest/test-log-ia32.cc
|
| diff --git a/test/cctest/test-log-ia32.cc b/test/cctest/test-log-ia32.cc
|
| index dde8512090ccabea85bbe7d5195077ac581605a0..43cb294b17fcb8881de91e52c5fbdde2de9c2bd7 100644
|
| --- a/test/cctest/test-log-ia32.cc
|
| +++ b/test/cctest/test-log-ia32.cc
|
| @@ -8,9 +8,11 @@
|
|
|
| #include "v8.h"
|
|
|
| +#include "codegen.h"
|
| #include "log.h"
|
| #include "top.h"
|
| #include "cctest.h"
|
| +#include "disassembler.h"
|
|
|
| using v8::Function;
|
| using v8::Local;
|
| @@ -20,12 +22,15 @@ using v8::String;
|
| using v8::Value;
|
|
|
| using v8::internal::byte;
|
| +using v8::internal::Address;
|
| using v8::internal::Handle;
|
| using v8::internal::JSFunction;
|
| using v8::internal::StackTracer;
|
| using v8::internal::TickSample;
|
| using v8::internal::Top;
|
|
|
| +namespace i = v8::internal;
|
| +
|
|
|
| static v8::Persistent<v8::Context> env;
|
|
|
| @@ -42,8 +47,8 @@ static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
|
| }
|
|
|
|
|
| -static void DoTrace(unsigned int fp) {
|
| - trace_env.sample->fp = fp;
|
| +static void DoTrace(Address fp) {
|
| + trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp);
|
| // sp is only used to define stack high bound
|
| trace_env.sample->sp =
|
| reinterpret_cast<unsigned int>(trace_env.sample) - 10240;
|
| @@ -53,7 +58,7 @@ static void DoTrace(unsigned int fp) {
|
|
|
| // Hide c_entry_fp to emulate situation when sampling is done while
|
| // pure JS code is being executed
|
| -static void DoTraceHideCEntryFPAddress(unsigned int fp) {
|
| +static void DoTraceHideCEntryFPAddress(Address fp) {
|
| v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
|
| CHECK(saved_c_frame_fp);
|
| *(Top::c_entry_fp_address()) = 0;
|
| @@ -62,6 +67,28 @@ static void DoTraceHideCEntryFPAddress(unsigned int fp) {
|
| }
|
|
|
|
|
| +static void CheckRetAddrIsInFunction(const char* func_name,
|
| + Address ret_addr,
|
| + Address func_start_addr,
|
| + unsigned int func_len) {
|
| + printf("CheckRetAddrIsInFunction \"%s\": %p %p %p\n",
|
| + func_name, func_start_addr, ret_addr, func_start_addr + func_len);
|
| + CHECK_GE(ret_addr, func_start_addr);
|
| + CHECK_GE(func_start_addr + func_len, ret_addr);
|
| +}
|
| +
|
| +
|
| +static void CheckRetAddrIsInJSFunction(const char* func_name,
|
| + Address ret_addr,
|
| + Handle<JSFunction> func) {
|
| + v8::internal::Code* func_code = func->code();
|
| + CheckRetAddrIsInFunction(
|
| + func_name, ret_addr,
|
| + func_code->instruction_start(),
|
| + func_code->ExecutableSize());
|
| +}
|
| +
|
| +
|
| // --- T r a c e E x t e n s i o n ---
|
|
|
| class TraceExtension : public v8::Extension {
|
| @@ -72,7 +99,7 @@ class TraceExtension : public v8::Extension {
|
| static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
|
| static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
|
| private:
|
| - static unsigned int GetFP(const v8::Arguments& args);
|
| + static Address GetFP(const v8::Arguments& args);
|
| static const char* kSource;
|
| };
|
|
|
| @@ -95,10 +122,10 @@ v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
|
| }
|
|
|
|
|
| -unsigned int TraceExtension::GetFP(const v8::Arguments& args) {
|
| +Address TraceExtension::GetFP(const v8::Arguments& args) {
|
| CHECK_EQ(1, args.Length());
|
| - unsigned int fp = args[0]->Int32Value() << 2;
|
| - printf("Trace: %08x\n", fp);
|
| + Address fp = reinterpret_cast<Address>(args[0]->Int32Value() << 2);
|
| + printf("Trace: %p\n", fp);
|
| return fp;
|
| }
|
|
|
| @@ -119,10 +146,162 @@ static TraceExtension kTraceExtension;
|
| v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
|
|
|
|
|
| +static void InitializeVM() {
|
| + if (env.IsEmpty()) {
|
| + v8::HandleScope scope;
|
| + const char* extensions[] = { "v8/trace" };
|
| + v8::ExtensionConfiguration config(1, extensions);
|
| + env = v8::Context::New(&config);
|
| + }
|
| + v8::HandleScope scope;
|
| + env->Enter();
|
| +}
|
| +
|
| +
|
| +static Handle<JSFunction> CompileFunction(const char* source) {
|
| + return v8::Utils::OpenHandle(*Script::Compile(String::New(source)));
|
| +}
|
| +
|
| +
|
| +static void CompileRun(const char* source) {
|
| + Script::Compile(String::New(source))->Run();
|
| +}
|
| +
|
| +
|
| +static Local<Value> GetGlobalProperty(const char* name) {
|
| + return env->Global()->Get(String::New(name));
|
| +}
|
| +
|
| +
|
| +static Handle<JSFunction> GetGlobalJSFunction(const char* name) {
|
| + Handle<JSFunction> js_func(JSFunction::cast(
|
| + *(v8::Utils::OpenHandle(
|
| + *GetGlobalProperty(name)))));
|
| + return js_func;
|
| +}
|
| +
|
| +
|
| +static void CheckRetAddrIsInJSFunction(const char* func_name,
|
| + Address ret_addr) {
|
| + CheckRetAddrIsInJSFunction(func_name, ret_addr,
|
| + GetGlobalJSFunction(func_name));
|
| +}
|
| +
|
| +
|
| +static void SetGlobalProperty(const char* name, Local<Value> value) {
|
| + env->Global()->Set(String::New(name), value);
|
| +}
|
| +
|
| +
|
| +static Handle<v8::internal::String> NewString(const char* s) {
|
| + return i::Factory::NewStringFromAscii(i::CStrVector(s));
|
| +}
|
| +
|
| +
|
| +namespace v8 { namespace internal {
|
| +
|
| +class CodeGeneratorPatcher {
|
| + public:
|
| + CodeGeneratorPatcher() {
|
| + CodeGenerator::InlineRuntimeLUT genGetFramePointer =
|
| + {&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"};
|
| + // _FastCharCodeAt is not used in our tests.
|
| + bool result = CodeGenerator::PatchInlineRuntimeEntry(
|
| + NewString("_FastCharCodeAt"),
|
| + genGetFramePointer, &oldInlineEntry);
|
| + CHECK(result);
|
| + }
|
| +
|
| + ~CodeGeneratorPatcher() {
|
| + CHECK(CodeGenerator::PatchInlineRuntimeEntry(
|
| + NewString("_GetFramePointer"),
|
| + oldInlineEntry, NULL));
|
| + }
|
| +
|
| + private:
|
| + CodeGenerator::InlineRuntimeLUT oldInlineEntry;
|
| +};
|
| +
|
| +} } // namespace v8::internal
|
| +
|
| +
|
| +// Creates a global function named 'func_name' that calls the tracing
|
| +// function 'trace_func_name' with an actual EBP register value,
|
| +// shifted right to be presented as Smi.
|
| +static void CreateTraceCallerFunction(const char* func_name,
|
| + const char* trace_func_name) {
|
| + i::EmbeddedVector<char, 256> trace_call_buf;
|
| + i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name);
|
| +
|
| + // Compile the script.
|
| + i::CodeGeneratorPatcher patcher;
|
| + bool allow_natives_syntax = i::FLAG_allow_natives_syntax;
|
| + i::FLAG_allow_natives_syntax = true;
|
| + Handle<JSFunction> func = CompileFunction(trace_call_buf.start());
|
| + CHECK(!func.is_null());
|
| + i::FLAG_allow_natives_syntax = allow_natives_syntax;
|
| +
|
| +#ifdef DEBUG
|
| + v8::internal::Code* func_code = func->code();
|
| + CHECK(func_code->IsCode());
|
| + func_code->Print();
|
| +#endif
|
| +
|
| + SetGlobalProperty(func_name, v8::ToApi<Value>(func));
|
| +}
|
| +
|
| +
|
| +TEST(CFromJSStackTrace) {
|
| + TickSample sample;
|
| + StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
|
| + InitTraceEnv(&tracer, &sample);
|
| +
|
| + InitializeVM();
|
| + v8::HandleScope scope;
|
| + CreateTraceCallerFunction("JSFuncDoTrace", "trace");
|
| + CompileRun(
|
| + "function JSTrace() {"
|
| + " JSFuncDoTrace();"
|
| + "};\n"
|
| + "JSTrace();");
|
| + CHECK_GT(sample.frames_count, 1);
|
| + // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
|
| + CheckRetAddrIsInJSFunction("JSFuncDoTrace",
|
| + sample.stack[0]);
|
| + CheckRetAddrIsInJSFunction("JSTrace",
|
| + sample.stack[1]);
|
| +}
|
| +
|
| +
|
| +TEST(PureJSStackTrace) {
|
| + TickSample sample;
|
| + StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
|
| + InitTraceEnv(&tracer, &sample);
|
| +
|
| + InitializeVM();
|
| + v8::HandleScope scope;
|
| + CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
|
| + CompileRun(
|
| + "function JSTrace() {"
|
| + " JSFuncDoTrace();"
|
| + "};\n"
|
| + "function OuterJSTrace() {"
|
| + " JSTrace();"
|
| + "};\n"
|
| + "OuterJSTrace();");
|
| + CHECK_GT(sample.frames_count, 1);
|
| + // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
|
| + CheckRetAddrIsInJSFunction("JSTrace",
|
| + sample.stack[0]);
|
| + CheckRetAddrIsInJSFunction("OuterJSTrace",
|
| + sample.stack[1]);
|
| +}
|
| +
|
| +
|
| static void CFuncDoTrace() {
|
| - unsigned int fp;
|
| + Address fp;
|
| #ifdef __GNUC__
|
| - fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0));
|
| + fp = reinterpret_cast<Address>(__builtin_frame_address(0));
|
| #elif defined _MSC_VER
|
| __asm mov [fp], ebp // NOLINT
|
| #endif
|
| @@ -142,7 +321,7 @@ static int CFunc(int depth) {
|
|
|
| TEST(PureCStackTrace) {
|
| TickSample sample;
|
| - StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
|
| + StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
|
| InitTraceEnv(&tracer, &sample);
|
| // Check that sampler doesn't crash
|
| CHECK_EQ(10, CFunc(10));
|
|
|