kevinschoon-pomo/vendor/github.com/maruel/panicparse/stack/stack_test.go

1019 lines
28 KiB
Go

// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package stack
import (
"bufio"
"bytes"
"errors"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/maruel/ut"
)
var goroot = goroots[0]
const crash = `panic: oh no!
goroutine 1 [running]:
panic(0x0, 0x0)
/home/user/src/golang/src/runtime/panic.go:464 +0x3e6
main.crash2(0x7fe50b49d028, 0xc82000a1e0)
/home/user/src/foo.go:45 +0x23
main.main()
/home/user/src/foo.go:50 +0xa6
`
func Example() {
in := bytes.NewBufferString(crash)
goroutines, err := ParseDump(in, os.Stdout)
if err != nil {
return
}
// Optional: Check for GOTRACEBACK being set, in particular if there is only
// one goroutine returned.
// Use a color palette based on ANSI code.
p := &Palette{}
buckets := SortBuckets(Bucketize(goroutines, AnyValue))
srcLen, pkgLen := CalcLengths(buckets, false)
for _, bucket := range buckets {
io.WriteString(os.Stdout, p.BucketHeader(&bucket, false, len(buckets) > 1))
io.WriteString(os.Stdout, p.StackLines(&bucket.Signature, srcLen, pkgLen, false))
}
// Output:
// panic: oh no!
//
// 1: running
// panic.go:464 panic(0, 0)
// main foo.go:45 crash2(0x7fe50b49d028, 0xc82000a1e0)
// main foo.go:50 main()
}
func TestParseDump1(t *testing.T) {
// One call from main, one from stdlib, one from third party.
// Create a long first line that will be ignored. It is to guard against
// https://github.com/maruel/panicparse/issues/17.
long := strings.Repeat("a", bufio.MaxScanTokenSize+1)
data := []string{
long,
"panic: reflect.Set: value of type",
"",
"goroutine 1 [running]:",
"github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek()",
" ??:0 +0x6d",
"gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
"reflect.Value.assignTo(0x570860, 0xc20803f3e0, 0x15)",
" " + goroot + "/src/reflect/value.go:2125 +0x368",
"main.main()",
" /gopath/src/github.com/foo/bar/baz.go:428 +0x27",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, nil, err)
ut.AssertEqual(t, long+"\npanic: reflect.Set: value of type\n\n", extra.String())
expected := []Goroutine{
{
Signature: Signature{
State: "running",
Stack: Stack{
Calls: []Call{
{
SourcePath: "??",
Func: Function{"github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek"},
},
{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.handleErr"},
Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
},
{
SourcePath: goroot + "/src/reflect/value.go",
Line: 2125,
Func: Function{"reflect.Value.assignTo"},
Args: Args{Values: []Arg{{Value: 0x570860}, {Value: 0xc20803f3e0}, {Value: 0x15}}},
},
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 428,
Func: Function{"main.main"},
},
},
},
},
ID: 1,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
}
func TestParseDumpLongWait(t *testing.T) {
// One call from main, one from stdlib, one from third party.
data := []string{
"panic: bleh",
"",
"goroutine 1 [chan send, 100 minutes]:",
"gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
"",
"goroutine 2 [chan send, locked to thread]:",
"gopkg.in/yaml%2ev2.handleErr(0xc208033b21)",
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
"",
"goroutine 3 [chan send, 101 minutes, locked to thread]:",
"gopkg.in/yaml%2ev2.handleErr(0xc208033b22)",
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, nil, err)
ut.AssertEqual(t, "panic: bleh\n\n", extra.String())
expected := []Goroutine{
{
Signature: Signature{
State: "chan send",
SleepMin: 100,
SleepMax: 100,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.handleErr"},
Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
},
},
},
},
ID: 1,
First: true,
},
{
Signature: Signature{
State: "chan send",
Locked: true,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.handleErr"},
Args: Args{Values: []Arg{{Value: 0xc208033b21, Name: "#1"}}},
},
},
},
},
ID: 2,
},
{
Signature: Signature{
State: "chan send",
SleepMin: 101,
SleepMax: 101,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.handleErr"},
Args: Args{Values: []Arg{{Value: 0xc208033b22, Name: "#2"}}},
},
},
},
Locked: true,
},
ID: 3,
},
}
ut.AssertEqual(t, expected, goroutines)
}
func TestParseDumpAsm(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 16 [garbage collection]:",
"runtime.switchtoM()",
"\t" + goroot + "/src/runtime/asm_amd64.s:198 fp=0xc20cfb80d8 sp=0xc20cfb80d0",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, nil, err)
expected := []Goroutine{
{
Signature: Signature{
State: "garbage collection",
Stack: Stack{
Calls: []Call{
{
SourcePath: goroot + "/src/runtime/asm_amd64.s",
Line: 198,
Func: Function{Raw: "runtime.switchtoM"},
},
},
},
},
ID: 16,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
ut.AssertEqual(t, "panic: reflect.Set: value of type\n\n", extra.String())
}
func TestParseDumpLineErr(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 1 [running]:",
"github.com/foo/bar.recurseType()",
"\t/gopath/src/github.com/foo/bar/baz.go:12345678901234567890",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, errors.New("failed to parse int on line: \"\t/gopath/src/github.com/foo/bar/baz.go:12345678901234567890\n\""), err)
expected := []Goroutine{
{
Signature: Signature{
State: "running",
Stack: Stack{Calls: []Call{{Func: Function{Raw: "github.com/foo/bar.recurseType"}}}},
},
ID: 1,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
}
func TestParseDumpValueErr(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 1 [running]:",
"github.com/foo/bar.recurseType(123456789012345678901)",
"\t/gopath/src/github.com/foo/bar/baz.go:9",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, errors.New("failed to parse int on line: \"github.com/foo/bar.recurseType(123456789012345678901)\n\""), err)
expected := []Goroutine{
{
Signature: Signature{State: "running"},
ID: 1,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
}
func TestParseDumpOrderErr(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 16 [garbage collection]:",
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
"runtime.switchtoM()",
"\t" + goroot + "/src/runtime/asm_amd64.s:198 fp=0xc20cfb80d8 sp=0xc20cfb80d0",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, errors.New("unexpected order"), err)
expected := []Goroutine{
{
Signature: Signature{State: "garbage collection"},
ID: 16,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
ut.AssertEqual(t, "panic: reflect.Set: value of type\n\n", extra.String())
}
func TestParseDumpElided(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 16 [garbage collection]:",
"github.com/foo/bar.recurseType(0x7f4fa9a3ec70, 0xc208062580, 0x7f4fa9a3e818, 0x50a820, 0xc20803a8a0)",
"\t/gopath/src/github.com/foo/bar/baz.go:53 +0x845 fp=0xc20cfc66d8 sp=0xc20cfc6470",
"...additional frames elided...",
"created by testing.RunTests",
"\t" + goroot + "/src/testing/testing.go:555 +0xa8b",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, nil, err)
expected := []Goroutine{
{
Signature: Signature{
State: "garbage collection",
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 53,
Func: Function{Raw: "github.com/foo/bar.recurseType"},
Args: Args{
Values: []Arg{
{Value: 0x7f4fa9a3ec70},
{Value: 0xc208062580},
{Value: 0x7f4fa9a3e818},
{Value: 0x50a820},
{Value: 0xc20803a8a0},
},
},
},
},
Elided: true,
},
CreatedBy: Call{
SourcePath: goroot + "/src/testing/testing.go",
Line: 555,
Func: Function{Raw: "testing.RunTests"},
},
},
ID: 16,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
ut.AssertEqual(t, "panic: reflect.Set: value of type\n\n", extra.String())
}
func TestParseDumpSysCall(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 5 [syscall]:",
"runtime.notetsleepg(0x918100, 0xffffffffffffffff, 0x1)",
"\t" + goroot + "/src/runtime/lock_futex.go:201 +0x52 fp=0xc208018f68 sp=0xc208018f40",
"runtime.signal_recv(0x0)",
"\t" + goroot + "/src/runtime/sigqueue.go:109 +0x135 fp=0xc208018fa0 sp=0xc208018f68",
"os/signal.loop()",
"\t" + goroot + "/src/os/signal/signal_unix.go:21 +0x1f fp=0xc208018fe0 sp=0xc208018fa0",
"runtime.goexit()",
"\t" + goroot + "/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc208018fe8 sp=0xc208018fe0",
"created by os/signal.init·1",
"\t" + goroot + "/src/os/signal/signal_unix.go:27 +0x35",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, nil, err)
expected := []Goroutine{
{
Signature: Signature{
State: "syscall",
Stack: Stack{
Calls: []Call{
{
SourcePath: goroot + "/src/runtime/lock_futex.go",
Line: 201,
Func: Function{Raw: "runtime.notetsleepg"},
Args: Args{
Values: []Arg{
{Value: 0x918100},
{Value: 0xffffffffffffffff},
{Value: 0x1},
},
},
},
{
SourcePath: goroot + "/src/runtime/sigqueue.go",
Line: 109,
Func: Function{Raw: "runtime.signal_recv"},
Args: Args{
Values: []Arg{{}},
},
},
{
SourcePath: goroot + "/src/os/signal/signal_unix.go",
Line: 21,
Func: Function{Raw: "os/signal.loop"},
},
{
SourcePath: goroot + "/src/runtime/asm_amd64.s",
Line: 2232,
Func: Function{Raw: "runtime.goexit"},
},
},
},
CreatedBy: Call{
SourcePath: goroot + "/src/os/signal/signal_unix.go",
Line: 27,
Func: Function{Raw: "os/signal.init·1"},
},
},
ID: 5,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
ut.AssertEqual(t, "panic: reflect.Set: value of type\n\n", extra.String())
}
func TestParseDumpUnavail(t *testing.T) {
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 24 [running]:",
"\tgoroutine running on other thread; stack unavailable",
"created by github.com/foo.New",
"\t/gopath/src/github.com/foo/bar.go:131 +0x381",
"",
}
extra := &bytes.Buffer{}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra)
ut.AssertEqual(t, nil, err)
expected := []Goroutine{
{
Signature: Signature{
State: "running",
Stack: Stack{
Calls: []Call{{SourcePath: "<unavailable>"}},
},
CreatedBy: Call{
SourcePath: "/gopath/src/github.com/foo/bar.go",
Line: 131,
Func: Function{Raw: "github.com/foo.New"},
},
},
ID: 24,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
ut.AssertEqual(t, "panic: reflect.Set: value of type\n\n", extra.String())
}
func TestParseDumpSameBucket(t *testing.T) {
// 2 goroutines with the same signature
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 6 [chan receive]:",
"main.func·001()",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"created by main.mainImpl",
" /gopath/src/github.com/foo/bar/baz.go:74 +0xeb",
"",
"goroutine 7 [chan receive]:",
"main.func·001()",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"created by main.mainImpl",
" /gopath/src/github.com/foo/bar/baz.go:74 +0xeb",
"",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), &bytes.Buffer{})
ut.AssertEqual(t, nil, err)
expectedGR := []Goroutine{
{
Signature: Signature{
State: "chan receive",
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
},
},
},
CreatedBy: Call{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 74,
Func: Function{"main.mainImpl"},
},
},
ID: 6,
First: true,
},
{
Signature: Signature{
State: "chan receive",
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
},
},
},
CreatedBy: Call{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 74,
Func: Function{"main.mainImpl"},
},
},
ID: 7,
},
}
ut.AssertEqual(t, expectedGR, goroutines)
expectedBuckets := Buckets{{expectedGR[0].Signature, []Goroutine{expectedGR[0], expectedGR[1]}}}
ut.AssertEqual(t, expectedBuckets, SortBuckets(Bucketize(goroutines, ExactLines)))
}
func TestBucketizeNotAggressive(t *testing.T) {
// 2 goroutines with the same signature
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 6 [chan receive]:",
"main.func·001(0x11000000, 2)",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"",
"goroutine 7 [chan receive]:",
"main.func·001(0x21000000, 2)",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), &bytes.Buffer{})
ut.AssertEqual(t, nil, err)
expectedGR := []Goroutine{
{
Signature: Signature{
State: "chan receive",
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
Args: Args{Values: []Arg{{0x11000000, ""}, {Value: 2}}},
},
},
},
},
ID: 6,
First: true,
},
{
Signature: Signature{
State: "chan receive",
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
Args: Args{Values: []Arg{{0x21000000, "#1"}, {Value: 2}}},
},
},
},
},
ID: 7,
},
}
ut.AssertEqual(t, expectedGR, goroutines)
expectedBuckets := Buckets{
{expectedGR[0].Signature, []Goroutine{expectedGR[0]}},
{expectedGR[1].Signature, []Goroutine{expectedGR[1]}},
}
ut.AssertEqual(t, expectedBuckets, SortBuckets(Bucketize(goroutines, ExactLines)))
}
func TestBucketizeAggressive(t *testing.T) {
// 2 goroutines with the same signature
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 6 [chan receive, 10 minutes]:",
"main.func·001(0x11000000, 2)",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"",
"goroutine 7 [chan receive, 50 minutes]:",
"main.func·001(0x21000000, 2)",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"",
"goroutine 8 [chan receive, 100 minutes]:",
"main.func·001(0x21000000, 2)",
" /gopath/src/github.com/foo/bar/baz.go:72 +0x49",
"",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), &bytes.Buffer{})
ut.AssertEqual(t, nil, err)
expectedGR := []Goroutine{
{
Signature: Signature{
State: "chan receive",
SleepMin: 10,
SleepMax: 10,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
Args: Args{Values: []Arg{{0x11000000, ""}, {Value: 2}}},
},
},
},
},
ID: 6,
First: true,
},
{
Signature: Signature{
State: "chan receive",
SleepMin: 50,
SleepMax: 50,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
Args: Args{Values: []Arg{{0x21000000, "#1"}, {Value: 2}}},
},
},
},
},
ID: 7,
},
{
Signature: Signature{
State: "chan receive",
SleepMin: 100,
SleepMax: 100,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
Args: Args{Values: []Arg{{0x21000000, "#1"}, {Value: 2}}},
},
},
},
},
ID: 8,
},
}
ut.AssertEqual(t, expectedGR, goroutines)
signature := Signature{
State: "chan receive",
SleepMin: 10,
SleepMax: 100,
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 72,
Func: Function{"main.func·001"},
Args: Args{Values: []Arg{{0x11000000, "*"}, {Value: 2}}},
},
},
},
}
expectedBuckets := Buckets{{signature, []Goroutine{expectedGR[0], expectedGR[1], expectedGR[2]}}}
ut.AssertEqual(t, expectedBuckets, SortBuckets(Bucketize(goroutines, AnyPointer)))
}
func TestParseDumpNoOffset(t *testing.T) {
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 37 [runnable]:",
"github.com/foo.func·002()",
" /gopath/src/github.com/foo/bar.go:110",
"created by github.com/foo.New",
" /gopath/src/github.com/foo/bar.go:113 +0x43b",
"",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), &bytes.Buffer{})
ut.AssertEqual(t, nil, err)
expectedGR := []Goroutine{
{
Signature: Signature{
State: "runnable",
Stack: Stack{
Calls: []Call{
{
SourcePath: "/gopath/src/github.com/foo/bar.go",
Line: 110,
Func: Function{"github.com/foo.func·002"},
},
},
},
CreatedBy: Call{
SourcePath: "/gopath/src/github.com/foo/bar.go",
Line: 113,
Func: Function{"github.com/foo.New"},
},
},
ID: 37,
First: true,
},
}
ut.AssertEqual(t, expectedGR, goroutines)
}
func TestParseDumpJunk(t *testing.T) {
// For coverage of scanLines.
data := []string{
"panic: reflect.Set: value of type",
"",
"goroutine 1 [running]:",
"junk",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), &bytes.Buffer{})
ut.AssertEqual(t, nil, err)
expectedGR := []Goroutine{
{
Signature: Signature{State: "running"},
ID: 1,
First: true,
},
}
ut.AssertEqual(t, expectedGR, goroutines)
}
func TestParseCCode(t *testing.T) {
data := []string{
"SIGQUIT: quit",
"PC=0x43f349",
"",
"goroutine 0 [idle]:",
"runtime.epollwait(0x4, 0x7fff671c7118, 0xffffffff00000080, 0x0, 0xffffffff0028c1be, 0x0, 0x0, 0x0, 0x0, 0x0, ...)",
" " + goroot + "/src/runtime/sys_linux_amd64.s:400 +0x19",
"runtime.netpoll(0x901b01, 0x0)",
" " + goroot + "/src/runtime/netpoll_epoll.go:68 +0xa3",
"findrunnable(0xc208012000)",
" " + goroot + "/src/runtime/proc.c:1472 +0x485",
"schedule()",
" " + goroot + "/src/runtime/proc.c:1575 +0x151",
"runtime.park_m(0xc2080017a0)",
" " + goroot + "/src/runtime/proc.c:1654 +0x113",
"runtime.mcall(0x432684)",
" " + goroot + "/src/runtime/asm_amd64.s:186 +0x5a",
"",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), &bytes.Buffer{})
ut.AssertEqual(t, nil, err)
expectedGR := []Goroutine{
{
Signature: Signature{
State: "idle",
Stack: Stack{
Calls: []Call{
{
SourcePath: goroot + "/src/runtime/sys_linux_amd64.s",
Line: 400,
Func: Function{"runtime.epollwait"},
Args: Args{
Values: []Arg{
{Value: 0x4},
{Value: 0x7fff671c7118},
{Value: 0xffffffff00000080},
{},
{Value: 0xffffffff0028c1be},
{},
{},
{},
{},
{},
},
Elided: true,
},
},
{
SourcePath: goroot + "/src/runtime/netpoll_epoll.go",
Line: 68,
Func: Function{"runtime.netpoll"},
Args: Args{Values: []Arg{{Value: 0x901b01}, {}}},
},
{
SourcePath: goroot + "/src/runtime/proc.c",
Line: 1472,
Func: Function{"findrunnable"},
Args: Args{Values: []Arg{{Value: 0xc208012000}}},
},
{
SourcePath: goroot + "/src/runtime/proc.c",
Line: 1575,
Func: Function{"schedule"},
},
{
SourcePath: goroot + "/src/runtime/proc.c",
Line: 1654,
Func: Function{"runtime.park_m"},
Args: Args{Values: []Arg{{Value: 0xc2080017a0}}},
},
{
SourcePath: goroot + "/src/runtime/asm_amd64.s",
Line: 186,
Func: Function{"runtime.mcall"},
Args: Args{Values: []Arg{{Value: 0x432684}}},
},
},
},
},
ID: 0,
First: true,
},
}
ut.AssertEqual(t, expectedGR, goroutines)
}
func TestParseWithCarriageReturn(t *testing.T) {
data := []string{
"goroutine 1 [running]:",
"github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek()",
" ??:0 +0x6d",
"gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
"reflect.Value.assignTo(0x570860, 0xc20803f3e0, 0x15)",
" " + goroot + "/src/reflect/value.go:2125 +0x368",
"main.main()",
" /gopath/src/github.com/foo/bar/baz.go:428 +0x27",
"",
}
goroutines, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\r\n")), ioutil.Discard)
ut.AssertEqual(t, nil, err)
expected := []Goroutine{
{
Signature: Signature{
State: "running",
Stack: Stack{
Calls: []Call{
{
SourcePath: "??",
Func: Function{"github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek"},
},
{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.handleErr"},
Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
},
{
SourcePath: goroot + "/src/reflect/value.go",
Line: 2125,
Func: Function{"reflect.Value.assignTo"},
Args: Args{Values: []Arg{{Value: 0x570860}, {Value: 0xc20803f3e0}, {Value: 0x15}}},
},
{
SourcePath: "/gopath/src/github.com/foo/bar/baz.go",
Line: 428,
Func: Function{"main.main"},
},
},
},
},
ID: 1,
First: true,
},
}
ut.AssertEqual(t, expected, goroutines)
}
func TestCallPkg1(t *testing.T) {
c := Call{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.handleErr"},
Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
}
ut.AssertEqual(t, "yaml.go", c.SourceName())
ut.AssertEqual(t, filepath.Join("yaml.v2", "yaml.go"), c.PkgSource())
ut.AssertEqual(t, "gopkg.in/yaml.v2.handleErr", c.Func.String())
ut.AssertEqual(t, "handleErr", c.Func.Name())
// This is due to directory name not matching the package name.
ut.AssertEqual(t, "yaml.v2", c.Func.PkgName())
ut.AssertEqual(t, false, c.Func.IsExported())
ut.AssertEqual(t, false, c.IsStdlib())
ut.AssertEqual(t, false, c.IsPkgMain())
}
func TestCallPkg2(t *testing.T) {
c := Call{
SourcePath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
Line: 153,
Func: Function{"gopkg.in/yaml%2ev2.(*decoder).unmarshal"},
Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
}
ut.AssertEqual(t, "yaml.go", c.SourceName())
ut.AssertEqual(t, filepath.Join("yaml.v2", "yaml.go"), c.PkgSource())
// TODO(maruel): Using '/' for this function is inconsistent on Windows
// w.r.t. other functions.
ut.AssertEqual(t, "gopkg.in/yaml.v2.(*decoder).unmarshal", c.Func.String())
ut.AssertEqual(t, "(*decoder).unmarshal", c.Func.Name())
// This is due to directory name not matching the package name.
ut.AssertEqual(t, "yaml.v2", c.Func.PkgName())
ut.AssertEqual(t, false, c.Func.IsExported())
ut.AssertEqual(t, false, c.IsStdlib())
ut.AssertEqual(t, false, c.IsPkgMain())
}
func TestCallStdlib(t *testing.T) {
c := Call{
SourcePath: goroot + "/src/reflect/value.go",
Line: 2125,
Func: Function{"reflect.Value.assignTo"},
Args: Args{Values: []Arg{{Value: 0x570860}, {Value: 0xc20803f3e0}, {Value: 0x15}}},
}
ut.AssertEqual(t, "value.go", c.SourceName())
ut.AssertEqual(t, "value.go:2125", c.SourceLine())
ut.AssertEqual(t, filepath.Join("reflect", "value.go"), c.PkgSource())
ut.AssertEqual(t, "reflect.Value.assignTo", c.Func.String())
ut.AssertEqual(t, "Value.assignTo", c.Func.Name())
ut.AssertEqual(t, "reflect", c.Func.PkgName())
ut.AssertEqual(t, false, c.Func.IsExported())
ut.AssertEqual(t, true, c.IsStdlib())
ut.AssertEqual(t, false, c.IsPkgMain())
}
func TestCallMain(t *testing.T) {
c := Call{
SourcePath: "/gopath/src/github.com/foo/bar/main.go",
Line: 428,
Func: Function{"main.main"},
}
ut.AssertEqual(t, "main.go", c.SourceName())
ut.AssertEqual(t, "main.go:428", c.SourceLine())
ut.AssertEqual(t, filepath.Join("bar", "main.go"), c.PkgSource())
ut.AssertEqual(t, "main.main", c.Func.String())
ut.AssertEqual(t, "main", c.Func.Name())
ut.AssertEqual(t, "main", c.Func.PkgName())
ut.AssertEqual(t, true, c.Func.IsExported())
ut.AssertEqual(t, false, c.IsStdlib())
ut.AssertEqual(t, true, c.IsPkgMain())
}
func TestCallC(t *testing.T) {
c := Call{
SourcePath: goroot + "/src/runtime/proc.c",
Line: 1472,
Func: Function{"findrunnable"},
Args: Args{Values: []Arg{{Value: 0xc208012000}}},
}
ut.AssertEqual(t, "proc.c", c.SourceName())
ut.AssertEqual(t, "proc.c:1472", c.SourceLine())
ut.AssertEqual(t, filepath.Join("runtime", "proc.c"), c.PkgSource())
ut.AssertEqual(t, "findrunnable", c.Func.String())
ut.AssertEqual(t, "findrunnable", c.Func.Name())
ut.AssertEqual(t, "", c.Func.PkgName())
ut.AssertEqual(t, false, c.Func.IsExported())
ut.AssertEqual(t, true, c.IsStdlib())
ut.AssertEqual(t, false, c.IsPkgMain())
}
func TestArgs(t *testing.T) {
a := Args{
Values: []Arg{
{Value: 0x4},
{Value: 0x7fff671c7118},
{Value: 0xffffffff00000080},
{},
{Value: 0xffffffff0028c1be},
{},
{},
{},
{},
{},
},
Elided: true,
}
ut.AssertEqual(t, "0x4, 0x7fff671c7118, 0xffffffff00000080, 0, 0xffffffff0028c1be, 0, 0, 0, 0, 0, ...", a.String())
}
func TestFunctionAnonymous(t *testing.T) {
f := Function{"main.func·001"}
ut.AssertEqual(t, "main.func·001", f.String())
ut.AssertEqual(t, "main.func·001", f.PkgDotName())
ut.AssertEqual(t, "func·001", f.Name())
ut.AssertEqual(t, "main", f.PkgName())
ut.AssertEqual(t, false, f.IsExported())
}
func TestFunctionGC(t *testing.T) {
f := Function{"gc"}
ut.AssertEqual(t, "gc", f.String())
ut.AssertEqual(t, "gc", f.PkgDotName())
ut.AssertEqual(t, "gc", f.Name())
ut.AssertEqual(t, "", f.PkgName())
ut.AssertEqual(t, false, f.IsExported())
}