1  
     2  
     3  
     4  
     5  package debug_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"log"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"runtime"
    16  	"runtime/debug"
    17  	. "runtime/debug"
    18  	"strings"
    19  	"testing"
    20  )
    21  
    22  func TestMain(m *testing.M) {
    23  	switch os.Getenv("GO_RUNTIME_DEBUG_TEST_ENTRYPOINT") {
    24  	case "dumpgoroot":
    25  		fmt.Println(runtime.GOROOT())
    26  		os.Exit(0)
    27  
    28  	case "setcrashoutput":
    29  		f, err := os.Create(os.Getenv("CRASHOUTPUT"))
    30  		if err != nil {
    31  			log.Fatal(err)
    32  		}
    33  		if err := SetCrashOutput(f, debug.CrashOptions{}); err != nil {
    34  			log.Fatal(err) 
    35  		}
    36  		println("hello")
    37  		panic("oops")
    38  	}
    39  
    40  	
    41  	os.Exit(m.Run())
    42  }
    43  
    44  type T int
    45  
    46  func (t *T) ptrmethod() []byte {
    47  	return Stack()
    48  }
    49  func (t T) method() []byte {
    50  	return t.ptrmethod()
    51  }
    52  
    53  
    71  func TestStack(t *testing.T) {
    72  	b := T(0).method()
    73  	lines := strings.Split(string(b), "\n")
    74  	if len(lines) < 6 {
    75  		t.Fatal("too few lines")
    76  	}
    77  
    78  	
    79  	
    80  	
    81  	
    82  	fileGoroot := ""
    83  	if envGoroot := os.Getenv("GOROOT"); envGoroot != "" {
    84  		
    85  		
    86  		
    87  		
    88  		
    89  		t.Logf("found GOROOT %q from environment; checking embedded GOROOT value", envGoroot)
    90  		cmd := exec.Command(testenv.Executable(t))
    91  		cmd.Env = append(os.Environ(), "GOROOT=", "GO_RUNTIME_DEBUG_TEST_ENTRYPOINT=dumpgoroot")
    92  		out, err := cmd.Output()
    93  		if err != nil {
    94  			t.Fatal(err)
    95  		}
    96  		fileGoroot = string(bytes.TrimSpace(out))
    97  	} else {
    98  		
    99  		
   100  		fileGoroot = runtime.GOROOT()
   101  	}
   102  	filePrefix := ""
   103  	if fileGoroot != "" {
   104  		filePrefix = filepath.ToSlash(fileGoroot) + "/src/"
   105  	}
   106  
   107  	n := 0
   108  	frame := func(file, code string) {
   109  		t.Helper()
   110  
   111  		line := lines[n]
   112  		if !strings.Contains(line, code) {
   113  			t.Errorf("expected %q in %q", code, line)
   114  		}
   115  		n++
   116  
   117  		line = lines[n]
   118  
   119  		wantPrefix := "\t" + filePrefix + file
   120  		if !strings.HasPrefix(line, wantPrefix) {
   121  			t.Errorf("in line %q, expected prefix %q", line, wantPrefix)
   122  		}
   123  		n++
   124  	}
   125  	n++
   126  
   127  	frame("runtime/debug/stack.go", "runtime/debug.Stack")
   128  	frame("runtime/debug/stack_test.go", "runtime/debug_test.(*T).ptrmethod")
   129  	frame("runtime/debug/stack_test.go", "runtime/debug_test.T.method")
   130  	frame("runtime/debug/stack_test.go", "runtime/debug_test.TestStack")
   131  	frame("testing/testing.go", "")
   132  }
   133  
   134  func TestSetCrashOutput(t *testing.T) {
   135  	crashOutput := filepath.Join(t.TempDir(), "crash.out")
   136  
   137  	cmd := exec.Command(testenv.Executable(t))
   138  	cmd.Stderr = new(strings.Builder)
   139  	cmd.Env = append(os.Environ(), "GO_RUNTIME_DEBUG_TEST_ENTRYPOINT=setcrashoutput", "CRASHOUTPUT="+crashOutput)
   140  	err := cmd.Run()
   141  	stderr := fmt.Sprint(cmd.Stderr)
   142  	if err == nil {
   143  		t.Fatalf("child process succeeded unexpectedly (stderr: %s)", stderr)
   144  	}
   145  	t.Logf("child process finished with error %v and stderr <<%s>>", err, stderr)
   146  
   147  	
   148  	
   149  	
   150  	
   151  	
   152  	
   153  	
   154  	
   155  	
   156  	
   157  	data, err := os.ReadFile(crashOutput)
   158  	if err != nil {
   159  		t.Fatalf("child process failed to write crash report: %v", err)
   160  	}
   161  	crash := string(data)
   162  	t.Logf("crash = <<%s>>", crash)
   163  	t.Logf("stderr = <<%s>>", stderr)
   164  
   165  	
   166  	for _, want := range []string{
   167  		"panic: oops",
   168  		"goroutine 1",
   169  		"debug_test.TestMain",
   170  	} {
   171  		if !strings.Contains(crash, want) {
   172  			t.Errorf("crash output does not contain %q", want)
   173  		}
   174  		if !strings.Contains(stderr, want) {
   175  			t.Errorf("stderr output does not contain %q", want)
   176  		}
   177  	}
   178  
   179  	
   180  	printlnOnly := "hello"
   181  	if strings.Contains(crash, printlnOnly) {
   182  		t.Errorf("crash output contains %q, but should not", printlnOnly)
   183  	}
   184  	if !strings.Contains(stderr, printlnOnly) {
   185  		t.Errorf("stderr output does not contain %q, but should", printlnOnly)
   186  	}
   187  }
   188  
View as plain text