| Index: test/cctest/test-log-ia32.cc
 | 
| diff --git a/test/cctest/test-log-ia32.cc b/test/cctest/test-log-ia32.cc
 | 
| index 588be7158c3e767ceaea885db135d10f4eb2bde9..b171339c24da6d9d83c83b7ad3270fef9ec25dbc 100644
 | 
| --- a/test/cctest/test-log-ia32.cc
 | 
| +++ b/test/cctest/test-log-ia32.cc
 | 
| @@ -9,6 +9,7 @@
 | 
|  #include "v8.h"
 | 
|  
 | 
|  #include "log.h"
 | 
| +#include "top.h"
 | 
|  #include "cctest.h"
 | 
|  
 | 
|  using v8::Function;
 | 
| @@ -23,6 +24,7 @@ using v8::internal::Handle;
 | 
|  using v8::internal::JSFunction;
 | 
|  using v8::internal::StackTracer;
 | 
|  using v8::internal::TickSample;
 | 
| +using v8::internal::Top;
 | 
|  
 | 
|  
 | 
|  static v8::Persistent<v8::Context> env;
 | 
| @@ -31,7 +33,7 @@ static v8::Persistent<v8::Context> env;
 | 
|  static struct {
 | 
|    StackTracer* tracer;
 | 
|    TickSample* sample;
 | 
| -} trace_env;
 | 
| +} trace_env = { NULL, NULL };
 | 
|  
 | 
|  
 | 
|  static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
 | 
| @@ -42,62 +44,43 @@ static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
 | 
|  
 | 
|  static void DoTrace(unsigned int fp) {
 | 
|    trace_env.sample->fp = fp;
 | 
| -  // something that is less than fp
 | 
| -  trace_env.sample->sp = trace_env.sample->fp - 100;
 | 
| +  // 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);
 | 
|  }
 | 
|  
 | 
|  
 | 
| -static void CFuncDoTrace() {
 | 
| -  unsigned int fp;
 | 
| -#ifdef __GNUC__
 | 
| -  fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0));
 | 
| -#elif defined _MSC_VER
 | 
| -  __asm mov [fp], ebp  // NOLINT
 | 
| -#endif
 | 
| +// Hide c_entry_fp to emulate situation when sampling is done while
 | 
| +// pure JS code is being executed
 | 
| +static void DoTraceHideCEntryFPAddress(unsigned int fp) {
 | 
| +  v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
 | 
| +  CHECK(saved_c_frame_fp);
 | 
| +  *(Top::c_entry_fp_address()) = 0;
 | 
|    DoTrace(fp);
 | 
| +  *(Top::c_entry_fp_address()) = saved_c_frame_fp;
 | 
|  }
 | 
|  
 | 
|  
 | 
| -static void CFunc(int i) {
 | 
| -  for (int j = i; j >= 0; --j) {
 | 
| -    CFuncDoTrace();
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -
 | 
| -static void CheckRetAddrIsInFunction(unsigned int ret_addr,
 | 
| +static void CheckRetAddrIsInFunction(const char* func_name,
 | 
| +                                     unsigned int ret_addr,
 | 
|                                       unsigned int func_start_addr,
 | 
|                                       unsigned int func_len) {
 | 
| -  printf("CheckRetAddrIsInFunction: %08x %08x %08x\n",
 | 
| -         func_start_addr, ret_addr, func_start_addr + func_len);
 | 
| +  printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\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);
 | 
|  }
 | 
|  
 | 
|  
 | 
| -#ifdef DEBUG
 | 
| -static const int kMaxCFuncLen = 0x40;  // seems enough for a small C function
 | 
| -
 | 
| -static void CheckRetAddrIsInCFunction(unsigned int ret_addr,
 | 
| -                                      unsigned int func_start_addr) {
 | 
| -  CheckRetAddrIsInFunction(ret_addr, func_start_addr, kMaxCFuncLen);
 | 
| -}
 | 
| -#endif
 | 
| -
 | 
| -
 | 
| -TEST(PureCStackTrace) {
 | 
| -  TickSample sample;
 | 
| -  StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
 | 
| -  InitTraceEnv(&tracer, &sample);
 | 
| -  CFunc(0);
 | 
| -#ifdef DEBUG
 | 
| -  // C stack trace works only in debug mode, in release mode EBP is
 | 
| -  // usually treated as a general-purpose register
 | 
| -  CHECK_GT(sample.frames_count, 0);
 | 
| -  CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]),
 | 
| -                            reinterpret_cast<unsigned int>(&CFunc));
 | 
