...

Source file src/github.com/golang/glog/internal/stackdump/stackdump_test.go

Documentation: github.com/golang/glog/internal/stackdump

     1  // stackdump_test checks that the heuristics the stackdump package applies to
     2  // prune frames work as expected in production Go compilers.
     3  
     4  package stackdump_test
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"regexp"
    10  	"runtime"
    11  	"testing"
    12  
    13  	"github.com/golang/glog/internal/stackdump"
    14  )
    15  
    16  var file string
    17  
    18  func init() {
    19  	_, file, _, _ = runtime.Caller(0)
    20  }
    21  
    22  func TestCallerText(t *testing.T) {
    23  	stack := stackdump.CallerText(0)
    24  	_, _, line, _ := runtime.Caller(0)
    25  	line--
    26  
    27  	wantRE := regexp.MustCompile(fmt.Sprintf(
    28  		`^goroutine \d+ \[running\]:
    29  github.com/golang/glog/internal/stackdump_test\.TestCallerText(\([^)]*\))?
    30  	%v:%v.*
    31  `, file, line))
    32  	if !wantRE.Match(stack) {
    33  		t.Errorf("Stack dump:\n%s\nwant matching regexp:\n%s", stack, wantRE.String())
    34  
    35  		buf := make([]byte, len(stack)*2)
    36  		origStack := buf[:runtime.Stack(buf, false)]
    37  		t.Logf("Unpruned stack:\n%s", origStack)
    38  	}
    39  }
    40  
    41  func callerAt(calls int, depth int) (stack []byte) {
    42  	if calls == 1 {
    43  		return stackdump.CallerText(depth)
    44  	}
    45  	return callerAt(calls-1, depth)
    46  }
    47  
    48  func TestCallerTextSkip(t *testing.T) {
    49  	const calls = 3
    50  	cases := []struct {
    51  		depth          int
    52  		callerAtFrames int
    53  		wantEndOfStack bool
    54  	}{
    55  		{depth: 0, callerAtFrames: calls},
    56  		{depth: calls - 1, callerAtFrames: 1},
    57  		{depth: calls, callerAtFrames: 0},
    58  		{depth: calls + 1, callerAtFrames: 0},
    59  		{depth: calls + 100, wantEndOfStack: true},
    60  	}
    61  
    62  	for _, tc := range cases {
    63  		stack := callerAt(calls, tc.depth)
    64  
    65  		wantREBuf := bytes.NewBuffer(nil)
    66  		fmt.Fprintf(wantREBuf, `^goroutine \d+ \[running\]:
    67  `)
    68  		if tc.wantEndOfStack {
    69  			fmt.Fprintf(wantREBuf, "\n|$")
    70  		} else {
    71  			for n := tc.callerAtFrames; n > 0; n-- {
    72  				fmt.Fprintf(wantREBuf, `github.com/golang/glog/internal/stackdump_test\.callerAt(\([^)]*\))?
    73  	%v:\d+.*
    74  `, file)
    75  			}
    76  
    77  			if tc.depth <= calls {
    78  				fmt.Fprintf(wantREBuf, `github.com/golang/glog/internal/stackdump_test\.TestCallerTextSkip(\([^)]*\))?
    79  	%v:\d+.*
    80  `, file)
    81  			}
    82  		}
    83  
    84  		wantRE := regexp.MustCompile(wantREBuf.String())
    85  
    86  		if !wantRE.Match(stack) {
    87  			t.Errorf("for %v calls, stackdump.CallerText(%v) =\n%s\n\nwant matching regexp:\n%s", calls, tc.depth, stack, wantRE.String())
    88  		}
    89  	}
    90  }
    91  
    92  func pcAt(calls int, depth int) (stack []uintptr) {
    93  	if calls == 1 {
    94  		return stackdump.CallerPC(depth)
    95  	}
    96  	stack = pcAt(calls-1, depth)
    97  	runtime.Gosched() // Thwart tail-call optimization.
    98  	return stack
    99  }
   100  
   101  func TestCallerPC(t *testing.T) {
   102  	const calls = 3
   103  	cases := []struct {
   104  		depth          int
   105  		pcAtFrames     int
   106  		wantEndOfStack bool
   107  	}{
   108  		{depth: 0, pcAtFrames: calls},
   109  		{depth: calls - 1, pcAtFrames: 1},
   110  		{depth: calls, pcAtFrames: 0},
   111  		{depth: calls + 1, pcAtFrames: 0},
   112  		{depth: calls + 100, wantEndOfStack: true},
   113  	}
   114  
   115  	for _, tc := range cases {
   116  		stack := pcAt(calls, tc.depth)
   117  		if tc.wantEndOfStack {
   118  			if len(stack) != 0 {
   119  				t.Errorf("for %v calls, stackdump.CallerPC(%v) =\n%q\nwant []", calls, tc.depth, stack)
   120  			}
   121  			continue
   122  		}
   123  
   124  		wantFuncs := []string{}
   125  		for n := tc.pcAtFrames; n > 0; n-- {
   126  			wantFuncs = append(wantFuncs, `github.com/golang/glog/internal/stackdump_test\.pcAt$`)
   127  		}
   128  		if tc.depth <= calls {
   129  			wantFuncs = append(wantFuncs, `^github.com/golang/glog/internal/stackdump_test\.TestCallerPC$`)
   130  		}
   131  
   132  		gotFuncs := []string{}
   133  		for _, pc := range stack {
   134  			gotFuncs = append(gotFuncs, runtime.FuncForPC(pc).Name())
   135  		}
   136  		if len(gotFuncs) > len(wantFuncs) {
   137  			gotFuncs = gotFuncs[:len(wantFuncs)]
   138  		}
   139  
   140  		ok := true
   141  		for i, want := range wantFuncs {
   142  			re := regexp.MustCompile(want)
   143  			if i >= len(gotFuncs) || !re.MatchString(gotFuncs[i]) {
   144  				ok = false
   145  				break
   146  			}
   147  		}
   148  		if !ok {
   149  			t.Errorf("for %v calls, stackdump.CallerPC(%v) =\n%q\nwant %q", calls, tc.depth, gotFuncs, wantFuncs)
   150  		}
   151  	}
   152  }
   153  

View as plain text