Author: [email protected]
Date: Thu May 7 02:24:43 2009
New Revision: 1892
Modified:
branches/bleeding_edge/src/arm/codegen-arm.h
branches/bleeding_edge/src/codegen.cc
branches/bleeding_edge/src/codegen.h
branches/bleeding_edge/src/ia32/codegen-ia32.cc
branches/bleeding_edge/src/ia32/codegen-ia32.h
branches/bleeding_edge/test/cctest/test-log-ia32.cc
Log:
Restore stack backtrace tests removed in revision 1785.
To re-enable tests, instead of compiled code patching, inlined code is used.
Inlined code is only installed in test.
Review URL: http://codereview.chromium.org/108015
Modified: branches/bleeding_edge/src/arm/codegen-arm.h
==============================================================================
--- branches/bleeding_edge/src/arm/codegen-arm.h (original)
+++ branches/bleeding_edge/src/arm/codegen-arm.h Thu May 7 02:24:43 2009
@@ -303,7 +303,17 @@
void Branch(bool if_true, JumpTarget* target);
void CheckStack();
+ struct InlineRuntimeLUT {
+ void (CodeGenerator::*method)(ZoneList<Expression*>*);
+ const char* name;
+ };
+
+ static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name);
bool CheckForInlineRuntimeCall(CallRuntime* node);
+ static bool PatchInlineRuntimeEntry(Handle<String> name,
+ const InlineRuntimeLUT& new_entry,
+ InlineRuntimeLUT* old_entry);
+
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
@@ -432,6 +442,8 @@
// called from spilled code, because they do not leave the virtual frame
// in a spilled state.
bool in_spilled_code_;
+
+ static InlineRuntimeLUT kInlineRuntimeLUT[];
friend class VirtualFrame;
friend class JumpTarget;
Modified: branches/bleeding_edge/src/codegen.cc
==============================================================================
--- branches/bleeding_edge/src/codegen.cc (original)
+++ branches/bleeding_edge/src/codegen.cc Thu May 7 02:24:43 2009
@@ -386,54 +386,66 @@
}
-struct InlineRuntimeLUT {
- void (CodeGenerator::*method)(ZoneList<Expression*>*);
- const char* name;
+
+// Special cases: These 'runtime calls' manipulate the current
+// frame and are only used 1 or two places, so we generate them
+// inline instead of generating calls to them. They are used
+// for implementing Function.prototype.call() and
+// Function.prototype.apply().
+CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
+ {&CodeGenerator::GenerateIsSmi, "_IsSmi"},
+ {&CodeGenerator::GenerateIsNonNegativeSmi, "_IsNonNegativeSmi"},
+ {&CodeGenerator::GenerateIsArray, "_IsArray"},
+ {&CodeGenerator::GenerateArgumentsLength, "_ArgumentsLength"},
+ {&CodeGenerator::GenerateArgumentsAccess, "_Arguments"},
+ {&CodeGenerator::GenerateValueOf, "_ValueOf"},
+ {&CodeGenerator::GenerateSetValueOf, "_SetValueOf"},
+ {&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"},
+ {&CodeGenerator::GenerateObjectEquals, "_ObjectEquals"},
+ {&CodeGenerator::GenerateLog, "_Log"}
};
+CodeGenerator::InlineRuntimeLUT* CodeGenerator::FindInlineRuntimeLUT(
+ Handle<String> name) {
+ const int entries_count =
+ sizeof(kInlineRuntimeLUT) / sizeof(InlineRuntimeLUT);
+ for (int i = 0; i < entries_count; i++) {
+ InlineRuntimeLUT* entry = &kInlineRuntimeLUT[i];
+ if (name->IsEqualTo(CStrVector(entry->name))) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+
bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
ZoneList<Expression*>* args = node->arguments();
- // Special cases: These 'runtime calls' manipulate the current
- // frame and are only used 1 or two places, so we generate them
- // inline instead of generating calls to them. They are used
- // for implementing Function.prototype.call() and
- // Function.prototype.apply().
- static const InlineRuntimeLUT kInlineRuntimeLUT[] = {
- {&v8::internal::CodeGenerator::GenerateIsSmi,
- "_IsSmi"},
- {&v8::internal::CodeGenerator::GenerateIsNonNegativeSmi,
- "_IsNonNegativeSmi"},
- {&v8::internal::CodeGenerator::GenerateIsArray,
- "_IsArray"},
- {&v8::internal::CodeGenerator::GenerateArgumentsLength,
- "_ArgumentsLength"},
- {&v8::internal::CodeGenerator::GenerateArgumentsAccess,
- "_Arguments"},
- {&v8::internal::CodeGenerator::GenerateValueOf,
- "_ValueOf"},
- {&v8::internal::CodeGenerator::GenerateSetValueOf,
- "_SetValueOf"},
- {&v8::internal::CodeGenerator::GenerateFastCharCodeAt,
- "_FastCharCodeAt"},
- {&v8::internal::CodeGenerator::GenerateObjectEquals,
- "_ObjectEquals"},
- {&v8::internal::CodeGenerator::GenerateLog,
- "_Log"}
- };
Handle<String> name = node->name();
if (name->length() > 0 && name->Get(0) == '_') {
- for (unsigned i = 0;
- i < sizeof(kInlineRuntimeLUT) / sizeof(InlineRuntimeLUT);
- i++) {
- const InlineRuntimeLUT* entry = kInlineRuntimeLUT + i;
- if (name->IsEqualTo(CStrVector(entry->name))) {
- ((*this).*(entry->method))(args);
- return true;
- }
+ InlineRuntimeLUT* entry = FindInlineRuntimeLUT(name);
+ if (entry != NULL) {
+ ((*this).*(entry->method))(args);
+ return true;
}
}
return false;
+}
+
+
+bool CodeGenerator::PatchInlineRuntimeEntry(Handle<String> name,
+ const CodeGenerator::InlineRuntimeLUT& new_entry,
+ CodeGenerator::InlineRuntimeLUT* old_entry) {
+ InlineRuntimeLUT* entry = FindInlineRuntimeLUT(name);
+ if (entry == NULL) return false;
+ if (old_entry != NULL) {
+ old_entry->name = entry->name;
+ old_entry->method = entry->method;
+ }
+ entry->name = new_entry.name;
+ entry->method = new_entry.method;
+ return true;
}
Modified: branches/bleeding_edge/src/codegen.h
==============================================================================
--- branches/bleeding_edge/src/codegen.h (original)
+++ branches/bleeding_edge/src/codegen.h Thu May 7 02:24:43 2009
@@ -59,7 +59,9 @@
// ComputeCallInitializeInLoop
// ProcessDeclarations
// DeclareGlobals
+// FindInlineRuntimeLUT
// CheckForInlineRuntimeCall
+// PatchInlineRuntimeEntry
// GenerateFastCaseSwitchStatement
// GenerateFastCaseSwitchCases
// TryGenerateFastCaseSwitchStatement
Modified: branches/bleeding_edge/src/ia32/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/ia32/codegen-ia32.cc (original)
+++ branches/bleeding_edge/src/ia32/codegen-ia32.cc Thu May 7 02:24:43 2009
@@ -4544,6 +4544,17 @@
}
+void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 0);
+ ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on
this
+ Result ebp_as_smi = allocator_->Allocate();
+ ASSERT(ebp_as_smi.is_valid());
+ __ mov(ebp_as_smi.reg(), Operand(ebp));
+ __ shr(ebp_as_smi.reg(), kSmiTagSize);
+ frame_->Push(&ebp_as_smi);
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
Modified: branches/bleeding_edge/src/ia32/codegen-ia32.h
==============================================================================
--- branches/bleeding_edge/src/ia32/codegen-ia32.h (original)
+++ branches/bleeding_edge/src/ia32/codegen-ia32.h Thu May 7 02:24:43 2009
@@ -473,7 +473,17 @@
void CheckStack();
+ struct InlineRuntimeLUT {
+ void (CodeGenerator::*method)(ZoneList<Expression*>*);
+ const char* name;
+ };
+
+ static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name);
bool CheckForInlineRuntimeCall(CallRuntime* node);
+ static bool PatchInlineRuntimeEntry(Handle<String> name,
+ const InlineRuntimeLUT& new_entry,
+ InlineRuntimeLUT* old_entry);
+
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
@@ -508,6 +518,7 @@
void GenerateLog(ZoneList<Expression*>* args);
+ void GenerateGetFramePointer(ZoneList<Expression*>* args);
// Methods and constants for fast case switch statement support.
//
@@ -604,10 +615,14 @@
// in a spilled state.
bool in_spilled_code_;
+ static InlineRuntimeLUT kInlineRuntimeLUT[];
+
friend class VirtualFrame;
friend class JumpTarget;
friend class Reference;
friend class Result;
+
+ friend class CodeGeneratorPatcher; // Used in test-log-ia32.cc
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
Modified: branches/bleeding_edge/test/cctest/test-log-ia32.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-log-ia32.cc (original)
+++ branches/bleeding_edge/test/cctest/test-log-ia32.cc Thu May 7 02:24:43
2009
@@ -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::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 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 @@
// 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 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 @@
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 @@
}
-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 @@
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 @@
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));
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---