1
2
3
4
5 package encodemeta
6
7
8
9
10
11 import (
12 "bytes"
13 "crypto/md5"
14 "encoding/binary"
15 "fmt"
16 "hash"
17 "internal/coverage"
18 "internal/coverage/stringtab"
19 "internal/coverage/uleb128"
20 "io"
21 "os"
22 )
23
24 type CoverageMetaDataBuilder struct {
25 stab stringtab.Writer
26 funcs []funcDesc
27 tmp []byte
28 h hash.Hash
29 pkgpath uint32
30 pkgname uint32
31 modpath uint32
32 debug bool
33 werr error
34 }
35
36 func NewCoverageMetaDataBuilder(pkgpath string, pkgname string, modulepath string) (*CoverageMetaDataBuilder, error) {
37 if pkgpath == "" {
38 return nil, fmt.Errorf("invalid empty package path")
39 }
40 x := &CoverageMetaDataBuilder{
41 tmp: make([]byte, 0, 256),
42 h: md5.New(),
43 }
44 x.stab.InitWriter()
45 x.stab.Lookup("")
46 x.pkgpath = x.stab.Lookup(pkgpath)
47 x.pkgname = x.stab.Lookup(pkgname)
48 x.modpath = x.stab.Lookup(modulepath)
49 io.WriteString(x.h, pkgpath)
50 io.WriteString(x.h, pkgname)
51 io.WriteString(x.h, modulepath)
52 return x, nil
53 }
54
55 func h32(x uint32, h hash.Hash, tmp []byte) {
56 tmp = tmp[:0]
57 tmp = append(tmp, 0, 0, 0, 0)
58 binary.LittleEndian.PutUint32(tmp, x)
59 h.Write(tmp)
60 }
61
62 type funcDesc struct {
63 encoded []byte
64 }
65
66
67 func (b *CoverageMetaDataBuilder) AddFunc(f coverage.FuncDesc) uint {
68 hashFuncDesc(b.h, &f, b.tmp)
69 fd := funcDesc{}
70 b.tmp = b.tmp[:0]
71 b.tmp = uleb128.AppendUleb128(b.tmp, uint(len(f.Units)))
72 b.tmp = uleb128.AppendUleb128(b.tmp, uint(b.stab.Lookup(f.Funcname)))
73 b.tmp = uleb128.AppendUleb128(b.tmp, uint(b.stab.Lookup(f.Srcfile)))
74 for _, u := range f.Units {
75 b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.StLine))
76 b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.StCol))
77 b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.EnLine))
78 b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.EnCol))
79 b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.NxStmts))
80 }
81 lit := uint(0)
82 if f.Lit {
83 lit = 1
84 }
85 b.tmp = uleb128.AppendUleb128(b.tmp, lit)
86 fd.encoded = bytes.Clone(b.tmp)
87 rv := uint(len(b.funcs))
88 b.funcs = append(b.funcs, fd)
89 return rv
90 }
91
92 func (b *CoverageMetaDataBuilder) emitFuncOffsets(w io.WriteSeeker, off int64) int64 {
93 nFuncs := len(b.funcs)
94 var foff int64 = coverage.CovMetaHeaderSize + int64(b.stab.Size()) + int64(nFuncs)*4
95 for idx := 0; idx < nFuncs; idx++ {
96 b.wrUint32(w, uint32(foff))
97 foff += int64(len(b.funcs[idx].encoded))
98 }
99 return off + (int64(len(b.funcs)) * 4)
100 }
101
102 func (b *CoverageMetaDataBuilder) emitFunc(w io.WriteSeeker, off int64, f funcDesc) (int64, error) {
103 ew := len(f.encoded)
104 if nw, err := w.Write(f.encoded); err != nil {
105 return 0, err
106 } else if ew != nw {
107 return 0, fmt.Errorf("short write emitting coverage meta-data")
108 }
109 return off + int64(ew), nil
110 }
111
112 func (b *CoverageMetaDataBuilder) reportWriteError(err error) {
113 if b.werr != nil {
114 b.werr = err
115 }
116 }
117
118 func (b *CoverageMetaDataBuilder) wrUint32(w io.WriteSeeker, v uint32) {
119 b.tmp = b.tmp[:0]
120 b.tmp = append(b.tmp, 0, 0, 0, 0)
121 binary.LittleEndian.PutUint32(b.tmp, v)
122 if nw, err := w.Write(b.tmp); err != nil {
123 b.reportWriteError(err)
124 } else if nw != 4 {
125 b.reportWriteError(fmt.Errorf("short write"))
126 }
127 }
128
129
130
131 func (b *CoverageMetaDataBuilder) Emit(w io.WriteSeeker) ([16]byte, error) {
132
133
134 var digest [16]byte
135 copy(digest[:], b.h.Sum(nil))
136 mh := coverage.MetaSymbolHeader{
137
138 PkgPath: uint32(b.pkgpath),
139 PkgName: uint32(b.pkgname),
140 ModulePath: uint32(b.modpath),
141 NumFiles: uint32(b.stab.Nentries()),
142 NumFuncs: uint32(len(b.funcs)),
143 MetaHash: digest,
144 }
145 if b.debug {
146 fmt.Fprintf(os.Stderr, "=-= writing header: %+v\n", mh)
147 }
148 if err := binary.Write(w, binary.LittleEndian, mh); err != nil {
149 return digest, fmt.Errorf("error writing meta-file header: %v", err)
150 }
151 off := int64(coverage.CovMetaHeaderSize)
152
153
154 off = b.emitFuncOffsets(w, off)
155
156
157 if b.werr != nil {
158 return digest, b.werr
159 }
160
161
162 if err := b.stab.Write(w); err != nil {
163 return digest, err
164 }
165 off += int64(b.stab.Size())
166
167
168 for _, f := range b.funcs {
169 var err error
170 off, err = b.emitFunc(w, off, f)
171 if err != nil {
172 return digest, err
173 }
174 }
175
176
177 totalLength := uint32(off)
178 if _, err := w.Seek(0, io.SeekStart); err != nil {
179 return digest, err
180 }
181 b.wrUint32(w, totalLength)
182 if b.werr != nil {
183 return digest, b.werr
184 }
185 return digest, nil
186 }
187
188
189
190 func HashFuncDesc(f *coverage.FuncDesc) [16]byte {
191 h := md5.New()
192 tmp := make([]byte, 0, 32)
193 hashFuncDesc(h, f, tmp)
194 var r [16]byte
195 copy(r[:], h.Sum(nil))
196 return r
197 }
198
199
200 func hashFuncDesc(h hash.Hash, f *coverage.FuncDesc, tmp []byte) {
201 io.WriteString(h, f.Funcname)
202 io.WriteString(h, f.Srcfile)
203 for _, u := range f.Units {
204 h32(u.StLine, h, tmp)
205 h32(u.StCol, h, tmp)
206 h32(u.EnLine, h, tmp)
207 h32(u.EnCol, h, tmp)
208 h32(u.NxStmts, h, tmp)
209 }
210 lit := uint32(0)
211 if f.Lit {
212 lit = 1
213 }
214 h32(lit, h, tmp)
215 }
216
View as plain text