Skip to content

Instantly share code, notes, and snippets.

@mmarchini
Last active September 3, 2018 14:49
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 mmarchini/647720e08468b8b96a7922f79c20c87e to your computer and use it in GitHub Desktop.
Save mmarchini/647720e08468b8b96a7922f79c20c87e to your computer and use it in GitHub Desktop.
V8 Postmortem API Usage Example
#include <v8.h>
#include <v8-postmortem.h>
#include <napi.h>
#include <lldb/API/SBDebugger.h>
#include <lldb/API/SBTarget.h>
#include <lldb/API/SBProcess.h>
#include <iostream>
using lldb::addr_t;
bool LookupConstant(lldb::SBTarget target, const char* name, PostmortemTips* tips) {
lldb::SBSymbolContextList context_list = target.FindSymbols(name);
if (!context_list.IsValid() || context_list.GetSize() == 0) {
return false;
}
lldb::SBSymbolContext context = context_list.GetContextAtIndex(0);
lldb::SBSymbol symbol = context.GetSymbol();
if (!symbol.IsValid()) {
return false;
}
lldb::SBAddress start = symbol.GetStartAddress();
lldb::SBAddress end = symbol.GetEndAddress();
uint32_t size = end.GetOffset() - start.GetOffset();
if (size != sizeof(PostmortemTips)) {
return false;
}
lldb::SBError sberr;
target.ReadMemory(start, tips, size, sberr);
return true;
}
class DebuggerAnalyzer : public v8::PostmortemAnalyzer {
public:
explicit DebuggerAnalyzer(lldb::SBProcess* process) : process_(process) {};
uintptr_t ReadPointer(const uintptr_t address) override {
lldb::SBError err;
std::cout << "reading pointer @ 0x" << std::hex << address << std::dec << std::endl;
auto ptr = process_->ReadPointerFromMemory(static_cast<addr_t>(address), err);
return static_cast<uintptr_t>(ptr);
}
private:
lldb::SBProcess* process_;
};
Napi::String Heap(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 2) {
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
return Napi::String::New(env, "");
}
auto arg0 = info[0].As<Napi::String>();
auto arg1 = info[1].As<Napi::String>();
lldb::SBDebugger::Initialize();
auto debugger = lldb::SBDebugger::Create();
auto target = debugger.CreateTarget(std::string(arg0).c_str());
if (!target.IsValid()) {
Napi::Error::New(env, "Invalid target").ThrowAsJavaScriptException();
return Napi::String::New(env, "");
}
auto process = target.LoadCore(std::string(arg1).c_str());
SherlockAnalyzer analyzer(&process);
analyzer.Enable();
PostmortemTips tips;
if (!LookupConstant(target, "v8dbg_postmortem_tips", &tips)) {
analyzer.Disable();
Napi::Error::New(env, "Failed").ThrowAsJavaScriptException();
return Napi::String::New(env, "");
}
auto isolate = analyzer.Get<v8::Isolate*>(reinterpret_cast<uintptr_t>(tips.current_isolate));
v8::PostmortemAnalyzer::HeapIterator heap_iterator(isolate);
v8::Value* val = nullptr;
std::cout << "Iterating the heap" << std::endl;
while ((val = heap_iterator.next()) != nullptr) {
if (val->IsObject()) std::cout << "Found an Object" << std::endl;
else std::cout << "Found something else" << std::endl;
}
std::cout << "Done" << std::endl;
analyzer.Disable();
return Napi::String::New(env, std::string(arg0) + " - " + std::string(arg1));
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "heap"), Napi::Function::New(env, Heap));
return exports;
}
NODE_API_MODULE(debugger, Init)
#ifndef __DEBUGGER_H
#define __DEBUGGER_H
#include "v8-profiler.h"
#include <nan.h>
#include <node_object_wrap.h>
#include <fstream>
namespace node {
class Debugger : public Nan::ObjectWrap {
public:
static void Initialize(v8::Local<v8::Object> target);
static NAN_METHOD(New);
static NAN_METHOD(Heap);
Debugger() = default;
~Debugger() = default;
};
};
#endif // __DEBUGGER_H
'use strict';
const binding = require('./build/Release/debugger');
module.exports = binding;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment