1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package main 6 7import ( 8 "bytes" 9 "cmd/internal/cov/covcmd" 10 "encoding/json" 11 "flag" 12 "fmt" 13 "go/ast" 14 "go/parser" 15 "go/token" 16 "internal/coverage" 17 "internal/coverage/encodemeta" 18 "internal/coverage/slicewriter" 19 "io" 20 "log" 21 "os" 22 "path/filepath" 23 "sort" 24 "strconv" 25 "strings" 26 27 "cmd/internal/edit" 28 "cmd/internal/objabi" 29 "cmd/internal/telemetry/counter" 30) 31 32const usageMessage = "" + 33 `Usage of 'go tool cover': 34Given a coverage profile produced by 'go test': 35 go test -coverprofile=c.out 36 37Open a web browser displaying annotated source code: 38 go tool cover -html=c.out 39 40Write out an HTML file instead of launching a web browser: 41 go tool cover -html=c.out -o coverage.html 42 43Display coverage percentages to stdout for each function: 44 go tool cover -func=c.out 45 46Finally, to generate modified source code with coverage annotations 47for a package (what go test -cover does): 48 go tool cover -mode=set -var=CoverageVariableName \ 49 -pkgcfg=<config> -outfilelist=<file> file1.go ... fileN.go 50 51where -pkgcfg points to a file containing the package path, 52package name, module path, and related info from "go build", 53and -outfilelist points to a file containing the filenames 54of the instrumented output files (one per input file). 55See https://pkg.go.dev/cmd/internal/cov/covcmd#CoverPkgConfig for 56more on the package config. 57` 58 59func usage() { 60 fmt.Fprint(os.Stderr, usageMessage) 61 fmt.Fprintln(os.Stderr, "\nFlags:") 62 flag.PrintDefaults() 63 fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.") 64 os.Exit(2) 65} 66 67var ( 68 mode = flag.String("mode", "", "coverage mode: set, count, atomic") 69 varVar = flag.String("var", "GoCover", "name of coverage variable to generate") 70 output = flag.String("o", "", "file for output") 71 outfilelist = flag.String("outfilelist", "", "file containing list of output files (one per line) if -pkgcfg is in use") 72 htmlOut = flag.String("html", "", "generate HTML representation of coverage profile") 73 funcOut = flag.String("func", "", "output coverage profile information for each function") 74 pkgcfg = flag.String("pkgcfg", "", "enable full-package instrumentation mode using params from specified config file") 75 pkgconfig covcmd.CoverPkgConfig 76 outputfiles []string // list of *.cover.go instrumented outputs to write, one per input (set when -pkgcfg is in use) 77 profile string // The profile to read; the value of -html or -func 78 counterStmt func(*File, string) string 79 covervarsoutfile string // an additional Go source file into which we'll write definitions of coverage counter variables + meta data variables (set when -pkgcfg is in use). 80 cmode coverage.CounterMode 81 cgran coverage.CounterGranularity 82) 83 84const ( 85 atomicPackagePath = "sync/atomic" 86 atomicPackageName = "_cover_atomic_" 87) 88 89func main() { 90 counter.Open() 91 92 objabi.AddVersionFlag() 93 flag.Usage = usage 94 objabi.Flagparse(usage) 95 counter.Inc("cover/invocations") 96 counter.CountFlags("cover/flag:", *flag.CommandLine) 97 98 // Usage information when no arguments. 99 if flag.NFlag() == 0 && flag.NArg() == 0 { 100 flag.Usage() 101 } 102 103 err := parseFlags() 104 if err != nil { 105 fmt.Fprintln(os.Stderr, err) 106 fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`) 107 os.Exit(2) 108 } 109 110 // Generate coverage-annotated source. 111 if *mode != "" { 112 annotate(flag.Args()) 113 return 114 } 115 116 // Output HTML or function coverage information. 117 if *htmlOut != "" { 118 err = htmlOutput(profile, *output) 119 } else { 120 err = funcOutput(profile, *output) 121 } 122 123 if err != nil { 124 fmt.Fprintf(os.Stderr, "cover: %v\n", err) 125 os.Exit(2) 126 } 127} 128 129// parseFlags sets the profile and counterStmt globals and performs validations. 130func parseFlags() error { 131 profile = *htmlOut 132 if *funcOut != "" { 133 if profile != "" { 134 return fmt.Errorf("too many options") 135 } 136 profile = *funcOut 137 } 138 139 // Must either display a profile or rewrite Go source. 140 if (profile == "") == (*mode == "") { 141 return fmt.Errorf("too many options") 142 } 143 144 if *varVar != "" && !token.IsIdentifier(*varVar) { 145 return fmt.Errorf("-var: %q is not a valid identifier", *varVar) 146 } 147 148 if *mode != "" { 149 switch *mode { 150 case "set": 151 counterStmt = setCounterStmt 152 cmode = coverage.CtrModeSet 153 case "count": 154 counterStmt = incCounterStmt 155 cmode = coverage.CtrModeCount 156 case "atomic": 157 counterStmt = atomicCounterStmt 158 cmode = coverage.CtrModeAtomic 159 case "regonly": 160 counterStmt = nil 161 cmode = coverage.CtrModeRegOnly 162 case "testmain": 163 counterStmt = nil 164 cmode = coverage.CtrModeTestMain 165 default: 166 return fmt.Errorf("unknown -mode %v", *mode) 167 } 168 169 if flag.NArg() == 0 { 170 return fmt.Errorf("missing source file(s)") 171 } else { 172 if *pkgcfg != "" { 173 if *output != "" { 174 return fmt.Errorf("please use '-outfilelist' flag instead of '-o'") 175 } 176 var err error 177 if outputfiles, err = readOutFileList(*outfilelist); err != nil { 178 return err 179 } 180 covervarsoutfile = outputfiles[0] 181 outputfiles = outputfiles[1:] 182 numInputs := len(flag.Args()) 183 numOutputs := len(outputfiles) 184 if numOutputs != numInputs { 185 return fmt.Errorf("number of output files (%d) not equal to number of input files (%d)", numOutputs, numInputs) 186 } 187 if err := readPackageConfig(*pkgcfg); err != nil { 188 return err 189 } 190 return nil 191 } else { 192 if *outfilelist != "" { 193 return fmt.Errorf("'-outfilelist' flag applicable only when -pkgcfg used") 194 } 195 } 196 if flag.NArg() == 1 { 197 return nil 198 } 199 } 200 } else if flag.NArg() == 0 { 201 return nil 202 } 203 return fmt.Errorf("too many arguments") 204} 205 206func readOutFileList(path string) ([]string, error) { 207 data, err := os.ReadFile(path) 208 if err != nil { 209 return nil, fmt.Errorf("error reading -outfilelist file %q: %v", path, err) 210 } 211 return strings.Split(strings.TrimSpace(string(data)), "\n"), nil 212} 213 214func readPackageConfig(path string) error { 215 data, err := os.ReadFile(path) 216 if err != nil { 217 return fmt.Errorf("error reading pkgconfig file %q: %v", path, err) 218 } 219 if err := json.Unmarshal(data, &pkgconfig); err != nil { 220 return fmt.Errorf("error reading pkgconfig file %q: %v", path, err) 221 } 222 switch pkgconfig.Granularity { 223 case "perblock": 224 cgran = coverage.CtrGranularityPerBlock 225 case "perfunc": 226 cgran = coverage.CtrGranularityPerFunc 227 default: 228 return fmt.Errorf(`%s: pkgconfig requires perblock/perfunc value`, path) 229 } 230 return nil 231} 232 233// Block represents the information about a basic block to be recorded in the analysis. 234// Note: Our definition of basic block is based on control structures; we don't break 235// apart && and ||. We could but it doesn't seem important enough to bother. 236type Block struct { 237 startByte token.Pos 238 endByte token.Pos 239 numStmt int 240} 241 242// Package holds package-specific state. 243type Package struct { 244 mdb *encodemeta.CoverageMetaDataBuilder 245 counterLengths []int 246} 247 248// Function holds func-specific state. 249type Func struct { 250 units []coverage.CoverableUnit 251 counterVar string 252} 253 254// File is a wrapper for the state of a file used in the parser. 255// The basic parse tree walker is a method of this type. 256type File struct { 257 fset *token.FileSet 258 name string // Name of file. 259 astFile *ast.File 260 blocks []Block 261 content []byte 262 edit *edit.Buffer 263 mdb *encodemeta.CoverageMetaDataBuilder 264 fn Func 265 pkg *Package 266} 267 268// findText finds text in the original source, starting at pos. 269// It correctly skips over comments and assumes it need not 270// handle quoted strings. 271// It returns a byte offset within f.src. 272func (f *File) findText(pos token.Pos, text string) int { 273 b := []byte(text) 274 start := f.offset(pos) 275 i := start 276 s := f.content 277 for i < len(s) { 278 if bytes.HasPrefix(s[i:], b) { 279 return i 280 } 281 if i+2 <= len(s) && s[i] == '/' && s[i+1] == '/' { 282 for i < len(s) && s[i] != '\n' { 283 i++ 284 } 285 continue 286 } 287 if i+2 <= len(s) && s[i] == '/' && s[i+1] == '*' { 288 for i += 2; ; i++ { 289 if i+2 > len(s) { 290 return 0 291 } 292 if s[i] == '*' && s[i+1] == '/' { 293 i += 2 294 break 295 } 296 } 297 continue 298 } 299 i++ 300 } 301 return -1 302} 303 304// Visit implements the ast.Visitor interface. 305func (f *File) Visit(node ast.Node) ast.Visitor { 306 switch n := node.(type) { 307 case *ast.BlockStmt: 308 // If it's a switch or select, the body is a list of case clauses; don't tag the block itself. 309 if len(n.List) > 0 { 310 switch n.List[0].(type) { 311 case *ast.CaseClause: // switch 312 for _, n := range n.List { 313 clause := n.(*ast.CaseClause) 314 f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false) 315 } 316 return f 317 case *ast.CommClause: // select 318 for _, n := range n.List { 319 clause := n.(*ast.CommClause) 320 f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false) 321 } 322 return f 323 } 324 } 325 f.addCounters(n.Lbrace, n.Lbrace+1, n.Rbrace+1, n.List, true) // +1 to step past closing brace. 326 case *ast.IfStmt: 327 if n.Init != nil { 328 ast.Walk(f, n.Init) 329 } 330 ast.Walk(f, n.Cond) 331 ast.Walk(f, n.Body) 332 if n.Else == nil { 333 return nil 334 } 335 // The elses are special, because if we have 336 // if x { 337 // } else if y { 338 // } 339 // we want to cover the "if y". To do this, we need a place to drop the counter, 340 // so we add a hidden block: 341 // if x { 342 // } else { 343 // if y { 344 // } 345 // } 346 elseOffset := f.findText(n.Body.End(), "else") 347 if elseOffset < 0 { 348 panic("lost else") 349 } 350 f.edit.Insert(elseOffset+4, "{") 351 f.edit.Insert(f.offset(n.Else.End()), "}") 352 353 // We just created a block, now walk it. 354 // Adjust the position of the new block to start after 355 // the "else". That will cause it to follow the "{" 356 // we inserted above. 357 pos := f.fset.File(n.Body.End()).Pos(elseOffset + 4) 358 switch stmt := n.Else.(type) { 359 case *ast.IfStmt: 360 block := &ast.BlockStmt{ 361 Lbrace: pos, 362 List: []ast.Stmt{stmt}, 363 Rbrace: stmt.End(), 364 } 365 n.Else = block 366 case *ast.BlockStmt: 367 stmt.Lbrace = pos 368 default: 369 panic("unexpected node type in if") 370 } 371 ast.Walk(f, n.Else) 372 return nil 373 case *ast.SelectStmt: 374 // Don't annotate an empty select - creates a syntax error. 375 if n.Body == nil || len(n.Body.List) == 0 { 376 return nil 377 } 378 case *ast.SwitchStmt: 379 // Don't annotate an empty switch - creates a syntax error. 380 if n.Body == nil || len(n.Body.List) == 0 { 381 if n.Init != nil { 382 ast.Walk(f, n.Init) 383 } 384 if n.Tag != nil { 385 ast.Walk(f, n.Tag) 386 } 387 return nil 388 } 389 case *ast.TypeSwitchStmt: 390 // Don't annotate an empty type switch - creates a syntax error. 391 if n.Body == nil || len(n.Body.List) == 0 { 392 if n.Init != nil { 393 ast.Walk(f, n.Init) 394 } 395 ast.Walk(f, n.Assign) 396 return nil 397 } 398 case *ast.FuncDecl: 399 // Don't annotate functions with blank names - they cannot be executed. 400 // Similarly for bodyless funcs. 401 if n.Name.Name == "_" || n.Body == nil { 402 return nil 403 } 404 fname := n.Name.Name 405 // Skip AddUint32 and StoreUint32 if we're instrumenting 406 // sync/atomic itself in atomic mode (out of an abundance of 407 // caution), since as part of the instrumentation process we 408 // add calls to AddUint32/StoreUint32, and we don't want to 409 // somehow create an infinite loop. 410 // 411 // Note that in the current implementation (Go 1.20) both 412 // routines are assembly stubs that forward calls to the 413 // internal/runtime/atomic equivalents, hence the infinite 414 // loop scenario is purely theoretical (maybe if in some 415 // future implementation one of these functions might be 416 // written in Go). See #57445 for more details. 417 if atomicOnAtomic() && (fname == "AddUint32" || fname == "StoreUint32") { 418 return nil 419 } 420 // Determine proper function or method name. 421 if r := n.Recv; r != nil && len(r.List) == 1 { 422 t := r.List[0].Type 423 star := "" 424 if p, _ := t.(*ast.StarExpr); p != nil { 425 t = p.X 426 star = "*" 427 } 428 if p, _ := t.(*ast.Ident); p != nil { 429 fname = star + p.Name + "." + fname 430 } 431 } 432 walkBody := true 433 if *pkgcfg != "" { 434 f.preFunc(n, fname) 435 if pkgconfig.Granularity == "perfunc" { 436 walkBody = false 437 } 438 } 439 if walkBody { 440 ast.Walk(f, n.Body) 441 } 442 if *pkgcfg != "" { 443 flit := false 444 f.postFunc(n, fname, flit, n.Body) 445 } 446 return nil 447 case *ast.FuncLit: 448 // For function literals enclosed in functions, just glom the 449 // code for the literal in with the enclosing function (for now). 450 if f.fn.counterVar != "" { 451 return f 452 } 453 454 // Hack: function literals aren't named in the go/ast representation, 455 // and we don't know what name the compiler will choose. For now, 456 // just make up a descriptive name. 457 pos := n.Pos() 458 p := f.fset.File(pos).Position(pos) 459 fname := fmt.Sprintf("func.L%d.C%d", p.Line, p.Column) 460 if *pkgcfg != "" { 461 f.preFunc(n, fname) 462 } 463 if pkgconfig.Granularity != "perfunc" { 464 ast.Walk(f, n.Body) 465 } 466 if *pkgcfg != "" { 467 flit := true 468 f.postFunc(n, fname, flit, n.Body) 469 } 470 return nil 471 } 472 return f 473} 474 475func mkCounterVarName(idx int) string { 476 return fmt.Sprintf("%s_%d", *varVar, idx) 477} 478 479func mkPackageIdVar() string { 480 return *varVar + "P" 481} 482 483func mkMetaVar() string { 484 return *varVar + "M" 485} 486 487func mkPackageIdExpression() string { 488 ppath := pkgconfig.PkgPath 489 if hcid := coverage.HardCodedPkgID(ppath); hcid != -1 { 490 return fmt.Sprintf("uint32(%d)", uint32(hcid)) 491 } 492 return mkPackageIdVar() 493} 494 495func (f *File) preFunc(fn ast.Node, fname string) { 496 f.fn.units = f.fn.units[:0] 497 498 // create a new counter variable for this function. 499 cv := mkCounterVarName(len(f.pkg.counterLengths)) 500 f.fn.counterVar = cv 501} 502 503func (f *File) postFunc(fn ast.Node, funcname string, flit bool, body *ast.BlockStmt) { 504 505 // Tack on single counter write if we are in "perfunc" mode. 506 singleCtr := "" 507 if pkgconfig.Granularity == "perfunc" { 508 singleCtr = "; " + f.newCounter(fn.Pos(), fn.Pos(), 1) 509 } 510 511 // record the length of the counter var required. 512 nc := len(f.fn.units) + coverage.FirstCtrOffset 513 f.pkg.counterLengths = append(f.pkg.counterLengths, nc) 514 515 // FIXME: for windows, do we want "\" and not "/"? Need to test here. 516 // Currently filename is formed as packagepath + "/" + basename. 517 fnpos := f.fset.Position(fn.Pos()) 518 ppath := pkgconfig.PkgPath 519 filename := ppath + "/" + filepath.Base(fnpos.Filename) 520 521 // The convention for cmd/cover is that if the go command that 522 // kicks off coverage specifies a local import path (e.g. "go test 523 // -cover ./thispackage"), the tool will capture full pathnames 524 // for source files instead of relative paths, which tend to work 525 // more smoothly for "go tool cover -html". See also issue #56433 526 // for more details. 527 if pkgconfig.Local { 528 filename = f.name 529 } 530 531 // Hand off function to meta-data builder. 532 fd := coverage.FuncDesc{ 533 Funcname: funcname, 534 Srcfile: filename, 535 Units: f.fn.units, 536 Lit: flit, 537 } 538 funcId := f.mdb.AddFunc(fd) 539 540 hookWrite := func(cv string, which int, val string) string { 541 return fmt.Sprintf("%s[%d] = %s", cv, which, val) 542 } 543 if *mode == "atomic" { 544 hookWrite = func(cv string, which int, val string) string { 545 return fmt.Sprintf("%sStoreUint32(&%s[%d], %s)", 546 atomicPackagePrefix(), cv, which, val) 547 } 548 } 549 550 // Generate the registration hook sequence for the function. This 551 // sequence looks like 552 // 553 // counterVar[0] = <num_units> 554 // counterVar[1] = pkgId 555 // counterVar[2] = fnId 556 // 557 cv := f.fn.counterVar 558 regHook := hookWrite(cv, 0, strconv.Itoa(len(f.fn.units))) + " ; " + 559 hookWrite(cv, 1, mkPackageIdExpression()) + " ; " + 560 hookWrite(cv, 2, strconv.Itoa(int(funcId))) + singleCtr 561 562 // Insert the registration sequence into the function. We want this sequence to 563 // appear before any counter updates, so use a hack to ensure that this edit 564 // applies before the edit corresponding to the prolog counter update. 565 566 boff := f.offset(body.Pos()) 567 ipos := f.fset.File(body.Pos()).Pos(boff) 568 ip := f.offset(ipos) 569 f.edit.Replace(ip, ip+1, string(f.content[ipos-1])+regHook+" ; ") 570 571 f.fn.counterVar = "" 572} 573 574func annotate(names []string) { 575 var p *Package 576 if *pkgcfg != "" { 577 pp := pkgconfig.PkgPath 578 pn := pkgconfig.PkgName 579 mp := pkgconfig.ModulePath 580 mdb, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp) 581 if err != nil { 582 log.Fatalf("creating coverage meta-data builder: %v\n", err) 583 } 584 p = &Package{ 585 mdb: mdb, 586 } 587 } 588 // TODO: process files in parallel here if it matters. 589 for k, name := range names { 590 if strings.ContainsAny(name, "\r\n") { 591 // annotateFile uses '//line' directives, which don't permit newlines. 592 log.Fatalf("cover: input path contains newline character: %q", name) 593 } 594 595 fd := os.Stdout 596 isStdout := true 597 if *pkgcfg != "" { 598 var err error 599 fd, err = os.Create(outputfiles[k]) 600 if err != nil { 601 log.Fatalf("cover: %s", err) 602 } 603 isStdout = false 604 } else if *output != "" { 605 var err error 606 fd, err = os.Create(*output) 607 if err != nil { 608 log.Fatalf("cover: %s", err) 609 } 610 isStdout = false 611 } 612 p.annotateFile(name, fd) 613 if !isStdout { 614 if err := fd.Close(); err != nil { 615 log.Fatalf("cover: %s", err) 616 } 617 } 618 } 619 620 if *pkgcfg != "" { 621 fd, err := os.Create(covervarsoutfile) 622 if err != nil { 623 log.Fatalf("cover: %s", err) 624 } 625 p.emitMetaData(fd) 626 if err := fd.Close(); err != nil { 627 log.Fatalf("cover: %s", err) 628 } 629 } 630} 631 632func (p *Package) annotateFile(name string, fd io.Writer) { 633 fset := token.NewFileSet() 634 content, err := os.ReadFile(name) 635 if err != nil { 636 log.Fatalf("cover: %s: %s", name, err) 637 } 638 parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments) 639 if err != nil { 640 log.Fatalf("cover: %s: %s", name, err) 641 } 642 643 file := &File{ 644 fset: fset, 645 name: name, 646 content: content, 647 edit: edit.NewBuffer(content), 648 astFile: parsedFile, 649 } 650 if p != nil { 651 file.mdb = p.mdb 652 file.pkg = p 653 } 654 655 if *mode == "atomic" { 656 // Add import of sync/atomic immediately after package clause. 657 // We do this even if there is an existing import, because the 658 // existing import may be shadowed at any given place we want 659 // to refer to it, and our name (_cover_atomic_) is less likely to 660 // be shadowed. The one exception is if we're visiting the 661 // sync/atomic package itself, in which case we can refer to 662 // functions directly without an import prefix. See also #57445. 663 if pkgconfig.PkgPath != "sync/atomic" { 664 file.edit.Insert(file.offset(file.astFile.Name.End()), 665 fmt.Sprintf("; import %s %q", atomicPackageName, atomicPackagePath)) 666 } 667 } 668 if pkgconfig.PkgName == "main" { 669 file.edit.Insert(file.offset(file.astFile.Name.End()), 670 "; import _ \"runtime/coverage\"") 671 } 672 673 if counterStmt != nil { 674 ast.Walk(file, file.astFile) 675 } 676 newContent := file.edit.Bytes() 677 678 if strings.ContainsAny(name, "\r\n") { 679 // This should have been checked by the caller already, but we double check 680 // here just to be sure we haven't missed a caller somewhere. 681 panic(fmt.Sprintf("annotateFile: name contains unexpected newline character: %q", name)) 682 } 683 fmt.Fprintf(fd, "//line %s:1:1\n", name) 684 fd.Write(newContent) 685 686 // After printing the source tree, add some declarations for the 687 // counters etc. We could do this by adding to the tree, but it's 688 // easier just to print the text. 689 file.addVariables(fd) 690 691 // Emit a reference to the atomic package to avoid 692 // import and not used error when there's no code in a file. 693 if *mode == "atomic" { 694 fmt.Fprintf(fd, "\nvar _ = %sLoadUint32\n", atomicPackagePrefix()) 695 } 696} 697 698// setCounterStmt returns the expression: __count[23] = 1. 699func setCounterStmt(f *File, counter string) string { 700 return fmt.Sprintf("%s = 1", counter) 701} 702 703// incCounterStmt returns the expression: __count[23]++. 704func incCounterStmt(f *File, counter string) string { 705 return fmt.Sprintf("%s++", counter) 706} 707 708// atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1) 709func atomicCounterStmt(f *File, counter string) string { 710 return fmt.Sprintf("%sAddUint32(&%s, 1)", atomicPackagePrefix(), counter) 711} 712 713// newCounter creates a new counter expression of the appropriate form. 714func (f *File) newCounter(start, end token.Pos, numStmt int) string { 715 var stmt string 716 if *pkgcfg != "" { 717 slot := len(f.fn.units) + coverage.FirstCtrOffset 718 if f.fn.counterVar == "" { 719 panic("internal error: counter var unset") 720 } 721 stmt = counterStmt(f, fmt.Sprintf("%s[%d]", f.fn.counterVar, slot)) 722 stpos := f.fset.Position(start) 723 enpos := f.fset.Position(end) 724 stpos, enpos = dedup(stpos, enpos) 725 unit := coverage.CoverableUnit{ 726 StLine: uint32(stpos.Line), 727 StCol: uint32(stpos.Column), 728 EnLine: uint32(enpos.Line), 729 EnCol: uint32(enpos.Column), 730 NxStmts: uint32(numStmt), 731 } 732 f.fn.units = append(f.fn.units, unit) 733 } else { 734 stmt = counterStmt(f, fmt.Sprintf("%s.Count[%d]", *varVar, 735 len(f.blocks))) 736 f.blocks = append(f.blocks, Block{start, end, numStmt}) 737 } 738 return stmt 739} 740 741// addCounters takes a list of statements and adds counters to the beginning of 742// each basic block at the top level of that list. For instance, given 743// 744// S1 745// if cond { 746// S2 747// } 748// S3 749// 750// counters will be added before S1 and before S3. The block containing S2 751// will be visited in a separate call. 752// TODO: Nested simple blocks get unnecessary (but correct) counters 753func (f *File) addCounters(pos, insertPos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) { 754 // Special case: make sure we add a counter to an empty block. Can't do this below 755 // or we will add a counter to an empty statement list after, say, a return statement. 756 if len(list) == 0 { 757 f.edit.Insert(f.offset(insertPos), f.newCounter(insertPos, blockEnd, 0)+";") 758 return 759 } 760 // Make a copy of the list, as we may mutate it and should leave the 761 // existing list intact. 762 list = append([]ast.Stmt(nil), list...) 763 // We have a block (statement list), but it may have several basic blocks due to the 764 // appearance of statements that affect the flow of control. 765 for { 766 // Find first statement that affects flow of control (break, continue, if, etc.). 767 // It will be the last statement of this basic block. 768 var last int 769 end := blockEnd 770 for last = 0; last < len(list); last++ { 771 stmt := list[last] 772 end = f.statementBoundary(stmt) 773 if f.endsBasicSourceBlock(stmt) { 774 // If it is a labeled statement, we need to place a counter between 775 // the label and its statement because it may be the target of a goto 776 // and thus start a basic block. That is, given 777 // foo: stmt 778 // we need to create 779 // foo: ; stmt 780 // and mark the label as a block-terminating statement. 781 // The result will then be 782 // foo: COUNTER[n]++; stmt 783 // However, we can't do this if the labeled statement is already 784 // a control statement, such as a labeled for. 785 if label, isLabel := stmt.(*ast.LabeledStmt); isLabel && !f.isControl(label.Stmt) { 786 newLabel := *label 787 newLabel.Stmt = &ast.EmptyStmt{ 788 Semicolon: label.Stmt.Pos(), 789 Implicit: true, 790 } 791 end = label.Pos() // Previous block ends before the label. 792 list[last] = &newLabel 793 // Open a gap and drop in the old statement, now without a label. 794 list = append(list, nil) 795 copy(list[last+1:], list[last:]) 796 list[last+1] = label.Stmt 797 } 798 last++ 799 extendToClosingBrace = false // Block is broken up now. 800 break 801 } 802 } 803 if extendToClosingBrace { 804 end = blockEnd 805 } 806 if pos != end { // Can have no source to cover if e.g. blocks abut. 807 f.edit.Insert(f.offset(insertPos), f.newCounter(pos, end, last)+";") 808 } 809 list = list[last:] 810 if len(list) == 0 { 811 break 812 } 813 pos = list[0].Pos() 814 insertPos = pos 815 } 816} 817 818// hasFuncLiteral reports the existence and position of the first func literal 819// in the node, if any. If a func literal appears, it usually marks the termination 820// of a basic block because the function body is itself a block. 821// Therefore we draw a line at the start of the body of the first function literal we find. 822// TODO: what if there's more than one? Probably doesn't matter much. 823func hasFuncLiteral(n ast.Node) (bool, token.Pos) { 824 if n == nil { 825 return false, 0 826 } 827 var literal funcLitFinder 828 ast.Walk(&literal, n) 829 return literal.found(), token.Pos(literal) 830} 831 832// statementBoundary finds the location in s that terminates the current basic 833// block in the source. 834func (f *File) statementBoundary(s ast.Stmt) token.Pos { 835 // Control flow statements are easy. 836 switch s := s.(type) { 837 case *ast.BlockStmt: 838 // Treat blocks like basic blocks to avoid overlapping counters. 839 return s.Lbrace 840 case *ast.IfStmt: 841 found, pos := hasFuncLiteral(s.Init) 842 if found { 843 return pos 844 } 845 found, pos = hasFuncLiteral(s.Cond) 846 if found { 847 return pos 848 } 849 return s.Body.Lbrace 850 case *ast.ForStmt: 851 found, pos := hasFuncLiteral(s.Init) 852 if found { 853 return pos 854 } 855 found, pos = hasFuncLiteral(s.Cond) 856 if found { 857 return pos 858 } 859 found, pos = hasFuncLiteral(s.Post) 860 if found { 861 return pos 862 } 863 return s.Body.Lbrace 864 case *ast.LabeledStmt: 865 return f.statementBoundary(s.Stmt) 866 case *ast.RangeStmt: 867 found, pos := hasFuncLiteral(s.X) 868 if found { 869 return pos 870 } 871 return s.Body.Lbrace 872 case *ast.SwitchStmt: 873 found, pos := hasFuncLiteral(s.Init) 874 if found { 875 return pos 876 } 877 found, pos = hasFuncLiteral(s.Tag) 878 if found { 879 return pos 880 } 881 return s.Body.Lbrace 882 case *ast.SelectStmt: 883 return s.Body.Lbrace 884 case *ast.TypeSwitchStmt: 885 found, pos := hasFuncLiteral(s.Init) 886 if found { 887 return pos 888 } 889 return s.Body.Lbrace 890 } 891 // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal. 892 // If it does, that's tricky because we want to exclude the body of the function from this block. 893 // Draw a line at the start of the body of the first function literal we find. 894 // TODO: what if there's more than one? Probably doesn't matter much. 895 found, pos := hasFuncLiteral(s) 896 if found { 897 return pos 898 } 899 return s.End() 900} 901 902// endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc., 903// or if it's just problematic, for instance contains a function literal, which will complicate 904// accounting due to the block-within-an expression. 905func (f *File) endsBasicSourceBlock(s ast.Stmt) bool { 906 switch s := s.(type) { 907 case *ast.BlockStmt: 908 // Treat blocks like basic blocks to avoid overlapping counters. 909 return true 910 case *ast.BranchStmt: 911 return true 912 case *ast.ForStmt: 913 return true 914 case *ast.IfStmt: 915 return true 916 case *ast.LabeledStmt: 917 return true // A goto may branch here, starting a new basic block. 918 case *ast.RangeStmt: 919 return true 920 case *ast.SwitchStmt: 921 return true 922 case *ast.SelectStmt: 923 return true 924 case *ast.TypeSwitchStmt: 925 return true 926 case *ast.ExprStmt: 927 // Calls to panic change the flow. 928 // We really should verify that "panic" is the predefined function, 929 // but without type checking we can't and the likelihood of it being 930 // an actual problem is vanishingly small. 931 if call, ok := s.X.(*ast.CallExpr); ok { 932 if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 { 933 return true 934 } 935 } 936 } 937 found, _ := hasFuncLiteral(s) 938 return found 939} 940 941// isControl reports whether s is a control statement that, if labeled, cannot be 942// separated from its label. 943func (f *File) isControl(s ast.Stmt) bool { 944 switch s.(type) { 945 case *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt: 946 return true 947 } 948 return false 949} 950 951// funcLitFinder implements the ast.Visitor pattern to find the location of any 952// function literal in a subtree. 953type funcLitFinder token.Pos 954 955func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) { 956 if f.found() { 957 return nil // Prune search. 958 } 959 switch n := node.(type) { 960 case *ast.FuncLit: 961 *f = funcLitFinder(n.Body.Lbrace) 962 return nil // Prune search. 963 } 964 return f 965} 966 967func (f *funcLitFinder) found() bool { 968 return token.Pos(*f) != token.NoPos 969} 970 971// Sort interface for []block1; used for self-check in addVariables. 972 973type block1 struct { 974 Block 975 index int 976} 977 978type blockSlice []block1 979 980func (b blockSlice) Len() int { return len(b) } 981func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte } 982func (b blockSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 983 984// offset translates a token position into a 0-indexed byte offset. 985func (f *File) offset(pos token.Pos) int { 986 return f.fset.Position(pos).Offset 987} 988 989// addVariables adds to the end of the file the declarations to set up the counter and position variables. 990func (f *File) addVariables(w io.Writer) { 991 if *pkgcfg != "" { 992 return 993 } 994 // Self-check: Verify that the instrumented basic blocks are disjoint. 995 t := make([]block1, len(f.blocks)) 996 for i := range f.blocks { 997 t[i].Block = f.blocks[i] 998 t[i].index = i 999 } 1000 sort.Sort(blockSlice(t)) 1001 for i := 1; i < len(t); i++ { 1002 if t[i-1].endByte > t[i].startByte { 1003 fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index) 1004 // Note: error message is in byte positions, not token positions. 1005 fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n", 1006 f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte), 1007 f.name, f.offset(t[i].startByte), f.offset(t[i].endByte)) 1008 } 1009 } 1010 1011 // Declare the coverage struct as a package-level variable. 1012 fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar) 1013 fmt.Fprintf(w, "\tCount [%d]uint32\n", len(f.blocks)) 1014 fmt.Fprintf(w, "\tPos [3 * %d]uint32\n", len(f.blocks)) 1015 fmt.Fprintf(w, "\tNumStmt [%d]uint16\n", len(f.blocks)) 1016 fmt.Fprintf(w, "} {\n") 1017 1018 // Initialize the position array field. 1019 fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks)) 1020 1021 // A nice long list of positions. Each position is encoded as follows to reduce size: 1022 // - 32-bit starting line number 1023 // - 32-bit ending line number 1024 // - (16 bit ending column number << 16) | (16-bit starting column number). 1025 for i, block := range f.blocks { 1026 start := f.fset.Position(block.startByte) 1027 end := f.fset.Position(block.endByte) 1028 1029 start, end = dedup(start, end) 1030 1031 fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i) 1032 } 1033 1034 // Close the position array. 1035 fmt.Fprintf(w, "\t},\n") 1036 1037 // Initialize the position array field. 1038 fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks)) 1039 1040 // A nice long list of statements-per-block, so we can give a conventional 1041 // valuation of "percent covered". To save space, it's a 16-bit number, so we 1042 // clamp it if it overflows - won't matter in practice. 1043 for i, block := range f.blocks { 1044 n := block.numStmt 1045 if n > 1<<16-1 { 1046 n = 1<<16 - 1 1047 } 1048 fmt.Fprintf(w, "\t\t%d, // %d\n", n, i) 1049 } 1050 1051 // Close the statements-per-block array. 1052 fmt.Fprintf(w, "\t},\n") 1053 1054 // Close the struct initialization. 1055 fmt.Fprintf(w, "}\n") 1056} 1057 1058// It is possible for positions to repeat when there is a line 1059// directive that does not specify column information and the input 1060// has not been passed through gofmt. 1061// See issues #27530 and #30746. 1062// Tests are TestHtmlUnformatted and TestLineDup. 1063// We use a map to avoid duplicates. 1064 1065// pos2 is a pair of token.Position values, used as a map key type. 1066type pos2 struct { 1067 p1, p2 token.Position 1068} 1069 1070// seenPos2 tracks whether we have seen a token.Position pair. 1071var seenPos2 = make(map[pos2]bool) 1072 1073// dedup takes a token.Position pair and returns a pair that does not 1074// duplicate any existing pair. The returned pair will have the Offset 1075// fields cleared. 1076func dedup(p1, p2 token.Position) (r1, r2 token.Position) { 1077 key := pos2{ 1078 p1: p1, 1079 p2: p2, 1080 } 1081 1082 // We want to ignore the Offset fields in the map, 1083 // since cover uses only file/line/column. 1084 key.p1.Offset = 0 1085 key.p2.Offset = 0 1086 1087 for seenPos2[key] { 1088 key.p2.Column++ 1089 } 1090 seenPos2[key] = true 1091 1092 return key.p1, key.p2 1093} 1094 1095func (p *Package) emitMetaData(w io.Writer) { 1096 if *pkgcfg == "" { 1097 return 1098 } 1099 1100 // If the "EmitMetaFile" path has been set, invoke a helper 1101 // that will write out a pre-cooked meta-data file for this package 1102 // to the specified location, in effect simulating the execution 1103 // of a test binary that doesn't do any testing to speak of. 1104 if pkgconfig.EmitMetaFile != "" { 1105 p.emitMetaFile(pkgconfig.EmitMetaFile) 1106 } 1107 1108 // Something went wrong if regonly/testmain mode is in effect and 1109 // we have instrumented functions. 1110 if counterStmt == nil && len(p.counterLengths) != 0 { 1111 panic("internal error: seen functions with regonly/testmain") 1112 } 1113 1114 // Emit package name. 1115 fmt.Fprintf(w, "\npackage %s\n\n", pkgconfig.PkgName) 1116 1117 // Emit package ID var. 1118 fmt.Fprintf(w, "\nvar %sP uint32\n", *varVar) 1119 1120 // Emit all of the counter variables. 1121 for k := range p.counterLengths { 1122 cvn := mkCounterVarName(k) 1123 fmt.Fprintf(w, "var %s [%d]uint32\n", cvn, p.counterLengths[k]) 1124 } 1125 1126 // Emit encoded meta-data. 1127 var sws slicewriter.WriteSeeker 1128 digest, err := p.mdb.Emit(&sws) 1129 if err != nil { 1130 log.Fatalf("encoding meta-data: %v", err) 1131 } 1132 p.mdb = nil 1133 fmt.Fprintf(w, "var %s = [...]byte{\n", mkMetaVar()) 1134 payload := sws.BytesWritten() 1135 for k, b := range payload { 1136 fmt.Fprintf(w, " 0x%x,", b) 1137 if k != 0 && k%8 == 0 { 1138 fmt.Fprintf(w, "\n") 1139 } 1140 } 1141 fmt.Fprintf(w, "}\n") 1142 1143 fixcfg := covcmd.CoverFixupConfig{ 1144 Strategy: "normal", 1145 MetaVar: mkMetaVar(), 1146 MetaLen: len(payload), 1147 MetaHash: fmt.Sprintf("%x", digest), 1148 PkgIdVar: mkPackageIdVar(), 1149 CounterPrefix: *varVar, 1150 CounterGranularity: pkgconfig.Granularity, 1151 CounterMode: *mode, 1152 } 1153 fixdata, err := json.Marshal(fixcfg) 1154 if err != nil { 1155 log.Fatalf("marshal fixupcfg: %v", err) 1156 } 1157 if err := os.WriteFile(pkgconfig.OutConfig, fixdata, 0666); err != nil { 1158 log.Fatalf("error writing %s: %v", pkgconfig.OutConfig, err) 1159 } 1160} 1161 1162// atomicOnAtomic returns true if we're instrumenting 1163// the sync/atomic package AND using atomic mode. 1164func atomicOnAtomic() bool { 1165 return *mode == "atomic" && pkgconfig.PkgPath == "sync/atomic" 1166} 1167 1168// atomicPackagePrefix returns the import path prefix used to refer to 1169// our special import of sync/atomic; this is either set to the 1170// constant atomicPackageName plus a dot or the empty string if we're 1171// instrumenting the sync/atomic package itself. 1172func atomicPackagePrefix() string { 1173 if atomicOnAtomic() { 1174 return "" 1175 } 1176 return atomicPackageName + "." 1177} 1178 1179func (p *Package) emitMetaFile(outpath string) { 1180 // Open output file. 1181 of, err := os.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) 1182 if err != nil { 1183 log.Fatalf("opening covmeta %s: %v", outpath, err) 1184 } 1185 1186 if len(p.counterLengths) == 0 { 1187 // This corresponds to the case where we have no functions 1188 // in the package to instrument. Leave the file empty file if 1189 // this happens. 1190 if err = of.Close(); err != nil { 1191 log.Fatalf("closing meta-data file: %v", err) 1192 } 1193 return 1194 } 1195 1196 // Encode meta-data. 1197 var sws slicewriter.WriteSeeker 1198 digest, err := p.mdb.Emit(&sws) 1199 if err != nil { 1200 log.Fatalf("encoding meta-data: %v", err) 1201 } 1202 payload := sws.BytesWritten() 1203 blobs := [][]byte{payload} 1204 1205 // Write meta-data file directly. 1206 mfw := encodemeta.NewCoverageMetaFileWriter(outpath, of) 1207 err = mfw.Write(digest, blobs, cmode, cgran) 1208 if err != nil { 1209 log.Fatalf("writing meta-data file: %v", err) 1210 } 1211 if err = of.Close(); err != nil { 1212 log.Fatalf("closing meta-data file: %v", err) 1213 } 1214} 1215