| -#endif
 | 
| +static void CheckRetAddrIsInJSFunction(const char* func_name,
 | 
| +                                       unsigned int ret_addr,
 | 
| +                                       Handle<JSFunction> func) {
 | 
| +  v8::internal::Code* func_code = func->code();
 | 
| +  CheckRetAddrIsInFunction(
 | 
| +      func_name, ret_addr,
 | 
| +      reinterpret_cast<unsigned int>(func_code->instruction_start()),
 | 
| +      func_code->ExecutableSize());
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -107,27 +90,49 @@ class TraceExtension : public v8::Extension {
 | 
|   public:
 | 
|    TraceExtension() : v8::Extension("v8/trace", kSource) { }
 | 
|    virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
 | 
| -      v8::Handle<v8::String> name);
 | 
| +      v8::Handle<String> name);
 | 
|    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 const char* kSource;
 | 
|  };
 | 
|  
 | 
|  
 | 
| -const char* TraceExtension::kSource = "native function trace();";
 | 
| +const char* TraceExtension::kSource =
 | 
| +    "native function trace();"
 | 
| +    "native function js_trace();";
 | 
|  
 | 
|  
 | 
|  v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
 | 
| -    v8::Handle<v8::String> str) {
 | 
| -  return v8::FunctionTemplate::New(TraceExtension::Trace);
 | 
| +    v8::Handle<String> name) {
 | 
| +  if (name->Equals(String::New("trace"))) {
 | 
| +    return v8::FunctionTemplate::New(TraceExtension::Trace);
 | 
| +  } else if (name->Equals(String::New("js_trace"))) {
 | 
| +    return v8::FunctionTemplate::New(TraceExtension::JSTrace);
 | 
| +  } else {
 | 
| +    CHECK(false);
 | 
| +    return v8::Handle<v8::FunctionTemplate>();
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  
 | 
| -v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
 | 
| +unsigned int TraceExtension::GetFP(const v8::Arguments& args) {
 | 
|    CHECK_EQ(1, args.Length());
 | 
|    unsigned int fp = args[0]->Int32Value() << 2;
 | 
|    printf("Trace: %08x\n", fp);
 | 
| -  DoTrace(fp);
 | 
| +  return fp;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
 | 
| +  DoTrace(GetFP(args));
 | 
| +  return v8::Undefined();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
 | 
| +  DoTraceHideCEntryFPAddress(GetFP(args));
 | 
|    return v8::Undefined();
 | 
|  }
 | 
|  
 | 
| @@ -163,6 +168,21 @@ static Local<Value> GetGlobalProperty(const char* 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,
 | 
| +                                       unsigned int 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);
 | 
|  }
 | 
| @@ -188,17 +208,17 @@ static bool Patch(byte* from,
 | 
|  }
 | 
|  
 | 
|  
 | 
| -TEST(PureJSStackTrace) {
 | 
| -  TickSample sample;
 | 
| -  StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
 | 
| -  InitTraceEnv(&tracer, &sample);
 | 
| -
 | 
| -  InitializeVM();
 | 
| -  v8::HandleScope scope;
 | 
| -  Handle<JSFunction> call_trace = CompileFunction("trace(0x6666);");
 | 
| -  CHECK(!call_trace.is_null());
 | 
| -  v8::internal::Code* call_trace_code = call_trace->code();
 | 
| -  CHECK(call_trace_code->IsCode());
 | 
| +// 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) {
 | 
| +  ::v8::internal::EmbeddedVector<char, 256> trace_call_buf;
 | 
| +  ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name);
 | 
| +  Handle<JSFunction> func = CompileFunction(trace_call_buf.start());
 | 
| +  CHECK(!func.is_null());
 | 
| +  v8::internal::Code* func_code = func->code();
 | 
| +  CHECK(func_code->IsCode());
 | 
|  
 | 
|    // push 0xcccc (= 0x6666 << 1)
 | 
|    byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 };
 | 
