Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Decode C/C++ and V8 JS stack frames.
#include "v8.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <cxxabi.h>
#include <dlfcn.h>
using namespace v8;
static Handle<Value> Enter(const Arguments& args);
static Handle<Value> Trace(const Arguments& args);
static void backtrace();
static void** stack_top;
int main(int argc, char** argv)
{
void* stack_canary;
stack_top = &stack_canary;
V8::SetFlagsFromCommandLine(&argc, argv, true);
HandleScope handle_scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
Local<Object> global = context->Global();
global->Set(String::New("enter"),
FunctionTemplate::New(Enter)->GetFunction());
global->Set(String::New("trace"),
FunctionTemplate::New(Trace)->GetFunction());
Local<String> script_source = String::New(
"enter(next); \n"
"function next() { \n"
" hop(); \n"
"} \n"
"function hop() { \n"
" trace(); \n"
"} \n");
Local<String> script_name = String::New("<internal>");
Local<Script> script = Script::New(script_source, script_name);
assert(script.IsEmpty() == false);
Local<Value> rc = script->Run();
assert(rc.IsEmpty() == false);
}
static Handle<Value> Enter(const Arguments& args)
{
assert(args[0]->IsFunction());
HandleScope handle_scope;
Local<Object> global = Context::GetCurrent()->Global();
Local<Function> fun = args[0].As<Function>();
Local<Value> rc = fun->Call(global, 0, NULL);
assert(rc.IsEmpty() == false);
return handle_scope.Close(rc);
}
static Handle<Value> Trace(const Arguments&)
{
backtrace();
return Undefined();
}
#define OFFSET(base, addr) \
(static_cast<long>(static_cast<const char*>(addr) - \
static_cast<const char*>(base)))
struct Frame
{
const Frame* frame_pointer;
const void* return_address;
};
// Linked list. Wildly inefficient.
struct Code
{
Code* next;
const void* start;
const void* end;
char name[1];
};
static int stack_trace_index;
static Local<StackTrace> stack_trace;
static struct Code* code_head;
static void add_code(const char* name,
unsigned int namelen,
const void* start,
const void* end)
{
Code* code = static_cast<Code*>(malloc(sizeof(*code) + namelen));
if (code == NULL) return;
memcpy(code->name, name, namelen);
code->name[namelen] = '\0';
code->start = start;
code->end = end;
code->next = code_head;
code_head = code;
}
static void free_code()
{
while (code_head != NULL) {
Code* code = code_head;
code_head = code->next;
free(code);
}
}
static Code* find_code(const void* addr)
{
for (Code* code = code_head; code != NULL; code = code->next)
if (code->start <= addr && code->end >= addr)
return code;
return NULL;
}
static void jit_code_event(const JitCodeEvent* ev)
{
if (ev->type == JitCodeEvent::CODE_ADDED) {
add_code(ev->name.str,
ev->name.len,
ev->code_start,
static_cast<const char*>(ev->code_start) + ev->code_len);
}
}
static bool print_c_frame(const Frame* frame, FILE* stream)
{
Dl_info info;
if (dladdr(frame->return_address, &info) == 0)
return false;
const char* name = info.dli_sname;
const char* demangled_name = abi::__cxa_demangle(name, NULL, NULL, NULL);
if (demangled_name != NULL)
name = demangled_name;
fprintf(stream,
"%lx+%lx %s %s(%p)\n",
reinterpret_cast<long>(info.dli_saddr),
OFFSET(info.dli_saddr, frame->return_address),
name,
info.dli_fname,
info.dli_fbase);
if (name == demangled_name)
free(const_cast<char*>(name));
return true;
}
static bool print_js_frame(const Frame* frame, FILE* stream)
{
if (code_head == NULL) {
// Lazy init.
V8::SetJitCodeEventHandler(kJitCodeEventEnumExisting, jit_code_event);
V8::SetJitCodeEventHandler(kJitCodeEventDefault, NULL);
stack_trace = StackTrace::CurrentStackTrace(64);
stack_trace_index = 0;
}
Code* code = find_code(frame->return_address);
if (code == NULL)
return false;
if (stack_trace_index < stack_trace->GetFrameCount()) {
Local<StackFrame> js_frame = stack_trace->GetFrame(stack_trace_index++);
String::Utf8Value function_name(js_frame->GetFunctionName());
if (function_name.length() > 0) {
String::Utf8Value script_name(js_frame->GetScriptName());
fprintf(stream,
"%lx+%lx %s %s:%d:%d\n",
reinterpret_cast<long>(code->start),
OFFSET(code->start, frame->return_address),
*function_name,
*script_name,
js_frame->GetLineNumber(),
js_frame->GetColumn());
return true;
}
}
fprintf(stream,
"%lx+%lx %s\n",
reinterpret_cast<long>(code->start),
OFFSET(code->start, frame->return_address),
code->name);
return true;
}
static void print_frame(const Frame* frame, FILE* stream)
{
if (print_c_frame(frame, stream))
return;
if (print_js_frame(frame, stream))
return;
// Unresolved. Just print the raw address.
fprintf(stream, "%lx\n", reinterpret_cast<long>(frame->return_address));
}
__attribute__((noinline))
void backtrace(void)
{
HandleScope handle_scope;
const Frame* frame;
__asm__ __volatile__ ("mov %%rbp, %0" : "=g" (frame));
while (frame < reinterpret_cast<Frame*>(stack_top)) {
print_frame(frame, stderr);
frame = frame->frame_pointer;
}
free_code();
}
@bnoordhuis

