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}