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