2020-06-02 22:48:55 +08:00
|
|
|
package framework
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"runtime/debug"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/onsi/ginkgo"
|
|
|
|
|
|
|
|
e2eginkgowrapper "github.com/fatedier/frp/test/e2e/framework/ginkgowrapper"
|
|
|
|
)
|
|
|
|
|
|
|
|
func nowStamp() string {
|
|
|
|
return time.Now().Format(time.StampMilli)
|
|
|
|
}
|
|
|
|
|
|
|
|
func log(level string, format string, args ...interface{}) {
|
|
|
|
fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logf logs the info.
|
|
|
|
func Logf(format string, args ...interface{}) {
|
|
|
|
log("INFO", format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Failf logs the fail info, including a stack trace.
|
|
|
|
func Failf(format string, args ...interface{}) {
|
|
|
|
FailfWithOffset(1, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FailfWithOffset calls "Fail" and logs the error with a stack trace that starts at "offset" levels above its caller
|
|
|
|
// (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f").
|
|
|
|
func FailfWithOffset(offset int, format string, args ...interface{}) {
|
|
|
|
msg := fmt.Sprintf(format, args...)
|
|
|
|
skip := offset + 1
|
|
|
|
log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip))
|
|
|
|
e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fail is a replacement for ginkgo.Fail which logs the problem as it occurs
|
|
|
|
// together with a stack trace and then calls ginkgowrapper.Fail.
|
|
|
|
func Fail(msg string, callerSkip ...int) {
|
|
|
|
skip := 1
|
|
|
|
if len(callerSkip) > 0 {
|
|
|
|
skip += callerSkip[0]
|
|
|
|
}
|
|
|
|
log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip))
|
|
|
|
e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip)
|
|
|
|
}
|
|
|
|
|
|
|
|
var codeFilterRE = regexp.MustCompile(`/github.com/onsi/ginkgo/`)
|
|
|
|
|
|
|
|
// PrunedStack is a wrapper around debug.Stack() that removes information
|
|
|
|
// about the current goroutine and optionally skips some of the initial stack entries.
|
|
|
|
// With skip == 0, the returned stack will start with the caller of PruneStack.
|
|
|
|
// From the remaining entries it automatically filters out useless ones like
|
|
|
|
// entries coming from Ginkgo.
|
|
|
|
//
|
2022-08-29 01:02:53 +08:00
|
|
|
// This is a modified copy of PruneStack in
|
|
|
|
// https://github.com/onsi/ginkgo/blob/f90f37d87fa6b1dd9625e2b1e83c23ffae3de228/internal/codelocation/code_location.go#L25:
|
|
|
|
// - simplified API and thus renamed (calls debug.Stack() instead of taking a parameter)
|
|
|
|
// - source code filtering updated to be specific to Kubernetes
|
|
|
|
// - optimized to use bytes and in-place slice filtering from
|
|
|
|
// https://github.com/golang/go/wiki/SliceTricks#filter-in-place
|
2020-06-02 22:48:55 +08:00
|
|
|
func PrunedStack(skip int) []byte {
|
|
|
|
fullStackTrace := debug.Stack()
|
|
|
|
stack := bytes.Split(fullStackTrace, []byte("\n"))
|
|
|
|
// Ensure that the even entries are the method names and the
|
2022-08-29 12:25:36 +08:00
|
|
|
// odd entries the source code information.
|
2020-06-02 22:48:55 +08:00
|
|
|
if len(stack) > 0 && bytes.HasPrefix(stack[0], []byte("goroutine ")) {
|
|
|
|
// Ignore "goroutine 29 [running]:" line.
|
|
|
|
stack = stack[1:]
|
|
|
|
}
|
|
|
|
// The "+2" is for skipping over:
|
|
|
|
// - runtime/debug.Stack()
|
|
|
|
// - PrunedStack()
|
|
|
|
skip += 2
|
|
|
|
if len(stack) > 2*skip {
|
|
|
|
stack = stack[2*skip:]
|
|
|
|
}
|
|
|
|
n := 0
|
|
|
|
for i := 0; i < len(stack)/2; i++ {
|
|
|
|
// We filter out based on the source code file name.
|
2022-08-29 01:02:53 +08:00
|
|
|
if !codeFilterRE.Match(stack[i*2+1]) {
|
2020-06-02 22:48:55 +08:00
|
|
|
stack[n] = stack[i*2]
|
|
|
|
stack[n+1] = stack[i*2+1]
|
|
|
|
n += 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stack = stack[:n]
|
|
|
|
|
|
|
|
return bytes.Join(stack, []byte("\n"))
|
|
|
|
}
|