Index: test/cctest/test-log-ia32.cc |
diff --git a/test/cctest/test-log-ia32.cc b/test/cctest/test-log-ia32.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..07ba07be9f189e72b3369bedbde6a197a720cb6b |
--- /dev/null |
+++ b/test/cctest/test-log-ia32.cc |
@@ -0,0 +1,219 @@ |
+// Copyright 2006-2009 the V8 project authors. All rights reserved. |
+// |
+// Tests of profiler-related functions from log.h |
+ |
+#include <stdlib.h> |
+ |
+#include "v8.h" |
+ |
+#include "log.h" |
+#include "cctest.h" |
+ |
+using v8::Function; |
+using v8::Local; |
+using v8::Object; |
+using v8::Script; |
+using v8::String; |
+using v8::Value; |
+ |
+using v8::internal::byte; |
+using v8::internal::Handle; |
+using v8::internal::JSFunction; |
+using v8::internal::StackTracer; |
+using v8::internal::TickSample; |
+ |
+ |
+static v8::Persistent<v8::Context> env; |
+ |
+ |
+static struct { |
+ StackTracer* tracer; |
+ TickSample* sample; |
+} trace_env; |
+ |
+ |
+static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { |
+ trace_env.tracer = tracer; |
+ trace_env.sample = 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 - sizeof(unsigned int); |
+ 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 |
+#endif |
+ DoTrace(fp); |
+} |
+ |
+ |
+static void CFunc(int i) { |
+ for (int j = i; j >= 0; --j) { |
+ CFuncDoTrace(); |
+ } |
+} |
+ |
+ |
+static void CheckRetAddrIsInFunction(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); |
+ 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 |
+ CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]), |
+ reinterpret_cast<unsigned int>(&CFunc)); |
+ CHECK_EQ(0, sample.stack[1]); |
+#endif |
+} |
+ |
+ |
+// --- T r a c e E x t e n s i o n --- |
+ |
+class TraceExtension : public v8::Extension { |
+ public: |
+ TraceExtension() : v8::Extension("v8/trace", kSource) { } |
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
+ v8::Handle<v8::String> name); |
+ static v8::Handle<v8::Value> Trace(const v8::Arguments& args); |
+ private: |
+ static const char* kSource; |
+}; |
+ |
+ |
+const char* TraceExtension::kSource = "native function trace();"; |
+ |
+ |
+v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( |
+ v8::Handle<v8::String> str) { |
+ return v8::FunctionTemplate::New(TraceExtension::Trace); |
+} |
+ |
+ |
+v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
+ CHECK_EQ(1, args.Length()); |
+ unsigned int fp = args[0]->Int32Value() << 2; |
+ printf("Trace: %08x\n", fp); |
+ DoTrace(fp); |
+ return v8::Undefined(); |
+} |
+ |
+ |
+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 void SetGlobalProperty(const char* name, Local<Value> value) { |
+ env->Global()->Set(String::New(name), value); |
+} |
+ |
+ |
+static bool Patch(byte* from, size_t num, byte* original, byte* patch, size_t patch_len) { |
+ byte* to = from + num; |
+ do { |
+ from = (byte*)memchr(from, *original, to - from); |
+ CHECK(from != NULL); |
+ if (memcmp(original, from, patch_len) == 0) { |
+ memcpy(from, patch, patch_len); |
+ return true; |
+ } else { |
+ from++; |
+ } |
+ } while (to - from > 0); |
+ return false; |
+} |
+ |
+ |
+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()); |
+ |
+ byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; // push 0xcccc (= 0x6666 << 1) |
+ byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; // mov eax,ebp; shr eax; push eax; |
+ // 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(), |
+ original, patch, sizeof(patch))); |
+ |
+ SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace)); |
+ |
+ CompileRun( |
+ "function JSTrace() {" |
+ " JSFuncDoTrace();" |
+ "};\n" |
+ "JSTrace();"); |
+ 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[0]), |
+ reinterpret_cast<unsigned int>(js_trace_code->instruction_start()), |
+ js_trace_code->instruction_size()); |
+ CHECK_EQ(0, sample.stack[1]); |
+} |