Enhance SafeStackFrameIterator to avoid triggering assertions in debug mode.

When running profiling in debug mode, several assertions in frame
iterators that are undoubtedly useful when iterator is started from a
VM thread in a known "good" state, may fail when running over a stack
of a suspended VM thread. This patch makes SafeStackFrameIterator
to proactively check addresses and bail out from iteration early,
before an assertion will be triggered.

BUG=crbug/55565

Review URL: http://codereview.chromium.org/3436006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5467 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent b6c5c5b5
...@@ -37,17 +37,8 @@ namespace v8 { ...@@ -37,17 +37,8 @@ namespace v8 {
namespace internal { namespace internal {
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { Address ExitFrame::ComputeStackPointer(Address fp) {
if (fp == 0) return NONE; return fp + ExitFrameConstants::kSPOffset;
// Compute frame type and stack pointer.
Address sp = fp + ExitFrameConstants::kSPOffset;
// Fill in the state.
state->sp = sp;
state->fp = fp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
ASSERT(*state->pc_address != NULL);
return EXIT;
} }
......
...@@ -143,8 +143,8 @@ void StackFrameIterator::Reset() { ...@@ -143,8 +143,8 @@ void StackFrameIterator::Reset() {
state.pc_address = state.pc_address =
reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp_)); reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp_));
type = StackFrame::ComputeType(&state); type = StackFrame::ComputeType(&state);
if (SingletonFor(type) == NULL) return;
} }
if (SingletonFor(type) == NULL) return;
frame_ = SingletonFor(type, &state); frame_ = SingletonFor(type, &state);
} }
...@@ -203,13 +203,24 @@ bool StackTraceFrameIterator::IsValidFrame() { ...@@ -203,13 +203,24 @@ bool StackTraceFrameIterator::IsValidFrame() {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
bool SafeStackFrameIterator::ExitFrameValidator::IsValidFP(Address fp) {
if (!validator_.IsValid(fp)) return false;
Address sp = ExitFrame::ComputeStackPointer(fp);
if (!validator_.IsValid(sp)) return false;
StackFrame::State state;
ExitFrame::FillState(fp, sp, &state);
if (!validator_.IsValid(reinterpret_cast<Address>(state.pc_address))) {
return false;
}
return *state.pc_address != NULL;
}
SafeStackFrameIterator::SafeStackFrameIterator( SafeStackFrameIterator::SafeStackFrameIterator(
Address fp, Address sp, Address low_bound, Address high_bound) : Address fp, Address sp, Address low_bound, Address high_bound) :
maintainer_(), low_bound_(low_bound), high_bound_(high_bound), maintainer_(),
is_valid_top_( stack_validator_(low_bound, high_bound),
IsWithinBounds(low_bound, high_bound, is_valid_top_(IsValidTop(low_bound, high_bound)),
Top::c_entry_fp(Top::GetCurrentThread())) &&
Top::handler(Top::GetCurrentThread()) != NULL),
is_valid_fp_(IsWithinBounds(low_bound, high_bound, fp)), is_valid_fp_(IsWithinBounds(low_bound, high_bound, fp)),
is_working_iterator_(is_valid_top_ || is_valid_fp_), is_working_iterator_(is_valid_top_ || is_valid_fp_),
iteration_done_(!is_working_iterator_), iteration_done_(!is_working_iterator_),
...@@ -217,6 +228,14 @@ SafeStackFrameIterator::SafeStackFrameIterator( ...@@ -217,6 +228,14 @@ SafeStackFrameIterator::SafeStackFrameIterator(
} }
bool SafeStackFrameIterator::IsValidTop(Address low_bound, Address high_bound) {
Address fp = Top::c_entry_fp(Top::GetCurrentThread());
ExitFrameValidator validator(low_bound, high_bound);
if (!validator.IsValidFP(fp)) return false;
return Top::handler(Top::GetCurrentThread()) != NULL;
}
void SafeStackFrameIterator::Advance() { void SafeStackFrameIterator::Advance() {
ASSERT(is_working_iterator_); ASSERT(is_working_iterator_);
ASSERT(!done()); ASSERT(!done());
...@@ -258,9 +277,8 @@ bool SafeStackFrameIterator::IsValidCaller(StackFrame* frame) { ...@@ -258,9 +277,8 @@ bool SafeStackFrameIterator::IsValidCaller(StackFrame* frame) {
// sure that caller FP address is valid. // sure that caller FP address is valid.
Address caller_fp = Memory::Address_at( Address caller_fp = Memory::Address_at(
frame->fp() + EntryFrameConstants::kCallerFPOffset); frame->fp() + EntryFrameConstants::kCallerFPOffset);
if (!IsValidStackAddress(caller_fp)) { ExitFrameValidator validator(stack_validator_);
return false; if (!validator.IsValidFP(caller_fp)) return false;
}
} else if (frame->is_arguments_adaptor()) { } else if (frame->is_arguments_adaptor()) {
// See ArgumentsAdaptorFrame::GetCallerStackPointer. It assumes that // See ArgumentsAdaptorFrame::GetCallerStackPointer. It assumes that
// the number of arguments is stored on stack as Smi. We need to check // the number of arguments is stored on stack as Smi. We need to check
...@@ -415,6 +433,22 @@ Address ExitFrame::GetCallerStackPointer() const { ...@@ -415,6 +433,22 @@ Address ExitFrame::GetCallerStackPointer() const {
} }
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (fp == 0) return NONE;
Address sp = ComputeStackPointer(fp);
FillState(fp, sp, state);
ASSERT(*state->pc_address != NULL);
return EXIT;
}
void ExitFrame::FillState(Address fp, Address sp, State* state) {
state->sp = sp;
state->fp = fp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
}
Address StandardFrame::GetExpressionAddress(int n) const { Address StandardFrame::GetExpressionAddress(int n) const {
const int offset = StandardFrameConstants::kExpressionsOffset; const int offset = StandardFrameConstants::kExpressionsOffset;
return fp() + offset - n * kPointerSize; return fp() + offset - n * kPointerSize;
......
...@@ -202,6 +202,7 @@ class StackFrame BASE_EMBEDDED { ...@@ -202,6 +202,7 @@ class StackFrame BASE_EMBEDDED {
protected: protected:
struct State { struct State {
State() : sp(NULL), fp(NULL), pc_address(NULL) { }
Address sp; Address sp;
Address fp; Address fp;
Address* pc_address; Address* pc_address;
...@@ -318,6 +319,8 @@ class ExitFrame: public StackFrame { ...@@ -318,6 +319,8 @@ class ExitFrame: public StackFrame {
// pointer. Used when constructing the first stack frame seen by an // pointer. Used when constructing the first stack frame seen by an
// iterator and the frames following entry frames. // iterator and the frames following entry frames.
static Type GetStateForFramePointer(Address fp, State* state); static Type GetStateForFramePointer(Address fp, State* state);
static Address ComputeStackPointer(Address fp);
static void FillState(Address fp, Address sp, State* state);
protected: protected:
explicit ExitFrame(StackFrameIterator* iterator) : StackFrame(iterator) { } explicit ExitFrame(StackFrameIterator* iterator) : StackFrame(iterator) { }
...@@ -443,6 +446,7 @@ class JavaScriptFrame: public StandardFrame { ...@@ -443,6 +446,7 @@ class JavaScriptFrame: public StandardFrame {
inline Object* function_slot_object() const; inline Object* function_slot_object() const;
friend class StackFrameIterator; friend class StackFrameIterator;
friend class StackTracer;
}; };
...@@ -654,12 +658,36 @@ class SafeStackFrameIterator BASE_EMBEDDED { ...@@ -654,12 +658,36 @@ class SafeStackFrameIterator BASE_EMBEDDED {
} }
private: private:
bool IsValidStackAddress(Address addr) const { class StackAddressValidator {
public:
StackAddressValidator(Address low_bound, Address high_bound)
: low_bound_(low_bound), high_bound_(high_bound) { }
bool IsValid(Address addr) const {
return IsWithinBounds(low_bound_, high_bound_, addr); return IsWithinBounds(low_bound_, high_bound_, addr);
} }
private:
Address low_bound_;
Address high_bound_;
};
class ExitFrameValidator {
public:
explicit ExitFrameValidator(const StackAddressValidator& validator)
: validator_(validator) { }
ExitFrameValidator(Address low_bound, Address high_bound)
: validator_(low_bound, high_bound) { }
bool IsValidFP(Address fp);
private:
StackAddressValidator validator_;
};
bool IsValidStackAddress(Address addr) const {
return stack_validator_.IsValid(addr);
}
bool CanIterateHandles(StackFrame* frame, StackHandler* handler); bool CanIterateHandles(StackFrame* frame, StackHandler* handler);
bool IsValidFrame(StackFrame* frame) const; bool IsValidFrame(StackFrame* frame) const;
bool IsValidCaller(StackFrame* frame); bool IsValidCaller(StackFrame* frame);
static bool IsValidTop(Address low_bound, Address high_bound);
// This is a nasty hack to make sure the active count is incremented // This is a nasty hack to make sure the active count is incremented
// before the constructor for the embedded iterator is invoked. This // before the constructor for the embedded iterator is invoked. This
...@@ -674,8 +702,7 @@ class SafeStackFrameIterator BASE_EMBEDDED { ...@@ -674,8 +702,7 @@ class SafeStackFrameIterator BASE_EMBEDDED {
ActiveCountMaintainer maintainer_; ActiveCountMaintainer maintainer_;
static int active_count_; static int active_count_;
Address low_bound_; StackAddressValidator stack_validator_;
Address high_bound_;
const bool is_valid_top_; const bool is_valid_top_;
const bool is_valid_fp_; const bool is_valid_fp_;
const bool is_working_iterator_; const bool is_working_iterator_;
......
...@@ -35,16 +35,8 @@ namespace v8 { ...@@ -35,16 +35,8 @@ namespace v8 {
namespace internal { namespace internal {
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { Address ExitFrame::ComputeStackPointer(Address fp) {
if (fp == 0) return NONE; return Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
// Compute the stack pointer.
Address sp = Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
// Fill in the state.
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
ASSERT(*state->pc_address != NULL);
return EXIT;
} }
......
...@@ -171,7 +171,9 @@ void StackTracer::Trace(TickSample* sample) { ...@@ -171,7 +171,9 @@ void StackTracer::Trace(TickSample* sample) {
SafeStackTraceFrameIterator it(sample->fp, sample->sp, SafeStackTraceFrameIterator it(sample->fp, sample->sp,
sample->sp, js_entry_sp); sample->sp, js_entry_sp);
while (!it.done() && i < TickSample::kMaxFramesCount) { while (!it.done() && i < TickSample::kMaxFramesCount) {
sample->stack[i++] = reinterpret_cast<Address>(it.frame()->function()); sample->stack[i++] =
reinterpret_cast<Address>(it.frame()->function_slot_object()) -
kHeapObjectTag;
it.Advance(); it.Advance();
} }
sample->frames_count = i; sample->frames_count = i;
......
...@@ -52,9 +52,7 @@ StackFrame::Type StackFrame::ComputeType(State* state) { ...@@ -52,9 +52,7 @@ StackFrame::Type StackFrame::ComputeType(State* state) {
} }
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { Address ExitFrame::ComputeStackPointer(Address fp) {
if (fp == 0) return NONE;
// Compute frame type and stack pointer.
Address sp = fp + ExitFrameConstants::kSPDisplacement; Address sp = fp + ExitFrameConstants::kSPDisplacement;
const int offset = ExitFrameConstants::kCodeOffset; const int offset = ExitFrameConstants::kCodeOffset;
Object* code = Memory::Object_at(fp + offset); Object* code = Memory::Object_at(fp + offset);
...@@ -62,11 +60,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { ...@@ -62,11 +60,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (is_debug_exit) { if (is_debug_exit) {
sp -= kNumJSCallerSaved * kPointerSize; sp -= kNumJSCallerSaved * kPointerSize;
} }
// Fill in the state. return sp;
state->sp = sp;
state->fp = fp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
return EXIT;
} }
......
...@@ -35,18 +35,8 @@ namespace v8 { ...@@ -35,18 +35,8 @@ namespace v8 {
namespace internal { namespace internal {
Address ExitFrame::ComputeStackPointer(Address fp) {
return Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (fp == 0) return NONE;
// Compute the stack pointer.
Address sp = Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
// Fill in the state.
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
ASSERT(*state->pc_address != NULL);
return EXIT;
} }
......
...@@ -206,21 +206,8 @@ static Handle<JSFunction> CompileFunction(const char* source) { ...@@ -206,21 +206,8 @@ static Handle<JSFunction> CompileFunction(const char* source) {
} }
static Local<Value> GetGlobalProperty(const char* name) { static void CheckJSFunctionAtAddress(const char* func_name, Address addr) {
return env->Global()->Get(String::New(name)); i::Object* obj = i::HeapObject::FromAddress(addr);
}
static Handle<JSFunction> GetGlobalJSFunction(const char* name) {
Handle<JSFunction> result(JSFunction::cast(
*v8::Utils::OpenHandle(*GetGlobalProperty(name))));
return result;
}
static void CheckObjectIsJSFunction(const char* func_name,
Address addr) {
i::Object* obj = reinterpret_cast<i::Object*>(addr);
CHECK(obj->IsJSFunction()); CHECK(obj->IsJSFunction());
CHECK(JSFunction::cast(obj)->shared()->name()->IsString()); CHECK(JSFunction::cast(obj)->shared()->name()->IsString());
i::SmartPointer<char> found_name = i::SmartPointer<char> found_name =
...@@ -304,7 +291,6 @@ static void CreateTraceCallerFunction(const char* func_name, ...@@ -304,7 +291,6 @@ static void CreateTraceCallerFunction(const char* func_name,
#endif #endif
SetGlobalProperty(func_name, v8::ToApi<Value>(func)); SetGlobalProperty(func_name, v8::ToApi<Value>(func));
CHECK_EQ(*func, *GetGlobalJSFunction(func_name));
} }
...@@ -332,13 +318,13 @@ TEST(CFromJSStackTrace) { ...@@ -332,13 +318,13 @@ TEST(CFromJSStackTrace) {
// script [JS] // script [JS]
// JSTrace() [JS] // JSTrace() [JS]
// JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
// trace(EBP encoded as Smi) [native (extension)] // trace(EBP) [native (extension)]
// DoTrace(EBP) [native] // DoTrace(EBP) [native]
// StackTracer::Trace // StackTracer::Trace
CHECK_GT(sample.frames_count, 1); CHECK_GT(sample.frames_count, 1);
// Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
CheckObjectIsJSFunction("JSFuncDoTrace", sample.stack[0]); CheckJSFunctionAtAddress("JSFuncDoTrace", sample.stack[0]);
CheckObjectIsJSFunction("JSTrace", sample.stack[1]); CheckJSFunctionAtAddress("JSTrace", sample.stack[1]);
} }
...@@ -370,19 +356,18 @@ TEST(PureJSStackTrace) { ...@@ -370,19 +356,18 @@ TEST(PureJSStackTrace) {
// script [JS] // script [JS]
// OuterJSTrace() [JS] // OuterJSTrace() [JS]
// JSTrace() [JS] // JSTrace() [JS]
// JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] // JSFuncDoTrace() [JS]
// js_trace(EBP encoded as Smi) [native (extension)] // js_trace(EBP) [native (extension)]
// DoTraceHideCEntryFPAddress(EBP) [native] // DoTraceHideCEntryFPAddress(EBP) [native]
// StackTracer::Trace // StackTracer::Trace
// //
// The last JS function called. It is only visible through // The last JS function called. It is only visible through
// sample.function, as its return address is above captured EBP value. // sample.function, as its return address is above captured EBP value.
CHECK_EQ(GetGlobalJSFunction("JSFuncDoTrace")->address(), CheckJSFunctionAtAddress("JSFuncDoTrace", sample.function);
sample.function);
CHECK_GT(sample.frames_count, 1); CHECK_GT(sample.frames_count, 1);
// Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
CheckObjectIsJSFunction("JSTrace", sample.stack[0]); CheckJSFunctionAtAddress("JSTrace", sample.stack[0]);
CheckObjectIsJSFunction("OuterJSTrace", sample.stack[1]); CheckJSFunctionAtAddress("OuterJSTrace", sample.stack[1]);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment