github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/ssa/stdlib_test.go (about)

     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  
     5  // Incomplete source tree on Android.
     6  
     7  // +build !android
     8  
     9  package ssa_test
    10  
    11  // This file runs the SSA builder in sanity-checking mode on all
    12  // packages beneath $GOROOT and prints some summary information.
    13  //
    14  // Run with "go test -cpu=8 to" set GOMAXPROCS.
    15  
    16  import (
    17  	"go/ast"
    18  	"go/build"
    19  	"go/token"
    20  	"runtime"
    21  	"testing"
    22  	"time"
    23  
    24  	"golang.org/x/tools/go/buildutil"
    25  	"golang.org/x/tools/go/loader"
    26  	"github.com/golangci/go-tools/ssa"
    27  	"github.com/golangci/go-tools/ssa/ssautil"
    28  )
    29  
    30  // Skip the set of packages that transitively depend on
    31  // cmd/internal/objfile, which uses vendoring,
    32  // which go/loader does not yet support.
    33  // TODO(adonovan): add support for vendoring and delete this.
    34  var skip = map[string]bool{
    35  	"cmd/addr2line":        true,
    36  	"cmd/internal/objfile": true,
    37  	"cmd/nm":               true,
    38  	"cmd/objdump":          true,
    39  	"cmd/pprof":            true,
    40  }
    41  
    42  func bytesAllocated() uint64 {
    43  	runtime.GC()
    44  	var stats runtime.MemStats
    45  	runtime.ReadMemStats(&stats)
    46  	return stats.Alloc
    47  }
    48  
    49  func TestStdlib(t *testing.T) {
    50  	if testing.Short() {
    51  		t.Skip("skipping in short mode; too slow (golang.org/issue/14113)")
    52  	}
    53  	// Load, parse and type-check the program.
    54  	t0 := time.Now()
    55  	alloc0 := bytesAllocated()
    56  
    57  	// Load, parse and type-check the program.
    58  	ctxt := build.Default // copy
    59  	ctxt.GOPATH = ""      // disable GOPATH
    60  	conf := loader.Config{Build: &ctxt}
    61  	for _, path := range buildutil.AllPackages(conf.Build) {
    62  		if skip[path] {
    63  			continue
    64  		}
    65  		conf.ImportWithTests(path)
    66  	}
    67  
    68  	iprog, err := conf.Load()
    69  	if err != nil {
    70  		t.Fatalf("Load failed: %v", err)
    71  	}
    72  
    73  	t1 := time.Now()
    74  	alloc1 := bytesAllocated()
    75  
    76  	// Create SSA packages.
    77  	var mode ssa.BuilderMode
    78  	// Comment out these lines during benchmarking.  Approx SSA build costs are noted.
    79  	mode |= ssa.SanityCheckFunctions // + 2% space, + 4% time
    80  	mode |= ssa.GlobalDebug          // +30% space, +18% time
    81  	prog := ssautil.CreateProgram(iprog, mode)
    82  
    83  	t2 := time.Now()
    84  
    85  	// Build SSA.
    86  	prog.Build()
    87  
    88  	t3 := time.Now()
    89  	alloc3 := bytesAllocated()
    90  
    91  	numPkgs := len(prog.AllPackages())
    92  	if want := 140; numPkgs < want {
    93  		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
    94  	}
    95  
    96  	// Keep iprog reachable until after we've measured memory usage.
    97  	if len(iprog.AllPackages) == 0 {
    98  		panic("unreachable")
    99  	}
   100  
   101  	allFuncs := ssautil.AllFunctions(prog)
   102  
   103  	// Check that all non-synthetic functions have distinct names.
   104  	// Synthetic wrappers for exported methods should be distinct too,
   105  	// except for unexported ones (explained at (*Function).RelString).
   106  	byName := make(map[string]*ssa.Function)
   107  	for fn := range allFuncs {
   108  		if fn.Synthetic == "" || ast.IsExported(fn.Name()) {
   109  			str := fn.String()
   110  			prev := byName[str]
   111  			byName[str] = fn
   112  			if prev != nil {
   113  				t.Errorf("%s: duplicate function named %s",
   114  					prog.Fset.Position(fn.Pos()), str)
   115  				t.Errorf("%s:   (previously defined here)",
   116  					prog.Fset.Position(prev.Pos()))
   117  			}
   118  		}
   119  	}
   120  
   121  	// Dump some statistics.
   122  	var numInstrs int
   123  	for fn := range allFuncs {
   124  		for _, b := range fn.Blocks {
   125  			numInstrs += len(b.Instrs)
   126  		}
   127  	}
   128  
   129  	// determine line count
   130  	var lineCount int
   131  	prog.Fset.Iterate(func(f *token.File) bool {
   132  		lineCount += f.LineCount()
   133  		return true
   134  	})
   135  
   136  	// NB: when benchmarking, don't forget to clear the debug +
   137  	// sanity builder flags for better performance.
   138  
   139  	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
   140  	t.Log("#Source lines:        ", lineCount)
   141  	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
   142  	t.Log("SSA create:           ", t2.Sub(t1))
   143  	t.Log("SSA build:            ", t3.Sub(t2))
   144  
   145  	// SSA stats:
   146  	t.Log("#Packages:            ", numPkgs)
   147  	t.Log("#Functions:           ", len(allFuncs))
   148  	t.Log("#Instructions:        ", numInstrs)
   149  	t.Log("#MB AST+types:        ", int64(alloc1-alloc0)/1e6)
   150  	t.Log("#MB SSA:              ", int64(alloc3-alloc1)/1e6)
   151  }