This comment has been minimized.

Copy link
Owner Author

commented Mar 19, 2013

$ cd v8
$ make -j4 x64.debug library=shared extrachecks=on
$ g++ -g -Iinclude -Wall -Wextra -o tmp/stacktrace tmp/stacktrace.cc -Lout/x64.debug -lv8
$ DYLD_LIBRARY_PATH=out/x64.debug tmp/stacktrace
103bb4c00+11 Trace(v8::Arguments const&) /Users/bnoordhuis/src/v8/tmp/stacktrace(0x103bb3000)
103c38ff0+415 v8::internal::MaybeObject* v8::internal::HandleApiCallHelper<false>(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103c38fb0+33 v8::internal::Builtin_Impl_HandleApiCall(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103c32710+7c v8::internal::Builtin_HandleApiCall(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
1b51c6b062a0+4e hop <internal>:6:3
1b51c6b2d400+23 next <internal>:3:3
1b51c6b2d320+23 LazyCompile:~next <internal>:2
1b51c6b25e00+64 Builtin:A builtin from the snapshot
1b51c6b07920+d7 Stub:JSEntryStub
103ca5c20+21d v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103ca56d0+224 v8::internal::Execution::Call(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*, bool) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103be04e0+248 v8::Function::Call(v8::Handle<v8::Object>, int, v8::Handle<v8::Value>*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103bb4480+5a7 Enter(v8::Arguments const&) /Users/bnoordhuis/src/v8/tmp/stacktrace(0x103bb3000)
103c38ff0+415 v8::internal::MaybeObject* v8::internal::HandleApiCallHelper<false>(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103c38fb0+33 v8::internal::Builtin_Impl_HandleApiCall(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103c32710+7c v8::internal::Builtin_HandleApiCall(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
1b51c6b062a0+4e Stub:CEntryStub
1b51c6b2d200+5e Script:~<internal>
1b51c6b25e00+64 Builtin:A builtin from the snapshot
1b51c6b07920+d7 Stub:JSEntryStub
103ca5c20+21d v8::internal::Invoke(bool, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103ca56d0+224 v8::internal::Execution::Call(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, bool*, bool) /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103bd2bf0+2e4 v8::Script::Run() /Users/bnoordhuis/src/v8/out/x64.debug/libv8.dylib(0x103bc0000)
103bb3950+834 main /Users/bnoordhuis/src/v8/tmp/stacktrace(0x103bb3000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.