Example #1
0
func TestStdlib(t *testing.T) {
	// Load, parse and type-check the program.
	t0 := time.Now()
	alloc0 := bytesAllocated()

	// Load, parse and type-check the program.
	ctxt := build.Default // copy
	ctxt.GOPATH = ""      // disable GOPATH
	conf := loader.Config{Build: &ctxt}
	if _, err := conf.FromArgs(buildutil.AllPackages(conf.Build), true); err != nil {
		t.Errorf("FromArgs failed: %v", err)
		return
	}

	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %v", err)
	}

	t1 := time.Now()
	alloc1 := bytesAllocated()

	// Create SSA packages.
	var mode ssa.BuilderMode
	// Comment out these lines during benchmarking.  Approx SSA build costs are noted.
	mode |= ssa.SanityCheckFunctions // + 2% space, + 4% time
	mode |= ssa.GlobalDebug          // +30% space, +18% time
	prog := ssa.Create(iprog, mode)

	t2 := time.Now()

	// Build SSA.
	prog.BuildAll()

	t3 := time.Now()
	alloc3 := bytesAllocated()

	numPkgs := len(prog.AllPackages())
	if want := 140; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Keep iprog reachable until after we've measured memory usage.
	if len(iprog.AllPackages) == 0 {
		print() // unreachable
	}

	allFuncs := ssautil.AllFunctions(prog)

	// Check that all non-synthetic functions have distinct names.
	// Synthetic wrappers for exported methods should be distinct too,
	// except for unexported ones (explained at (*Function).RelString).
	byName := make(map[string]*ssa.Function)
	for fn := range allFuncs {
		if fn.Synthetic == "" || ast.IsExported(fn.Name()) {
			str := fn.String()
			prev := byName[str]
			byName[str] = fn
			if prev != nil {
				t.Errorf("%s: duplicate function named %s",
					prog.Fset.Position(fn.Pos()), str)
				t.Errorf("%s:   (previously defined here)",
					prog.Fset.Position(prev.Pos()))
			}
		}
	}

	// Dump some statistics.
	var numInstrs int
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			numInstrs += len(b.Instrs)
		}
	}

	// determine line count
	var lineCount int
	prog.Fset.Iterate(func(f *token.File) bool {
		lineCount += f.LineCount()
		return true
	})

	// NB: when benchmarking, don't forget to clear the debug +
	// sanity builder flags for better performance.

	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
	t.Log("#Source lines:        ", lineCount)
	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
	t.Log("SSA create:           ", t2.Sub(t1))
	t.Log("SSA build:            ", t3.Sub(t2))

	// SSA stats:
	t.Log("#Packages:            ", numPkgs)
	t.Log("#Functions:           ", len(allFuncs))
	t.Log("#Instructions:        ", numInstrs)
	t.Log("#MB AST+types:        ", int64(alloc1-alloc0)/1e6)
	t.Log("#MB SSA:              ", int64(alloc3-alloc1)/1e6)
}
Example #2
0
func TestStdlib(t *testing.T) {
	if !*runStdlibTest {
		t.Skip("skipping (slow) stdlib test (use --stdlib)")
	}

	// Load, parse and type-check the program.
	ctxt := build.Default // copy
	ctxt.GOPATH = ""      // disable GOPATH
	conf := loader.Config{Build: &ctxt}
	if _, err := conf.FromArgs(buildutil.AllPackages(conf.Build), true); err != nil {
		t.Errorf("FromArgs failed: %v", err)
		return
	}

	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %v", err)
	}

	// Create SSA packages.
	prog := ssa.Create(iprog, 0)
	prog.BuildAll()

	numPkgs := len(prog.AllPackages())
	if want := 240; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Determine the set of packages/tests to analyze.
	var testPkgs []*ssa.Package
	for _, info := range iprog.InitialPackages() {
		testPkgs = append(testPkgs, prog.Package(info.Pkg))
	}
	testmain := prog.CreateTestMainPackage(testPkgs...)
	if testmain == nil {
		t.Fatal("analysis scope has tests")
	}

	// Run the analysis.
	config := &Config{
		Reflection:     false, // TODO(adonovan): fix remaining bug in rVCallConstraint, then enable.
		BuildCallGraph: true,
		Mains:          []*ssa.Package{testmain},
	}
	// TODO(adonovan): add some query values (affects track bits).

	t0 := time.Now()

	result, err := Analyze(config)
	if err != nil {
		t.Fatal(err) // internal error in pointer analysis
	}
	_ = result // TODO(adonovan): measure something

	t1 := time.Now()

	// Dump some statistics.
	allFuncs := ssautil.AllFunctions(prog)
	var numInstrs int
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			numInstrs += len(b.Instrs)
		}
	}

	// determine line count
	var lineCount int
	prog.Fset.Iterate(func(f *token.File) bool {
		lineCount += f.LineCount()
		return true
	})

	t.Log("#Source lines:          ", lineCount)
	t.Log("#Instructions:          ", numInstrs)
	t.Log("Pointer analysis:       ", t1.Sub(t0))
}
Example #3
0
func run(t *testing.T, dir, input string, success successPredicate) bool {
	fmt.Printf("Input: %s\n", input)

	start := time.Now()

	var inputs []string
	for _, i := range strings.Split(input, " ") {
		if strings.HasSuffix(i, ".go") {
			i = dir + i
		}
		inputs = append(inputs, i)
	}

	var conf loader.Config
	if _, err := conf.FromArgs(inputs, true); err != nil {
		t.Errorf("FromArgs(%s) failed: %s", inputs, err)
		return false
	}

	conf.Import("runtime")

	// Print a helpful hint if we don't make it to the end.
	var hint string
	defer func() {
		if hint != "" {
			fmt.Println("FAIL")
			fmt.Println(hint)
		} else {
			fmt.Println("PASS")
		}

		interp.CapturedOutput = nil
	}()

	hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)

	iprog, err := conf.Load()
	if err != nil {
		t.Errorf("conf.Load(%s) failed: %s", inputs, err)
		return false
	}

	prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
	prog.BuildAll()

	var mainPkg *ssa.Package
	var initialPkgs []*ssa.Package
	for _, info := range iprog.InitialPackages() {
		if info.Pkg.Path() == "runtime" {
			continue // not an initial package
		}
		p := prog.Package(info.Pkg)
		initialPkgs = append(initialPkgs, p)
		if mainPkg == nil && p.Func("main") != nil {
			mainPkg = p
		}
	}
	if mainPkg == nil {
		testmainPkg := prog.CreateTestMainPackage(initialPkgs...)
		if testmainPkg == nil {
			t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg)
			return false
		}
		if testmainPkg.Func("main") == nil {
			t.Errorf("synthetic testmain package has no main")
			return false
		}
		mainPkg = testmainPkg
	}

	var out bytes.Buffer
	interp.CapturedOutput = &out

	hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input)
	exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{})

	// The definition of success varies with each file.
	if err := success(exitCode, out.String()); err != nil {
		t.Errorf("interp.Interpret(%s) failed: %s", inputs, err)
		return false
	}

	hint = "" // call off the hounds

	if false {
		fmt.Println(input, time.Since(start)) // test profiling
	}

	return true
}
Example #4
0
// TestFromArgs checks that conf.FromArgs populates conf correctly.
// It does no I/O.
func TestFromArgs(t *testing.T) {
	type result struct {
		Err        string
		Rest       []string
		ImportPkgs map[string]bool
		CreatePkgs []loader.PkgSpec
	}
	for _, test := range []struct {
		args  []string
		tests bool
		want  result
	}{
		// Mix of existing and non-existent packages.
		{
			args: []string{"nosuchpkg", "errors"},
			want: result{
				ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false},
			},
		},
		// Same, with -test flag.
		{
			args:  []string{"nosuchpkg", "errors"},
			tests: true,
			want: result{
				ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true},
			},
		},
		// Surplus arguments.
		{
			args: []string{"fmt", "errors", "--", "surplus"},
			want: result{
				Rest:       []string{"surplus"},
				ImportPkgs: map[string]bool{"errors": false, "fmt": false},
			},
		},
		// Ad hoc package specified as *.go files.
		{
			args: []string{"foo.go", "bar.go"},
			want: result{CreatePkgs: []loader.PkgSpec{{
				Filenames: []string{"foo.go", "bar.go"},
			}}},
		},
		// Mixture of *.go and import paths.
		{
			args: []string{"foo.go", "fmt"},
			want: result{
				Err: "named files must be .go files: fmt",
			},
		},
	} {
		var conf loader.Config
		rest, err := conf.FromArgs(test.args, test.tests)
		got := result{
			Rest:       rest,
			ImportPkgs: conf.ImportPkgs,
			CreatePkgs: conf.CreatePkgs,
		}
		if err != nil {
			got.Err = err.Error()
		}
		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want)
		}
	}
}