blob: 4c640e81b06421b82d8bcdddc698c5e020e1d173 [file]
// Copyright 2018 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 (
"bytes"
"io"
"io/ioutil"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/maruel/panicparse/v2/internal/internaltest"
)
func TestAggregateNotAggressive(t *testing.T) {
t.Parallel()
// 2 goroutines with similar but not exact same signature.
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 6 [chan receive]:",
"main.func·001(0x11000000, 2)",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"",
"goroutine 7 [chan receive]:",
"main.func·001(0x21000000, 2)",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"",
}
s, suffix, err := ScanSnapshot(bytes.NewBufferString(strings.Join(data, "\n")), ioutil.Discard, defaultOpts())
if err != io.EOF {
t.Fatal(err)
}
if s == nil {
t.Fatal("expected snapshot")
}
want := []*Bucket{
{
Signature: Signature{
State: "chan receive",
Stack: Stack{
Calls: []Call{
newCall(
"main.func·001",
Args{Values: []Arg{{Value: 0x11000000, IsPtr: true}, {Value: 2}}},
"/gopath/src/github.com/maruel/panicparse/stack/stack.go",
72),
},
},
},
IDs: []int{6},
First: true,
},
{
Signature: Signature{
State: "chan receive",
Stack: Stack{
Calls: []Call{
newCall(
"main.func·001",
Args{Values: []Arg{{Value: 0x21000000, Name: "#1", IsPtr: true}, {Value: 2}}},
"/gopath/src/github.com/maruel/panicparse/stack/stack.go",
72),
},
},
},
IDs: []int{7},
},
}
a := s.Aggregate(ExactLines)
compareBuckets(t, want, a.Buckets)
if a.Snapshot != s {
t.Fatal("unexpected snapshot")
}
compareString(t, "", string(suffix))
}
func TestAggregateExactMatching(t *testing.T) {
t.Parallel()
// 2 goroutines with the exact same signature.
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 6 [chan receive]:",
"main.func·001()",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"created by main.mainImpl",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:74 +0xeb",
"",
"goroutine 7 [chan receive]:",
"main.func·001()",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"created by main.mainImpl",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:74 +0xeb",
"",
}
s, suffix, err := ScanSnapshot(bytes.NewBufferString(strings.Join(data, "\n")), ioutil.Discard, defaultOpts())
if err != io.EOF {
t.Fatal(err)
}
if s == nil {
t.Fatal("expected snapshot")
}
want := []*Bucket{
{
Signature: Signature{
State: "chan receive",
CreatedBy: Stack{
Calls: []Call{
newCall(
"main.mainImpl",
Args{},
"/gopath/src/github.com/maruel/panicparse/stack/stack.go",
74),
},
},
Stack: Stack{
Calls: []Call{
newCall(
"main.func·001",
Args{},
"/gopath/src/github.com/maruel/panicparse/stack/stack.go",
72),
},
},
},
IDs: []int{6, 7},
First: true,
},
}
compareBuckets(t, want, s.Aggregate(ExactLines).Buckets)
compareString(t, "", string(suffix))
}
func TestAggregateAggressive(t *testing.T) {
t.Parallel()
// 3 goroutines with similar signatures.
data := []string{
"panic: runtime error: index out of range",
"",
"goroutine 6 [chan receive, 10 minutes]:",
"main.func·001(0x21000000, 2)",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"",
"goroutine 7 [chan receive, 50 minutes]:",
"main.func·001(0x31000000, 2)",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"",
"goroutine 8 [chan receive, 100 minutes]:",
"main.func·001(0x41000000, 2)",
"\t/gopath/src/github.com/maruel/panicparse/stack/stack.go:72 +0x49",
"",
}
s, suffix, err := ScanSnapshot(bytes.NewBufferString(strings.Join(data, "\n")), ioutil.Discard, defaultOpts())
if err != io.EOF {
t.Fatal(err)
}
if s == nil {
t.Fatal("expected snapshot")
}
want := []*Bucket{
{
Signature: Signature{
State: "chan receive",
SleepMin: 10,
SleepMax: 100,
Stack: Stack{
Calls: []Call{
newCall(
"main.func·001",
Args{Values: []Arg{{Value: 0x21000000, Name: "*", IsPtr: true}, {Value: 2}}},
"/gopath/src/github.com/maruel/panicparse/stack/stack.go",
72),
},
},
},
IDs: []int{6, 7, 8},
First: true,
},
}
compareBuckets(t, want, s.Aggregate(AnyPointer).Buckets)
compareString(t, "", string(suffix))
}
func BenchmarkAggregate(b *testing.B) {
b.ReportAllocs()
s, suffix, err := ScanSnapshot(bytes.NewReader(internaltest.StaticPanicwebOutput()), ioutil.Discard, defaultOpts())
if err != io.EOF {
b.Fatal(err)
}
if s == nil {
b.Fatal("missing context")
}
if string(suffix) != "" {
b.Fatalf("unexpected suffix: %q", string(suffix))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
buckets := s.Aggregate(AnyPointer).Buckets
if len(buckets) < 5 {
b.Fatal("expected more buckets")
}
}
}
func compareBuckets(t *testing.T, want, got []*Bucket) {
helper(t)()
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("Bucket mismatch (-want +got):\n%s", diff)
}
}