From 7a4b108b37ccf415f75d74738dddbdba23af1805 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 19 Mar 2018 01:18:36 +0200 Subject: [PATCH] Avoid showing panics directly if debug mode is not enabled --- gomuks.go | 52 ++++++++++++++++++++++++++++++----------------- matrix/matrix.go | 4 ++-- ui/debug/debug.go | 49 +++++++++++++++++++++++++++++++++++++++++--- ui/view-main.go | 4 +++- 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/gomuks.go b/gomuks.go index fecb67a..418fc9e 100644 --- a/gomuks.go +++ b/gomuks.go @@ -17,8 +17,10 @@ package main import ( + "fmt" "os" "path/filepath" + "time" "maunium.net/go/gomatrix" "maunium.net/go/gomuks/config" @@ -29,17 +31,18 @@ import ( "maunium.net/go/tview" ) -type gomuks struct { - app *tview.Application - ui *ui.GomuksUI - matrix *matrix.Container - debug *debug.Pane - config *config.Config +type Gomuks struct { + app *tview.Application + ui *ui.GomuksUI + matrix *matrix.Container + debug *debug.Pane + debugMode bool + config *config.Config } -func NewGomuks(enableDebug bool) *gomuks { +func NewGomuks(enableDebug bool) *Gomuks { configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks") - gmx := &gomuks{ + gmx := &Gomuks{ app: tview.NewApplication(), } @@ -63,13 +66,14 @@ func NewGomuks(enableDebug bool) *gomuks { main := gmx.ui.InitViews() if enableDebug { main = gmx.debug.Wrap(main) + gmx.debugMode = true } gmx.app.SetRoot(main, true) return gmx } -func (gmx *gomuks) Stop() { +func (gmx *Gomuks) Stop() { gmx.debug.Print("Disconnecting from Matrix...") gmx.matrix.Stop() gmx.debug.Print("Cleaning up UI...") @@ -78,44 +82,54 @@ func (gmx *gomuks) Stop() { gmx.debug.Print("Saving session...") gmx.config.Session.Save() } + os.Exit(0) } -func (gmx *gomuks) Recover() { +func (gmx *Gomuks) Recover() { if p := recover(); p != nil { if gmx.App().GetScreen() != nil { gmx.App().GetScreen().Fini() } - panic(p) + if gmx.debugMode { + panic(p) + } else { + debug.PrettyPanic() + } } } -func (gmx *gomuks) Start() { +func (gmx *Gomuks) Start() { if err := gmx.app.Run(); err != nil { panic(err) } } -func (gmx *gomuks) Matrix() *gomatrix.Client { +func (gmx *Gomuks) Matrix() *gomatrix.Client { return gmx.matrix.Client() } -func (gmx *gomuks) MatrixContainer() ifc.MatrixContainer { +func (gmx *Gomuks) MatrixContainer() ifc.MatrixContainer { return gmx.matrix } -func (gmx *gomuks) App() *tview.Application { +func (gmx *Gomuks) App() *tview.Application { return gmx.app } -func (gmx *gomuks) Config() *config.Config { +func (gmx *Gomuks) Config() *config.Config { return gmx.config } -func (gmx *gomuks) UI() ifc.GomuksUI { +func (gmx *Gomuks) UI() ifc.GomuksUI { return gmx.ui } func main() { - debug := os.Getenv("DEBUG") - NewGomuks(len(debug) > 0).Start() + enableDebug := len(os.Getenv("DEBUG")) > 0 + NewGomuks(enableDebug).Start() + + // We use os.Exit() everywhere, so exiting by returning from Start() shouldn't happen. + time.Sleep(5 * time.Second) + fmt.Println("Unexpected exit by return from Gomuks#Start().") + os.Exit(2) } diff --git a/matrix/matrix.go b/matrix/matrix.go index 7652188..564dc81 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -198,13 +198,13 @@ func (c *Container) HandleTyping(evt *gomatrix.Event) { } func (c *Container) SendMessage(roomID, message string) { - c.gmx.Recover() + defer c.gmx.Recover() c.SendTyping(roomID, false) c.client.SendText(roomID, message) } func (c *Container) SendTyping(roomID string, typing bool) { - c.gmx.Recover() + defer c.gmx.Recover() ts := time.Now().Unix() if c.typing > ts && typing { return diff --git a/ui/debug/debug.go b/ui/debug/debug.go index c855897..a87d64c 100644 --- a/ui/debug/debug.go +++ b/ui/debug/debug.go @@ -18,6 +18,11 @@ package debug import ( "fmt" + "io/ioutil" + "os" + "time" + + "runtime/debug" "maunium.net/go/tview" ) @@ -30,7 +35,7 @@ type Printer interface { type Pane struct { *tview.TextView Height int - num int + num int } var Default Printer @@ -46,8 +51,8 @@ func NewPane() *Pane { return &Pane{ TextView: pane, - Height: 35, - num: 0, + Height: 35, + num: 0, } } @@ -81,3 +86,41 @@ func Print(text ...interface{}) { Default.Print(text...) } } + +const Oops = ` __________ +< Oh noes! > + ‾‾‾\‾‾‾‾‾‾ + \ ^__^ + \ (XX)\_______ + (__)\ )\/\ + U ||----W | + || ||` + +func PrettyPanic() { + fmt.Println(Oops) + fmt.Println("") + fmt.Println("A fatal error has occurred.") + fmt.Println("") + traceFile := fmt.Sprintf("/tmp/gomuks-panic-%s.txt", time.Now().Format("2006-01-02--15-04-05")) + data := debug.Stack() + err := ioutil.WriteFile(traceFile, data, 0644) + if err != nil { + fmt.Println("Saving the stack trace to", traceFile, "failed:") + fmt.Println("--------------------------------------------------------------------------------") + fmt.Println(err) + fmt.Println("--------------------------------------------------------------------------------") + fmt.Println("") + fmt.Println("You can file an issue at https://github.com/tulir/gomuks/issues.") + fmt.Println("Please provide the file save error (above) and the stack trace of the original error (below) when filing an issue.") + fmt.Println("") + fmt.Println("--------------------------------------------------------------------------------") + debug.PrintStack() + fmt.Println("--------------------------------------------------------------------------------") + } else { + fmt.Println("The stack trace has been saved to", traceFile) + fmt.Println("") + fmt.Println("You can file an issue at https://github.com/tulir/gomuks/issues.") + fmt.Println("Please provide the contents of that file when filing an issue.") + } + os.Exit(1) +} diff --git a/ui/view-main.go b/ui/view-main.go index 8ec482e..c2cd30f 100644 --- a/ui/view-main.go +++ b/ui/view-main.go @@ -148,7 +148,7 @@ func (view *MainView) InputDone(key tcell.Key) { } func (view *MainView) HandleCommand(room, command string, args []string) { - view.gmx.Recover() + defer view.gmx.Recover() debug.Print("Handling command", command, args) switch command { case "/quit": @@ -156,6 +156,8 @@ func (view *MainView) HandleCommand(room, command string, args []string) { case "/clearcache": view.config.Session.Clear() view.gmx.Stop() + case "/panic": + panic("This is a test panic.") case "/part": fallthrough case "/leave":