diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/manager/repro.go | 22 | ||||
| -rw-r--r-- | pkg/manager/repro_test.go | 21 |
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) { |
