diag.go raw

   1  //go:build go1.18
   2  // +build go1.18
   3  
   4  // Copyright (c) Microsoft Corporation. All rights reserved.
   5  // Licensed under the MIT License.
   6  
   7  package diag
   8  
   9  import (
  10  	"fmt"
  11  	"runtime"
  12  	"strings"
  13  )
  14  
  15  // Caller returns the file and line number of a frame on the caller's stack.
  16  // If the funtion fails an empty string is returned.
  17  // skipFrames - the number of frames to skip when determining the caller.
  18  // Passing a value of 0 will return the immediate caller of this function.
  19  func Caller(skipFrames int) string {
  20  	if pc, file, line, ok := runtime.Caller(skipFrames + 1); ok {
  21  		// the skipFrames + 1 is to skip ourselves
  22  		frame := runtime.FuncForPC(pc)
  23  		return fmt.Sprintf("%s()\n\t%s:%d", frame.Name(), file, line)
  24  	}
  25  	return ""
  26  }
  27  
  28  // StackTrace returns a formatted stack trace string.
  29  // If the funtion fails an empty string is returned.
  30  // skipFrames - the number of stack frames to skip before composing the trace string.
  31  // totalFrames - the maximum number of stack frames to include in the trace string.
  32  func StackTrace(skipFrames, totalFrames int) string {
  33  	pcCallers := make([]uintptr, totalFrames)
  34  	if frames := runtime.Callers(skipFrames, pcCallers); frames == 0 {
  35  		return ""
  36  	}
  37  	frames := runtime.CallersFrames(pcCallers)
  38  	sb := strings.Builder{}
  39  	for {
  40  		frame, more := frames.Next()
  41  		sb.WriteString(frame.Function)
  42  		sb.WriteString("()\n\t")
  43  		sb.WriteString(frame.File)
  44  		sb.WriteRune(':')
  45  		sb.WriteString(fmt.Sprintf("%d\n", frame.Line))
  46  		if !more {
  47  			break
  48  		}
  49  	}
  50  	return sb.String()
  51  }
  52