1
2
3
4
5 package encodecounter
6
7 import (
8 "bufio"
9 "encoding/binary"
10 "fmt"
11 "internal/coverage"
12 "internal/coverage/slicewriter"
13 "internal/coverage/stringtab"
14 "internal/coverage/uleb128"
15 "io"
16 "maps"
17 "os"
18 "slices"
19 )
20
21
22
23
24
25
26
27 type CoverageDataWriter struct {
28 stab *stringtab.Writer
29 w *bufio.Writer
30 csh coverage.CounterSegmentHeader
31 tmp []byte
32 cflavor coverage.CounterFlavor
33 segs uint32
34 debug bool
35 }
36
37 func NewCoverageDataWriter(w io.Writer, flav coverage.CounterFlavor) *CoverageDataWriter {
38 r := &CoverageDataWriter{
39 stab: &stringtab.Writer{},
40 w: bufio.NewWriter(w),
41
42 tmp: make([]byte, 64),
43 cflavor: flav,
44 }
45 r.stab.InitWriter()
46 r.stab.Lookup("")
47 return r
48 }
49
50
51
52
53
54
55 type CounterVisitor interface {
56 VisitFuncs(f CounterVisitorFn) error
57 }
58
59
60
61 type CounterVisitorFn func(pkid uint32, funcid uint32, counters []uint32) error
62
63
64
65
66 func (cfw *CoverageDataWriter) Write(metaFileHash [16]byte, args map[string]string, visitor CounterVisitor) error {
67 if err := cfw.writeHeader(metaFileHash); err != nil {
68 return err
69 }
70 return cfw.AppendSegment(args, visitor)
71 }
72
73 func padToFourByteBoundary(ws *slicewriter.WriteSeeker) error {
74 sz := len(ws.BytesWritten())
75 zeros := []byte{0, 0, 0, 0}
76 rem := uint32(sz) % 4
77 if rem != 0 {
78 pad := zeros[:(4 - rem)]
79 if nw, err := ws.Write(pad); err != nil {
80 return err
81 } else if nw != len(pad) {
82 return fmt.Errorf("error: short write")
83 }
84 }
85 return nil
86 }
87
88 func (cfw *CoverageDataWriter) patchSegmentHeader(ws *slicewriter.WriteSeeker) error {
89
90 off, err := ws.Seek(0, io.SeekCurrent)
91 if err != nil {
92 return fmt.Errorf("error seeking in patchSegmentHeader: %v", err)
93 }
94
95 if _, err := ws.Seek(0, io.SeekStart); err != nil {
96 return fmt.Errorf("error seeking in patchSegmentHeader: %v", err)
97 }
98 if cfw.debug {
99 fmt.Fprintf(os.Stderr, "=-= writing counter segment header: %+v", cfw.csh)
100 }
101 if err := binary.Write(ws, binary.LittleEndian, cfw.csh); err != nil {
102 return err
103 }
104
105 if _, err := ws.Seek(off, io.SeekStart); err != nil {
106 return fmt.Errorf("error seeking in patchSegmentHeader: %v", err)
107 }
108 return nil
109 }
110
111 func (cfw *CoverageDataWriter) writeSegmentPreamble(args map[string]string, ws *slicewriter.WriteSeeker) error {
112 if err := binary.Write(ws, binary.LittleEndian, cfw.csh); err != nil {
113 return err
114 }
115 hdrsz := uint32(len(ws.BytesWritten()))
116
117
118
119
120 cfw.stab.Freeze()
121 if err := cfw.stab.Write(ws); err != nil {
122 return err
123 }
124 cfw.csh.StrTabLen = uint32(len(ws.BytesWritten())) - hdrsz
125
126 akeys := slices.Sorted(maps.Keys(args))
127
128 wrULEB128 := func(v uint) error {
129 cfw.tmp = cfw.tmp[:0]
130 cfw.tmp = uleb128.AppendUleb128(cfw.tmp, v)
131 if _, err := ws.Write(cfw.tmp); err != nil {
132 return err
133 }
134 return nil
135 }
136
137
138 if err := wrULEB128(uint(len(args))); err != nil {
139 return err
140 }
141
142 for _, k := range akeys {
143 ki := uint(cfw.stab.Lookup(k))
144 if err := wrULEB128(ki); err != nil {
145 return err
146 }
147 v := args[k]
148 vi := uint(cfw.stab.Lookup(v))
149 if err := wrULEB128(vi); err != nil {
150 return err
151 }
152 }
153 if err := padToFourByteBoundary(ws); err != nil {
154 return err
155 }
156 cfw.csh.ArgsLen = uint32(len(ws.BytesWritten())) - (cfw.csh.StrTabLen + hdrsz)
157
158 return nil
159 }
160
161
162
163 func (cfw *CoverageDataWriter) AppendSegment(args map[string]string, visitor CounterVisitor) error {
164 cfw.stab = &stringtab.Writer{}
165 cfw.stab.InitWriter()
166 cfw.stab.Lookup("")
167
168 var err error
169 for k, v := range args {
170 cfw.stab.Lookup(k)
171 cfw.stab.Lookup(v)
172 }
173
174 ws := &slicewriter.WriteSeeker{}
175 if err = cfw.writeSegmentPreamble(args, ws); err != nil {
176 return err
177 }
178 if err = cfw.writeCounters(visitor, ws); err != nil {
179 return err
180 }
181 if err = cfw.patchSegmentHeader(ws); err != nil {
182 return err
183 }
184 if err := cfw.writeBytes(ws.BytesWritten()); err != nil {
185 return err
186 }
187 if err = cfw.writeFooter(); err != nil {
188 return err
189 }
190 if err := cfw.w.Flush(); err != nil {
191 return fmt.Errorf("write error: %v", err)
192 }
193 cfw.stab = nil
194 return nil
195 }
196
197 func (cfw *CoverageDataWriter) writeHeader(metaFileHash [16]byte) error {
198
199 ch := coverage.CounterFileHeader{
200 Magic: coverage.CovCounterMagic,
201 Version: coverage.CounterFileVersion,
202 MetaHash: metaFileHash,
203 CFlavor: cfw.cflavor,
204 BigEndian: false,
205 }
206 if err := binary.Write(cfw.w, binary.LittleEndian, ch); err != nil {
207 return err
208 }
209 return nil
210 }
211
212 func (cfw *CoverageDataWriter) writeBytes(b []byte) error {
213 if len(b) == 0 {
214 return nil
215 }
216 nw, err := cfw.w.Write(b)
217 if err != nil {
218 return fmt.Errorf("error writing counter data: %v", err)
219 }
220 if len(b) != nw {
221 return fmt.Errorf("error writing counter data: short write")
222 }
223 return nil
224 }
225
226 func (cfw *CoverageDataWriter) writeCounters(visitor CounterVisitor, ws *slicewriter.WriteSeeker) error {
227
228
229
230
231
232
233 ctrb := make([]byte, 4)
234 wrval := func(val uint32) error {
235 var buf []byte
236 var towr int
237 if cfw.cflavor == coverage.CtrRaw {
238 binary.LittleEndian.PutUint32(ctrb, val)
239 buf = ctrb
240 towr = 4
241 } else if cfw.cflavor == coverage.CtrULeb128 {
242 cfw.tmp = cfw.tmp[:0]
243 cfw.tmp = uleb128.AppendUleb128(cfw.tmp, uint(val))
244 buf = cfw.tmp
245 towr = len(buf)
246 } else {
247 panic("internal error: bad counter flavor")
248 }
249 if sz, err := ws.Write(buf); err != nil {
250 return err
251 } else if sz != towr {
252 return fmt.Errorf("writing counters: short write")
253 }
254 return nil
255 }
256
257
258 emitter := func(pkid uint32, funcid uint32, counters []uint32) error {
259 cfw.csh.FcnEntries++
260 if err := wrval(uint32(len(counters))); err != nil {
261 return err
262 }
263
264 if err := wrval(pkid); err != nil {
265 return err
266 }
267
268 if err := wrval(funcid); err != nil {
269 return err
270 }
271 for _, val := range counters {
272 if err := wrval(val); err != nil {
273 return err
274 }
275 }
276 return nil
277 }
278 if err := visitor.VisitFuncs(emitter); err != nil {
279 return err
280 }
281 return nil
282 }
283
284 func (cfw *CoverageDataWriter) writeFooter() error {
285 cfw.segs++
286 cf := coverage.CounterFileFooter{
287 Magic: coverage.CovCounterMagic,
288 NumSegments: cfw.segs,
289 }
290 if err := binary.Write(cfw.w, binary.LittleEndian, cf); err != nil {
291 return err
292 }
293 return nil
294 }
295
View as plain text