ChromeDevTools connected to Inspector, but error responses... missing native JS functions?

4,882 views
Skip to first unread message

Graham Reeves

unread,
Aug 27, 2018, 8:49:37 AM8/27/18
to v8-users
Through various sources (ie, googling & github) I've finally got a bit of a grasp of the flow for connecting chrome dev tools to my v8 app. (which is using a pretty recent HEAD build I've built myself with all default settings other than compiling as a static lib[s]. v8-version.h says 7.0.0.0)

I connect via an explicit url (can't quite get json/list/ to show up in chrome yet)
chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:8008
(Not sure how relevant the v8only=true and experiments=true are, couldn't find any documentation on this)

This connects to my websocket, and I pass all messages straight to Session->dispatchProtocolMessage

My channel then gets sendResponse's in return, which I send back to chrome over my websocket.

Chrome dev tools shows essentially an empty debugger, no sources, no console output, no errors...

The responses my isolate/context sends back, suggest maybe I have some JS symbols/modules missing, which maybe I need to implement?
I'm kinda assuming this, as the debugger. methods succeed, but things like Inspector.enable (which I haven't found anyone implementing, but some people are implementing Inspector objects, just with different methods) fail

Chrome tools message: {"id":16,"method":"Inspector.enable"}

Channel response: {"error":{"code":-32601,"message":"'Inspector.enable' wasn't found"},"id":16}

Is this why chrome isn't proceeding with anything?
Am I supposed to implement these, or perhaps are they missing from my native blobs when I built my v8 libraries?

My overloads of the inspector client functions like runMessageLoopOnPause aren't being called, but I assume that's just because I haven't gotten to any stage where commands are being evaluated?

Below is what I get when I connect chrome, but then it just sits there :)

Thanks for any pointers in the right direction!

Chrome tools message: {"id":1,"method":"Network.enable","params":{"maxPostDataSize":65536}}

Channel response: {"error":{"code":-32601,"message":"'Network.enable' wasn't found"},"id":1}

Chrome tools message: {"id":2,"method":"Page.enable"}

Channel response: {"error":{"code":-32601,"message":"'Page.enable' wasn't found"},"id":2}

Chrome tools message: {"id":3,"method":"Page.getResourceTree"}

Chrome tools message: {"id":4,"method":"Profiler.enable"}

Channel response: {"error":{"code":-32601,"message":"'Page.getResourceTree' wasn't found"},"id":3}

Channel response: {"id":4,"result":{}}

Chrome tools message: {"id":5,"method":"Runtime.enable"}

