123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- package stack
- import (
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "reflect"
- "strings"
- "testing"
- )
- func TestAugment(t *testing.T) {
- data := []struct {
- name string
- input string
- expected Stack
- }{
- {
- "Local function doesn't interfere",
- `package main
- func f(s string) {
- a := func(i int) int {
- return 1 + i
- }
- _ = a(3)
- panic("ooh")
- }
- func main() {
- f("yo")
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 7, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer, Name: ""}, {Value: 0x2}},
- },
- },
- {SrcPath: "main.go", Line: 10, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "func",
- `package main
- func f(a func() string) {
- panic(a())
- }
- func main() {
- f(func() string { return "ooh" })
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{Values: []Arg{{Value: pointer}}},
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "func ellipsis",
- `package main
- func f(a ...func() string) {
- panic(a[0]())
- }
- func main() {
- f(func() string { return "ooh" })
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}, {Value: 0x1}, {Value: 0x1}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "interface{}",
- `package main
- func f(a []interface{}) {
- panic("ooh")
- }
- func main() {
- f(make([]interface{}, 5, 7))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}, {Value: 0x5}, {Value: 0x7}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "[]int",
- `package main
- func f(a []int) {
- panic("ooh")
- }
- func main() {
- f(make([]int, 5, 7))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "[]interface{}",
- `package main
- func f(a []interface{}) {
- panic(a[0].(string))
- }
- func main() {
- f([]interface{}{"ooh"})
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}, {Value: 1}, {Value: 1}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "map[int]int",
- `package main
- func f(a map[int]int) {
- panic("ooh")
- }
- func main() {
- f(map[int]int{1: 2})
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "map[interface{}]interface{}",
- `package main
- func f(a map[interface{}]interface{}) {
- panic("ooh")
- }
- func main() {
- f(make(map[interface{}]interface{}))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "chan int",
- `package main
- func f(a chan int) {
- panic("ooh")
- }
- func main() {
- f(make(chan int))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "chan interface{}",
- `package main
- func f(a chan interface{}) {
- panic("ooh")
- }
- func main() {
- f(make(chan interface{}))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "non-pointer method",
- `package main
- type S struct {
- }
- func (s S) f() {
- panic("ooh")
- }
- func main() {
- var s S
- s.f()
- }`,
- Stack{
- Calls: []Call{
- {SrcPath: "main.go", Line: 5, Func: Func{Raw: "main.S.f"}},
- {SrcPath: "main.go", Line: 9, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "pointer method",
- `package main
- type S struct {
- }
- func (s *S) f() {
- panic("ooh")
- }
- func main() {
- var s S
- s.f()
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 5, Func: Func{Raw: "main.(*S).f"},
- Args: Args{Values: []Arg{{Value: pointer}}},
- },
- {SrcPath: "main.go", Line: 9, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "string",
- `package main
- func f(s string) {
- panic(s)
- }
- func main() {
- f("ooh")
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{Values: []Arg{{Value: pointer}, {Value: 0x3}}},
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "string and int",
- `package main
- func f(s string, i int) {
- panic(s)
- }
- func main() {
- f("ooh", 42)
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{Values: []Arg{{Value: pointer}, {Value: 0x3}, {Value: 42}}},
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "values are elided",
- `package main
- func f(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12 int, s13 interface{}) {
- panic("ooh")
- }
- func main() {
- f(0, 0, 0, 0, 0, 0, 0, 0, 42, 43, 44, 45, nil)
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{}, {}, {}, {}, {}, {}, {}, {}, {Value: 42}, {Value: 43}},
- Elided: true,
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "error",
- `package main
- import "errors"
- func f(err error) {
- panic(err.Error())
- }
- func main() {
- f(errors.New("ooh"))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 4, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}, {Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 7, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "error unnamed",
- `package main
- import "errors"
- func f(error) {
- panic("ooh")
- }
- func main() {
- f(errors.New("ooh"))
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 4, Func: Func{Raw: "main.f"},
- Args: Args{
- Values: []Arg{{Value: pointer}, {Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 7, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "float32",
- `package main
- func f(v float32) {
- panic("ooh")
- }
- func main() {
- f(0.5)
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
-
-
- Values: []Arg{{Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- {
- "float64",
- `package main
- func f(v float64) {
- panic("ooh")
- }
- func main() {
- f(0.5)
- }`,
- Stack{
- Calls: []Call{
- {
- SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
- Args: Args{
-
-
- Values: []Arg{{Value: pointer}},
- },
- },
- {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
- },
- },
- },
- }
- for i, line := range data {
- extra := bytes.Buffer{}
- _, content, clean := getCrash(t, line.input)
- c, err := ParseDump(bytes.NewBuffer(content), &extra, false)
- if err != nil {
- clean()
- t.Fatalf("failed to parse input for test %s: %v", line.name, err)
- }
-
- actual := extra.String()
- if actual != "panic: ooh\n\nexit status 2\n" && actual != "panic: ooh\nexit status 2\n" {
- clean()
- t.Fatalf("Unexpected panic output:\n%#v", actual)
- }
- s := c.Goroutines[0].Signature.Stack
- t.Logf("Test: %v", line.name)
- zapPointers(t, line.name, &line.expected, &s)
- zapPaths(&s)
- clean()
- if !reflect.DeepEqual(line.expected, s) {
- t.Fatalf("#%d: Different:\n- %v\n- %v", i, line.expected, s)
- }
- }
- }
- func TestAugmentDummy(t *testing.T) {
- goroutines := []*Goroutine{
- {
- Signature: Signature{
- Stack: Stack{
- Calls: []Call{{SrcPath: "missing.go"}},
- },
- },
- },
- }
- Augment(goroutines)
- }
- func TestLoad(t *testing.T) {
- c := &cache{
- files: map[string][]byte{"bad.go": []byte("bad content")},
- parsed: map[string]*parsedFile{},
- }
- c.load("foo.asm")
- c.load("bad.go")
- c.load("doesnt_exist.go")
- if l := len(c.parsed); l != 3 {
- t.Fatalf("expected 3, got %d", l)
- }
- if c.parsed["foo.asm"] != nil {
- t.Fatalf("foo.asm is not present; should not have been loaded")
- }
- if c.parsed["bad.go"] != nil {
- t.Fatalf("bad.go is not valid code; should not have been loaded")
- }
- if c.parsed["doesnt_exist.go"] != nil {
- t.Fatalf("doesnt_exist.go is not present; should not have been loaded")
- }
- if c.getFuncAST(&Call{SrcPath: "other"}) != nil {
- t.Fatalf("there's no 'other'")
- }
- }
- const pointer = uint64(0xfffffffff)
- const pointerStr = "0xfffffffff"
- func overrideEnv(env []string, key, value string) []string {
- prefix := key + "="
- for i, e := range env {
- if strings.HasPrefix(e, prefix) {
- env[i] = prefix + value
- return env
- }
- }
- return append(env, prefix+value)
- }
- func getCrash(t *testing.T, content string) (string, []byte, func()) {
-
-
- name, err := ioutil.TempDir("", "panicparse")
- if err != nil {
- t.Fatalf("failed to create temporary directory: %v", err)
- }
- clean := func() {
- if err := os.RemoveAll(name); err != nil {
- t.Fatalf("failed to remove temporary directory %q: %v", name, err)
- }
- }
- main := filepath.Join(name, "main.go")
- if err := ioutil.WriteFile(main, []byte(content), 0500); err != nil {
- clean()
- t.Fatalf("failed to write %q: %v", main, err)
- }
- cmd := exec.Command("go", "run", main)
-
- cmd.Env = overrideEnv(os.Environ(), "GOTRACEBACK", "1")
- out, err := cmd.CombinedOutput()
- if err == nil {
- clean()
- t.Fatal("expected error since this is supposed to crash")
- }
- return main, out, clean
- }
- func zapPointers(t *testing.T, name string, expected, s *Stack) {
- for i := range s.Calls {
- if i >= len(expected.Calls) {
-
-
-
- s.Calls = s.Calls[:len(expected.Calls)]
- break
- }
- for j := range s.Calls[i].Args.Values {
- if j >= len(expected.Calls[i].Args.Values) {
- break
- }
- if expected.Calls[i].Args.Values[j].Value == pointer {
-
- if s.Calls[i].Args.Values[j].Value == 0 {
- t.Fatalf("%s: Call %d, value %d, expected pointer, got 0", name, i, j)
- }
- old := fmt.Sprintf("0x%x", s.Calls[i].Args.Values[j].Value)
- s.Calls[i].Args.Values[j].Value = pointer
- for k := range s.Calls[i].Args.Processed {
- s.Calls[i].Args.Processed[k] = strings.Replace(s.Calls[i].Args.Processed[k], old, pointerStr, -1)
- }
- }
- }
- }
- }
- func zapPaths(s *Stack) {
- for j := range s.Calls {
- s.Calls[j].SrcPath = filepath.Base(s.Calls[j].SrcPath)
- s.Calls[j].LocalSrcPath = ""
- }
- }
|