Index: test/cctest/test-log-ia32.cc |
diff --git a/test/cctest/test-log-ia32.cc b/test/cctest/test-log-ia32.cc |
index 588be7158c3e767ceaea885db135d10f4eb2bde9..b171339c24da6d9d83c83b7ad3270fef9ec25dbc 100644 |
--- a/test/cctest/test-log-ia32.cc |
+++ b/test/cctest/test-log-ia32.cc |
@@ -9,6 +9,7 @@ |
#include "v8.h" |
#include "log.h" |
+#include "top.h" |
#include "cctest.h" |
using v8::Function; |
@@ -23,6 +24,7 @@ using v8::internal::Handle; |
using v8::internal::JSFunction; |
using v8::internal::StackTracer; |
using v8::internal::TickSample; |
+using v8::internal::Top; |
static v8::Persistent<v8::Context> env; |
@@ -31,7 +33,7 @@ static v8::Persistent<v8::Context> env; |
static struct { |
StackTracer* tracer; |
TickSample* sample; |
-} trace_env; |
+} trace_env = { NULL, NULL }; |
static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { |
@@ -42,62 +44,43 @@ static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { |
static void DoTrace(unsigned int fp) { |
trace_env.sample->fp = fp; |
- // something that is less than fp |
- trace_env.sample->sp = trace_env.sample->fp - 100; |
+ // sp is only used to define stack high bound |
+ trace_env.sample->sp = |
+ reinterpret_cast<unsigned int>(trace_env.sample) - 10240; |
trace_env.tracer->Trace(trace_env.sample); |
} |
-static void CFuncDoTrace() { |
- unsigned int fp; |
-#ifdef __GNUC__ |
- fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); |
-#elif defined _MSC_VER |
- __asm mov [fp], ebp // NOLINT |
-#endif |
+// Hide c_entry_fp to emulate situation when sampling is done while |
+// pure JS code is being executed |
+static void DoTraceHideCEntryFPAddress(unsigned int fp) { |
+ v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |
+ CHECK(saved_c_frame_fp); |
+ *(Top::c_entry_fp_address()) = 0; |
DoTrace(fp); |
+ *(Top::c_entry_fp_address()) = saved_c_frame_fp; |
} |
-static void CFunc(int i) { |
- for (int j = i; j >= 0; --j) { |
- CFuncDoTrace(); |
- } |
-} |
- |
- |
-static void CheckRetAddrIsInFunction(unsigned int ret_addr, |
+static void CheckRetAddrIsInFunction(const char* func_name, |
+ unsigned int ret_addr, |
unsigned int func_start_addr, |
unsigned int func_len) { |
- printf("CheckRetAddrIsInFunction: %08x %08x %08x\n", |
- func_start_addr, ret_addr, func_start_addr + func_len); |
+ printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\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); |
} |
-#ifdef DEBUG |
-static const int kMaxCFuncLen = 0x40; // seems enough for a small C function |
- |
-static void CheckRetAddrIsInCFunction(unsigned int ret_addr, |
- unsigned int func_start_addr) { |
- CheckRetAddrIsInFunction(ret_addr, func_start_addr, kMaxCFuncLen); |
-} |
-#endif |
- |
- |
-TEST(PureCStackTrace) { |
- TickSample sample; |
- StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
- InitTraceEnv(&tracer, &sample); |
- CFunc(0); |
-#ifdef DEBUG |
- // C stack trace works only in debug mode, in release mode EBP is |
- // usually treated as a general-purpose register |
- CHECK_GT(sample.frames_count, 0); |
- CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]), |
- reinterpret_cast<unsigned int>(&CFunc)); |
-#endif |
+static void CheckRetAddrIsInJSFunction(const char* func_name, |
+ unsigned int ret_addr, |
+ Handle<JSFunction> func) { |
+ v8::internal::Code* func_code = func->code(); |
+ CheckRetAddrIsInFunction( |
+ func_name, ret_addr, |
+ reinterpret_cast<unsigned int>(func_code->instruction_start()), |
+ func_code->ExecutableSize()); |
} |
@@ -107,27 +90,49 @@ class TraceExtension : public v8::Extension { |
public: |
TraceExtension() : v8::Extension("v8/trace", kSource) { } |
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
- v8::Handle<v8::String> name); |
+ v8::Handle<String> name); |
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 const char* kSource; |
}; |
-const char* TraceExtension::kSource = "native function trace();"; |
+const char* TraceExtension::kSource = |
+ "native function trace();" |
+ "native function js_trace();"; |
v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( |
- v8::Handle<v8::String> str) { |
- return v8::FunctionTemplate::New(TraceExtension::Trace); |
+ v8::Handle<String> name) { |
+ if (name->Equals(String::New("trace"))) { |
+ return v8::FunctionTemplate::New(TraceExtension::Trace); |
+ } else if (name->Equals(String::New("js_trace"))) { |
+ return v8::FunctionTemplate::New(TraceExtension::JSTrace); |
+ } else { |
+ CHECK(false); |
+ return v8::Handle<v8::FunctionTemplate>(); |
+ } |
} |
-v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
+unsigned int TraceExtension::GetFP(const v8::Arguments& args) { |
CHECK_EQ(1, args.Length()); |
unsigned int fp = args[0]->Int32Value() << 2; |
printf("Trace: %08x\n", fp); |
- DoTrace(fp); |
+ return fp; |
+} |
+ |
+ |
+v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
+ DoTrace(GetFP(args)); |
+ return v8::Undefined(); |
+} |
+ |
+ |
+v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |
+ DoTraceHideCEntryFPAddress(GetFP(args)); |
return v8::Undefined(); |
} |
@@ -163,6 +168,21 @@ static Local<Value> GetGlobalProperty(const char* 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, |
+ unsigned int 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); |
} |
@@ -188,17 +208,17 @@ static bool Patch(byte* from, |
} |
-TEST(PureJSStackTrace) { |
- TickSample sample; |
- StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
- InitTraceEnv(&tracer, &sample); |
- |
- InitializeVM(); |
- v8::HandleScope scope; |
- Handle<JSFunction> call_trace = CompileFunction("trace(0x6666);"); |
- CHECK(!call_trace.is_null()); |
- v8::internal::Code* call_trace_code = call_trace->code(); |
- CHECK(call_trace_code->IsCode()); |
+// 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) { |
+ ::v8::internal::EmbeddedVector<char, 256> trace_call_buf; |
+ ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name); |
+ Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |
+ CHECK(!func.is_null()); |
+ v8::internal::Code* func_code = func->code(); |
+ CHECK(func_code->IsCode()); |
// push 0xcccc (= 0x6666 << 1) |
byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; |
@@ -206,29 +226,89 @@ TEST(PureJSStackTrace) { |
byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; |
// Patch generated code to replace pushing of a constant with |
// pushing of ebp contents in a Smi |
- CHECK(Patch(call_trace_code->instruction_start(), |
- call_trace_code->instruction_size(), |
+ CHECK(Patch(func_code->instruction_start(), |
+ func_code->instruction_size(), |
original, patch, sizeof(patch))); |
- SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace)); |
+ SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |
+} |
+ |
+TEST(CFromJSStackTrace) { |
+ TickSample sample; |
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
+ InitTraceEnv(&tracer, &sample); |
+ |
+ InitializeVM(); |
+ v8::HandleScope scope; |
+ CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |
CompileRun( |
"function JSTrace() {" |
" JSFuncDoTrace();" |
"};\n" |
"JSTrace();"); |
CHECK_GT(sample.frames_count, 1); |
- CheckRetAddrIsInFunction( |
- reinterpret_cast<unsigned int>(sample.stack[0]), |
- reinterpret_cast<unsigned int>(call_trace_code->instruction_start()), |
- call_trace_code->instruction_size()); |
- Handle<JSFunction> js_trace(JSFunction::cast(*(v8::Utils::OpenHandle( |
- *GetGlobalProperty("JSTrace"))))); |
- v8::internal::Code* js_trace_code = js_trace->code(); |
- CheckRetAddrIsInFunction( |
- reinterpret_cast<unsigned int>(sample.stack[1]), |
- reinterpret_cast<unsigned int>(js_trace_code->instruction_start()), |
- js_trace_code->instruction_size()); |
+ // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace" |
+ CheckRetAddrIsInJSFunction("JSFuncDoTrace", |
+ reinterpret_cast<unsigned int>(sample.stack[0])); |
+ CheckRetAddrIsInJSFunction("JSTrace", |
+ reinterpret_cast<unsigned int>(sample.stack[1])); |
+} |
+ |
+ |
+TEST(PureJSStackTrace) { |
+ TickSample sample; |
+ StackTracer tracer(reinterpret_cast<unsigned int>(&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", |
+ reinterpret_cast<unsigned int>(sample.stack[0])); |
+ CheckRetAddrIsInJSFunction("OuterJSTrace", |
+ reinterpret_cast<unsigned int>(sample.stack[1])); |
} |
+ |
+static void CFuncDoTrace() { |
+ unsigned int fp; |
+#ifdef __GNUC__ |
+ fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); |
+#elif defined _MSC_VER |
+ __asm mov [fp], ebp // NOLINT |
+#endif |
+ DoTrace(fp); |
+} |
+ |
+ |
+static int CFunc(int depth) { |
+ if (depth <= 0) { |
+ CFuncDoTrace(); |
+ return 0; |
+ } else { |
+ return CFunc(depth - 1) + 1; |
+ } |
+} |
+ |
+ |
+TEST(PureCStackTrace) { |
+ TickSample sample; |
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
+ InitTraceEnv(&tracer, &sample); |
+ // Check that sampler doesn't crash |
+ CHECK_EQ(10, CFunc(10)); |
+} |
+ |
+ |
#endif // ENABLE_LOGGING_AND_PROFILING |