77 "bufio"
88 "bytes"
99 _ "embed"
10+ "encoding/base64"
1011 "encoding/csv"
1112 "encoding/json"
1213 "fmt"
@@ -16,6 +17,7 @@ import (
1617 "math"
1718 "os"
1819 "path/filepath"
20+ "slices"
1921 "sort"
2022 "strconv"
2123 "strings"
@@ -178,7 +180,7 @@ func fileLineContents(file *file, lines [][]byte) lineCoverExport {
178180
179181func (rg * ReportGenerator ) DoRawCoverFiles (w io.Writer , params HandlerParams ) error {
180182 progs := fixUpPCs (params .Progs , params .Filter )
181- if err := rg .symbolizePCs (uniquePCs (progs )); err != nil {
183+ if err := rg .symbolizePCs (uniquePCs (progs ... )); err != nil {
182184 return err
183185 }
184186
@@ -215,6 +217,24 @@ type CoverageInfo struct {
215217 PC uint64 `json:"pc"`
216218}
217219
220+ func makeCoverageInfo (frame * backend.Frame ) * CoverageInfo {
221+ endCol := frame .Range .EndCol
222+ if endCol == backend .LineEnd {
223+ endCol = - 1
224+ }
225+ return & CoverageInfo {
226+ FilePath : frame .Name ,
227+ FuncName : frame .FuncName ,
228+ StartLine : frame .Range .StartLine ,
229+ StartCol : frame .Range .StartCol ,
230+ EndLine : frame .Range .EndLine ,
231+ EndCol : endCol ,
232+ HitCount : 1 , // We know it is at least 1.
233+ Inline : frame .Inline ,
234+ PC : frame .PC ,
235+ }
236+ }
237+
218238// DoCoverJSONL is a handler for "/cover?jsonl=1".
219239func (rg * ReportGenerator ) DoCoverJSONL (w io.Writer , params HandlerParams ) error {
220240 if rg .CallbackPoints != nil {
@@ -223,7 +243,7 @@ func (rg *ReportGenerator) DoCoverJSONL(w io.Writer, params HandlerParams) error
223243 }
224244 }
225245 progs := fixUpPCs (params .Progs , params .Filter )
226- if err := rg .symbolizePCs (uniquePCs (progs )); err != nil {
246+ if err := rg .symbolizePCs (uniquePCs (progs ... )); err != nil {
227247 return err
228248 }
229249 pcProgCount := make (map [uint64 ]int )
@@ -234,28 +254,62 @@ func (rg *ReportGenerator) DoCoverJSONL(w io.Writer, params HandlerParams) error
234254 }
235255 encoder := json .NewEncoder (w )
236256 for _ , frame := range rg .Frames {
237- endCol := frame .Range .EndCol
238- if endCol == backend .LineEnd {
239- endCol = - 1
240- }
241- covInfo := & CoverageInfo {
242- FilePath : frame .Name ,
243- FuncName : frame .FuncName ,
244- StartLine : frame .Range .StartLine ,
245- StartCol : frame .Range .StartCol ,
246- EndLine : frame .Range .EndLine ,
247- EndCol : endCol ,
248- HitCount : pcProgCount [frame .PC ],
249- Inline : frame .Inline ,
250- PC : frame .PC ,
251- }
257+ covInfo := makeCoverageInfo (frame )
258+ covInfo .HitCount = pcProgCount [frame .PC ]
252259 if err := encoder .Encode (covInfo ); err != nil {
253260 return fmt .Errorf ("failed to json.Encode(): %w" , err )
254261 }
255262 }
256263 return nil
257264}
258265
266+ type ProgramCoverage struct {
267+ Program string
268+ CoveredLines []* CoverageInfo
269+ }
270+
271+ // DoCoverPrograms returns the corpus programs with the associated coverage.
272+ // The result is a jsonl stream.
273+ // Each record represents the single program and multiple CoverageInfo records.
274+ func (rg * ReportGenerator ) DoCoverPrograms (w io.Writer , params HandlerParams ) error {
275+ if rg .CallbackPoints != nil {
276+ if err := rg .symbolizePCs (rg .CallbackPoints ); err != nil {
277+ return fmt .Errorf ("failed to symbolize PCs(): %w" , err )
278+ }
279+ }
280+ pcToFrames := map [uint64 ][]* backend.Frame {}
281+ for _ , frame := range rg .Frames {
282+ pcToFrames [frame .PC ] = append (pcToFrames [frame .PC ], frame )
283+ }
284+ encoder := json .NewEncoder (w )
285+ for prog := range slices .Values (params .Progs ) {
286+ var covInfos []* CoverageInfo
287+ for pc := range slices .Values (uniquePCs (prog )) {
288+ for frame := range slices .Values (pcToFrames [pc ]) {
289+ covInfos = append (covInfos , makeCoverageInfo (frame ))
290+ }
291+ }
292+
293+ progBase64 := bytes.Buffer {}
294+ base64Enc := base64 .NewEncoder (base64 .StdEncoding , & progBase64 )
295+ if _ , err := base64Enc .Write ([]byte (prog .Data )); err != nil {
296+ return fmt .Errorf ("base64Enc.Write: %w" , err )
297+ }
298+ base64Enc .Close ()
299+
300+ if err := encoder .Encode (struct {
301+ ProgBase64 string
302+ Coverage []* CoverageInfo
303+ }{
304+ ProgBase64 : progBase64 .String (),
305+ Coverage : covInfos ,
306+ }); err != nil {
307+ return fmt .Errorf ("encoder.Encode: %w" , err )
308+ }
309+ }
310+ return nil
311+ }
312+
259313func (rg * ReportGenerator ) DoRawCover (w io.Writer , params HandlerParams ) error {
260314 progs := fixUpPCs (params .Progs , params .Filter )
261315 var pcs []uint64
0 commit comments