Channel response: {"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"origin":"","name":"PopEngineContextName"}}}

Channel response: {"id":5,"result":{}}

Chrome tools message: {"id":6,"method":"Debugger.enable"}

Channel response: {"method":"Debugger.scriptParsed","params":{"scriptId":"9","url":"","startLine":0,"startColumn":0,"endLine":1,"endColumn":0,"executionContextId":1,"hash":"2a70962568dbbde00fb323decd63c2ca137b304c","isLiveEdit":false,"sourceMapURL":"","hasSourceURL":false,"isModule":false,"length":17}}

Channel response: {"id":6,"result":{"debuggerId":"(6B1A58050CBFAE70E5B41C5556E5520D)"}}

Chrome tools message: {"id":7,"method":"Debugger.setPauseOnExceptions","params":{"state":"uncaught"}}

Channel response: {"id":7,"result":{}}

Chrome tools message: {"id":8,"method":"Debugger.setAsyncCallStackDepth","params":{"maxDepth":32}}

Channel response: {"id":8,"result":{}}

Chrome tools message: {"id":9,"method":"DOM.enable"}

Channel response: {"error":{"code":-32601,"message":"'DOM.enable' wasn't found"},"id":9}

Chrome tools message: {"id":10,"method":"CSS.enable"}

Channel response: {"error":{"code":-32601,"message":"'CSS.enable' wasn't found"},"id":10}

Chrome tools message: {"id":11,"method":"Overlay.enable"}

Channel response: {"error":{"code":-32601,"message":"'Overlay.enable' wasn't found"},"id":11}

Chrome tools message: {"id":12,"method":"Overlay.setShowViewportSizeOnResize","params":{"show":true}}

Channel response: {"error":{"code":-32601,"message":"'Overlay.setShowViewportSizeOnResize' wasn't found"},"id":12}

Chrome tools message: {"id":13,"method":"Log.enable"}

Channel response: {"error":{"code":-32601,"message":"'Log.enable' wasn't found"},"id":13}

Chrome tools message: {"id":14,"method":"Log.startViolationsReport","params":{"config":[{"name":"longTask","threshold":200},{"name":"longLayout","threshold":30},{"name":"blockedEvent","threshold":100},{"name":"blockedParser","threshold":-1},{"name":"handler","threshold":150},{"name":"recurringHandler","threshold":50},{"name":"discouragedAPIUse","threshold":-1}]}}

Channel response: {"error":{"code":-32601,"message":"'Log.startViolationsReport' wasn't found"},"id":14}

Chrome tools message: {"id":15,"method":"ServiceWorker.enable"}

Channel response: {"error":{"code":-32601,"message":"'ServiceWorker.enable' wasn't found"},"id":15}

Chrome tools message: {"id":16,"method":"Inspector.enable"}

Channel response: {"error":{"code":-32601,"message":"'Inspector.enable' wasn't found"},"id":16}

Chrome tools message: {"id":17,"method":"Target.setAutoAttach","params":{"autoAttach":true,"waitForDebuggerOnStart":true}}

Channel response: {"error":{"code":-32601,"message":"'Target.setAutoAttach' wasn't found"},"id":17}

Chrome tools message: {"id":18,"method":"Target.setDiscoverTargets","params":{"discover":true}}

Channel response: {"error":{"code":-32601,"message":"'Target.setDiscoverTargets' wasn't found"},"id":18}

Chrome tools message: {"id":19,"method":"Target.setRemoteLocations","params":{"locations":[{"host":"localhost","port":9229}]}}

Channel response: {"error":{"code":-32601,"message":"'Target.setRemoteLocations' wasn't found"},"id":19}

Chrome tools message: {"id":20,"method":"Debugger.setBlackboxPatterns","params":{"patterns":["/main\\.js\\b"]}}

Channel response: {"id":20,"result":{}}

Chrome tools message: {"id":21,"method":"Page.getNavigationHistory","params":{}}

Channel response: {"error":{"code":-32601,"message":"'Page.getNavigationHistory' wasn't found"},"id":21}


ibon

unread,
Aug 27, 2018, 1:34:20 PM8/27/18
to v8-users
I get the same sequence of messages, and I don't need to implement anything about them to have a fully functional debugging session between embedded v8 (android) and chrome dev tools.

@soylentgraham

unread,
Sep 11, 2018, 10:32:07 AM9/11/18
to v8-users
I'm still plugging away on this. I reverted my v8 build back to a stable version (6.9.427.22), and got a few crashes (which weren't occurring in 7.0.0, but still nothing in CDT)

So I set to making a minimal inspector example with websockets (which AFAIK, still doesn't exist on the internet)
I have a single func (from helloworld) with a loop at the end which pumps the message loop, and then handles messages I've buffered up from the websocket.

I've gotten a tiny bit further (CDT now shows a "VM12" with a single "this" in the source when I break)
But I still hit the assert below. (which should never happen? is there no support for delayed tasks?) 
delay_in_seconds is 0.5

I've typed h into the console for auto complete, and my inspector/session/client has responded with some interfaces, but then CDT sends this message

Message={"id":25,"method":"Runtime.evaluate","params":{"expression":"h","includeCommandLineAPI":true,"contextId":1,"generatePreview":true,"userGesture":true,"awaitPromise":false,"throwOnSideEffect":true,"timeout":500}}


Still cutting this down to a very minimal example...






void DefaultWorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,

                                                     double delay_in_seconds) {

  base::LockGuard<base::Mutex> guard(&lock_);

  if (terminated_) return;

  if (delay_in_seconds == 0) {

    queue_.Append(std::move(task));

    return;

  }

  // There is no use case for this function with non zero delay_in_second on a

  // worker thread at the moment, but it is still part of the interface.

  UNIMPLEMENTED();

}


Stacktrace in case it's handy


#0 0x00000001007d5ff2 in v8::base::OS::Abort() at /Volumes/Code/v8build/v8/out.gn/x64.release/../../src/base/platform/platform-posix.cc:397

#1 0x00000001005f1a29 in v8::platform::DefaultWorkerThreadsTaskRunner::PostDelayedTask(std::__1::unique_ptr<v8::Task, std::__1::default_delete<v8::Task> >, double) at /Volumes/Code/v8build/v8/out.gn/x64.release/../../src/libplatform/default-worker-threads-task-runner.cc:49

#2 0x00000001005f0ce8 in v8::platform::DefaultPlatform::CallDelayedOnWorkerThread(std::__1::unique_ptr<v8::Task, std::__1::default_delete<v8::Task> >, double) at /Volumes/Code/v8build/v8/out.gn/x64.release/../../src/libplatform/default-platform.cc:204

