mirror of
https://github.com/mum4k/termdash.git
synced 2026-04-27 03:15:55 +03:00
[GH-ISSUE #268] termdash makes debugging panics difficult #144
Labels
No labels
bug
cleanup
enhancement
enhancement
enhancement
good first issue
help wanted
help wanted
pull-request
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/termdash#144
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @dyc3 on GitHub (Nov 21, 2020).
Original GitHub issue: https://github.com/mum4k/termdash/issues/268
By default, when a termdash program panics, the panic is not printed to the output.
Is there some way to intercept the panic to print it to a log file?
@mum4k commented on GitHub (Nov 21, 2020):
Thank you for the question @dyc3. This is remotely related to #247.
The short answer is - I don't think we can do it within
termdash, because Go only allows recovery from a panic from within the panicking goroutine. Thetermdashinfrastructure would only be able to capture panics originating within thetermdashinfrastructure. If you are facing those, we can add something to catch them, but it might be more interesting to solve the issues that causetermdashto panic.If the panics originate from the code that uses
termdash, the recovery must happen within the same code (goroutine). Note that I have recently added a wiki page on how I usually debug issues: https://github.com/mum4k/termdash/wiki/DebuggingI suspect that most of the content will be fairly obvious and known, but thought that it might be worth mentioning.
Would you mind sharing what sort of panics you are encountering?
@dyc3 commented on GitHub (Nov 21, 2020):
Just panics when I'm developing my own widgets. Mostly index out of bounds mistakes.
When I'm debugging stuff, I've found that the log package does a pretty good job.
log.SetOutputlets you set a writer to output log messages to, which I direct to a file. For some reason, I'm unable to attach delve to debug my specific application, and I'm not sure why. IIRC, that would catch panics and show them to me.@mum4k commented on GitHub (Nov 21, 2020):
The advice about
log.SetOutputis a good one, I didn't know about it and it likely provides an easier way to setup debugging that the one in the wiki.Panics originating from widgets actually do originate in
termdash, so catching the panic there and logging it or similar before exiting would be a viable solution. We could also partially address #247 by catching a panic, resetting the terminal and reprinting the panic in clear. Or just provide a top level option that will let users oftermdashspecify a file to log panics to. What do you think?@dyc3 commented on GitHub (Nov 21, 2020):
I think it should reset the terminal and reprint the panic in clear.
It looks like the
recover()builtin recovers from panics, and it returns the error that it panicked with to print out.@dyc3 commented on GitHub (Nov 21, 2020):
We could also return the error as an error from
termdash.Run, letting the user log the error themselves.@mum4k commented on GitHub (Nov 21, 2020):
Logging or printing the panic is very likely a good idea. To be clear we would use
recover()only for the purposes to clear the terminal and then panic again.Turning a panic into an error (essentially stopping the panicking process) needs more thought. There usually is a reason why the program is panicking and recovery from that state is often impossible or impractical. The program could also remain in an undefined state if we do that.
Assuming the first approach achieves our goal, I think we have a path forward. Please let me know.
@dyc3 commented on GitHub (Nov 21, 2020):
Yeah, recovering, printing, and re-panicking sounds like the best option.
@mum4k commented on GitHub (Nov 21, 2020):
Sounds good. Care should be taken as to where we apply this code. Termdash itself runs a couple of goroutines internally. If we want to recover from panics in the widget's Draw function we need to recover in the goroutine that calls it.
Is this something you would like to look into yourself, or would you prefer if I try to address it? As before I will be happy to provide some code pointers if you are interested in giving it a try.
@dyc3 commented on GitHub (Nov 21, 2020):
I'd prefer if you'd try to address it.
@mum4k commented on GitHub (Nov 21, 2020):
Sounds good, I'll give it a try.
@mum4k commented on GitHub (Nov 21, 2020):
@dyc3 I am having some trouble reproducing the problem and hoping you can give me advice.
I have added a
panic()call directly inside theDraw()method of one of the widgets. When the code reaches thepanic(), it actually correctly closes the terminal and prints the panic in clear. That is becauseDraw()is actually called from themaingoroutine which is the same one that defers a call to the terminal closure method here:github.com/mum4k/termdash@fbd21e7904/widgets/barchart/barchartdemo/barchartdemo.go (L63)Here is the panic that I got in a clear readable form on the terminal:
Here is a PR that shows how I caused the panic:
https://github.com/mum4k/termdash/pull/269
Can you help me identify what is different in your setup? Is the panic originating from a different method on the widget? Do you also have a deferred call to
t.Close()in your main? Or can you maybe share a panic that can help me reproduce the problem?@dyc3 commented on GitHub (Nov 21, 2020):
Strange... maybe tcell is handling the panics?
The project I'm working on is this: https://github.com/dyc3/teleprompt-studio
The specific widget I was working on: https://github.com/dyc3/teleprompt-studio/blob/main/script_display_widget.go
Causing an index out of bounds panic in that widget's
Drawwill reproduce what I was getting. Maybe it's a different type of panic?In
main.go, I'm handling the panics with this little snippet infunc main, so it has to be disabled to reproduce.I do have
defer t.Close()in my main.@mum4k commented on GitHub (Nov 21, 2020):
Thank you for that. Would you be able to point me to a PR or a branch where I can execute something that directly reproduces the kind of panic we are discussing here? If that is possible, that would help me a lot in trying to address the issue.
@dyc3 commented on GitHub (Nov 22, 2020):
Sure, I'll throw together a branch
@dyc3 commented on GitHub (Nov 22, 2020):
The
panicbranch reliably reproduces the behavior I was getting last night. https://github.com/dyc3/teleprompt-studio/tree/panicRun with
The log gets outputted to
./debug.log@mum4k commented on GitHub (Nov 22, 2020):
Perfect, I managed to reproduce it. This is a different problem than I thought originally. The terminal actually gets reset to the correct state, but literally nothing gets printed out.
I will poke at it a bit and update with what I found.
@mum4k commented on GitHub (Nov 22, 2020):
I think understand it now, or well at least have a workable theory. This is (as with most hard to figure out problems) a race condition.
When we follow the call to
StartSession()in the main:github.com/dyc3/teleprompt-studio@1e0dff3e56/main.go (L380)We find that a goroutine is created that eventually calls
osutil.CaptureWithCGo():github.com/dyc3/teleprompt-studio@1e0dff3e56/recording.go (L38-L40)I don't know much about the function, but from it description it looks like it either captures stderr or changes where that goes.
Now with goroutines, we don't know when they get scheduled. My theory is that we lose the output of the panic if it happens concurrently with the execution of
osutil.CaptureWithCGo().I have two experiments that seem to confirm that:
If we remove the call to
osutil.CaptureWithCGo()and just call the innerportaudio.Initialize()directly, I can no longer reproduce the problem of the disappearing panic. It seems to be correctly reported on the screen.If I add a
time.Sleep(1*time.Seconds)just above the call totermdash.Run(), the panic again displays properly.Does this ring any bells? Maybe you know a it more about what
osutil.CaptureWithCGo()does? Does the explanation above make sense?@dyc3 commented on GitHub (Nov 22, 2020):
osutil.CaptureWithCGo()eats both stdout and stderr output from portaudio when it initializes, and lets me redirect it to the log file. If we don't do this, then that output goes directly to the terminal and it messes with the rendering.It seems like the best thing for me to do is initialize portaudio in the main goroutine. I feel kinda bad for making you debug my program for me haha.
@mum4k commented on GitHub (Nov 22, 2020):
No worries about that at all @dyc3, I am happy we figured it out. Helping each other is exactly what open source is about. Besides I too am grateful for your contribution to
termdash.Yes initializing in the main goroutine before the UI (
termdash) get started is likely a good solution. It sounds like, at least for now, we don't have anything actionable intermdashitself, but please feel free to reopen this if you do find something.And do reach out if you encounter further issues or missing features.