Author: [email protected]
Date: Wed Feb 25 08:00:21 2009
New Revision: 1365

Added:
    branches/bleeding_edge/test/cctest/test-log-ia32.cc
Modified:
    branches/bleeding_edge/src/log.cc
    branches/bleeding_edge/src/log.h
    branches/bleeding_edge/test/cctest/SConscript
    branches/bleeding_edge/tools/visual_studio/v8_cctest.vcproj

Log:
Adding unit tests for profiler's stack tracer.

The testing is a bit tricky because we need to obtain a frame
pointer (EBP on IA-32) from inside of a function. This is especially
interesting in case of a compiled JavaScript function.

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

Modified: branches/bleeding_edge/src/log.cc
==============================================================================
--- branches/bleeding_edge/src/log.cc   (original)
+++ branches/bleeding_edge/src/log.cc   Wed Feb 25 08:00:21 2009
@@ -134,6 +134,22 @@


  //
+// StackTracer implementation
+//
+void StackTracer::Trace(TickSample* sample) {
+  // Assuming that stack grows from lower addresses
+  if (sample->sp < sample->fp && sample->fp < low_stack_bound_) {
+    sample->InitStack(1);
+    sample->stack[0] = Memory::Address_at(
+        (Address)(sample->fp + StandardFrameConstants::kCallerPCOffset));
+  } else {
+    // FP seems to be in some intermediate state, better discard this  
sample
+    sample->InitStack(0);
+  }
+}
+
+
+//
  // Ticker used to provide ticks to the profiler and the sliding state
  // window.
  //
@@ -141,12 +157,12 @@
   public:
    explicit Ticker(int interval, unsigned int low_stack_bound):
        Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL),
-      low_stack_bound_(low_stack_bound) {}
+      stack_tracer_(low_stack_bound) {}

    ~Ticker() { if (IsActive()) Stop(); }

    void Tick(TickSample* sample) {
-    if (IsProfiling()) SampleStack(sample);
+    if (IsProfiling()) stack_tracer_.Trace(sample);
      if (profiler_) profiler_->Insert(sample);
      if (window_) window_->AddState(sample->state);
    }
@@ -172,21 +188,9 @@
    }

   private:
-  void SampleStack(TickSample* sample) {
-    // Assuming that stack grows from lower addresses
-    if (sample->sp < sample->fp && sample->fp < low_stack_bound_) {
-      sample->InitStack(1);
-      sample->stack[0] = Memory::Address_at(
-          (Address)(sample->fp + StandardFrameConstants::kCallerPCOffset));
-    } else {
-      // FP seems to be in some intermediate state, better discard this  
sample
-      sample->InitStack(0);
-    }
-  }
-
    SlidingStateWindow* window_;
    Profiler* profiler_;
-  unsigned int low_stack_bound_;
+  StackTracer stack_tracer_;
  };



Modified: branches/bleeding_edge/src/log.h
==============================================================================
--- branches/bleeding_edge/src/log.h    (original)
+++ branches/bleeding_edge/src/log.h    Wed Feb 25 08:00:21 2009
@@ -272,6 +272,17 @@
  };


+// Class that extracts stack trace, used for profiling
+class StackTracer BASE_EMBEDDED {
+ public:
+  StackTracer(unsigned int low_stack_bound):  
low_stack_bound_(low_stack_bound) {
+  }
+  void Trace(TickSample* sample);
+ private:
+  unsigned int low_stack_bound_;
+};
+
+
  } }  // namespace v8::internal

  #endif  // V8_LOG_H_

Modified: branches/bleeding_edge/test/cctest/SConscript
==============================================================================
--- branches/bleeding_edge/test/cctest/SConscript       (original)
+++ branches/bleeding_edge/test/cctest/SConscript       Wed Feb 25 08:00:21 2009
@@ -42,7 +42,7 @@
      'test-sockets.cc'
    ],
    'arch:arm':  ['test-assembler-arm.cc', 'test-disasm-arm.cc'],
-  'arch:ia32': ['test-assembler-ia32.cc', 'test-disasm-ia32.cc'],
+  'arch:ia32':  
['test-assembler-ia32.cc', 'test-disasm-ia32.cc', 'test-log-ia32.cc'],
    'os:linux':  ['test-platform-linux.cc'],
    'os:macos':  ['test-platform-macos.cc'],
    'os:nullos': ['test-platform-nullos.cc'],

