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 Inspector object instance will be enough to debug all your javascript 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 runMessageLoopOnPause and 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 quitMessageLoopOnPause automatically.

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: V8InspectorClientV8InspectorV8Inspector::ChannelV8InspectorSession ? 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
v8_inspector::StringView message_view(
    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:

    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 msg from 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.

Also note, this inspector-over-devtools integration also works on the Android emulator. In my Mac, I have an android emulator, running my app with embedded v8 and connected to dev tools on chrome to debug javascript… what a time to be alive !

Published by ibon

Chocolate engineer, software eater. Cool stuff at PlatoApp.com Past: Workday, Platochat, SdkBox, Chukong, Ludei.

8 thoughts on “V8 inspector from an embedder standpoint

  1. Hello,

    that’s a very nice article and I appreciate it. But how do you exactly load this InspectorClient in d8? Where do you put it? You wouldn’t have a working example somewhere on github, would you?


  2. Which WebSocket library are you using for cross-platform support (you mentioned Android and Mac)? Any chance you’ll consider open sourcing your implementation? Thanks!


    1. I am using a random websockets provider for android. Anyone will do.
      I will consider open sourcing all my v8 related stuff. The inspector is just one piece of it.


  3. Hi,
    Can I call make a call to js callback method from within runMessageLoopOnPause method ? ( so, calling JS method from C++ side via v8 api )
    or i can only make a call to session_->dispatchProtocolMessage method and forward the messages received from frontend ?


    1. When you run from runMessageLoopOnPause, v8 is synchronously calling to the outside world, primarily to dev tools front end. Javascript execution is paused, and the thread holding the isolate is waiting for a response, so no javascript can run at this point.
      V8 is only expecting you to call into v8 using the backend debugging session channel (dispatchProtocolMessage).

      Since the thread holding the isolate is locked, you won’t be able to call into javascript at all. Bear also in mind, you should consider runMessageLoopOnPause to be reentrant.

      The information expected to be exchanged during this dialog between front/back end endopoints, are things like closure state, stack trace, variable values, etc. Whatever the frontend needs to show a proper debugging state.
      Also, your JS thread will keep locked until v8 says otherwise (quitMessageLoopOnPause). E.g., when resuming execution. Or temporarily, when executing step-in/out.

      Hope that helps.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: