1// Copyright 2022 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package cmerge 6 7// package cmerge provides a few small utility APIs for helping 8// with merging of counter data for a given function. 9 10import ( 11 "fmt" 12 "internal/coverage" 13 "math" 14) 15 16type ModeMergePolicy uint8 17 18const ( 19 ModeMergeStrict ModeMergePolicy = iota 20 ModeMergeRelaxed 21) 22 23// Merger provides state and methods to help manage the process of 24// merging together coverage counter data for a given function, for 25// tools that need to implicitly merge counter as they read multiple 26// coverage counter data files. 27type Merger struct { 28 cmode coverage.CounterMode 29 cgran coverage.CounterGranularity 30 policy ModeMergePolicy 31 overflow bool 32} 33 34func (cm *Merger) SetModeMergePolicy(policy ModeMergePolicy) { 35 cm.policy = policy 36} 37 38// MergeCounters takes the counter values in 'src' and merges them 39// into 'dst' according to the correct counter mode. 40func (m *Merger) MergeCounters(dst, src []uint32) (error, bool) { 41 if len(src) != len(dst) { 42 return fmt.Errorf("merging counters: len(dst)=%d len(src)=%d", len(dst), len(src)), false 43 } 44 if m.cmode == coverage.CtrModeSet { 45 for i := 0; i < len(src); i++ { 46 if src[i] != 0 { 47 dst[i] = 1 48 } 49 } 50 } else { 51 for i := 0; i < len(src); i++ { 52 dst[i] = m.SaturatingAdd(dst[i], src[i]) 53 } 54 } 55 ovf := m.overflow 56 m.overflow = false 57 return nil, ovf 58} 59 60// Saturating add does a saturating addition of 'dst' and 'src', 61// returning added value or math.MaxUint32 if there is an overflow. 62// Overflows are recorded in case the client needs to track them. 63func (m *Merger) SaturatingAdd(dst, src uint32) uint32 { 64 result, overflow := SaturatingAdd(dst, src) 65 if overflow { 66 m.overflow = true 67 } 68 return result 69} 70 71// Saturating add does a saturating addition of 'dst' and 'src', 72// returning added value or math.MaxUint32 plus an overflow flag. 73func SaturatingAdd(dst, src uint32) (uint32, bool) { 74 d, s := uint64(dst), uint64(src) 75 sum := d + s 76 overflow := false 77 if uint64(uint32(sum)) != sum { 78 overflow = true 79 sum = math.MaxUint32 80 } 81 return uint32(sum), overflow 82} 83 84// SetModeAndGranularity records the counter mode and granularity for 85// the current merge. In the specific case of merging across coverage 86// data files from different binaries, where we're combining data from 87// more than one meta-data file, we need to check for and resolve 88// mode/granularity clashes. 89func (cm *Merger) SetModeAndGranularity(mdf string, cmode coverage.CounterMode, cgran coverage.CounterGranularity) error { 90 if cm.cmode == coverage.CtrModeInvalid { 91 // Set merger mode based on what we're seeing here. 92 cm.cmode = cmode 93 cm.cgran = cgran 94 } else { 95 // Granularity clashes are always errors. 96 if cm.cgran != cgran { 97 return fmt.Errorf("counter granularity clash while reading meta-data file %s: previous file had %s, new file has %s", mdf, cm.cgran.String(), cgran.String()) 98 } 99 // Mode clashes are treated as errors if we're using the 100 // default strict policy. 101 if cm.cmode != cmode { 102 if cm.policy == ModeMergeStrict { 103 return fmt.Errorf("counter mode clash while reading meta-data file %s: previous file had %s, new file has %s", mdf, cm.cmode.String(), cmode.String()) 104 } 105 // In the case of a relaxed mode merge policy, upgrade 106 // mode if needed. 107 if cm.cmode < cmode { 108 cm.cmode = cmode 109 } 110 } 111 } 112 return nil 113} 114 115func (cm *Merger) ResetModeAndGranularity() { 116 cm.cmode = coverage.CtrModeInvalid 117 cm.cgran = coverage.CtrGranularityInvalid 118 cm.overflow = false 119} 120 121func (cm *Merger) Mode() coverage.CounterMode { 122 return cm.cmode 123} 124 125func (cm *Merger) Granularity() coverage.CounterGranularity { 126 return cm.cgran 127} 128