github.com/waldiirawan/apm-agent-go/v2@v2.2.2/stacktrace/stacktrace_test.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package stacktrace_test
    19  
    20  import (
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"github.com/waldiirawan/apm-agent-go/v2/stacktrace"
    27  )
    28  
    29  func TestStacktrace(t *testing.T) {
    30  	expect := []string{
    31  		"github.com/waldiirawan/apm-agent-go/v2/stacktrace_test.callPanickerDefer",
    32  		"runtime.gopanic",
    33  		"github.com/waldiirawan/apm-agent-go/v2/stacktrace_test.(*panicker).panic",
    34  		"github.com/waldiirawan/apm-agent-go/v2/stacktrace_test.callPanicker",
    35  	}
    36  
    37  	ch := make(chan []string)
    38  	go callPanicker(ch)
    39  	functions := <-ch
    40  	require.NotNil(t, functions)
    41  	if diff := cmp.Diff(expect, functions); diff != "" {
    42  		t.Fatalf("%s", diff)
    43  	}
    44  }
    45  
    46  func callPanicker(ch chan<- []string) {
    47  	defer callPanickerDefer(ch)
    48  	(&panicker{}).panic()
    49  }
    50  
    51  func callPanickerDefer(ch chan<- []string) {
    52  	if recover() == nil {
    53  		ch <- nil
    54  		return
    55  	}
    56  	allFrames := stacktrace.AppendStacktrace(nil, 1, 5)
    57  	functions := make([]string, 0, len(allFrames))
    58  	for _, frame := range allFrames {
    59  		switch frame.Function {
    60  		case "runtime.call32", "runtime.goexit":
    61  			// Depending on the Go toolchain version, these may or may not be present.
    62  		default:
    63  			functions = append(functions, frame.Function)
    64  		}
    65  	}
    66  	ch <- functions
    67  }
    68  
    69  type panicker struct{}
    70  
    71  func (*panicker) panic() {
    72  	panic("oh noes")
    73  }
    74  
    75  func TestSplitFunctionName(t *testing.T) {
    76  	testSplitFunctionName(t, "main", "main")
    77  	testSplitFunctionName(t, "main", "Foo.Bar")
    78  	testSplitFunctionName(t, "main", "(*Foo).Bar")
    79  	testSplitFunctionName(t, "github.com/waldiirawan/apm-agent-go/v2/foo", "bar")
    80  	testSplitFunctionName(t,
    81  		"github.com/waldiirawan/apm-agent-go/module/apmgin/v2",
    82  		"(*middleware).(github.com/waldiirawan/apm-agent-go/module/apmgin.handle)-fm",
    83  	)
    84  }
    85  
    86  func testSplitFunctionName(t *testing.T, module, function string) {
    87  	outModule, outFunction := stacktrace.SplitFunctionName(module + "." + function)
    88  	assertModule(t, outModule, module)
    89  	assertFunction(t, outFunction, function)
    90  }
    91  
    92  func TestSplitFunctionNameUnescape(t *testing.T) {
    93  	module, function := stacktrace.SplitFunctionName("github.com/elastic/apm-agent%2ego.funcName")
    94  	assertModule(t, module, "github.com/elastic/apm-agent.go")
    95  	assertFunction(t, function, "funcName")
    96  
    97  	// malformed escape sequences are left alone
    98  	module, function = stacktrace.SplitFunctionName("github.com/elastic/apm-agent%.funcName")
    99  	assertModule(t, module, "github.com/elastic/apm-agent%")
   100  	assertFunction(t, function, "funcName")
   101  }
   102  
   103  func assertModule(t *testing.T, got, expect string) {
   104  	if got != expect {
   105  		t.Errorf("got module %q, expected %q", got, expect)
   106  	}
   107  }
   108  
   109  func assertFunction(t *testing.T, got, expect string) {
   110  	if got != expect {
   111  		t.Errorf("got function %q, expected %q", got, expect)
   112  	}
   113  }
   114  
   115  func BenchmarkAppendStacktraceUnlimited(b *testing.B) {
   116  	var frames []stacktrace.Frame
   117  	for i := 0; i < b.N; i++ {
   118  		frames = stacktrace.AppendStacktrace(frames[:0], 0, -1)
   119  	}
   120  }
   121  
   122  func BenchmarkAppendStacktrace10(b *testing.B) {
   123  	var frames []stacktrace.Frame
   124  	for i := 0; i < b.N; i++ {
   125  		frames = stacktrace.AppendStacktrace(frames[:0], 0, 10)
   126  	}
   127  }
   128  
   129  func BenchmarkAppendStacktrace50(b *testing.B) {
   130  	var frames []stacktrace.Frame
   131  	for i := 0; i < b.N; i++ {
   132  		frames = stacktrace.AppendStacktrace(frames[:0], 0, 50)
   133  	}
   134  }