// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ld import ( "fmt" "internal/testenv" "os" "regexp" "strconv" "testing" ) // See also $GOROOT/test/nosplit.go for multi-platform edge case tests. func TestStackCheckOutput(t *testing.T) { testenv.MustHaveGoBuild(t) t.Parallel() cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck") // The rules for computing frame sizes on all of the // architectures are complicated, so just do this on amd64. cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux") outB, err := cmd.CombinedOutput() if err == nil { t.Fatalf("expected link to fail") } out := string(outB) t.Logf("linker output:\n%s", out) // Get expected limit. limitRe := regexp.MustCompile(`nosplit stack over (\d+) byte limit`) m := limitRe.FindStringSubmatch(out) if m == nil { t.Fatalf("no overflow errors in output") } limit, _ := strconv.Atoi(m[1]) wantMap := map[string]string{ "main.startSelf": fmt.Sprintf( `main.startSelf<0> grows 1008 bytes %d bytes over limit `, 1008-limit), "main.startChain": fmt.Sprintf( `main.startChain<0> grows 32 bytes, calls main.chain0<0> grows 48 bytes, calls main.chainEnd<0> grows 1008 bytes %d bytes over limit grows 32 bytes, calls main.chain2<0> grows 80 bytes, calls main.chainEnd<0> grows 1008 bytes %d bytes over limit `, 32+48+1008-limit, 32+80+1008-limit), "main.startRec": `main.startRec<0> grows 8 bytes, calls main.startRec0<0> grows 8 bytes, calls main.startRec<0> infinite cycle `, } // Parse stanzas stanza := regexp.MustCompile(`^(.*): nosplit stack over \d+ byte limit\n(.*\n(?: .*\n)*)`) // Strip comments from cmd/go out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "") for len(out) > 0 { m := stanza.FindStringSubmatch(out) if m == nil { t.Fatalf("unexpected output:\n%s", out) } out = out[len(m[0]):] fn := m[1] got := m[2] want, ok := wantMap[fn] if !ok { t.Errorf("unexpected function: %s", fn) } else if want != got { t.Errorf("want:\n%sgot:\n%s", want, got) } } }