diff options
Diffstat (limited to 'tests/core/strip/strip_test.go')
-rw-r--r-- | tests/core/strip/strip_test.go | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/tests/core/strip/strip_test.go b/tests/core/strip/strip_test.go new file mode 100644 index 00000000..b7183033 --- /dev/null +++ b/tests/core/strip/strip_test.go @@ -0,0 +1,270 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strip_test + +import ( + "bytes" + "errors" + "fmt" + "os/exec" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "strip", + srcs = ["strip.go"], +) +-- strip.go -- +package main + +import ( + "debug/elf" + "debug/macho" + "debug/pe" + "errors" + "flag" + "fmt" + "io" + "os" + "regexp" + "runtime" + "runtime/debug" + "strings" +) + +var wantStrip = flag.Bool("wantstrip", false, "") + +func main() { + flag.Parse() + stackTrace, err := panicAndRecover() + if err != nil { + panic(err) + } + gotStackTrace := strings.Split(stackTrace, "\n") + if len(gotStackTrace) != len(wantStackTrace) { + panic(fmt.Sprintf("got %d lines of stack trace, want %d", len(gotStackTrace), len(wantStackTrace))) + } + for i := range gotStackTrace { + expectedLine := regexp.MustCompile(wantStackTrace[i]) + if !expectedLine.MatchString(gotStackTrace[i]) { + panic(fmt.Sprintf("got unexpected stack trace line %q at index %d", gotStackTrace[i], i)) + } + } + stripped, err := isStripped() + if err != nil { + panic(err) + } + if stripped != *wantStrip { + panic(fmt.Sprintf("got stripped=%t, want stripped=%t", stripped, *wantStrip)) + } +} + +func panicAndRecover() (stackTrace string, err error) { + defer func() { + if r := recover(); r != nil { + stackTrace = string(debug.Stack()) + } + }() + panic("test") + return "", errors.New("should not reach here") +} + +func isStripped() (bool, error) { + ownLocation, err := os.Executable() + if err != nil { + return false, err + } + ownBinary, err := os.Open(ownLocation) + if err != nil { + return false, err + } + defer ownBinary.Close() + switch runtime.GOOS { + case "darwin": + return isStrippedMachO(ownBinary) + case "linux": + return isStrippedElf(ownBinary) + case "windows": + return isStrippedPE(ownBinary) + default: + return false, fmt.Errorf("unsupported OS: %s", runtime.GOOS) + } +} + +func isStrippedMachO(f io.ReaderAt) (bool, error) { + macho, err := macho.NewFile(f) + if err != nil { + return false, err + } + gotDwarf := macho.Segment("__DWARF") != nil + gotDebugInfo := macho.Section("__zdebug_info") != nil + if gotDwarf != gotDebugInfo { + return false, fmt.Errorf("inconsistent stripping: gotDwarf=%v, gotDebugInfo=%v", gotDwarf, gotDebugInfo) + } + return !gotDwarf, nil +} + +func isStrippedElf(f io.ReaderAt) (bool, error) { + elf, err := elf.NewFile(f) + if err != nil { + return false, err + } + var gotSymtab bool + for _, section := range elf.Sections { + if section.Name == ".symtab" { + gotSymtab = true + break + } + } + return !gotSymtab, nil +} + + +func isStrippedPE(f io.ReaderAt) (bool, error) { + pe, err := pe.NewFile(f) + if err != nil { + return false, err + } + symtab := pe.Section(".symtab") + if symtab == nil { + return false, fmt.Errorf("no .symtab section") + } + emptySymtab := (symtab.VirtualSize <= 4) && (symtab.Size <= 512) + return emptySymtab, nil +} + + +` + embedWantedStackTraces(), + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, stripFlag, compilationMode string + wantStrip bool + }{ + { + desc: "run_auto", + wantStrip: true, + }, + { + desc: "run_fastbuild", + compilationMode: "fastbuild", + wantStrip: true, + }, + { + desc: "run_dbg", + compilationMode: "dbg", + }, + { + desc: "run_opt", + compilationMode: "opt", + }, + { + desc: "run_always", + stripFlag: "always", + wantStrip: true, + }, + { + desc: "run_always_opt", + stripFlag: "always", + compilationMode: "opt", + wantStrip: true, + }, + { + desc: "run_never", + stripFlag: "never", + }, + { + desc: "run_sometimes_fastbuild", + stripFlag: "sometimes", + compilationMode: "fastbuild", + wantStrip: true, + }, + { + desc: "run_sometimes_dbg", + stripFlag: "sometimes", + compilationMode: "dbg", + }, + { + desc: "run_sometimes_opt", + stripFlag: "sometimes", + compilationMode: "opt", + }, + } { + t.Run(test.desc, func(t *testing.T) { + args := []string{"run"} + if len(test.stripFlag) > 0 { + args = append(args, "--strip", test.stripFlag) + } + if len(test.compilationMode) > 0 { + args = append(args, "--compilation_mode", test.compilationMode) + } + args = append(args, "//:strip", "--", fmt.Sprintf("-wantstrip=%v", test.wantStrip)) + cmd := bazel_testing.BazelCmd(args...) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + t.Logf("running: bazel %s", strings.Join(args, " ")) + if err := cmd.Run(); err != nil { + var xerr *exec.ExitError + if !errors.As(err, &xerr) { + t.Fatalf("unexpected error: %v", err) + } + if xerr.ExitCode() == bazel_testing.BUILD_FAILURE { + t.Fatalf("unexpected build failure: %v\nstderr:\n%s", err, stderr.Bytes()) + return + } else if xerr.ExitCode() == bazel_testing.TESTS_FAILED { + t.Fatalf("error running %s:\n%s", strings.Join(cmd.Args, " "), stderr.Bytes()) + } else { + t.Fatalf("unexpected error: %v\nstderr:\n%s", err, stderr.Bytes()) + } + } + }) + } +} + +var wantStackTrace = []string{ + `^goroutine \d+ \[running\]:$`, + `^runtime/debug\.Stack\(\)$`, + `^ GOROOT/src/runtime/debug/stack\.go:\d+ \+0x[0-9a-f]+$`, + `^main\.panicAndRecover\.func1\(\)$`, + `^ strip\.go:\d+ \+0x[0-9a-f]+$`, + `^panic\({0x[0-9a-f]+, 0x[0-9a-f]+}\)$`, + `^ GOROOT/src/runtime/panic\.go:\d+ \+0x[0-9a-f]+$`, + `^main\.panicAndRecover\(\)$`, + `^ strip\.go:\d+ \+0x[0-9a-f]+$`, + `^main\.main\(\)$`, + `^ strip\.go:\d+ \+0x[0-9a-f]+$`, + `^$`, +} + +func embedWantedStackTraces() string { + buf := &bytes.Buffer{} + fmt.Fprintln(buf, "var wantStackTrace = []string{") + for _, s := range wantStackTrace { + fmt.Fprintf(buf, "`%s`,\n", s) + } + fmt.Fprintln(buf, "}") + return buf.String() +} |