github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/errors/errors.go (about) 1 /* 2 * Copyright (c) 2019, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 /* 21 22 Package errors provides error wrapping helpers that add inline, single frame 23 stack trace information to error messages. 24 25 */ 26 package errors 27 28 import ( 29 "fmt" 30 "runtime" 31 32 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/stacktrace" 33 ) 34 35 // TraceNew returns a new error with the given message, wrapped with the caller 36 // stack frame information. 37 func TraceNew(message string) error { 38 err := fmt.Errorf("%s", message) 39 pc, _, line, ok := runtime.Caller(1) 40 if !ok { 41 return fmt.Errorf("[unknown]: %w", err) 42 } 43 return fmt.Errorf("%s#%d: %w", stacktrace.GetFunctionName(pc), line, err) 44 } 45 46 // BackTraceNew returns a new error with the given message, wrapped with the 47 // caller stack frame information going back up the stack until the caller of 48 // the specified function name is encountered. 49 func BackTraceNew(backTraceFuncName, message string) error { 50 err := fmt.Errorf("%s", message) 51 return fmt.Errorf("%s%w", backTrace(backTraceFuncName), err) 52 } 53 54 // Tracef returns a new error with the given formatted message, wrapped with 55 // the caller stack frame information. 56 func Tracef(format string, args ...interface{}) error { 57 err := fmt.Errorf(format, args...) 58 pc, _, line, ok := runtime.Caller(1) 59 if !ok { 60 return fmt.Errorf("[unknown]: %w", err) 61 } 62 return fmt.Errorf("%s#%d: %w", stacktrace.GetFunctionName(pc), line, err) 63 } 64 65 // Trace wraps the given error with the caller stack frame information. 66 func Trace(err error) error { 67 if err == nil { 68 return nil 69 } 70 pc, _, line, ok := runtime.Caller(1) 71 if !ok { 72 return fmt.Errorf("[unknown]: %w", err) 73 } 74 return fmt.Errorf("%s#%d: %w", stacktrace.GetFunctionName(pc), line, err) 75 } 76 77 // TraceMsg wraps the given error with the caller stack frame information 78 // and the given message. 79 func TraceMsg(err error, message string) error { 80 if err == nil { 81 return nil 82 } 83 pc, _, line, ok := runtime.Caller(1) 84 if !ok { 85 return fmt.Errorf("[unknown]: %s: %w", message, err) 86 } 87 return fmt.Errorf("%s#%d: %s: %w", stacktrace.GetFunctionName(pc), line, message, err) 88 } 89 90 func backTrace(backTraceFuncName string) string { 91 stop := false 92 trace := "" 93 // Skip starts at 2, assuming backTrace is called as a helper function. 94 for n := 2; ; n++ { 95 pc, _, line, ok := runtime.Caller(n) 96 if !ok { 97 break 98 } 99 funcName := stacktrace.GetFunctionName(pc) 100 trace = fmt.Sprintf("%s#%d: ", funcName, line) + trace 101 if stop { 102 break 103 } 104 if funcName == backTraceFuncName { 105 // Stop after the _next_ function 106 stop = true 107 } 108 } 109 return trace 110 }