Support profiler stack sampling in any situation. After this change, almost...

Support profiler stack sampling in any situation. After this change, almost all profiler ticks (except GC ones) have a stack sample data associated.

Tested under Linux, OS X, and Windows.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1565 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent c35bacb2
......@@ -574,7 +574,7 @@ def BuildSpecific(env, mode, env_overrides):
library_name = 'v8' + suffix
env['LIBRARY'] = library_name
# Build the object files by invoking SCons recursively.
# Build the object files by invoking SCons recursively.
(object_files, shell_files, mksnapshot) = env.SConscript(
join('src', 'SConscript'),
build_dir=join('obj', target_id),
......@@ -596,7 +596,7 @@ def BuildSpecific(env, mode, env_overrides):
pdb_name = library_name + '.dll.pdb'
library = env.SharedLibrary(library_name, object_files, PDB=pdb_name)
context.library_targets.append(library)
d8_env = Environment()
d8_env.Replace(**context.flags['d8'])
shell = d8_env.Program('d8' + suffix, object_files + shell_files)
......@@ -616,7 +616,7 @@ def BuildSpecific(env, mode, env_overrides):
sample_program = sample_env.Program(sample_name, sample_object)
sample_env.Depends(sample_program, library)
context.sample_targets.append(sample_program)
cctest_program = env.SConscript(
join('test', 'cctest', 'SConscript'),
build_dir=join('obj', 'test', target_id),
......@@ -624,7 +624,7 @@ def BuildSpecific(env, mode, env_overrides):
duplicate=False
)
context.cctest_targets.append(cctest_program)
return context
......
......@@ -154,11 +154,9 @@ class InternalFrameConstants : public AllStatic {
};
inline Object* JavaScriptFrame::function() const {
inline Object* JavaScriptFrame::function_slot_object() const {
const int offset = JavaScriptFrameConstants::kFunctionOffset;
Object* result = Memory::Object_at(fp() + offset);
ASSERT(result->IsJSFunction());
return result;
return Memory::Object_at(fp() + offset);
}
......
......@@ -129,11 +129,9 @@ class InternalFrameConstants : public AllStatic {
};
inline Object* JavaScriptFrame::function() const {
inline Object* JavaScriptFrame::function_slot_object() const {
const int offset = JavaScriptFrameConstants::kFunctionOffset;
Object* result = Memory::Object_at(fp() + offset);
ASSERT(result->IsJSFunction());
return result;
return Memory::Object_at(fp() + offset);
}
......
......@@ -169,6 +169,20 @@ inline bool JavaScriptFrame::has_adapted_arguments() const {
}
inline bool JavaScriptFrame::is_at_function() const {
Object* result = function_slot_object();
return Heap::Contains(reinterpret_cast<Address>(result)) &&
result->IsJSFunction();
}
inline Object* JavaScriptFrame::function() const {
Object* result = function_slot_object();
ASSERT(result->IsJSFunction());
return result;
}
template<typename Iterator>
inline JavaScriptFrame* JavaScriptFrameIteratorTemp<Iterator>::frame() const {
// TODO(1233797): The frame hierarchy needs to change. It's
......
......@@ -66,23 +66,32 @@ class StackHandlerIterator BASE_EMBEDDED {
#define INITIALIZE_SINGLETON(type, field) field##_(this),
StackFrameIterator::StackFrameIterator()
: STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
frame_(NULL), handler_(NULL), thread_(Top::GetCurrentThread()) {
frame_(NULL), handler_(NULL), thread_(Top::GetCurrentThread()),
fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) {
Reset();
}
StackFrameIterator::StackFrameIterator(ThreadLocalTop* t)
: STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
frame_(NULL), handler_(NULL), thread_(t) {
frame_(NULL), handler_(NULL), thread_(t),
fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) {
Reset();
}
StackFrameIterator::StackFrameIterator(bool reset)
StackFrameIterator::StackFrameIterator(bool use_top, Address fp, Address sp)
: STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
frame_(NULL), handler_(NULL), thread_(Top::GetCurrentThread()) {
if (reset) Reset();
frame_(NULL), handler_(NULL),
thread_(use_top ? Top::GetCurrentThread() : NULL),
fp_(use_top ? NULL : fp), sp_(sp),
advance_(use_top ? &StackFrameIterator::AdvanceWithHandler :
&StackFrameIterator::AdvanceWithoutHandler) {
if (use_top || fp != NULL) {
Reset();
}
}
#undef INITIALIZE_SINGLETON
void StackFrameIterator::Advance() {
void StackFrameIterator::AdvanceWithHandler() {
ASSERT(!done());
// Compute the state of the calling frame before restoring
// callee-saved registers and unwinding handlers. This allows the
......@@ -105,17 +114,45 @@ void StackFrameIterator::Advance() {
}
void StackFrameIterator::AdvanceWithoutHandler() {
// A simpler version of Advance which doesn't care about handler.
ASSERT(!done());
StackFrame::State state;
StackFrame::Type type = frame_->GetCallerState(&state);
frame_ = SingletonFor(type, &state);
}
void StackFrameIterator::Reset() {
Address fp = Top::c_entry_fp(thread_);
StackFrame::State state;
StackFrame::Type type = ExitFrame::GetStateForFramePointer(fp, &state);
StackFrame::Type type;
if (thread_ != NULL) {
type = ExitFrame::GetStateForFramePointer(Top::c_entry_fp(thread_), &state);
handler_ = StackHandler::FromAddress(Top::handler(thread_));
} else {
ASSERT(fp_ != NULL);
state.fp = fp_;
state.sp = sp_;
state.pc_address =
reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp_));
type = StackFrame::ComputeType(&state);
if (SingletonFor(type) == NULL) return;
}
frame_ = SingletonFor(type, &state);
handler_ = StackHandler::FromAddress(Top::handler(thread_));
}
StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type,
StackFrame::State* state) {
if (type == StackFrame::NONE) return NULL;
StackFrame* result = SingletonFor(type);
ASSERT(result != NULL);
result->state_ = *state;
return result;
}
StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type) {
#define FRAME_TYPE_CASE(type, field) \
case StackFrame::type: result = &field##_; break;
......@@ -125,8 +162,6 @@ StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type,
STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
default: break;
}
ASSERT(result != NULL);
result->state_ = *state;
return result;
#undef FRAME_TYPE_CASE
......@@ -154,29 +189,50 @@ void StackTraceFrameIterator::Advance() {
SafeStackFrameIterator::SafeStackFrameIterator(
Address low_bound, Address high_bound) :
Address fp, Address sp, Address low_bound, Address high_bound) :
low_bound_(low_bound), high_bound_(high_bound),
is_working_iterator_(IsInBounds(low_bound, high_bound,
Top::c_entry_fp(Top::GetCurrentThread()))),
iteration_done_(!is_working_iterator_), iterator_(is_working_iterator_) {
is_valid_top_(
IsWithinBounds(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_working_iterator_(is_valid_top_ || is_valid_fp_),
iteration_done_(!is_working_iterator_),
iterator_(is_valid_top_, is_valid_fp_ ? fp : NULL, sp) {
}
void SafeStackFrameIterator::Advance() {
ASSERT(is_working_iterator_);
ASSERT(!done());
StackFrame* frame = iterator_.frame();
iteration_done_ =
!IsGoodStackAddress(frame->sp()) || !IsGoodStackAddress(frame->fp());
if (!iteration_done_) {
iterator_.Advance();
if (!iterator_.done()) {
// Check that we have actually moved to the previous frame in the stack
StackFrame* prev_frame = iterator_.frame();
iteration_done_ =
prev_frame->sp() < frame->sp() || prev_frame->fp() < frame->fp();
}
}
StackFrame* last_frame = iterator_.frame();
Address last_sp = last_frame->sp(), last_fp = last_frame->fp();
// Before advancing to the next stack frame, perform pointer validity tests
iteration_done_ = !IsValidFrame(last_frame) || !IsValidCaller(last_frame);
if (iteration_done_) return;
iterator_.Advance();
if (iterator_.done()) return;
// Check that we have actually moved to the previous frame in the stack
StackFrame* prev_frame = iterator_.frame();
iteration_done_ = prev_frame->sp() < last_sp || prev_frame->fp() < last_fp;
}
bool SafeStackFrameIterator::IsValidFrame(StackFrame* frame) const {
return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp()) &&
// JavaScriptFrame uses function shared info to advance, hence it must
// point to a valid function object.
(!frame->is_java_script() ||
reinterpret_cast<JavaScriptFrame*>(frame)->is_at_function());
}
bool SafeStackFrameIterator::IsValidCaller(StackFrame* frame) {
StackFrame::State state;
frame->ComputeCallerState(&state);
return IsValidStackAddress(state.sp) && IsValidStackAddress(state.fp) &&
iterator_.SingletonFor(frame->GetCallerState(&state)) != NULL;
}
......@@ -193,9 +249,9 @@ void SafeStackFrameIterator::Reset() {
#ifdef ENABLE_LOGGING_AND_PROFILING
SafeStackTraceFrameIterator::SafeStackTraceFrameIterator(
Address low_bound, Address high_bound) :
SafeJavaScriptFrameIterator(low_bound, high_bound) {
if (!done() && !frame()->function()->IsJSFunction()) Advance();
Address fp, Address sp, Address low_bound, Address high_bound) :
SafeJavaScriptFrameIterator(fp, sp, low_bound, high_bound) {
if (!done() && !frame()->is_at_function()) Advance();
}
......@@ -203,7 +259,7 @@ void SafeStackTraceFrameIterator::Advance() {
while (true) {
SafeJavaScriptFrameIterator::Advance();
if (done()) return;
if (frame()->function()->IsJSFunction()) return;
if (frame()->is_at_function()) return;
}
}
#endif
......@@ -279,11 +335,22 @@ void StackFrame::Uncook() {
}
StackFrame::Type StackFrame::GetCallerState(State* state) const {
ComputeCallerState(state);
return ComputeType(state);
}
Code* EntryFrame::code() const {
return Heap::js_entry_code();
}
void EntryFrame::ComputeCallerState(State* state) const {
GetCallerState(state);
}
StackFrame::Type EntryFrame::GetCallerState(State* state) const {
const int offset = EntryFrameConstants::kCallerFPOffset;
Address fp = Memory::Address_at(this->fp() + offset);
......@@ -301,13 +368,12 @@ Code* ExitFrame::code() const {
}
StackFrame::Type ExitFrame::GetCallerState(State* state) const {
void ExitFrame::ComputeCallerState(State* state) const {
// Setup the caller state.
state->sp = pp();
state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset);
state->pc_address
= reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset);
return ComputeType(state);
}
......@@ -338,11 +404,10 @@ int StandardFrame::ComputeExpressionsCount() const {
}
StackFrame::Type StandardFrame::GetCallerState(State* state) const {
void StandardFrame::ComputeCallerState(State* state) const {
state->sp = caller_sp();
state->fp = caller_fp();
state->pc_address = reinterpret_cast<Address*>(ComputePCAddress(fp()));
return ComputeType(state);
}
......
......@@ -190,8 +190,11 @@ class StackFrame BASE_EMBEDDED {
const StackFrameIterator* iterator_;
State state_;
// Fill in the state of the calling frame.
virtual void ComputeCallerState(State* state) const = 0;
// Get the type and the state of the calling frame.
virtual Type GetCallerState(State* state) const = 0;
virtual Type GetCallerState(State* state) const;
// Cooking/uncooking support.
void Cook();
......@@ -199,6 +202,7 @@ class StackFrame BASE_EMBEDDED {
friend class StackFrameIterator;
friend class StackHandlerIterator;
friend class SafeStackFrameIterator;
DISALLOW_IMPLICIT_CONSTRUCTORS(StackFrame);
};
......@@ -228,6 +232,7 @@ class EntryFrame: public StackFrame {
virtual Address GetCallerStackPointer() const { return 0; }
private:
virtual void ComputeCallerState(State* state) const;
virtual Type GetCallerState(State* state) const;
friend class StackFrameIterator;
......@@ -280,7 +285,7 @@ class ExitFrame: public StackFrame {
virtual Address GetCallerStackPointer() const;
private:
virtual Type GetCallerState(State* state) const;
virtual void ComputeCallerState(State* state) const;
friend class StackFrameIterator;
};
......@@ -328,7 +333,7 @@ class StandardFrame: public StackFrame {
explicit StandardFrame(StackFrameIterator* iterator)
: StackFrame(iterator) { }
virtual Type GetCallerState(State* state) const;
virtual void ComputeCallerState(State* state) const;
// Accessors.
inline Address caller_sp() const;
......@@ -368,6 +373,7 @@ class JavaScriptFrame: public StandardFrame {
virtual Type type() const { return JAVA_SCRIPT; }
// Accessors.
inline bool is_at_function() const;
inline Object* function() const;
inline Object* receiver() const;
inline void set_receiver(Object* value);
......@@ -413,6 +419,8 @@ class JavaScriptFrame: public StandardFrame {
virtual Address GetCallerStackPointer() const;
private:
inline Object* function_slot_object() const;
friend class StackFrameIterator;
};
......@@ -509,8 +517,10 @@ class StackFrameIterator BASE_EMBEDDED {
// An iterator that iterates over a given thread's stack.
explicit StackFrameIterator(ThreadLocalTop* thread);
// An iterator that conditionally resets itself on init.
explicit StackFrameIterator(bool reset);
// An iterator that can start from a given FP address.
// If use_top, then work as usual, if fp isn't NULL, use it,
// otherwise, do nothing.
StackFrameIterator(bool use_top, Address fp, Address sp);
StackFrame* frame() const {
ASSERT(!done());
......@@ -518,7 +528,7 @@ class StackFrameIterator BASE_EMBEDDED {
}
bool done() const { return frame_ == NULL; }
void Advance();
void Advance() { (this->*advance_)(); }
// Go back to the first frame.
void Reset();
......@@ -530,6 +540,9 @@ class StackFrameIterator BASE_EMBEDDED {
StackFrame* frame_;
StackHandler* handler_;
ThreadLocalTop* thread_;
Address fp_;
Address sp_;
void (StackFrameIterator::*advance_)();
StackHandler* handler() const {
ASSERT(!done());
......@@ -538,8 +551,14 @@ class StackFrameIterator BASE_EMBEDDED {
// Get the type-specific frame singleton in a given state.
StackFrame* SingletonFor(StackFrame::Type type, StackFrame::State* state);
// A helper function, can return a NULL pointer.
StackFrame* SingletonFor(StackFrame::Type type);
void AdvanceWithHandler();
void AdvanceWithoutHandler();
friend class StackFrame;
friend class SafeStackFrameIterator;
DISALLOW_COPY_AND_ASSIGN(StackFrameIterator);
};
......@@ -558,8 +577,11 @@ class JavaScriptFrameIteratorTemp BASE_EMBEDDED {
// Skip frames until the frame with the given id is reached.
explicit JavaScriptFrameIteratorTemp(StackFrame::Id id);
explicit JavaScriptFrameIteratorTemp(Address low_bound, Address high_bound) :
iterator_(low_bound, high_bound) { if (!done()) Advance(); }
JavaScriptFrameIteratorTemp(Address fp, Address sp,
Address low_bound, Address high_bound) :
iterator_(fp, sp, low_bound, high_bound) {
if (!done()) Advance();
}
inline JavaScriptFrame* frame() const;
......@@ -595,7 +617,8 @@ class StackTraceFrameIterator: public JavaScriptFrameIterator {
class SafeStackFrameIterator BASE_EMBEDDED {
public:
explicit SafeStackFrameIterator(Address low_bound, Address high_bound);
SafeStackFrameIterator(Address fp, Address sp,
Address low_bound, Address high_bound);
StackFrame* frame() const {
ASSERT(is_working_iterator_);
......@@ -608,16 +631,20 @@ class SafeStackFrameIterator BASE_EMBEDDED {
void Reset();
private:
static bool IsInBounds(
static bool IsWithinBounds(
Address low_bound, Address high_bound, Address addr) {
return low_bound <= addr && addr <= high_bound;
}
bool IsGoodStackAddress(Address addr) const {
return IsInBounds(low_bound_, high_bound_, addr);
bool IsValidStackAddress(Address addr) const {
return IsWithinBounds(low_bound_, high_bound_, addr);
}
bool IsValidFrame(StackFrame* frame) const;
bool IsValidCaller(StackFrame* frame);
Address low_bound_;
Address high_bound_;
const bool is_valid_top_;
const bool is_valid_fp_;
const bool is_working_iterator_;
bool iteration_done_;
StackFrameIterator iterator_;
......@@ -631,7 +658,8 @@ typedef JavaScriptFrameIteratorTemp<SafeStackFrameIterator>
class SafeStackTraceFrameIterator: public SafeJavaScriptFrameIterator {
public:
explicit SafeStackTraceFrameIterator(Address low_bound, Address high_bound);
explicit SafeStackTraceFrameIterator(Address fp, Address sp,
Address low_bound, Address high_bound);
void Advance();
};
#endif
......
......@@ -142,28 +142,17 @@ void StackTracer::Trace(TickSample* sample) {
return;
}
// If c_entry_fp is available, this means that we are inside a C++
// function and sample->fp value isn't reliable due to FPO.
if (Top::c_entry_fp(Top::GetCurrentThread()) != NULL) {
SafeStackTraceFrameIterator it(
reinterpret_cast<Address>(sample->sp),
reinterpret_cast<Address>(low_stack_bound_));
int i = 0;
while (!it.done() && i < TickSample::kMaxFramesCount) {
sample->stack[i++] = it.frame()->pc();
it.Advance();
}
sample->frames_count = i;
} else if (sample->sp < sample->fp && sample->fp < low_stack_bound_) {
// The check assumes that stack grows from lower addresses.
sample->stack[0] = Memory::Address_at(
(Address)(sample->fp + StandardFrameConstants::kCallerPCOffset));
sample->frames_count = 1;
} else {
// FP seems to be in some intermediate state,
// better discard this sample
sample->frames_count = 0;
SafeStackTraceFrameIterator it(
reinterpret_cast<Address>(sample->fp),
reinterpret_cast<Address>(sample->sp),
reinterpret_cast<Address>(sample->sp),
reinterpret_cast<Address>(low_stack_bound_));
int i = 0;
while (!it.done() && i < TickSample::kMaxFramesCount) {
sample->stack[i++] = it.frame()->pc();
it.Advance();
}
sample->frames_count = i;
}
......@@ -936,7 +925,7 @@ void Logger::TickEvent(TickSample* sample, bool overflow) {
msg.Append(",overflow");
}
for (int i = 0; i < sample->frames_count; ++i) {
msg.Append(",%p", sample->stack[i]);
msg.Append(",0x%x", reinterpret_cast<uint32_t>(sample->stack[i]));
}
msg.Append('\n');
msg.WriteToLogFile();
......
......@@ -270,7 +270,7 @@ class Logger {
};
// Class that extracts stack trace, used for profiling
// Class that extracts stack trace, used for profiling.
class StackTracer BASE_EMBEDDED {
public:
explicit StackTracer(unsigned int low_stack_bound)
......
......@@ -1750,7 +1750,6 @@ class Sampler::PlatformData : public Malloced {
SuspendThread(profiled_thread_);
context.ContextFlags = CONTEXT_FULL;
GetThreadContext(profiled_thread_, &context);
ResumeThread(profiled_thread_);
// Invoke tick handler with program counter and stack pointer.
sample.pc = context.Eip;
sample.sp = context.Esp;
......@@ -1761,6 +1760,10 @@ class Sampler::PlatformData : public Malloced {
sample.state = Logger::state();
sampler_->Tick(&sample);
if (sampler_->IsProfiling()) {
ResumeThread(profiled_thread_);
}
// Wait until next sampling.
Sleep(sampler_->interval_);
}
......
This diff is collapsed.
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