| @@ -206,29 +226,89 @@ TEST(PureJSStackTrace) {
 | 
|    byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 };
 | 
|    // Patch generated code to replace pushing of a constant with
 | 
|    // pushing of ebp contents in a Smi
 | 
| -  CHECK(Patch(call_trace_code->instruction_start(),
 | 
| -              call_trace_code->instruction_size(),
 | 
| +  CHECK(Patch(func_code->instruction_start(),
 | 
| +              func_code->instruction_size(),
 | 
|                original, patch, sizeof(patch)));
 | 
|  
 | 
| -  SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace));
 | 
| +  SetGlobalProperty(func_name, v8::ToApi<Value>(func));
 | 
| +}
 | 
| +
 | 
|  
 | 
| +TEST(CFromJSStackTrace) {
 | 
| +  TickSample sample;
 | 
| +  StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
 | 
| +  InitTraceEnv(&tracer, &sample);
 | 
| +
 | 
| +  InitializeVM();
 | 
| +  v8::HandleScope scope;
 | 
| +  CreateTraceCallerFunction("JSFuncDoTrace", "trace");
 | 
|    CompileRun(
 | 
|        "function JSTrace() {"
 | 
|        "  JSFuncDoTrace();"
 | 
|        "};\n"
 | 
|        "JSTrace();");
 | 
|    CHECK_GT(sample.frames_count, 1);
 | 
| -  CheckRetAddrIsInFunction(
 | 
| -      reinterpret_cast<unsigned int>(sample.stack[0]),
 | 
| -      reinterpret_cast<unsigned int>(call_trace_code->instruction_start()),
 | 
| -      call_trace_code->instruction_size());
 | 
| -  Handle<JSFunction> js_trace(JSFunction::cast(*(v8::Utils::OpenHandle(
 | 
| -      *GetGlobalProperty("JSTrace")))));
 | 
| -  v8::internal::Code* js_trace_code = js_trace->code();
 | 
| -  CheckRetAddrIsInFunction(
 | 
| -      reinterpret_cast<unsigned int>(sample.stack[1]),
 | 
| -      reinterpret_cast<unsigned int>(js_trace_code->instruction_start()),
 | 
| -      js_trace_code->instruction_size());
 | 
| +  // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
 | 
| +  CheckRetAddrIsInJSFunction("JSFuncDoTrace",
 | 
| +                             reinterpret_cast<unsigned int>(sample.stack[0]));
 | 
| +  CheckRetAddrIsInJSFunction("JSTrace",
 | 
| +                             reinterpret_cast<unsigned int>(sample.stack[1]));
 | 
| +}
 | 
| +
 | 
| +
 | 
| +TEST(PureJSStackTrace) {
 | 
| +  TickSample sample;
 | 
| +  StackTracer tracer(reinterpret_cast<unsigned int>(&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",
 | 
| +                             reinterpret_cast<unsigned int>(sample.stack[0]));
 | 
| +  CheckRetAddrIsInJSFunction("OuterJSTrace",
 | 
| +                             reinterpret_cast<unsigned int>(sample.stack[1]));
 | 
|  }
 | 
|  
 | 
| +
 | 
| +static void CFuncDoTrace() {
 | 
| +  unsigned int fp;
 | 
| +#ifdef __GNUC__
 | 
| +  fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0));
 | 
| +#elif defined _MSC_VER
 | 
| +  __asm mov [fp], ebp  // NOLINT
 | 
| +#endif
 | 
| +  DoTrace(fp);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static int CFunc(int depth) {
 | 
| +  if (depth <= 0) {
 | 
| +    CFuncDoTrace();
 | 
| +    return 0;
 | 
| +  } else {
 | 
| +    return CFunc(depth - 1) + 1;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +TEST(PureCStackTrace) {
 | 
| +  TickSample sample;
 | 
| +  StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
 | 
| +  InitTraceEnv(&tracer, &sample);
 | 
| +  // Check that sampler doesn't crash
 | 
| +  CHECK_EQ(10, CFunc(10));
 | 
| +}
 | 
| +
 | 
| +
 | 
|  #endif  // ENABLE_LOGGING_AND_PROFILING
 | 
| 
 |