Source file src/cmd/link/internal/ld/stackcheck_test.go

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ld
     6  
     7  import (
     8  	"fmt"
     9  	"internal/testenv"
    10  	"os"
    11  	"regexp"
    12  	"strconv"
    13  	"testing"
    14  )
    15  
    16  // See also $GOROOT/test/nosplit.go for multi-platform edge case tests.
    17  
    18  func TestStackCheckOutput(t *testing.T) {
    19  	testenv.MustHaveGoBuild(t)
    20  	t.Parallel()
    21  
    22  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck")
    23  	// The rules for computing frame sizes on all of the
    24  	// architectures are complicated, so just do this on amd64.
    25  	cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
    26  	outB, err := cmd.CombinedOutput()
    27  
    28  	if err == nil {
    29  		t.Fatalf("expected link to fail")
    30  	}
    31  	out := string(outB)
    32  
    33  	t.Logf("linker output:\n%s", out)
    34  
    35  	// Get expected limit.
    36  	limitRe := regexp.MustCompile(`nosplit stack over (\d+) byte limit`)
    37  	m := limitRe.FindStringSubmatch(out)
    38  	if m == nil {
    39  		t.Fatalf("no overflow errors in output")
    40  	}
    41  	limit, _ := strconv.Atoi(m[1])
    42  
    43  	wantMap := map[string]string{
    44  		"main.startSelf": fmt.Sprintf(
    45  			`main.startSelf<0>
    46      grows 1008 bytes
    47      %d bytes over limit
    48  `, 1008-limit),
    49  		"main.startChain": fmt.Sprintf(
    50  			`main.startChain<0>
    51      grows 32 bytes, calls main.chain0<0>
    52          grows 48 bytes, calls main.chainEnd<0>
    53              grows 1008 bytes
    54              %d bytes over limit
    55      grows 32 bytes, calls main.chain2<0>
    56          grows 80 bytes, calls main.chainEnd<0>
    57              grows 1008 bytes
    58              %d bytes over limit
    59  `, 32+48+1008-limit, 32+80+1008-limit),
    60  		"main.startRec": `main.startRec<0>
    61      grows 8 bytes, calls main.startRec0<0>
    62          grows 8 bytes, calls main.startRec<0>
    63          infinite cycle
    64  `,
    65  	}
    66  
    67  	// Parse stanzas
    68  	stanza := regexp.MustCompile(`^(.*): nosplit stack over \d+ byte limit\n(.*\n(?: .*\n)*)`)
    69  	// Strip comments from cmd/go
    70  	out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "")
    71  	for len(out) > 0 {
    72  		m := stanza.FindStringSubmatch(out)
    73  		if m == nil {
    74  			t.Fatalf("unexpected output:\n%s", out)
    75  		}
    76  		out = out[len(m[0]):]
    77  		fn := m[1]
    78  		got := m[2]
    79  
    80  		want, ok := wantMap[fn]
    81  		if !ok {
    82  			t.Errorf("unexpected function: %s", fn)
    83  		} else if want != got {
    84  			t.Errorf("want:\n%sgot:\n%s", want, got)
    85  		}
    86  	}
    87  }
    88  

View as plain text