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
7package diag
8
9import (
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.
19func 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.
32func 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}