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)); |