V8 inspector from an embedder standpoint
It’s been recently that the old V8 debugger API has been removed from V8’s source code in favor of the more modern Inspector API.
This Inspector API is great, and allows me to debug my embedded Android V8 code using Chrome dev tools directly from the browser, or by using an standalone version of them. Profiling, memory dumps, source maps, breakpoints, all works like a charm (except minor bugs here and there mainly related to chrome version though). Unfortunately, there’s not much documentation on this Inspector integration from the embedder point of view.
Inspector integration process
The first to note about the Inspector is that inspection is per
Isolate. One single
Context s. The
Isolate is thread dependent, and as such you must keep your isolate in scope
Isolate::Scope when necessary. That said, the elements that will conform your inspection code are very simple:
This object will be used to select what
Context we are currently debugging, but more importantly, it will handle
quitMessageLoopOnPause methods. These two methods are called by V8 debugging internals when you are breaking into
js code from Dev Tools. While
runMessageLoopOnPause is being called, you must synchronously consume all front end (Dev Tools) debugging messages. If not, you will not get all context information of the code you are debugging. Once V8 knows it has no more inspector messages pending, it will call
The InspectorClient could do the debugging initialisation process like this:
// create a v8 inspector client:
// InspectorClientImpl : public v8_inspector::V8InspectorClient
v8_inspector::InspectorClient = new InspectorClientImpl();
// create a v8 inspector instance.
v8_inspector::V8Inspector inspector_ =
v8_inspector::V8Inspector.create( isolate, inspectorClient );
// create a v8 channel.
// ChannelImpl : public v8_inspector::V8Inspector::Channel
v8_inspector::V8Inspector::Channel channel_ = new ChannelImpl();
v8_inspector::StringView view( ... )
// Create a debugging session by connecting the V8Inspector
// instance to the channel
v8_inspector::V8InspectorSession session_ =
v8_inspector::StringView ctx_name( /*ctx_name*/ )
// make sure you register Context objects in the V8Inspector.
// ctx_name will be shown in CDT/console. Call this for each context
// your app creates. Normally just one btw.
That’s pretty much it. After this, you’ll have a valid debugging session. How do you, as a dev, interact with each of these elements:
V8InspectorSession ? Well, to answer this question, first we call all this code happening in our V8-enabled app the debugging backend, which implicitly means we should have a debugging front-end.
Ideally, the debugging front-end would be Chrome Dev Tools. CDT opens a
WebSocket to communicate with the debugging back-end. You can make this happen in Chrome with something like this:
This causes chrome to open a dev tools only tab, w/o most DOM specific stuff. In my case, the 20000 is a forwarded port from my android app to a local port
(adb forward tcp:20000 tcp:20000) and
/backend in the url is a mount point on the backend
WebSocket listener. All front end inspector messages will be received on the backend websocket listening code, and must be forwarded to the debug session:
// msg is a std::string with whatever front sent to back.
// normally a json object with sequence and payload.
v8::internal::Vector<const char> v(msg.c_str(), msg.length());
// inspector session requires a v8_inspector::StringView
reinterpret_cast<const uint8_t *>(v.start()), v.length());
// let the magic happen:
session_->dispatchProtocolMessage( message_view );
The V8InspectorSession object is full of inspection love. I recommend you having a look at
v8-inspector.h header file. While all interaction happens from CDT front end, you’ll recognise a lot of functionality there like
breakProgram, pause or resume methods.
All inspector protocol handling happens automagically. You don’t have to worry about front end message id sequences, or their responses. The only missing part is forward inspector session message results from backend to front end. Responses happen in the custom
v8_inspector::V8Inspector::Channel object implementation. Both methods:
sendProtocolResponse(int callId,const v8_inspector::StringView& msg)
void sendProtocolNotification(const v8_inspector::StringView& msg)
will handle inspector protocol responses from commands received from inspection front end. Just convert
StringView to std::string (or whatever your code requires) and send to front end
This is a small diagram of how things work:
At the end of the process, you’ll get a full browser-enabled remote v8 debugging session. Here’s an screenshot of a sample app. All objects but
console are custom bound native objects. In this sample screenshot, the host application OS is Android.