merge.mx raw

   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  
   5  package cmerge
   6  
   7  // package cmerge provides a few small utility APIs for helping
   8  // with merging of counter data for a given function.
   9  
  10  import (
  11  	"fmt"
  12  	"internal/coverage"
  13  	"math"
  14  )
  15  
  16  type ModeMergePolicy uint8
  17  
  18  const (
  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.
  27  type Merger struct {
  28  	cmode    coverage.CounterMode
  29  	cgran    coverage.CounterGranularity
  30  	policy   ModeMergePolicy
  31  	overflow bool
  32  }
  33  
  34  func (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.
  40  func (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.
  63  func (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.
  73  func 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.
  89  func (cm *Merger) SetModeAndGranularity(mdf []byte, 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  
 115  func (cm *Merger) ResetModeAndGranularity() {
 116  	cm.cmode = coverage.CtrModeInvalid
 117  	cm.cgran = coverage.CtrGranularityInvalid
 118  	cm.overflow = false
 119  }
 120  
 121  func (cm *Merger) Mode() coverage.CounterMode {
 122  	return cm.cmode
 123  }
 124  
 125  func (cm *Merger) Granularity() coverage.CounterGranularity {
 126  	return cm.cgran
 127  }
 128