Added: branches/bleeding_edge/test/cctest/test-log-ia32.cc
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/test/cctest/test-log-ia32.cc Wed Feb 25 08:00:21  
2009
@@ -0,0 +1,219 @@
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+//
+// Tests of profiler-related functions from log.h
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "log.h"
+#include "cctest.h"
+
+using v8::Function;
+using v8::Local;
+using v8::Object;
+using v8::Script;
+using v8::String;
+using v8::Value;
+
+using v8::internal::byte;
+using v8::internal::Handle;
+using v8::internal::JSFunction;
+using v8::internal::StackTracer;
+using v8::internal::TickSample;
+
+
+static v8::Persistent<v8::Context> env;
+
+
+static struct {
+  StackTracer* tracer;
+  TickSample* sample;
+} trace_env;
+
+
+static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
+  trace_env.tracer = tracer;
+  trace_env.sample = 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 - sizeof(unsigned int);
+  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
+#endif
+  DoTrace(fp);
+}
+
+
+static void CFunc(int i) {
+  for (int j = i; j >= 0; --j) {
+    CFuncDoTrace();
+  }
+}
+
+
+static void CheckRetAddrIsInFunction(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);
+  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
+  CheckRetAddrIsInCFunction(reinterpret_cast<unsigned  
int>(sample.stack[0]),
+                            reinterpret_cast<unsigned int>(&CFunc));
+  CHECK_EQ(0, sample.stack[1]);
+#endif
+}
+
+
+// --- T r a c e   E x t e n s i o n ---
+
+class TraceExtension : public v8::Extension {
+ public:
+  TraceExtension() : v8::Extension("v8/trace", kSource) { }
+  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+      v8::Handle<v8::String> name);
+  static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
+ private:
+  static const char* kSource;
+};
+
+
+const char* TraceExtension::kSource = "native function trace();";
+
+
+v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
+    v8::Handle<v8::String> str) {
+  return v8::FunctionTemplate::New(TraceExtension::Trace);
+}
+
+
+v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
+  CHECK_EQ(1, args.Length());
+  unsigned int fp = args[0]->Int32Value() << 2;
+  printf("Trace: %08x\n", fp);
+  DoTrace(fp);
+  return v8::Undefined();
+}
+
+
+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 void SetGlobalProperty(const char* name, Local<Value> value) {
+  env->Global()->Set(String::New(name), value);
+}
+
+
+static bool Patch(byte* from, size_t num, byte* original, byte* patch,  
size_t patch_len) {
+  byte* to = from + num;
+  do {
+    from = (byte*)memchr(from, *original, to - from);
+    CHECK(from != NULL);
+    if (memcmp(original, from, patch_len) == 0) {
+      memcpy(from, patch, patch_len);
+      return true;
+    } else {
+      from++;
+    }
+  } while (to - from > 0);
+  return false;
+}
+
+
+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());
+
+  byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; // push 0xcccc (=  
0x6666 << 1)
+  byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; // mov eax,ebp; shr  
eax; push eax;
+  // 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(),
+              original, patch, sizeof(patch)));
+
+  SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace));
+
+  CompileRun(
+      "function JSTrace() {"
+      "  JSFuncDoTrace();"
+      "};\n"
+      "JSTrace();");
+  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[0]),
+                           reinterpret_cast<unsigned  
int>(js_trace_code->instruction_start()),
+                           js_trace_code->instruction_size());
+  CHECK_EQ(0, sample.stack[1]);
+}

Modified: branches/bleeding_edge/tools/visual_studio/v8_cctest.vcproj
==============================================================================
--- branches/bleeding_edge/tools/visual_studio/v8_cctest.vcproj (original)
+++ branches/bleeding_edge/tools/visual_studio/v8_cctest.vcproj Wed Feb 25  
08:00:21 2009
@@ -198,6 +198,10 @@
                        >
                </File>
                <File
+                       RelativePath="..\..\test\cctest\test-log-ia32.cc"
+                       >
+               </File>
+               <File
                        RelativePath="..\..\test\cctest\test-mark-compact.cc"
                        >
                </File>

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to