@ -12,6 +12,7 @@ import (
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"path/filepath" 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"regexp" 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"runtime" 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"strconv" 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"strings" 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"testing" 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					"time" 
 
			
		 
		
	
	
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
				
			
			 
			 
			
				@ -59,7 +60,7 @@ func BenchmarkBuild(b *testing.B) {
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					qt . Assert ( b ,  err ,  qt . IsNil ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					// We collect extra metrics.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					var  cachedTime,  user  Time,  systemTime  int64 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					var  memoryAllocs,  cached  Time,  systemTime  int64 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					outputBin  :=  filepath . Join ( b . TempDir ( ) ,  "output" ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					sourceDir  :=  filepath . Join ( b . TempDir ( ) ,  "src" ) 
 
			
		 
		
	
	
		
			
				
					
						
						
						
							
								 
							 
						
					 
				
			
			 
			 
			
				@ -73,46 +74,69 @@ func BenchmarkBuild(b *testing.B) {
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					writeSourceFile ( "go.mod" ,  [ ] byte ( "module test/main" ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					writeSourceFile ( "main.go" ,  benchSourceMain ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					rxGarbleAllocs  :=  regexp . MustCompile ( ` (?m)^garble allocs: ([0-9]+) ` ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					b . ResetTimer ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					b . StopTimer ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					for  i  :=  0 ;  i  <  b . N ;  i ++  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						// First we do a fresh build, using a new GOCACHE.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						// and the second does an incremental rebuild reusing the cache.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						gocache ,  err  :=  os . MkdirTemp ( b . TempDir ( ) ,  "gocache-*" ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  err ,  qt . IsNil ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						env  :=  append ( os . Environ ( ) ,  "GOGARBLE=*" ,  "GOCACHE=" + gocache ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						env  :=  append ( os . Environ ( ) , 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							"GOGARBLE=*" , 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							"GOCACHE=" + gocache , 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							"GARBLE_WRITE_ALLOCS=true" , 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						args  :=  [ ] string { "build" ,  "-v" ,  "-o="  +  outputBin ,  sourceDir } 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cmd  :=  exec . Command ( garbleBin ,  args ... ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cmd . Env  =  env 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cmd . Dir  =  sourceDir 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						out ,  err  :=  cmd . CombinedOutput ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  err ,  qt . IsNil ,  qt . Commentf ( "output: %s" ,  out ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						// Ensure that we built all packages, as expected.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  rxBuiltRuntime . Match ( out ) ,  qt . IsTrue ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  rxBuiltMain . Match ( out ) ,  qt . IsTrue ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						// The cached rebuild will reuse all dependencies,
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						// but rebuild the main package itself.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cachedStart  :=  time . Now ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						writeSourceFile ( "rebuild.go" ,  [ ] byte ( fmt . Sprintf ( "package main\nvar v%d int" ,  i ) ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cmd  =  exec . Command ( garbleBin ,  args ... ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cmd . Env  =  env 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cmd . Dir  =  sourceDir 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						out ,  err  =  cmd . CombinedOutput ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  err ,  qt . IsNil ,  qt . Commentf ( "output: %s" ,  out ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						// Ensure that we only rebuilt the main package, as expected.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  rxBuiltRuntime . Match ( out ) ,  qt . IsFalse ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						qt . Assert ( b ,  rxBuiltMain . Match ( out ) ,  qt . IsTrue ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						cachedTime  +=  time . Since ( cachedStart ) . Nanoseconds ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						userTime  +=  int64 ( cmd . ProcessState . UserTime ( ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						systemTime  +=  int64 ( cmd . ProcessState . SystemTime ( ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						for  _ ,  cached  :=  range  [ ] bool { false ,  true }  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							// The cached rebuild will reuse all dependencies,
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							// but rebuild the main package itself.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							if  cached  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								writeSourceFile ( "rebuild.go" ,  [ ] byte ( fmt . Sprintf ( "package main\nvar v%d int" ,  i ) ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							cmd  :=  exec . Command ( garbleBin ,  args ... ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							cmd . Env  =  env 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							cmd . Dir  =  sourceDir 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							cachedStart  :=  time . Now ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							b . StartTimer ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							out ,  err  :=  cmd . CombinedOutput ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							b . StopTimer ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							if  cached  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								cachedTime  +=  time . Since ( cachedStart ) . Nanoseconds ( ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							qt . Assert ( b ,  err ,  qt . IsNil ,  qt . Commentf ( "output: %s" ,  out ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							if  ! cached  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								// Ensure that we built all packages, as expected.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								qt . Assert ( b ,  rxBuiltRuntime . Match ( out ) ,  qt . IsTrue ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							}  else  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								// Ensure that we only rebuilt the main package, as expected.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								qt . Assert ( b ,  rxBuiltRuntime . Match ( out ) ,  qt . IsFalse ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							qt . Assert ( b ,  rxBuiltMain . Match ( out ) ,  qt . IsTrue ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							matches  :=  rxGarbleAllocs . FindAllSubmatch ( out ,  - 1 ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							if  ! cached  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								// The non-cached version should have at least a handful of
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								// sub-processes; catch if our logic breaks.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								qt . Assert ( b ,  len ( matches )  >  5 ,  qt . IsTrue ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							for  _ ,  match  :=  range  matches  { 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								allocs ,  err  :=  strconv . ParseInt ( string ( match [ 1 ] ) ,  10 ,  64 ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								qt . Assert ( b ,  err ,  qt . IsNil ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
								memoryAllocs  +=  allocs 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
							systemTime  +=  int64 ( cmd . ProcessState . SystemTime ( ) ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
						} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					} 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					// We can't use "allocs/op" as it's reserved for ReportAllocs.
 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					b . ReportMetric ( float64 ( memoryAllocs ) / float64 ( b . N ) ,  "mallocs/op" ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					b . ReportMetric ( float64 ( cachedTime ) / float64 ( b . N ) ,  "cached-ns/op" ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					b . ReportMetric ( float64 ( userTime ) / float64 ( b . N ) ,  "user-ns/op" ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					b . ReportMetric ( float64 ( systemTime ) / float64 ( b . N ) ,  "sys-ns/op" ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					info ,  err  :=  os . Stat ( outputBin ) 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
					if  err  !=  nil  {