#3 0x00000001007b3c10 in v8_inspector::V8InspectorImpl::EvaluateScope::setTimeout(double) at /Volumes/Code/v8build/v8/out.gn/x64.release/../../src/inspector/v8-inspector-impl.cc:423

#4 0x00000001007be1b4 in v8_inspector::V8RuntimeAgentImpl::evaluate(v8_inspector::String16 const&, v8_inspector::protocol::Maybe<v8_inspector::String16>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<int>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<bool>, v8_inspector::protocol::Maybe<double>, std::__1::unique_ptr<v8_inspector::protocol::Runtime::Backend::EvaluateCallback, std::__1::default_delete<v8_inspector::protocol::Runtime::Backend::EvaluateCallback> >) at /Volumes/Code/v8build/v8/out.gn/x64.release/../../src/inspector/v8-runtime-agent-impl.cc:266

#5 0x000000010076b221 in v8_inspector::protocol::Runtime::DispatcherImpl::evaluate(int, std::__1::unique_ptr<v8_inspector::protocol::DictionaryValue, std::__1::default_delete<v8_inspector::protocol::DictionaryValue> >, v8_inspector::protocol::ErrorSupport*) at /Volumes/Code/v8build/v8/out.gn/x64.release/gen/src/inspector/protocol/Runtime.cpp:1717

#6 0x00000001007691c4 in v8_inspector::protocol::Runtime::DispatcherImpl::dispatch(int, v8_inspector::String16 const&, std::__1::unique_ptr<v8_inspector::protocol::DictionaryValue, std::__1::default_delete<v8_inspector::protocol::DictionaryValue> >) at /Volumes/Code/v8build/v8/out.gn/x64.release/gen/src/inspector/protocol/Runtime.cpp:1372

#7 0x000000010073d7c2 in v8_inspector::protocol::UberDispatcher::dispatch(std::__1::unique_ptr<v8_inspector::protocol::Value, std::__1::default_delete<v8_inspector::protocol::Value> >, int*, v8_inspector::String16*) at /Volumes/Code/v8build/v8/out.gn/x64.release/gen/src/inspector/protocol/Protocol.cpp:822

#8 0x00000001007b6ecd in v8_inspector::V8InspectorSessionImpl::dispatchProtocolMessage(v8_inspector::StringView const&) at /Volumes/Code/v8build/v8/out.gn/x64.release/../../src/inspector/v8-inspector-session-impl.cc:316

@soylentgraham

unread,
Sep 12, 2018, 12:34:30 PM9/12/18
to v8-users
With a lot of back & forth help from Ibon/hyperandroid, we've got a bit further in working out which things are good, and which are bad. 
Feels like we're close, but things are still unstable, and a bit unresponsive.

I can kinda break into the code (which only shows this in sources)
Still get the assert above from trying to autocomplete too many times. (albeit after a RunMessageLoop callback now)
And if I add a script origin, I get a crash trying to stacktrace (immediately when connecting, still trying to figure this out, as my scriptorigin is okay)

More stripped back version here (cutting more dependencies out, but all my setup, inspector, frontend, messaging is in v8minimal.cpp)


On Monday, 27 August 2018 13:49:37 UTC+1, @soylentgraham wrote:

@soylentgraham

unread,
Sep 13, 2018, 12:14:58 PM9/13/18
to v8-users
For any future readers;
I went back to trying to build a debug v8 set of libs (wouldnt compile before, added is_component_build=false which I think fixed that)
Built with latest master (4544e18b0c3845a9fca422cf0903df4803343cf1) 

Which forced me to correct a few uses of deprecated functions, including v8::String::NewFromUtf8() with no isolate, so *possibly* that was the source of the crash re: scriptorigin. I now get an origin in CDT (kinda, it still breaks in a VMXX file)

Then asserts as before in "code that should never be reached" (see earlier in the thread)

Screen Shot 2018-09-13 at 17.08.09.png

@soylentgraham

unread,
Sep 14, 2018, 6:44:33 AM9/14/18
to v8-users
So the core of my issue at the moment, is this assert (UNIMPLEMENTED)

void DefaultWorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,

                                                     double delay_in_seconds) {

  base::LockGuard<base::Mutex> guard(&lock_);

  if (terminated_) return;

  if (delay_in_seconds == 0) {

    queue_.Append(std::move(task));

    return;

  }

  // There is no use case for this function with non zero delay_in_second on a

  // worker thread at the moment, but it is still part of the interface.

  UNIMPLEMENTED();

}

from 

