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  }