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