aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/manager/repro.go22
-rw-r--r--pkg/manager/repro_test.go21
2 files changed, 29 insertions, 14 deletions
diff --git a/pkg/manager/repro.go b/pkg/manager/repro.go
index 7a50f06ab..038980624 100644
--- a/pkg/manager/repro.go
+++ b/pkg/manager/repro.go
@@ -64,7 +64,8 @@ type ReproLoop struct {
mu sync.Mutex
queue []*Crash
reproducing map[string]bool
- attempted map[string]bool
+ enqueued map[string]bool
+ attempts map[string]int
}
func NewReproLoop(mgr ReproManagerView, reproVMs int, onlyOnce bool) *ReproLoop {
@@ -75,7 +76,8 @@ func NewReproLoop(mgr ReproManagerView, reproVMs int, onlyOnce bool) *ReproLoop
reproVMs: reproVMs,
reproducing: map[string]bool{},
pingQueue: make(chan struct{}, 1),
- attempted: map[string]bool{},
+ enqueued: map[string]bool{},
+ attempts: map[string]int{},
}
ret.statNumReproducing = stat.New("reproducing", "Number of crashes being reproduced",
stat.Console, stat.NoGraph, func() int {
@@ -133,7 +135,7 @@ func (r *ReproLoop) Enqueue(crash *Crash) {
defer r.mu.Unlock()
title := crash.FullTitle()
- if r.onlyOnce && r.attempted[title] {
+ if r.onlyOnce && r.enqueued[title] {
// Try to reproduce each bug at most 1 time in this mode.
// Since we don't upload bugs/repros to dashboard, it likely won't have
// the reproducer even if we succeeded last time, and will repeatedly
@@ -141,7 +143,7 @@ func (r *ReproLoop) Enqueue(crash *Crash) {
return
}
log.Logf(1, "scheduled a reproduction of '%v'", title)
- r.attempted[title] = true
+ r.enqueued[title] = true
r.queue = append(r.queue, crash)
// Ping the loop.
@@ -156,6 +158,12 @@ func (r *ReproLoop) popCrash() *Crash {
defer r.mu.Unlock()
newBetter := func(base, new *Crash) bool {
+ // The more times we failed, the less likely we are to actually
+ // find a reproducer. Give preference to not yet attempted repro runs.
+ baseTitle, newTitle := base.FullTitle(), new.FullTitle()
+ if r.attempts[baseTitle] != r.attempts[newTitle] {
+ return r.attempts[newTitle] < r.attempts[baseTitle]
+ }
// First, serve manual requests.
if new.Manual != base.Manual {
return new.Manual
@@ -213,8 +221,10 @@ func (r *ReproLoop) Loop(ctx context.Context) {
return
}
+ title := crash.FullTitle()
r.mu.Lock()
- r.reproducing[crash.FullTitle()] = true
+ r.attempts[title]++
+ r.reproducing[title] = true
r.adjustPoolSizeLocked()
r.mu.Unlock()
@@ -225,7 +235,7 @@ func (r *ReproLoop) Loop(ctx context.Context) {
r.handle(crash)
r.mu.Lock()
- delete(r.reproducing, crash.FullTitle())
+ delete(r.reproducing, title)
r.adjustPoolSizeLocked()
r.mu.Unlock()
diff --git a/pkg/manager/repro_test.go b/pkg/manager/repro_test.go
index 0149aafdb..3433341d0 100644
--- a/pkg/manager/repro_test.go
+++ b/pkg/manager/repro_test.go
@@ -63,7 +63,12 @@ func TestReproOrder(t *testing.T) {
mock := &reproMgrMock{
run: make(chan runCallback),
}
- obj := NewReproLoop(mock, 3, false)
+ obj := NewReproLoop(mock, 1, false)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ go func() {
+ obj.Loop(ctx)
+ }()
// The right order is A B C.
crashes := []*Crash{
@@ -85,16 +90,16 @@ func TestReproOrder(t *testing.T) {
obj.Enqueue(crashes[2])
obj.Enqueue(crashes[1])
obj.Enqueue(crashes[0])
- assert.Equal(t, crashes[0], obj.popCrash())
- assert.Equal(t, crashes[1], obj.popCrash())
- assert.Equal(t, crashes[2], obj.popCrash())
-
obj.Enqueue(crashes[1])
obj.Enqueue(crashes[0])
obj.Enqueue(crashes[2])
- assert.Equal(t, crashes[0], obj.popCrash())
- assert.Equal(t, crashes[1], obj.popCrash())
- assert.Equal(t, crashes[2], obj.popCrash())
+
+ obj.StartReproduction()
+ for i := 0; i < len(crashes)*2; i++ {
+ called := <-mock.run
+ assert.Equal(t, crashes[i%len(crashes)], called.crash)
+ called.ret <- &ReproResult{}
+ }
}
func TestReproRWRace(t *testing.T) {