blob: 84d58d4c9eafd09e10d0057c1ba3d32ad23d0d9a [file] [log] [blame]
package deduplicator
import (
"fmt"
"sync"
"go.skia.org/infra/fuzzer/go/common"
"go.skia.org/infra/fuzzer/go/data"
"go.skia.org/infra/go/util"
)
const _MAX_STACKTRACE_LINES = 4
type Deduplicator interface {
// Deduplicators are based on the Skia revision being analyzed. To clear out a deduplicator,
// set the commit to something else.
SetRevision(string)
IsUnique(report data.FuzzReport) bool
}
// localDeduplicator keeps a local cache of keys based on what has been passed to IsUnique
type localDeduplicator struct {
// maps keys to true/false
seen map[string]bool
mutex sync.Mutex
}
func NewLocalDeduplicator() Deduplicator {
return &localDeduplicator{
seen: make(map[string]bool),
}
}
func (d *localDeduplicator) SetRevision(r string) {
d.mutex.Lock()
defer d.mutex.Unlock()
d.seen = make(map[string]bool)
}
func (d *localDeduplicator) IsUnique(report data.FuzzReport) bool {
allEmpty := true
for _, st := range report.Stacktraces {
allEmpty = allEmpty && st.IsEmpty()
}
// Empty stacktraces should be manually deduplicated.
if allEmpty {
return true
}
anyOther := false
for _, flags := range report.Flags {
anyOther = anyOther || util.In("Other", flags)
}
// Other flags should also be looked at manually.
if anyOther {
return true
}
d.mutex.Lock()
defer d.mutex.Unlock()
if k := key(report); d.seen[k] {
return false
} else {
d.seen[k] = true
return true
}
}
func key(r data.FuzzReport) string {
s := fmt.Sprintf("C:%s,A:%s", r.FuzzCategory, r.FuzzArchitecture)
for _, c := range common.ANALYSIS_TYPES {
st := trim(r.Stacktraces[c])
s += fmt.Sprintf("F:%q,S:%s", r.Flags[c], st.String())
}
return s
}
// trim returns a copy of the given stacktrace, with the line numbers removed and all but the
// first _MAX_STACKTRACE_LINES stacktraces removed.
func trim(st data.StackTrace) data.StackTrace {
if frames := st.Frames; len(frames) > _MAX_STACKTRACE_LINES {
st.Frames = st.Frames[0:_MAX_STACKTRACE_LINES]
}
// copy the frames, so we don't accidentally change the real report.
st.Frames = append([]data.StackTraceFrame(nil), st.Frames...)
// Remove line numbers from our deduping criteria.
for i := range st.Frames {
st.Frames[i].LineNumber = 0
}
return st
}