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
-~----------~----~----~----~------~----~------~--~---