xref: /aosp_15_r20/external/swiftshader/tests/regres/cov/import.go (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
2*03ce13f7SAndroid Build Coastguard Worker//
3*03ce13f7SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*03ce13f7SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*03ce13f7SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*03ce13f7SAndroid Build Coastguard Worker//
7*03ce13f7SAndroid Build Coastguard Worker//    http://www.apache.org/licenses/LICENSE-2.0
8*03ce13f7SAndroid Build Coastguard Worker//
9*03ce13f7SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*03ce13f7SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*03ce13f7SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*03ce13f7SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*03ce13f7SAndroid Build Coastguard Worker// limitations under the License.
14*03ce13f7SAndroid Build Coastguard Worker
15*03ce13f7SAndroid Build Coastguard Workerpackage cov
16*03ce13f7SAndroid Build Coastguard Worker
17*03ce13f7SAndroid Build Coastguard Workerimport (
18*03ce13f7SAndroid Build Coastguard Worker	"bytes"
19*03ce13f7SAndroid Build Coastguard Worker	"encoding/binary"
20*03ce13f7SAndroid Build Coastguard Worker	"encoding/json"
21*03ce13f7SAndroid Build Coastguard Worker	"fmt"
22*03ce13f7SAndroid Build Coastguard Worker	"os"
23*03ce13f7SAndroid Build Coastguard Worker	"os/exec"
24*03ce13f7SAndroid Build Coastguard Worker	"path/filepath"
25*03ce13f7SAndroid Build Coastguard Worker	"strings"
26*03ce13f7SAndroid Build Coastguard Worker
27*03ce13f7SAndroid Build Coastguard Worker	"swiftshader.googlesource.com/SwiftShader/tests/regres/llvm"
28*03ce13f7SAndroid Build Coastguard Worker)
29*03ce13f7SAndroid Build Coastguard Worker
30*03ce13f7SAndroid Build Coastguard Worker// File describes the coverage spans in a single source file.
31*03ce13f7SAndroid Build Coastguard Workertype File struct {
32*03ce13f7SAndroid Build Coastguard Worker	Path      string
33*03ce13f7SAndroid Build Coastguard Worker	Covered   SpanList // Spans with coverage
34*03ce13f7SAndroid Build Coastguard Worker	Uncovered SpanList // Compiled spans without coverage
35*03ce13f7SAndroid Build Coastguard Worker}
36*03ce13f7SAndroid Build Coastguard Worker
37*03ce13f7SAndroid Build Coastguard Worker// Coverage describes the coverage spans for all the source files for a single
38*03ce13f7SAndroid Build Coastguard Worker// process invocation.
39*03ce13f7SAndroid Build Coastguard Workertype Coverage struct {
40*03ce13f7SAndroid Build Coastguard Worker	Files []File
41*03ce13f7SAndroid Build Coastguard Worker}
42*03ce13f7SAndroid Build Coastguard Worker
43*03ce13f7SAndroid Build Coastguard Worker// Env holds the enviroment settings for performing coverage processing.
44*03ce13f7SAndroid Build Coastguard Workertype Env struct {
45*03ce13f7SAndroid Build Coastguard Worker	LLVM     llvm.Toolchain
46*03ce13f7SAndroid Build Coastguard Worker	RootDir  string // path to SwiftShader git root directory
47*03ce13f7SAndroid Build Coastguard Worker	ExePath  string // path to the executable binary
48*03ce13f7SAndroid Build Coastguard Worker	TurboCov string // path to turbo-cov (optional)
49*03ce13f7SAndroid Build Coastguard Worker}
50*03ce13f7SAndroid Build Coastguard Worker
51*03ce13f7SAndroid Build Coastguard Worker// AppendRuntimeEnv returns the environment variables env with the
52*03ce13f7SAndroid Build Coastguard Worker// LLVM_PROFILE_FILE environment variable appended.
53*03ce13f7SAndroid Build Coastguard Workerfunc AppendRuntimeEnv(env []string, coverageFile string) []string {
54*03ce13f7SAndroid Build Coastguard Worker	return append(env, "LLVM_PROFILE_FILE="+coverageFile)
55*03ce13f7SAndroid Build Coastguard Worker}
56*03ce13f7SAndroid Build Coastguard Worker
57*03ce13f7SAndroid Build Coastguard Worker// AllSourceFiles returns a *Coverage containing all the source files without
58*03ce13f7SAndroid Build Coastguard Worker// coverage data. This populates the coverage view with files even if they
59*03ce13f7SAndroid Build Coastguard Worker// didn't get compiled.
60*03ce13f7SAndroid Build Coastguard Workerfunc (e Env) AllSourceFiles() *Coverage {
61*03ce13f7SAndroid Build Coastguard Worker	var ignorePaths = map[string]bool{
62*03ce13f7SAndroid Build Coastguard Worker		"src/System": true,
63*03ce13f7SAndroid Build Coastguard Worker	}
64*03ce13f7SAndroid Build Coastguard Worker
65*03ce13f7SAndroid Build Coastguard Worker	// Gather all the source files to include them even if there is no coverage
66*03ce13f7SAndroid Build Coastguard Worker	// information produced for these files. This highlights files that aren't
67*03ce13f7SAndroid Build Coastguard Worker	// even compiled.
68*03ce13f7SAndroid Build Coastguard Worker	cov := Coverage{}
69*03ce13f7SAndroid Build Coastguard Worker	allFiles := map[string]struct{}{}
70*03ce13f7SAndroid Build Coastguard Worker	filepath.Walk(filepath.Join(e.RootDir, "src"), func(path string, info os.FileInfo, err error) error {
71*03ce13f7SAndroid Build Coastguard Worker		if err != nil {
72*03ce13f7SAndroid Build Coastguard Worker			return err
73*03ce13f7SAndroid Build Coastguard Worker		}
74*03ce13f7SAndroid Build Coastguard Worker		rel, err := filepath.Rel(e.RootDir, path)
75*03ce13f7SAndroid Build Coastguard Worker		if err != nil || ignorePaths[rel] {
76*03ce13f7SAndroid Build Coastguard Worker			return filepath.SkipDir
77*03ce13f7SAndroid Build Coastguard Worker		}
78*03ce13f7SAndroid Build Coastguard Worker		if !info.IsDir() {
79*03ce13f7SAndroid Build Coastguard Worker			switch filepath.Ext(path) {
80*03ce13f7SAndroid Build Coastguard Worker			case ".h", ".c", ".cc", ".cpp", ".hpp":
81*03ce13f7SAndroid Build Coastguard Worker				if _, seen := allFiles[rel]; !seen {
82*03ce13f7SAndroid Build Coastguard Worker					cov.Files = append(cov.Files, File{Path: rel})
83*03ce13f7SAndroid Build Coastguard Worker				}
84*03ce13f7SAndroid Build Coastguard Worker			}
85*03ce13f7SAndroid Build Coastguard Worker		}
86*03ce13f7SAndroid Build Coastguard Worker		return nil
87*03ce13f7SAndroid Build Coastguard Worker	})
88*03ce13f7SAndroid Build Coastguard Worker	return &cov
89*03ce13f7SAndroid Build Coastguard Worker}
90*03ce13f7SAndroid Build Coastguard Worker
91*03ce13f7SAndroid Build Coastguard Worker// Import uses the llvm-profdata and llvm-cov tools to import the coverage
92*03ce13f7SAndroid Build Coastguard Worker// information from a .profraw file.
93*03ce13f7SAndroid Build Coastguard Workerfunc (e Env) Import(profrawPath string) (*Coverage, error) {
94*03ce13f7SAndroid Build Coastguard Worker	profdata := profrawPath + ".profdata"
95*03ce13f7SAndroid Build Coastguard Worker
96*03ce13f7SAndroid Build Coastguard Worker	if err := exec.Command(e.LLVM.Profdata(), "merge", "-sparse", profrawPath, "-output", profdata).Run(); err != nil {
97*03ce13f7SAndroid Build Coastguard Worker		return nil, fmt.Errorf("llvm-profdata errored: %w", err)
98*03ce13f7SAndroid Build Coastguard Worker	}
99*03ce13f7SAndroid Build Coastguard Worker	defer os.Remove(profdata)
100*03ce13f7SAndroid Build Coastguard Worker
101*03ce13f7SAndroid Build Coastguard Worker	if e.TurboCov == "" {
102*03ce13f7SAndroid Build Coastguard Worker		args := []string{
103*03ce13f7SAndroid Build Coastguard Worker			"export",
104*03ce13f7SAndroid Build Coastguard Worker			e.ExePath,
105*03ce13f7SAndroid Build Coastguard Worker			"-instr-profile=" + profdata,
106*03ce13f7SAndroid Build Coastguard Worker			"-format=text",
107*03ce13f7SAndroid Build Coastguard Worker		}
108*03ce13f7SAndroid Build Coastguard Worker		if e.LLVM.Version.GreaterEqual(llvm.Version{Major: 9}) {
109*03ce13f7SAndroid Build Coastguard Worker			// LLVM 9 has new flags that omit stuff we don't care about.
110*03ce13f7SAndroid Build Coastguard Worker			args = append(args,
111*03ce13f7SAndroid Build Coastguard Worker				"-skip-expansions",
112*03ce13f7SAndroid Build Coastguard Worker				"-skip-functions",
113*03ce13f7SAndroid Build Coastguard Worker			)
114*03ce13f7SAndroid Build Coastguard Worker		}
115*03ce13f7SAndroid Build Coastguard Worker
116*03ce13f7SAndroid Build Coastguard Worker		data, err := exec.Command(e.LLVM.Cov(), args...).Output()
117*03ce13f7SAndroid Build Coastguard Worker		if err != nil {
118*03ce13f7SAndroid Build Coastguard Worker			return nil, fmt.Errorf("llvm-cov errored: %v\n%v", string(err.(*exec.ExitError).Stderr), err)
119*03ce13f7SAndroid Build Coastguard Worker		}
120*03ce13f7SAndroid Build Coastguard Worker		cov, err := e.parseCov(data)
121*03ce13f7SAndroid Build Coastguard Worker		if err != nil {
122*03ce13f7SAndroid Build Coastguard Worker			return nil, fmt.Errorf("failed to parse coverage json data: %w", err)
123*03ce13f7SAndroid Build Coastguard Worker		}
124*03ce13f7SAndroid Build Coastguard Worker		return cov, nil
125*03ce13f7SAndroid Build Coastguard Worker	}
126*03ce13f7SAndroid Build Coastguard Worker
127*03ce13f7SAndroid Build Coastguard Worker	data, err := exec.Command(e.TurboCov, e.ExePath, profdata).Output()
128*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
129*03ce13f7SAndroid Build Coastguard Worker		return nil, fmt.Errorf("turbo-cov errored: %v\n%v", string(err.(*exec.ExitError).Stderr), err)
130*03ce13f7SAndroid Build Coastguard Worker	}
131*03ce13f7SAndroid Build Coastguard Worker	cov, err := e.parseTurboCov(data)
132*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
133*03ce13f7SAndroid Build Coastguard Worker		return nil, fmt.Errorf("failed to process turbo-cov output: %w", err)
134*03ce13f7SAndroid Build Coastguard Worker	}
135*03ce13f7SAndroid Build Coastguard Worker
136*03ce13f7SAndroid Build Coastguard Worker	return cov, nil
137*03ce13f7SAndroid Build Coastguard Worker}
138*03ce13f7SAndroid Build Coastguard Worker
139*03ce13f7SAndroid Build Coastguard Workerfunc appendSpan(spans []Span, span Span) []Span {
140*03ce13f7SAndroid Build Coastguard Worker	if c := len(spans); c > 0 && spans[c-1].End == span.Start {
141*03ce13f7SAndroid Build Coastguard Worker		spans[c-1].End = span.End
142*03ce13f7SAndroid Build Coastguard Worker	} else {
143*03ce13f7SAndroid Build Coastguard Worker		spans = append(spans, span)
144*03ce13f7SAndroid Build Coastguard Worker	}
145*03ce13f7SAndroid Build Coastguard Worker	return spans
146*03ce13f7SAndroid Build Coastguard Worker}
147*03ce13f7SAndroid Build Coastguard Worker
148*03ce13f7SAndroid Build Coastguard Worker// https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
149*03ce13f7SAndroid Build Coastguard Worker// https://stackoverflow.com/a/56792192
150*03ce13f7SAndroid Build Coastguard Workerfunc (e Env) parseCov(raw []byte) (*Coverage, error) {
151*03ce13f7SAndroid Build Coastguard Worker	// line int, col int, count int64, hasCount bool, isRegionEntry bool
152*03ce13f7SAndroid Build Coastguard Worker	type segment []interface{}
153*03ce13f7SAndroid Build Coastguard Worker
154*03ce13f7SAndroid Build Coastguard Worker	type file struct {
155*03ce13f7SAndroid Build Coastguard Worker		// expansions ignored
156*03ce13f7SAndroid Build Coastguard Worker		Name     string    `json:"filename"`
157*03ce13f7SAndroid Build Coastguard Worker		Segments []segment `json:"segments"`
158*03ce13f7SAndroid Build Coastguard Worker		// summary ignored
159*03ce13f7SAndroid Build Coastguard Worker	}
160*03ce13f7SAndroid Build Coastguard Worker
161*03ce13f7SAndroid Build Coastguard Worker	type data struct {
162*03ce13f7SAndroid Build Coastguard Worker		Files []file `json:"files"`
163*03ce13f7SAndroid Build Coastguard Worker	}
164*03ce13f7SAndroid Build Coastguard Worker
165*03ce13f7SAndroid Build Coastguard Worker	root := struct {
166*03ce13f7SAndroid Build Coastguard Worker		Data []data `json:"data"`
167*03ce13f7SAndroid Build Coastguard Worker	}{}
168*03ce13f7SAndroid Build Coastguard Worker	err := json.NewDecoder(bytes.NewReader(raw)).Decode(&root)
169*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
170*03ce13f7SAndroid Build Coastguard Worker		return nil, err
171*03ce13f7SAndroid Build Coastguard Worker	}
172*03ce13f7SAndroid Build Coastguard Worker
173*03ce13f7SAndroid Build Coastguard Worker	c := &Coverage{Files: make([]File, 0, len(root.Data[0].Files))}
174*03ce13f7SAndroid Build Coastguard Worker	for _, f := range root.Data[0].Files {
175*03ce13f7SAndroid Build Coastguard Worker		relpath, err := filepath.Rel(e.RootDir, f.Name)
176*03ce13f7SAndroid Build Coastguard Worker		if err != nil {
177*03ce13f7SAndroid Build Coastguard Worker			return nil, err
178*03ce13f7SAndroid Build Coastguard Worker		}
179*03ce13f7SAndroid Build Coastguard Worker		if strings.HasPrefix(relpath, "..") {
180*03ce13f7SAndroid Build Coastguard Worker			continue
181*03ce13f7SAndroid Build Coastguard Worker		}
182*03ce13f7SAndroid Build Coastguard Worker		file := File{Path: relpath}
183*03ce13f7SAndroid Build Coastguard Worker		for sIdx := 0; sIdx+1 < len(f.Segments); sIdx++ {
184*03ce13f7SAndroid Build Coastguard Worker			start := Location{(int)(f.Segments[sIdx][0].(float64)), (int)(f.Segments[sIdx][1].(float64))}
185*03ce13f7SAndroid Build Coastguard Worker			end := Location{(int)(f.Segments[sIdx+1][0].(float64)), (int)(f.Segments[sIdx+1][1].(float64))}
186*03ce13f7SAndroid Build Coastguard Worker			if covered := f.Segments[sIdx][2].(float64) != 0; covered {
187*03ce13f7SAndroid Build Coastguard Worker				file.Covered = appendSpan(file.Covered, Span{start, end})
188*03ce13f7SAndroid Build Coastguard Worker			} else {
189*03ce13f7SAndroid Build Coastguard Worker				file.Uncovered = appendSpan(file.Uncovered, Span{start, end})
190*03ce13f7SAndroid Build Coastguard Worker			}
191*03ce13f7SAndroid Build Coastguard Worker		}
192*03ce13f7SAndroid Build Coastguard Worker		if len(file.Covered) > 0 {
193*03ce13f7SAndroid Build Coastguard Worker			c.Files = append(c.Files, file)
194*03ce13f7SAndroid Build Coastguard Worker		}
195*03ce13f7SAndroid Build Coastguard Worker	}
196*03ce13f7SAndroid Build Coastguard Worker
197*03ce13f7SAndroid Build Coastguard Worker	return c, nil
198*03ce13f7SAndroid Build Coastguard Worker}
199*03ce13f7SAndroid Build Coastguard Worker
200*03ce13f7SAndroid Build Coastguard Workerfunc (e Env) parseTurboCov(data []byte) (*Coverage, error) {
201*03ce13f7SAndroid Build Coastguard Worker	u32 := func() uint32 {
202*03ce13f7SAndroid Build Coastguard Worker		out := binary.LittleEndian.Uint32(data)
203*03ce13f7SAndroid Build Coastguard Worker		data = data[4:]
204*03ce13f7SAndroid Build Coastguard Worker		return out
205*03ce13f7SAndroid Build Coastguard Worker	}
206*03ce13f7SAndroid Build Coastguard Worker	u8 := func() uint8 {
207*03ce13f7SAndroid Build Coastguard Worker		out := data[0]
208*03ce13f7SAndroid Build Coastguard Worker		data = data[1:]
209*03ce13f7SAndroid Build Coastguard Worker		return out
210*03ce13f7SAndroid Build Coastguard Worker	}
211*03ce13f7SAndroid Build Coastguard Worker	str := func() string {
212*03ce13f7SAndroid Build Coastguard Worker		len := u32()
213*03ce13f7SAndroid Build Coastguard Worker		out := data[:len]
214*03ce13f7SAndroid Build Coastguard Worker		data = data[len:]
215*03ce13f7SAndroid Build Coastguard Worker		return string(out)
216*03ce13f7SAndroid Build Coastguard Worker	}
217*03ce13f7SAndroid Build Coastguard Worker
218*03ce13f7SAndroid Build Coastguard Worker	numFiles := u32()
219*03ce13f7SAndroid Build Coastguard Worker	c := &Coverage{Files: make([]File, 0, numFiles)}
220*03ce13f7SAndroid Build Coastguard Worker	for i := 0; i < int(numFiles); i++ {
221*03ce13f7SAndroid Build Coastguard Worker		path := str()
222*03ce13f7SAndroid Build Coastguard Worker		relpath, err := filepath.Rel(e.RootDir, path)
223*03ce13f7SAndroid Build Coastguard Worker		if err != nil {
224*03ce13f7SAndroid Build Coastguard Worker			return nil, err
225*03ce13f7SAndroid Build Coastguard Worker		}
226*03ce13f7SAndroid Build Coastguard Worker		if strings.HasPrefix(relpath, "..") {
227*03ce13f7SAndroid Build Coastguard Worker			continue
228*03ce13f7SAndroid Build Coastguard Worker		}
229*03ce13f7SAndroid Build Coastguard Worker
230*03ce13f7SAndroid Build Coastguard Worker		file := File{Path: relpath}
231*03ce13f7SAndroid Build Coastguard Worker
232*03ce13f7SAndroid Build Coastguard Worker		type segment struct {
233*03ce13f7SAndroid Build Coastguard Worker			location Location
234*03ce13f7SAndroid Build Coastguard Worker			count    int
235*03ce13f7SAndroid Build Coastguard Worker			covered  bool
236*03ce13f7SAndroid Build Coastguard Worker		}
237*03ce13f7SAndroid Build Coastguard Worker
238*03ce13f7SAndroid Build Coastguard Worker		numSegements := u32()
239*03ce13f7SAndroid Build Coastguard Worker		segments := make([]segment, numSegements)
240*03ce13f7SAndroid Build Coastguard Worker		for j := range segments {
241*03ce13f7SAndroid Build Coastguard Worker			segment := &segments[j]
242*03ce13f7SAndroid Build Coastguard Worker			segment.location.Line = int(u32())
243*03ce13f7SAndroid Build Coastguard Worker			segment.location.Column = int(u32())
244*03ce13f7SAndroid Build Coastguard Worker			segment.count = int(u32())
245*03ce13f7SAndroid Build Coastguard Worker			segment.covered = u8() != 0
246*03ce13f7SAndroid Build Coastguard Worker		}
247*03ce13f7SAndroid Build Coastguard Worker
248*03ce13f7SAndroid Build Coastguard Worker		for sIdx := 0; sIdx+1 < len(segments); sIdx++ {
249*03ce13f7SAndroid Build Coastguard Worker			start := segments[sIdx].location
250*03ce13f7SAndroid Build Coastguard Worker			end := segments[sIdx+1].location
251*03ce13f7SAndroid Build Coastguard Worker			if segments[sIdx].covered {
252*03ce13f7SAndroid Build Coastguard Worker				if segments[sIdx].count > 0 {
253*03ce13f7SAndroid Build Coastguard Worker					file.Covered = appendSpan(file.Covered, Span{start, end})
254*03ce13f7SAndroid Build Coastguard Worker				} else {
255*03ce13f7SAndroid Build Coastguard Worker					file.Uncovered = appendSpan(file.Uncovered, Span{start, end})
256*03ce13f7SAndroid Build Coastguard Worker				}
257*03ce13f7SAndroid Build Coastguard Worker			}
258*03ce13f7SAndroid Build Coastguard Worker		}
259*03ce13f7SAndroid Build Coastguard Worker
260*03ce13f7SAndroid Build Coastguard Worker		if len(file.Covered) > 0 {
261*03ce13f7SAndroid Build Coastguard Worker			c.Files = append(c.Files, file)
262*03ce13f7SAndroid Build Coastguard Worker		}
263*03ce13f7SAndroid Build Coastguard Worker	}
264*03ce13f7SAndroid Build Coastguard Worker
265*03ce13f7SAndroid Build Coastguard Worker	return c, nil
266*03ce13f7SAndroid Build Coastguard Worker}
267*03ce13f7SAndroid Build Coastguard Worker
268*03ce13f7SAndroid Build Coastguard Worker// Path is a tree node path formed from a list of strings
269*03ce13f7SAndroid Build Coastguard Workertype Path []string
270