This is an archive of the discontinued LLVM Phabricator instance.

[lldb-vscode] Redirect stderr and stdout to DAPs console message
AbandonedPublic

Authored by wallace on May 27 2020, 1:14 PM.

Details

Summary

Doing this for 2 main reasons:

  • Avoid stdout from corrupting DAP messages
  • Make sure we can see in VSCode (or any other DAP client) the same output we see when debugging from the command line

Diff Detail

Event Timeline

aadsm created this revision.May 27 2020, 1:14 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 27 2020, 1:14 PM

I don't actually use vscode, so I don't really know whether this is a good idea, but if you say it is then I can go with that.

However, I think the implementation needs a lot of work:

  • it is very windows-hostile: dup, pipe, select, etc. don't exist or don't work on windows (btw, doing a blocking select before a blocking read is completely useless -- you might as well just do the read alone)
  • the lack of synchronization between the (detached) forwarding threads makes it very prone to crashes if something happens to write to stderr during shutdown or early in initialization

The way I would imagine doing this is:

  • redirect stdout/err as early as possible to avoid something writing to it, but don't forward anything yet
  • when things are sufficiently initialized, start up the forwarding machinery
  • at, an appropriate point during shutdown, stop forwarding

The stop forwarding is the trickiest part I think the most portable solution would be to just close the write end of the pipe, and join the forwarding thread (which will exit once read returns EOF).

We should also think about testing all of this. Do we have a reliable mechanism to produce stdout/err output from lldb? I guess it the worst case we could try debugging a module which produces one of these warnings.

Or, if this output is going to the same place as the SBDebuggers notion of stdout/err, then we could just stop calling SetOutputFileHandle, and have everything flow through this. That would reduce the overall complexity and would ensure this path gets better coverage, as it would be used in the common case instead of just for printing random errors.

thanks for the feedback, I'll read it more carefully later. As for the tests it should be easy to set up, didn't want to spend time on that before we agreed on doing it this way.

Btw, given that Richard has found some vscode code which already tries to send stderr to the console window https://github.com/microsoft/vscode/blob/master/src/vs/workbench/contrib/debug/node/debugAdapter.ts#L234, it would be good to first understand why doesn't that kick in. Maybe there's a vscode bug that could be fixed instead.

Absolutely, I'll look into that first.

rmaz added a comment.Jun 1 2020, 8:26 AM

Btw, given that Richard has found some vscode code which already tries to send stderr to the console window https://github.com/microsoft/vscode/blob/master/src/vs/workbench/contrib/debug/node/debugAdapter.ts#L234, it would be good to first understand why doesn't that kick in. Maybe there's a vscode bug that could be fixed instead.

The issue is fairly clear, that listener is only added if the optional constructor arg outputService is defined, but it never is. I was planning on putting up a diff this week some time to always read from stderr regardless.

aadsm updated this revision to Diff 276894.Jul 9 2020, 7:01 PM

This update still doesn't add windows support but I just want to get a feel that I'm going on the right direction.
It addresses the following:

  • redirect stdout/err as early as possible to avoid something writing to it, and only starts or ends redirection it once it's safe.
  • adds a mutex to OutputStream::write_full to make sure multiple threads don't try to write to the output fd at the same time.

We should also think about testing all of this. Do we have a reliable mechanism to produce stdout/err output from lldb?

this turned out to be harder than I thought it would be, my plan was to print to stdout in python from a thread but I never see that print anywhere (not sure why though). A sure way to test this (what I did manually) is to put a script print('foo') command in lldninit. Unfortunately I can't use a local lldbinit since that's a concept of the lldb binary and not of liblldb (unless we want to add that option to lldb-vscode as well), so not really sure about this as well.
I also tried to use a breakpoint command add -o 'script print("foo")' but same behaviour, I never saw that print. Still don't know how to tackle this, need to think more about it.

Or, if this output is going to the same place as the SBDebuggers notion of stdout/err,

There no guarantee of this, people can just use sys.stdout.write in python.

given that Richard has found some vscode code which already tries to send stderr to the console window

I would still prefer to wrap liblldb's stderr into a console message so people can see it in the client(e.g.: VSCode) console rather than outputting it through the lldb-vscode stderr which will then be up to the client to decide what to do with it.

Sorry, I missed this patch. Modulo the inline comments and the missing windows support and tests, I think this patch is ok-ish. However, I am still not convinced of its usefulness. Why would we be doing something (particularly a thing which will be hard to do in a cross-platform manner, and will very likely border on, or downright cross into, undefined behavior territory), if we get that from vscode for free?

lldb/tools/lldb-vscode/lldb-vscode.cpp
518–519

This is very fishy. If the thread is detached in StartRedirecting, joinable will never be true.

520

Who's closing m_captured_fd[0] ?

2899–2902

Rely on the desctructor calling these? Or have the destructor assert they have been called?

Why would we be doing something (particularly a thing which will be hard to do in a cross-platform manner, and will very likely border on, or downright cross into, undefined behavior territory), if we get that from vscode for free?

I'm not sure what you mean with get this for free on vscode... Right now lldb-vscode will just crash if there's any command in the lldbinit that outputs to stdout because it pollutes the protocol, I don't think think there's anything vscode can do about this. But even if it did vscode is not the only client of lldb-vscode, there are multiple others: https://microsoft.github.io/debug-adapter-protocol/implementors/tools/.

I haven't answered this yet because I was trying to figure out why this only happens when parsing the lldbinit. I was looking into the CommandInterpreter{Python} code but to be honest it's not easy to figure out because there are multiple levels of indirection when it comes to processing the IO of the python interpreter, but it seems that the stdout while executing the lldbinit script is being flushed right away to the ImmediateOutputStream but haven't figure out when this is set in the case of CommandInterpreter::IOHandlerInputComplete.

Sorry, I confused this with the patch that was wrapping stderr into DAP messages.

Having python output break the protocol is definitely not good. Fixing it would be nice, but the solution should work on windows as well.

One possibility I'd consider is making this fix at the python level (as this is a python problem). We already are trying to do some stdin/out/err redirection there, but it seems it's not good enough. It seems it should be easy enough (though hacky, but all of this is going to be hacky anyway) to replace sys.stdin/out/err with a custom wrapper object.

wallace commandeered this revision.Apr 22 2021, 9:18 PM
wallace abandoned this revision.
wallace edited reviewers, added: aadsm; removed: wallace.

not needed anymore because of D99974