github.com/distbuild/reclient@v0.0.0-20240401075343-3de72e395564/experiments/cmd/exprunner/main.go (about)

     1  // Copyright 2023 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package main is the main package for the experiment exprunner binary.
    16  //
    17  // Use the exprunner tool to run experiments defined in an experiment textproto
    18  // file, tabulate them into a bigquery table, and upload results to perfgate.
    19  // Example:
    20  // bazelisk run //experiments/cmd/exprunner:exprunner -- --exp_def_path=$PWD/experiments/examples/chrome-build.textproto --perfgate_path="reclient_perfgate" --benchmark_path=$PWD/tests/performance/benchmarks/chrome-perfgate.config
    21  //
    22  // To view logging info as the experiment is running, run with --alsologtostderr.
    23  package main
    24  
    25  import (
    26  	"flag"
    27  	"os"
    28  	"path/filepath"
    29  	"text/template"
    30  
    31  	"github.com/bazelbuild/reclient/experiments/internal/pkg/perfgate"
    32  	"github.com/bazelbuild/reclient/experiments/internal/pkg/runner"
    33  	"github.com/bazelbuild/reclient/experiments/internal/pkg/tabulator"
    34  
    35  	"github.com/bazelbuild/remote-apis-sdks/go/pkg/moreflag"
    36  
    37  	log "github.com/golang/glog"
    38  )
    39  
    40  var (
    41  	gcpProject    = flag.String("gcp_project", "foundry-x-experiments", "Name of the GCP project to run experiments on. Defaults to foundry-x-experiments.")
    42  	resBucket     = flag.String("results_bucket", "foundry-x-experiments-results", "Name of the GCS bucket to store results. Defaults to foundry-x-experiments-results.")
    43  	expDefPath    = flag.String("exp_def_path", "", "Path to the .textproto file containing the experiment definition. If none specified, does not run. If exp_def_vars is provided then this file will be parsed as a text/template.")
    44  	dateSuffix    = flag.String("date_suffix", "", "Date string suffix to use instead of checking the current time. If none specified, the current time will be used.")
    45  	perfgatePath  = flag.String("perfgate_path", "", "Path to the perfgate wrapper binary. If none specified, results will not be uploaded to perfgate.")
    46  	benchmarkFile = flag.String("benchmark_path", "", "Path to the BenchmarkInfo file containing metric definitions.")
    47  	logFrequency  = flag.Int("log_frequency", -1, "Time between polling the log file for updates, in seconds. Default 5 for virtual machine experiments, 0 (disabled) for workstation experiments")
    48  	expDefVars    map[string]string
    49  )
    50  
    51  func main() {
    52  	flag.Var((*moreflag.StringMapValue)(&expDefVars), "exp_def_vars", "Comma-separated key value pairs in the form key=value to be used in exp_def_path file in the form {{.key}}.")
    53  
    54  	flag.Parse()
    55  	if *expDefPath == "" {
    56  		log.Fatal("exp_def_path cannot be empty.")
    57  	}
    58  	if len(expDefVars) > 0 {
    59  		filledTemplate := generateExpDefFromTemplate(*expDefPath, expDefVars)
    60  		defer os.Remove(filledTemplate)
    61  		*expDefPath = filledTemplate
    62  	}
    63  	log.Infof("Running experiment: %v", *expDefPath)
    64  	expName, err := runner.RunExperiment(*expDefPath, *dateSuffix, *gcpProject, *resBucket, *logFrequency)
    65  	if err != nil {
    66  		log.Fatalf("Error running experiment: %v", err)
    67  	}
    68  	log.Infof("Running tabulator on experiment: %v", expName)
    69  	if err := tabulator.RunExperimentResultCollector(*resBucket, expName, *gcpProject); err != nil {
    70  		log.Fatalf("Error running tabulator: %v", err)
    71  	}
    72  	log.Infof("Done tabulating results for %v", expName)
    73  	if *perfgatePath != "" {
    74  		log.Infof("Uploading results to perfgate using: %v", *perfgatePath)
    75  		if err := perfgate.RunUploader(*expDefPath, *resBucket, expName, *gcpProject, *perfgatePath, *benchmarkFile); err != nil {
    76  			log.Fatalf("Error running perfgate: %v", err)
    77  		}
    78  	}
    79  }
    80  
    81  func generateExpDefFromTemplate(tmplPath string, tmplMap map[string]string) string {
    82  	log.Infof("Populating template")
    83  	f, err := os.CreateTemp("", filepath.Base(tmplPath))
    84  	defer f.Close()
    85  	if err != nil {
    86  		log.Fatalf("Error creating tmp file: %w", err)
    87  	}
    88  	tmpl, err := template.ParseFiles(tmplPath)
    89  	if err != nil {
    90  		log.Fatalf("Error parsing template file: %w", err)
    91  	}
    92  	err = tmpl.Execute(f, tmplMap)
    93  	if err != nil {
    94  		log.Fatalf("Error generating experiment definition file from template: %w")
    95  	}
    96  	return f.Name()
    97  }