svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.93k stars 514 forks source link

Debugger questions (message semantics) #537

Closed fatcerberus closed 8 years ago

fatcerberus commented 8 years ago

Despite working directly with the debugger code, I still have a few stupid questions left. :)

Anyway, I'm currently working on a console debugger a la GDB, which will need to switch between interactive command entry (which is blocking, due to stdin line buffering) and waiting for messages (which is also blocking). Unlike in my GUI debugger, message processing can't happen while waiting for a command, and vice versa. So my questions are:

  1. Is it safe to assume that, after receiving a Status notify with State=1 (paused), no further messages will be received until the client sends a request?
  2. While the target is paused, suppose a request is sent by the client. Is it then safe to assume no further messages will be received after the reply to this request (REP or ERR)? Note: This assumes the request in question isn't a Resume.

If these assumptions don't hold, then any tips on working around the limitations in play would be appreciated. Thanks for any help you can provide!

svaarala commented 8 years ago

@fatcerberus Oops, it seems I missed this issue in my inbox - I'll get back to you on this tomorrow.

fatcerberus commented 8 years ago

@svaarala ...bump? :)

svaarala commented 8 years ago

I'll be back on this later tonight :)

fatcerberus commented 8 years ago

Okay, no problem, just wanted to make sure you didn't forget it again. ;)

svaarala commented 8 years ago

At a high level I'd really try to investigate first if there's any way you could either read the console and the debug connection at the same time, or perhaps use a separate thread for the debug connection and let the other thread work with the console and the debug connection. It's just easier if you don't need to make unnecessary assumptions about the debugger behavior.

Also note that even if Duktape didn't send any debug messages while your code was blocked reading the console, there can still be asynchronous events which need the debugger's attention -- for example, the debug connection may be lost which the debugger console would ideally react to.

But assuming it's not possible to do something like this:

Is it safe to assume that, after receiving a Status notify with State=1 (paused), no further messages will be received until the client sends a request?

Looking at it from a black box perspective there's no such guarantee. For example, it's possible for Duktape to send duplicate Status messages, so that you could get two "paused" status messages. It's also possible that future notifications are sent before/after a paused state.

From a white box perspective this will most likely hold for the current implementation (not sure about a duplicate Status, that'd need to be checked). In particular, once Duktape blocks waiting for an incoming request (calling into the user read callback) there's no practical way for Duktape to send anything because the control is entirely in the user callback.

While the target is paused, suppose a request is sent by the client. Is it then safe to assume no further messages will be received after the reply to this request (REP or ERR)? Note: This assumes the request in question isn't a Resume.

From a black box perspective the answer is pretty much the same as above. From a white box perspective the current implementation may at least send a Status notify after a REP/ERR:

A fresh Status is currently triggered by TriggerStatus and the step commands (any command handler that sets dbg_state_dirty = 1).

In the future there's no guarantee that Status won't involve some field which causes a new Status to be sent in response to other commands (or perhaps after every command).

Stepping back a bit, with protocols I'm more used to thinking in terms of black box guarantees rather than thinking "what does the current version do?". So I'm quite hesitant to provide additional guarantees beyond the ones already in place (the basic request/response/error and notify model) because such guarantees may be limiting future features unnecessarily.

fatcerberus commented 8 years ago

I agree, the black box perspective is the right way to look at it. The protocol just defines a standard for communication and anything the implementation does that doesn't involve sending or receiving messages is out of scope.

That said, I'm more concerned with the current implantation at present. I'm a refactoring fiend so I'm open to making it more robust later. It's just that I'm trying to get a 1.0 version of the debugger up and running now and the easiest way to implement a command line cross-platform happens to be getchar(), which of course invokes stdin line buffering.

svaarala commented 8 years ago

Right, I understand.

With the current implementation I think what might happen (maybe) is that a Status message is sent after a REP/ERR and you'd miss that as your code had already called getchar(). But it'd be relatively harmless: once the stdin read was done, you'd notice and skip/process it I guess?

fatcerberus commented 8 years ago

One more question and then I'll (hopefully) stop bugging you about this: Is it guaranteed by the protocol that the REP/ERR for a given REQ will always follow receipt of the request, with no intervening notifys? For example, would TriggerStatus send its acknowledgement (REP EOM) before the Status notify?

None of this stuff was an issue in my GUI debugger because each request is sent on a new thread, which blocks until the corresponding acknowledgement is received. Things get more complicated in a single-threaded design.

Now that I'm thinking about this, I guess the Eval command at least would violate this assumption, as the Eval'd code might throw, generating Throw notifys.

svaarala commented 8 years ago

For the current implementation that would mostly be the case, but the protocol doesn't guarantee it (black box). And indeed it's quite possible some new commands would cause notifys to be sent during their processing. Eval most likely does that already as you point out, and it's a good example why it'd be very anti-modular not to allow for that.

fatcerberus commented 8 years ago

I found one case at least where a duplicate Status notify gets sent:

SSJ 2.99.812 Sphere Game Debugger x64
A powerful JavaScript debugger for minisphere
(c) 2016 Fat Cerberus

Connecting to '127.0.0.1:1208'... OK.
Handshaking... OK.
  Connected to minisphere 2.99.812
  Duktape version is v1.4.0

STATUS: Paused at scripts/main.js:6 (eval)
ssj: r
FATAL: ReferenceError: a pig ate everyone at 8:12 at scripts/main.js:30
STATUS: Paused at scripts/main.js:30 (game)
ssj: e session
undefined
ssj: e session
STATUS: Paused at scripts/main.js:30 (game)
undefined
ssj:
svaarala commented 8 years ago

So are these now a nuisance or an actual issue with the console debugger? :)

fatcerberus commented 8 years ago

It's not a practical issue, no. I was just posting that as proof that Duktape can sometimes send duplicate Status notifys. What I'm assuming is that Eval dirties the state so Duktape sends another notify, but SSJ doesn't receive it until after responding to the Eval's REP with another command prompt.

So far I haven't had any big issues, the hardest part was implementing the dvalue parsing because the C socket library I use is asynchronous.

svaarala commented 8 years ago

By the way, the local dvalue parser (https://github.com/svaarala/duktape/blob/master/examples/debug-trans-dvalue/duk_trans_dvalue.c) should be quite easy to adapt for remote parsing in async C :)

I could maybe decouple the read/write callbacks and the actual dvalue parsing so that it'd be trivial to do that.

fatcerberus commented 8 years ago

Thanks, I looked into using duk_trans_dvalue when I started, but ultimately went with a custom approach where I read and write full messages at a time so the debugger code doesn't need to deal with creating and freeing individual dvalue structures.

fatcerberus commented 8 years ago

My console debugger is working well so far, so I'll close this. I already wrote a debug client once, I know what to do from here. :)