Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DAP logging: Allow sending tracing data to DAP client Debug Console #2457

Merged
merged 15 commits into from
May 21, 2024

Conversation

noppej
Copy link
Contributor

@noppej noppej commented May 10, 2024

The mechanism of passing messages from the dap_server to the client via stderr is replaced with writing dap_server messages and tracing output to the DAP client's Debug Console, using the DAP Output event.

  • In addition to making message delivery more reliable, this also paves the way for a multi-session debugger, where each session can log their own messages to the corresponding client console.
  • Cleanup of messages relating to when PR Improve dap server logging #2331 changed the logging to be driven by log-file parameter.
  • To facilitate better testing, I took the liberty of downgrading some of our logging verbosity where calls such as Core::status were using tracing instrument spans with a default level of INFO (obviously caused a lot of noise).

@noppej noppej added the skip-changelog This PR does not require a changelog entry on release label May 10, 2024
@noppej noppej requested a review from bugadani May 10, 2024 18:55
@bugadani
Copy link
Contributor

bugadani commented May 10, 2024

I'm not agains cleaning this up but I think it would be nice if we could figure out @Yatekii's difficulties logging probe-rs events to vscode in this same PR.

Reading through the changes, maybe this addresses the technicalities, but we should probably address the expectations, too.

@noppej
Copy link
Contributor Author

noppej commented May 10, 2024

As far as I know, there are two problems:

@noppej
Copy link
Contributor Author

noppej commented May 10, 2024

but we should probably address the expectations, too.

@bugadani Can you clarify what you mean by this please?

@bugadani
Copy link
Contributor

but we should probably address the expectations, too.

@bugadani Can you clarify what you mean by this please?

My (and Noah's and maybe more people's) expectation is that setting RUST_LOG will result in debug logs to be printed to Debug Console. The code currently hard-codes LogLevel::Error to stderr regardless of RUST_LOG. Should we add a third tracing subscriber that logs to stdout if the env variable is set?

@noppej
Copy link
Contributor Author

noppej commented May 10, 2024

but we should probably address the expectations, too.

@bugadani Can you clarify what you mean by this please?

My (and Noah's and maybe more people's) expectation is that setting RUST_LOG will result in debug logs to be printed to Debug Console. The code currently hard-codes LogLevel::Error to stderr regardless of RUST_LOG. Should we add a third tracing subscriber that logs to stdout if the env variable is set?

There are two tracing subscribers.

  1. The stderr one which we need to pass essential errors to VSCode.
  2. Whatever is set by RUST_LOG. It won't write that to Console Log, but it will write one of two things ...
    a) Log output will be written to: "/Users/the_rock/dev/log.txt" if the user uses the --log-file option.
    b) No logging data will be written because no log-file destination was specified. if the user uses the --log-dir option. I will fix this here
    c) No logging data will be written because no log-file destination was specified. if neither the above are set.

Before the log to file was introduced, we used to log to the Debug Console. We can revert to that for (c) ... what do you think?

@bugadani
Copy link
Contributor

Before the log to file was introduced, we used to log to the Debug Console. We can revert to that for (c) ... what do you think?

I think that sounds reasonable, especially if we document that setting any of the options disables log-to-console

@noppej noppej marked this pull request as draft May 10, 2024 20:12
@noppej noppej changed the title Fix DAP logging message. DAP logging: Allow sending tracing data to DAP client Debug Console May 20, 2024
@noppej
Copy link
Contributor Author

noppej commented May 20, 2024

@bugadani If you want to manually test this during review, please use the VS Code changes from probe-rs/vscode#94. Older versions should still work, but some messages will be duplicated.

probe-rs-debugger-0.23.0.vsix.zip

@noppej noppej marked this pull request as ready for review May 20, 2024 20:56
Comment on lines 107 to 111
let read_from_log = self.buffer_file_handle()?;
let mut tracing_log_handle = BufReader::new(read_from_log.try_clone()?);
std::io::Seek::seek(&mut tracing_log_handle, self.seek_pointer)?;
let mut buffer_lines = tracing_log_handle.lines();
while let Some(Ok(next_line)) = buffer_lines.next() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand what this wants to be, but I also think this is worth factoring out into its own function. The difference between flush and flush_to_dap is (or... seems to be) what happens with the log lines, and that single line could be passed in as a closure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that, but it means that I have to pass an Option<DebugAdapter<P>> to the combined flush function. That isn't a problem in itself, but the compiler wants you to add a type when you pass a None value, and for the life of me I couldn't make the syntax work. If you can make it work (you probably can), I'd appreciate the solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None::<type> :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That being said I'm not sure we understand each other here, so this can go in as-is, and we can tackle this part later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It requires all the generics in the type, and I can't remember what the error was, but it doesn't like None::<DapAdapter<P>> ... or any of the variants I could come up with :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I've said, I'm not sure at all what you're planning or why it needs a None. If you pass in a closure as I've suggested, the closure captures debug_adapter for you if you need to use it.

/// and will be automatically removed by the OS when the last handle to it is closed.
/// - When the DAP server is running, the tracing messages are sent to the console.
/// - When the DAP server exits, the remaining messages in this buffer are sent to `stderr`.
buffer_file: Mutex<File>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this have to be inside a mutex, if we're just cloning the file descriptor? We could save a lock, a try_clone and a bunch of error handling if this was just a File and if buffer_file_handle returned a &File.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The buffer file is used by the tracing implementation to write the logs to, as well as by the DebugLogger for both writing and reading. I need the lock so that I can write to the file, and then update the read pointer to make sure a future flush() knows where the last 'un-read' data was.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need the lock

But you don't! The only .lock() here is when you clone the FD and the lock is immediately released. What am I not seeing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was not the intention. The intention was to hold onto the lock until I've finished updating the read pointers, but I see now that it is being released. Let me work with it a bit more before we merge.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things:

  • I think we should prefer parking_lot::Mutex, that does not return an error from lock. I'm not convinced we need to deal with poisoning here.
  • If you need the lock to stick for a bit, you can return impl Deref<Target = File>. That way the user (even if it's just us) doesn't have to know about the lock's internals, but the lock will be held as long as it's needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bugadani I've made the changes to hold onto the MutexGuard until after I've updated the file pointers. I've done some manual testing with excessive amounts of log data being written to the Debug Console, and all seems to be in order. That said, I'd really appreciate if you can do a sanity check for me.

Copy link
Contributor

@bugadani bugadani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation looks to be on the more-complex-than-necessary side at points, but it also seems to be working well, so thanks!

I'm not sure this change deserves about 15 PR's worth of changelog, though 😅

@noppej
Copy link
Contributor Author

noppej commented May 21, 2024

The implementation looks to be on the more-complex-than-necessary side at points, but it also seems to be working well, so thanks!

It took a lot of wrangling to get the tracing implementation to share the file it write to. They take ownership of anything you pass in as a impl Write. I started off thinking this will be simple, and then had to build out complexity to avoid ownership and reference and runtime access collisions. As always, I'd be happy to incorporate suggestions.

I'm not sure this change deserves about 15 PR's worth of changelog, though 😅

Haha ... you know me, I can get a little verbose. I will trim the changelog :)

@noppej noppej removed the skip-changelog This PR does not require a changelog entry on release label May 21, 2024
/// Try to clone the buffer file handle, so that it can be used by either
/// the DebugAdapter to send messages to the DAP client, or the `tracing` module to record log data.
pub(crate) fn buffer_file_handle(&self) -> Result<File, DebuggerError> {
self.locked_buffer_file()?.try_clone().map_err(|_| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will still allow racy access. If you really think we need the mutex, we should be working with an Arc<Mutex<File>> and clone the Arc, not the file.

@noppej
Copy link
Contributor Author

noppej commented May 21, 2024

@bugadani Thank you very much for taking the time to collaborate on this. I feel much better about the quality of this.

@noppej noppej enabled auto-merge May 21, 2024 21:12
@noppej noppej added this pull request to the merge queue May 21, 2024
Merged via the queue into master with commit df90aa8 May 21, 2024
19 checks passed
@noppej noppej deleted the fix_dap_logging_message branch May 21, 2024 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants