void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
Label invoke, exit;
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ Label not_outermost_js, not_outermost_js_2;
+#endif
// Setup frame.
__ push(ebp);
ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
__ push(Operand::StaticVariable(c_entry_fp));
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ // If this is the outermost JS call, set js_entry_sp value.
+ ExternalReference js_entry_sp(Top::k_js_entry_sp_address);
+ __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
+ __ j(NegateCondition(equal), ¬_outermost_js);
+ __ mov(Operand::StaticVariable(js_entry_sp), ebp);
+ __ bind(¬_outermost_js);
+#endif
+
// Call a faked try-block that does the invoke.
__ call(&invoke);
// Pop next_sp.
__ add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize));
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ // If current EBP value is the same as js_entry_sp value, it means that
+ // the current function is the outermost.
+ __ cmp(ebp, Operand::StaticVariable(js_entry_sp));
+ __ j(NegateCondition(equal), ¬_outermost_js_2);
+ __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
+ __ bind(¬_outermost_js_2);
+#endif
+
// Restore the top frame descriptor from the stack.
__ bind(&exit);
__ pop(Operand::StaticVariable(ExternalReference(Top::k_c_entry_fp_address)));
return;
}
+ const Address js_entry_sp = Top::js_entry_sp(Top::GetCurrentThread());
+ if (js_entry_sp == 0) {
+ // Not executing JS now.
+ sample->frames_count = 0;
+ return;
+ }
+
SafeStackTraceFrameIterator it(
reinterpret_cast<Address>(sample->fp),
reinterpret_cast<Address>(sample->sp),
reinterpret_cast<Address>(sample->sp),
- reinterpret_cast<Address>(low_stack_bound_));
+ js_entry_sp);
int i = 0;
while (!it.done() && i < TickSample::kMaxFramesCount) {
sample->stack[i++] = it.frame()->pc();
//
class Ticker: public Sampler {
public:
- explicit Ticker(int interval, uintptr_t low_stack_bound):
- Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL),
- stack_tracer_(low_stack_bound) {}
+ explicit Ticker(int interval):
+ Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL) {}
~Ticker() { if (IsActive()) Stop(); }
void Tick(TickSample* sample) {
- if (IsProfiling()) stack_tracer_.Trace(sample);
+ if (IsProfiling()) StackTracer::Trace(sample);
if (profiler_) profiler_->Insert(sample);
if (window_) window_->AddState(sample->state);
}
private:
SlidingStateWindow* window_;
Profiler* profiler_;
- StackTracer stack_tracer_;
};
current_state_ = &bottom_state_;
- // as log is initialized early with V8, we can assume that JS execution
- // frames can never reach this point on stack
- int stack_var;
- ticker_ = new Ticker(
- kSamplingIntervalMs, reinterpret_cast<uintptr_t>(&stack_var));
+ ticker_ = new Ticker(kSamplingIntervalMs);
if (FLAG_sliding_state_window && sliding_state_window_ == NULL) {
sliding_state_window_ = new SlidingStateWindow();
// Class that extracts stack trace, used for profiling.
-class StackTracer BASE_EMBEDDED {
+class StackTracer : public AllStatic {
public:
- explicit StackTracer(uintptr_t low_stack_bound)
- : low_stack_bound_(low_stack_bound) { }
- void Trace(TickSample* sample);
- private:
-
- uintptr_t low_stack_bound_;
+ static void Trace(TickSample* sample);
};
Address top_addresses[] = {
#define C(name) reinterpret_cast<Address>(Top::name()),
TOP_ADDRESS_LIST(C)
+ TOP_ADDRESS_LIST_PROF(C)
#undef C
NULL
};
void Top::InitializeThreadLocal() {
thread_local_.c_entry_fp_ = 0;
thread_local_.handler_ = 0;
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ thread_local_.js_entry_sp_ = 0;
+#endif
thread_local_.stack_is_cooked_ = false;
thread_local_.try_catch_handler_ = NULL;
thread_local_.context_ = NULL;
// Stack.
Address c_entry_fp_; // the frame pointer of the top c entry frame
Address handler_; // try-blocks are chained through the stack
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ Address js_entry_sp_; // the stack pointer of the bottom js entry frame
+#endif
bool stack_is_cooked_;
inline bool stack_is_cooked() { return stack_is_cooked_; }
inline void set_stack_is_cooked(bool value) { stack_is_cooked_ = value; }
C(pending_exception_address) \
C(external_caught_exception_address)
+#ifdef ENABLE_LOGGING_AND_PROFILING
+#define TOP_ADDRESS_LIST_PROF(C) \
+ C(js_entry_sp_address)
+#else
+#define TOP_ADDRESS_LIST_PROF(C)
+#endif
+
+
class Top {
public:
enum AddressId {
#define C(name) k_##name,
TOP_ADDRESS_LIST(C)
+ TOP_ADDRESS_LIST_PROF(C)
#undef C
k_top_address_count
};
}
static inline Address* handler_address() { return &thread_local_.handler_; }
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ // Bottom JS entry (see StackTracer::Trace in log.cc).
+ static Address js_entry_sp(ThreadLocalTop* thread) {
+ return thread->js_entry_sp_;
+ }
+ static inline Address* js_entry_sp_address() {
+ return &thread_local_.js_entry_sp_;
+ }
+#endif
+
// Generated code scratch locations.
static void* formal_count_address() { return &thread_local_.formal_count_; }
static struct {
- StackTracer* tracer;
TickSample* sample;
-} trace_env = { NULL, NULL };
+} trace_env = { NULL };
-static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
- trace_env.tracer = tracer;
+static void InitTraceEnv(TickSample* sample) {
trace_env.sample = sample;
}
// sp is only used to define stack high bound
trace_env.sample->sp =
reinterpret_cast<unsigned int>(trace_env.sample) - 10240;
- trace_env.tracer->Trace(trace_env.sample);
+ StackTracer::Trace(trace_env.sample);
}
v8::Handle<String> name);
static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args);
private:
static Address GetFP(const v8::Arguments& args);
static const char* kSource;
const char* TraceExtension::kSource =
"native function trace();"
- "native function js_trace();";
-
+ "native function js_trace();"
+ "native function js_entry_sp();"
+ "native function js_entry_sp_level2();";
v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
v8::Handle<String> name) {
return v8::FunctionTemplate::New(TraceExtension::Trace);
} else if (name->Equals(String::New("js_trace"))) {
return v8::FunctionTemplate::New(TraceExtension::JSTrace);
+ } else if (name->Equals(String::New("js_entry_sp"))) {
+ return v8::FunctionTemplate::New(TraceExtension::JSEntrySP);
+ } else if (name->Equals(String::New("js_entry_sp_level2"))) {
+ return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2);
} else {
CHECK(false);
return v8::Handle<v8::FunctionTemplate>();
}
+static Address GetJsEntrySp() {
+ CHECK_NE(NULL, Top::GetCurrentThread());
+ return Top::js_entry_sp(Top::GetCurrentThread());
+}
+
+
+v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) {
+ CHECK_NE(0, GetJsEntrySp());
+ return v8::Undefined();
+}
+
+
+static void CompileRun(const char* source) {
+ Script::Compile(String::New(source))->Run();
+}
+
+
+v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2(
+ const v8::Arguments& args) {
+ v8::HandleScope scope;
+ const Address js_entry_sp = GetJsEntrySp();
+ CHECK_NE(0, js_entry_sp);
+ CompileRun("js_entry_sp();");
+ CHECK_EQ(js_entry_sp, GetJsEntrySp());
+ return v8::Undefined();
+}
+
+
static TraceExtension kTraceExtension;
v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
}
-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));
}
TEST(CFromJSStackTrace) {
TickSample sample;
- StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
- InitTraceEnv(&tracer, &sample);
+ InitTraceEnv(&sample);
InitializeVM();
v8::HandleScope scope;
TEST(PureJSStackTrace) {
TickSample sample;
- StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
- InitTraceEnv(&tracer, &sample);
+ InitTraceEnv(&sample);
InitializeVM();
v8::HandleScope scope;
TEST(PureCStackTrace) {
TickSample sample;
- StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
- InitTraceEnv(&tracer, &sample);
+ InitTraceEnv(&sample);
// Check that sampler doesn't crash
CHECK_EQ(10, CFunc(10));
}
+TEST(JsEntrySp) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK_EQ(0, GetJsEntrySp());
+ CompileRun("a = 1; b = a + 1;");
+ CHECK_EQ(0, GetJsEntrySp());
+ CompileRun("js_entry_sp();");
+ CHECK_EQ(0, GetJsEntrySp());
+ CompileRun("js_entry_sp_level2();");
+ CHECK_EQ(0, GetJsEntrySp());
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING