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
cmd/dlv: print out message with stack trace when breakpoint is hit but has no waiting client #3632
base: master
Are you sure you want to change the base?
cmd/dlv: print out message with stack trace when breakpoint is hit but has no waiting client #3632
Conversation
c12a582
to
66a3316
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think that needing this option is a strong symptom that there is something very wrong with your development environment. In the context of running something locally during development you would want the program to stop at a panic to examine why the panic happened.
In the context of running the program in production having a debugger attached to it constantly is a security risk.
I'm also inclined to agree with @aarzilli, however I am familiar with the workflow described in the associated issue, and in that context I could see the benefit to the actual crash being apparent and not silenced by Delve by virtue of waiting on user input to produce the panic. My initial feedback is I am against wholesale deletion of these breakpoints, I think it would be better to actually disable them, so they can be re-enabled later without having to know the specifics of the runtime. |
We could also print something if we stop at a breakpoint while there isn't a connected client. |
That's good too. Might even be nice to print the traceback and panic message automatically when hitting the panic breakpoints anyways, to be honest. |
I agree with you. This option is needed in our orgs because not everyone are familiar with the debugger and we want to have a setup that is practical and convenient for everyone.
I tried this one actually, instead of deleting them I tried to toggling off those breakpoints, but it's not possible to do that through the API because we will reject any request that has
Ideally yeah this is what I'm looking for, but it seems to be a bit tricky to pull off. Let me see if i can find a way to do this. |
One way to do it would be like this:
|
66a3316
to
c29cb69
Compare
oh ya i have implemented this in this PR but in a different fashion.
Initially im thinking to move the unhandled panic breakpoint to somewhere after we print out the message but it's going to be a bit tricky to make it sure it works for older (and future) go versions so I'm going with implementing this on the server side. Hope that's acceptable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(this was a leftover comment)
Hey @fatanugraha there has been no update on this PR in a month, are you still planning to work on this? |
Hi @derekparker I have plans to pick this up again in 1-2 weeks from now, but if anyone else wants to work on this issue I'm also okay with that. Sorry for the delay :( |
No worries, and no rush! Just wanted to check in. |
@fatanugraha would you like some additional hands on this? |
cf2069e
to
c9531a9
Compare
service/rpc2/server.go
Outdated
var out CommandOut | ||
out.State = *st | ||
cb.Return(out, nil) | ||
if clientIsConnected := cb.Return(out, nil); s.config.Headless && !clientIsConnected { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't do this in this layer. It will not work if delve is started with dlv dap
or if someone connects to it using VSCode (or any other program that uses the DAP protocol) and then that client quits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I missed that. I'm leaning towards doing this in both rpc2
and dap
or do you suggest that we should do it in the debugger
layer instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I lean slightly towards doing it in debugger but both would be ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @aarzilli , correct me if i'm wrong, after checking dap's code I'm not sure if we need to implement this for dap. reasons being:
dlv dap
doesn't support multiclientdlv dap
will quit when the client disconnected (so printing the log here won't be helpful here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be right about dlv dap
but DAP connections are also accepted when delve is started with dlv --headless debug
. See serveConnectionDemux in service/rpccommon/server.go as well as onDisconnectRequest in service/dap/server.go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah you're right, thanks for the clarification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading more of the debugger
code I agree that we should implement this on the debugger layer. By doing that we can also cover more cases e.g. disconnecting client after sending a long call
command.
Can you help to take a look when you have time and see if it's acceptable? Thanks!
ac5dd6c
to
593758a
Compare
@@ -412,9 +415,28 @@ func (cb *RPCCallback) Return(out interface{}, err error) { | |||
outbytes, _ := json.Marshal(out) | |||
cb.s.log.Debugf("(async %d) -> %T%s error: %q", cb.req.Seq, out, outbytes, errmsg) | |||
} | |||
|
|||
if hasDisconnected := cb.hasDisconnected(); hasDisconnected { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be simplified to if cb.hasDisconnected()
@@ -2428,3 +2448,43 @@ var attachErrorMessage = attachErrorMessageDefault | |||
func attachErrorMessageDefault(pid int, err error) error { | |||
return fmt.Errorf("could not attach to pid %d: %s", pid, err) | |||
} | |||
|
|||
func (d *Debugger) dumpGoroutineStack(currentThread *api.Thread) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be named more in line with what it does. Of course it does dump the goroutine stack, but it's main purpose is to print a message / warning on an unattended breakpoint hit.
@@ -524,6 +527,12 @@ func (s *Session) ServeDAPCodec() { | |||
s.config.triggerServerStop() | |||
} | |||
}() | |||
|
|||
s.disconnectCh = make(chan struct{}) | |||
defer func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just defer close(s.disconnectCh)
without the anonymous function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, maybe the channel should live on the s.conn
connection and be closed via the above defer s.conn.Close()
?
@@ -1190,6 +1188,24 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc | |||
|
|||
if command.Name != api.SwitchGoroutine && command.Name != api.SwitchThread && command.Name != api.Halt { | |||
d.target.ResumeNotify(resumeNotify) | |||
|
|||
if clientStatusCh != nil { | |||
defer func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to move this just before the return state, err
at the end of the function. I think it's clearer to do it directly instead of using a defer because there is only one path that returns a non nil state and we don't need to actually receive from clientStatusCh in cases where we return an error.
Print out a message and stack trace to the stderr when we hit a breakpoint and the client that sends the continue command has disconnected.
Fixes #3469