Skip to content

Instantly share code, notes, and snippets.

@yjhjstz
Forked from bnoordhuis/stacktrace.cc
Created January 28, 2016 09:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yjhjstz/14c64f42a6f23d807808 to your computer and use it in GitHub Desktop.
Save yjhjstz/14c64f42a6f23d807808 to your computer and use it in GitHub Desktop.
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();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment