about summary refs log tree commit diff
path: root/main/civisibility/utils/names.go
blob: 1edcb2b97271ba89ad22349aaa8f5565f786bd1c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2024 Datadog, Inc.

package utils

import (
	"bytes"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

// GetModuleAndSuiteName extracts the module name and suite name from a given program counter (pc).
// This function utilizes runtime.FuncForPC to retrieve the full function name associated with the
// program counter, then splits the string to separate the package name from the function name.
//
// Example 1:
//
//	Input:  github.com/DataDog/dd-sdk-go-testing.TestRun
//	Output:
//	   module: github.com/DataDog/dd-sdk-go-testing
//	   suite:  testing_test.go
//
// Example 2:
//
//	Input:  github.com/DataDog/dd-sdk-go-testing.TestRun.func1
//	Output:
//	   module: github.com/DataDog/dd-sdk-go-testing
//	   suite:  testing_test.go
//
// Parameters:
//
//	pc - The program counter for which the module and suite name should be retrieved.
//
// Returns:
//
//	module - The module name extracted from the full function name.
//	suite  - The base name of the file where the function is located.
func GetModuleAndSuiteName(pc uintptr) (module string, suite string) {
	funcValue := runtime.FuncForPC(pc)
	funcFullName := funcValue.Name()
	lastSlash := strings.LastIndexByte(funcFullName, '/')
	if lastSlash < 0 {
		lastSlash = 0
	}
	firstDot := strings.IndexByte(funcFullName[lastSlash:], '.') + lastSlash
	file, _ := funcValue.FileLine(funcValue.Entry())
	return funcFullName[:firstDot], filepath.Base(file)
}

// GetStacktrace retrieves the current stack trace, skipping a specified number of frames.
//
// This function captures the stack trace of the current goroutine, formats it, and returns it as a string.
// It uses runtime.Callers to capture the program counters of the stack frames and runtime.CallersFrames
// to convert these program counters into readable frames. The stack trace is formatted to include the function
// name, file name, and line number of each frame.
//
// Parameters:
//
//	skip - The number of stack frames to skip before capturing the stack trace.
//
// Returns:
//
//	A string representation of the current stack trace, with each frame on a new line.
func GetStacktrace(skip int) string {
	pcs := make([]uintptr, 256)
	total := runtime.Callers(skip+2, pcs)
	frames := runtime.CallersFrames(pcs[:total])
	buffer := new(bytes.Buffer)
	for {
		if frame, ok := frames.Next(); ok {
			_, _ = fmt.Fprintf(buffer, "%s\n\t%s:%d\n", frame.Function, frame.File, frame.Line)
		} else {
			break
		}

	}
	return buffer.String()
}