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:
parent
cba7c023f0
commit
3e82fce446
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -59,7 +59,9 @@
|
||||
// ComputeCallInitializeInLoop
|
||||
// ProcessDeclarations
|
||||
// DeclareGlobals
|
||||
// FindInlineRuntimeLUT
|
||||
// CheckForInlineRuntimeCall
|
||||
// PatchInlineRuntimeEntry
|
||||
// GenerateFastCaseSwitchStatement
|
||||
// GenerateFastCaseSwitchCases
|
||||
// TryGenerateFastCaseSwitchStatement
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user