| OLD | NEW |
| 1 package deduplicator | 1 package deduplicator |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "fmt" | 4 "fmt" |
| 5 "sync" |
| 5 | 6 |
| 6 » "go.skia.org/infra/fuzzer/go/frontend/data" | 7 » "go.skia.org/infra/fuzzer/go/data" |
| 7 "go.skia.org/infra/go/util" | 8 "go.skia.org/infra/go/util" |
| 8 ) | 9 ) |
| 9 | 10 |
| 10 const _MAX_STACKTRACE_LINES = 4 | 11 const _MAX_STACKTRACE_LINES = 4 |
| 11 | 12 |
| 12 type Deduplicator struct { | 13 type Deduplicator struct { |
| 13 » seen map[string]bool | 14 » seen map[string]bool |
| 15 » mutex sync.Mutex |
| 14 } | 16 } |
| 15 | 17 |
| 16 func New() *Deduplicator { | 18 func New() *Deduplicator { |
| 17 return &Deduplicator{ | 19 return &Deduplicator{ |
| 18 seen: make(map[string]bool), | 20 seen: make(map[string]bool), |
| 19 } | 21 } |
| 20 } | 22 } |
| 21 | 23 |
| 22 func (d *Deduplicator) Clear() { | 24 func (d *Deduplicator) Clear() { |
| 25 d.mutex.Lock() |
| 26 defer d.mutex.Unlock() |
| 23 d.seen = make(map[string]bool) | 27 d.seen = make(map[string]bool) |
| 24 } | 28 } |
| 25 | 29 |
| 26 func (d *Deduplicator) IsUnique(report data.FuzzReport) bool { | 30 func (d *Deduplicator) IsUnique(report data.FuzzReport) bool { |
| 27 // Empty stacktraces should be manually deduplicated. | 31 // Empty stacktraces should be manually deduplicated. |
| 28 if report.DebugStackTrace.IsEmpty() && report.ReleaseStackTrace.IsEmpty(
) { | 32 if report.DebugStackTrace.IsEmpty() && report.ReleaseStackTrace.IsEmpty(
) { |
| 29 return true | 33 return true |
| 30 } | 34 } |
| 31 // Other flags should also be looked at manually. | 35 // Other flags should also be looked at manually. |
| 32 if util.In("Other", report.DebugFlags) || util.In("Other", report.Releas
eFlags) { | 36 if util.In("Other", report.DebugFlags) || util.In("Other", report.Releas
eFlags) { |
| 33 return true | 37 return true |
| 34 } | 38 } |
| 39 d.mutex.Lock() |
| 40 defer d.mutex.Unlock() |
| 35 if k := key(report); d.seen[k] { | 41 if k := key(report); d.seen[k] { |
| 36 return false | 42 return false |
| 37 } else { | 43 } else { |
| 38 d.seen[k] = true | 44 d.seen[k] = true |
| 39 return true | 45 return true |
| 40 } | 46 } |
| 41 } | 47 } |
| 42 | 48 |
| 43 func key(r data.FuzzReport) string { | 49 func key(r data.FuzzReport) string { |
| 44 ds := trim(r.DebugStackTrace) | 50 ds := trim(r.DebugStackTrace) |
| 45 rs := trim(r.ReleaseStackTrace) | 51 rs := trim(r.ReleaseStackTrace) |
| 46 return fmt.Sprintf("C:%s,F:%q,F:%q,S:%s,S:%s", r.FuzzCategory, r.DebugFl
ags, r.ReleaseFlags, ds.String(), rs.String()) | 52 return fmt.Sprintf("C:%s,F:%q,F:%q,S:%s,S:%s", r.FuzzCategory, r.DebugFl
ags, r.ReleaseFlags, ds.String(), rs.String()) |
| 47 } | 53 } |
| 48 | 54 |
| 49 // trim returns a copy of the given stacktrace, with the line numbers removed an
d all but the | 55 // trim returns a copy of the given stacktrace, with the line numbers removed an
d all but the |
| 50 // first _MAX_STACKTRACE_LINES stacktraces removed. | 56 // first _MAX_STACKTRACE_LINES stacktraces removed. |
| 51 func trim(st data.StackTrace) data.StackTrace { | 57 func trim(st data.StackTrace) data.StackTrace { |
| 52 if frames := st.Frames; len(frames) > _MAX_STACKTRACE_LINES { | 58 if frames := st.Frames; len(frames) > _MAX_STACKTRACE_LINES { |
| 53 st.Frames = st.Frames[0:_MAX_STACKTRACE_LINES] | 59 st.Frames = st.Frames[0:_MAX_STACKTRACE_LINES] |
| 54 } | 60 } |
| 55 // copy the frames, so we don't accidentally change the real report. | 61 // copy the frames, so we don't accidentally change the real report. |
| 56 st.Frames = append([]data.StackTraceFrame(nil), st.Frames...) | 62 st.Frames = append([]data.StackTraceFrame(nil), st.Frames...) |
| 57 // Remove line numbers from our deduping criteria. | 63 // Remove line numbers from our deduping criteria. |
| 58 for i := range st.Frames { | 64 for i := range st.Frames { |
| 59 st.Frames[i].LineNumber = 0 | 65 st.Frames[i].LineNumber = 0 |
| 60 } | 66 } |
| 61 return st | 67 return st |
| 62 } | 68 } |
| OLD | NEW |