DefaultPlatform::CallDelayedOnWorkerThread(


being called by

protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {

  if (m_isolate->IsExecutionTerminating()) {

    return protocol::Response::Error("Execution was terminated");

  }

  m_cancelToken.reset(new CancelToken());

  v8::debug::GetCurrentPlatform()->CallDelayedOnWorkerThread(

      v8::base::make_unique<TerminateTask>(m_isolate, m_cancelToken), timeout);

  return protocol::Response::OK();

}



so, SetTimeout in the inspector, explicitly uses something we[developers] know isn't implemented...
From reading things before, Node.js was using the default platform, I think now it has it's own platform. Maybe chromium does too...
Perhaps the demo/unittest debugger/d8/inspector doesn't call settimeout, so this hasn't been found with default setups...

Maybe I HAVE to implement my own platform to support chrome dev tools and handle delayed jobs...

@soylentgraham

unread,
Sep 18, 2018, 10:49:43 AM9/18/18
to v8-users
YetAnotherUpdate

After some more digging, I found the issue I have is identified node and chromium too... 

So, it looks like the default v8 [platform] isn't chrome-dev-tools compatible.
Ibon's case worked as it has a specific Android platform implementation. Chromium and Node also have their own platform.

So, I made the most basic platform proxy possible to work around it...

And it works!
I can now modify code on the fly, execute from the console.
I can't seem to debug, and pressing pause-execution a few times gives me a new assert, but the higher level code is a bit of a mess at this point, so I'm probably implementing something wrong...

Still, if anyone else ever finds this problem... V8 alone cannot be used with chrome dev tools. (as of 7.1.0.0 

4544e18b0c3845a9fca422cf0903df4803343cf1)

kpia...@magicleap.com

unread,
Nov 13, 2018, 4:06:59 PM11/13/18
to v8-users
Hi @soylentgraham,

I'm trying to replicate what you're doing as well but am running into some other problems.  The most notable of which is that my websocket is on a different thread than my isolate creation.
I see from your solution that you're adding the frontend messages to a queue and looking through them in your event loop.  My problem is that I have long running javascript that is not intended to exit/return until the program stops.  Do you have any suggestions/ideas?

Thanks,
Konrad

@soylentgraham

unread,
Nov 14, 2018, 7:53:57 AM11/14/18
to v8-users
The only way I could interrupt long runs of javascript, assuming you can't break it yourself into asynchronous code (via promises/await, this is the nicest approach IMO me), is adding a yield function to let the event loop run (I have other multithread-y reasons for this too, but letting external actions run is one)

But if you can't add code at all, you are probably stuck (afaik, there's no real interrupting, because you're basically calling one big RunJavascript() func on your thread :)

In case it's useful, this is the C++ side, (and I call yield() in JS to let other threads do JS calls, and let the debugger interrupt)


void TV8Container::Yield(size_t SleepMilliseconds)

{

// gr: temporary unlock, but need to exit&enter too

{

mIsolate->Exit();

v8::Unlocker unlocker(mIsolate);

// isolate unlock for a moment, let another thread jump in and run stuff

auto ms = std::chrono::milliseconds(SleepMilliseconds);

std::this_thread::sleep_for( ms );

}

// re-enter after unlocker has gone out of scope

mIsolate->Enter();

}

Konrad Piascik

unread,
Nov 14, 2018, 8:55:34 AM11/14/18
to v8-users
Yeah I can't make any assumptions or impose any requirements about what the javascript to be executed will look like.
It must be possible since Node.js already does this for long running JavaScript.

Thanks for the suggestions and quick responses

-Konrad

@soylentgraham

unread,
Nov 14, 2018, 10:52:53 AM11/14/18
to v8-users
Going off topic now, but can you attach & break into node.js whilst it's in a big blocking function? 
Or can you only break if you've previously attached? (I've only done light, very-async stuff in node)

In long-running funcs (well, accidental massive loops) in chrome I find I struggle to break, so I kinda thought this was how it was, but maybe I'm wrong :)

Konrad Piascik

unread,
Nov 14, 2018, 12:45:03 PM11/14/18
to v8-users
I have attached to node with the program executing in the middle of a while/busy loop.
I think I found what node is doing. 

void v8::Isolate::RequestInterrupt(InterruptCallback callback, void* data);
This function stops JavaScript execution and might be the answer to my problem.  The callback is called on the isolate's thread.

Cheers,
Konrad

@soylentgraham

unread,
Nov 15, 2018, 10:56:21 AM11/15/18
to v8-users
Ah that sounds more like the proper way to do it :)
I've not tried interrupts yet, so I'm no use here :)
Reply all
Reply to author
Forward
0 new messages