2018-03-13 14:27:12 +01:00
|
|
|
// gomuks - A terminal Matrix client written in Go.
|
2020-04-19 17:10:14 +02:00
|
|
|
// Copyright (C) 2020 Tulir Asokan
|
2018-03-13 14:27:12 +01:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
2019-01-17 13:13:25 +01:00
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
2018-03-13 14:27:12 +01:00
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2019-01-17 13:13:25 +01:00
|
|
|
// GNU Affero General Public License for more details.
|
2018-03-13 14:27:12 +01:00
|
|
|
//
|
2019-01-17 13:13:25 +01:00
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2018-03-13 14:27:12 +01:00
|
|
|
|
2018-03-18 20:24:03 +01:00
|
|
|
package debug
|
2018-03-13 14:27:12 +01:00
|
|
|
|
|
|
|
import (
|
2018-04-10 15:00:55 +02:00
|
|
|
"bytes"
|
2018-03-13 14:27:12 +01:00
|
|
|
"fmt"
|
2018-04-09 22:45:54 +02:00
|
|
|
"io"
|
2018-03-19 00:18:36 +01:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2023-07-01 11:05:25 +02:00
|
|
|
"os/user"
|
2019-04-27 14:02:52 +02:00
|
|
|
"path/filepath"
|
2023-07-06 20:25:33 +02:00
|
|
|
"runtime"
|
2018-03-19 00:18:36 +01:00
|
|
|
"runtime/debug"
|
2019-01-17 13:13:25 +01:00
|
|
|
"time"
|
2019-04-27 14:02:52 +02:00
|
|
|
|
|
|
|
"github.com/sasha-s/go-deadlock"
|
2018-03-13 14:27:12 +01:00
|
|
|
)
|
|
|
|
|
2018-04-09 22:45:54 +02:00
|
|
|
var writer io.Writer
|
2023-07-09 22:01:18 +02:00
|
|
|
var RecoverPrettyPanic bool = true
|
2019-04-27 14:02:52 +02:00
|
|
|
var DeadlockDetection bool
|
|
|
|
var WriteLogs bool
|
2018-04-18 17:35:24 +02:00
|
|
|
var OnRecover func()
|
2023-07-06 20:25:33 +02:00
|
|
|
var LogDirectory = GetUserDebugDir()
|
|
|
|
|
|
|
|
func GetUserDebugDir() string {
|
|
|
|
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
|
|
|
return filepath.Join(os.TempDir(), "gomuks-"+getUname())
|
|
|
|
}
|
|
|
|
// See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
|
|
|
if xdgStateHome := os.Getenv("XDG_STATE_HOME"); xdgStateHome != "" {
|
2023-07-09 22:01:18 +02:00
|
|
|
return filepath.Join(xdgStateHome, "gomuks")
|
2023-07-06 20:25:33 +02:00
|
|
|
}
|
|
|
|
home := os.Getenv("HOME")
|
|
|
|
if home == "" {
|
|
|
|
fmt.Println("XDG_STATE_HOME and HOME are both unset")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
return filepath.Join(home, ".local", "state", "gomuks")
|
|
|
|
}
|
2023-07-01 11:05:25 +02:00
|
|
|
|
|
|
|
func getUname() string {
|
|
|
|
currUser, err := user.Current()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return currUser.Username
|
|
|
|
}
|
2018-03-13 20:58:43 +01:00
|
|
|
|
2019-04-27 14:02:52 +02:00
|
|
|
func Initialize() {
|
|
|
|
err := os.MkdirAll(LogDirectory, 0750)
|
2018-04-09 22:45:54 +02:00
|
|
|
if err != nil {
|
2023-07-09 22:01:18 +02:00
|
|
|
RecoverPrettyPanic = false
|
2019-04-27 14:02:52 +02:00
|
|
|
DeadlockDetection = false
|
|
|
|
WriteLogs = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if WriteLogs {
|
|
|
|
writer, err = os.OpenFile(filepath.Join(LogDirectory, "debug.log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-04-27 14:44:01 +02:00
|
|
|
_, _ = fmt.Fprintf(writer, "======================= Debug init @ %s =======================\n", time.Now().Format("2006-01-02 15:04:05"))
|
2019-04-27 14:02:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if DeadlockDetection {
|
|
|
|
deadlocks, err := os.OpenFile(filepath.Join(LogDirectory, "deadlock.log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
deadlock.Opts.LogBuf = deadlocks
|
|
|
|
deadlock.Opts.OnPotentialDeadlock = func() {
|
|
|
|
if OnRecover != nil {
|
|
|
|
OnRecover()
|
|
|
|
}
|
2019-04-27 14:11:27 +02:00
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Potential deadlock detected. See %s/deadlock.log for more information.", LogDirectory)
|
2019-04-27 14:02:52 +02:00
|
|
|
os.Exit(88)
|
|
|
|
}
|
|
|
|
_, err = fmt.Fprintf(deadlocks, "======================= Debug init @ %s =======================\n", time.Now().Format("2006-01-02 15:04:05"))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
deadlock.Opts.Disable = true
|
2018-03-13 20:58:43 +01:00
|
|
|
}
|
2018-03-13 14:27:12 +01:00
|
|
|
}
|
|
|
|
|
2018-04-09 22:45:54 +02:00
|
|
|
func Printf(text string, args ...interface{}) {
|
|
|
|
if writer != nil {
|
2019-04-27 14:02:52 +02:00
|
|
|
_, _ = fmt.Fprintf(writer, time.Now().Format("[2006-01-02 15:04:05] "))
|
|
|
|
_, _ = fmt.Fprintf(writer, text+"\n", args...)
|
2018-04-01 09:05:29 +02:00
|
|
|
}
|
2018-03-18 20:24:03 +01:00
|
|
|
}
|
|
|
|
|
2018-04-09 22:45:54 +02:00
|
|
|
func Print(text ...interface{}) {
|
|
|
|
if writer != nil {
|
2019-04-27 14:02:52 +02:00
|
|
|
_, _ = fmt.Fprintf(writer, time.Now().Format("[2006-01-02 15:04:05] "))
|
|
|
|
_, _ = fmt.Fprintln(writer, text...)
|
2018-03-18 20:24:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-09 22:45:54 +02:00
|
|
|
func PrintStack() {
|
|
|
|
if writer != nil {
|
2019-04-27 14:02:52 +02:00
|
|
|
_, _ = writer.Write(debug.Stack())
|
2018-03-18 20:24:03 +01:00
|
|
|
}
|
2018-03-13 14:27:12 +01:00
|
|
|
}
|
2018-03-19 00:18:36 +01:00
|
|
|
|
2018-04-18 17:35:24 +02:00
|
|
|
// Recover recovers a panic, runs the OnRecover handler and either re-panics or
|
|
|
|
// shows an user-friendly message about the panic depending on whether or not
|
|
|
|
// the pretty panic mode is enabled.
|
|
|
|
func Recover() {
|
|
|
|
if p := recover(); p != nil {
|
|
|
|
if OnRecover != nil {
|
|
|
|
OnRecover()
|
|
|
|
}
|
|
|
|
if RecoverPrettyPanic {
|
|
|
|
PrettyPanic(p)
|
|
|
|
} else {
|
|
|
|
panic(p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 00:18:36 +01:00
|
|
|
const Oops = ` __________
|
|
|
|
< Oh noes! >
|
|
|
|
‾‾‾\‾‾‾‾‾‾
|
|
|
|
\ ^__^
|
|
|
|
\ (XX)\_______
|
|
|
|
(__)\ )\/\
|
|
|
|
U ||----W |
|
2019-03-28 22:28:27 +01:00
|
|
|
|| ||
|
|
|
|
|
|
|
|
A fatal error has occurred.
|
|
|
|
|
|
|
|
`
|
2018-03-19 00:18:36 +01:00
|
|
|
|
2018-04-10 15:00:55 +02:00
|
|
|
func PrettyPanic(panic interface{}) {
|
2019-03-28 22:28:27 +01:00
|
|
|
fmt.Print(Oops)
|
2019-04-27 14:02:52 +02:00
|
|
|
traceFile := fmt.Sprintf(filepath.Join(LogDirectory, "panic-%s.txt"), time.Now().Format("2006-01-02--15-04-05"))
|
2018-04-10 15:00:55 +02:00
|
|
|
|
|
|
|
var buf bytes.Buffer
|
2019-04-27 14:02:52 +02:00
|
|
|
_, _ = fmt.Fprintln(&buf, panic)
|
2018-04-10 15:00:55 +02:00
|
|
|
buf.Write(debug.Stack())
|
2019-04-27 14:02:21 +02:00
|
|
|
err := ioutil.WriteFile(traceFile, buf.Bytes(), 0640)
|
2018-04-10 15:00:55 +02:00
|
|
|
|
2018-03-19 00:18:36 +01:00
|
|
|
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("--------------------------------------------------------------------------------")
|
2018-04-10 15:00:55 +02:00
|
|
|
fmt.Println(panic)
|
2018-03-19 00:18:36 +01:00
|
|
|
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)
|
|
|
|
}
|