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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1892 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mikhail.naganov@gmail.com 2009-05-07 09:24:43 +00:00
parent cba7c023f0
commit 3e82fce446
6 changed files with 279 additions and 48 deletions

View File

@ -303,7 +303,17 @@ class CodeGenerator: public AstVisitor {
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);
@ -433,6 +443,8 @@ class CodeGenerator: public AstVisitor {
// in a spilled state.
bool in_spilled_code_;
static InlineRuntimeLUT kInlineRuntimeLUT[];
friend class VirtualFrame;
friend class JumpTarget;
friend class Reference;

View File

@ -386,57 +386,69 @@ void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
}
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;
}
void CodeGenerator::GenerateFastCaseSwitchStatement(SwitchStatement* node,
int min_index,
int range,

View File

@ -59,7 +59,9 @@
// ComputeCallInitializeInLoop
// ProcessDeclarations
// DeclareGlobals
// FindInlineRuntimeLUT
// CheckForInlineRuntimeCall
// PatchInlineRuntimeEntry
// GenerateFastCaseSwitchStatement
// GenerateFastCaseSwitchCases
// TryGenerateFastCaseSwitchStatement

View File

@ -4544,6 +4544,17 @@ void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
}
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;

View File

@ -473,7 +473,17 @@ class CodeGenerator: public AstVisitor {
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 @@ class CodeGenerator: public AstVisitor {
void GenerateLog(ZoneList<Expression*>* args);
void GenerateGetFramePointer(ZoneList<Expression*>* args);
// Methods and constants for fast case switch statement support.
//
@ -604,11 +615,15 @@ class CodeGenerator: public AstVisitor {
// 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);
};

View File

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