// Copyright 2020 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Binary parser parses Benchmark data from golang benchmarks, // puts it into a Schema for BigQuery, and sends it to BigQuery. // parser will also initialize a table with the Benchmarks BigQuery schema. package main import ( "context" "fmt" "io/ioutil" "log" "os" "gvisor.dev/gvisor/runsc/flag" bq "gvisor.dev/gvisor/tools/bigquery" "gvisor.dev/gvisor/tools/parsers" ) const ( initString = "init" initDescription = "initializes a new table with benchmarks schema" parseString = "parse" parseDescription = "parses given benchmarks file and sends it to BigQuery table." ) var ( // The init command will create a new dataset/table in the given project and initialize // the table with the schema in //tools/bigquery/bigquery.go. If the table/dataset exists // or has been initialized, init has no effect and successfully returns. initCmd = flag.NewFlagSet(initString, flag.ContinueOnError) initProject = initCmd.String("project", "", "GCP project to send benchmarks.") initDataset = initCmd.String("dataset", "", "dataset to send benchmarks data.") initTable = initCmd.String("table", "", "table to send benchmarks data.") // The parse command parses benchmark data in `file` and sends it to the // requested table. parseCmd = flag.NewFlagSet(parseString, flag.ContinueOnError) file = parseCmd.String("file", "", "file to parse for benchmarks") name = parseCmd.String("suite_name", "", "name of the benchmark suite") parseProject = parseCmd.String("project", "", "GCP project to send benchmarks.") parseDataset = parseCmd.String("dataset", "", "dataset to send benchmarks data.") parseTable = parseCmd.String("table", "", "table to send benchmarks data.") official = parseCmd.Bool("official", false, "mark input data as official.") runtime = parseCmd.String("runtime", "", "runtime used to run the benchmark") debug = parseCmd.Bool("debug", false, "print debug logs") ) // initBenchmarks initializes a dataset/table in a BigQuery project. func initBenchmarks(ctx context.Context) error { return bq.InitBigQuery(ctx, *initProject, *initDataset, *initTable, nil) } // parseBenchmarks parses the given file into the BigQuery schema, // adds some custom data for the commit, and sends the data to BigQuery. func parseBenchmarks(ctx context.Context) error { debugLog("Reading file: %s", *file) data, err := ioutil.ReadFile(*file) if err != nil { return fmt.Errorf("failed to read file %s: %v", *file, err) } debugLog("Parsing output: %s", string(data)) suite, err := parsers.ParseOutput(string(data), *name, *official) if err != nil { return fmt.Errorf("failed parse data: %v", err) } debugLog("Parsed benchmarks: %d", len(suite.Benchmarks)) if len(suite.Benchmarks) < 1 { fmt.Fprintf(os.Stderr, "Failed to find benchmarks for file: %s", *file) return nil } extraConditions := []*bq.Condition{ { Name: "runtime", Value: *runtime, }, { Name: "version", Value: version, }, } suite.Official = *official suite.Conditions = append(suite.Conditions, extraConditions...) debugLog("Sending benchmarks") return bq.SendBenchmarks(ctx, suite, *parseProject, *parseDataset, *parseTable, nil) } func main() { ctx := context.Background() switch { // the "init" command case len(os.Args) >= 2 && os.Args[1] == initString: if err := initCmd.Parse(os.Args[2:]); err != nil { log.Fatalf("Failed parse flags: %v\n", err) os.Exit(1) } if err := initBenchmarks(ctx); err != nil { failure := "failed to initialize project: %s dataset: %s table: %s: %v\n" log.Fatalf(failure, *parseProject, *parseDataset, *parseTable, err) os.Exit(1) } // the "parse" command. case len(os.Args) >= 2 && os.Args[1] == parseString: if err := parseCmd.Parse(os.Args[2:]); err != nil { log.Fatalf("Failed parse flags: %v\n", err) os.Exit(1) } if err := parseBenchmarks(ctx); err != nil { log.Fatalf("Failed parse benchmarks: %v\n", err) os.Exit(1) } default: printUsage() os.Exit(1) } } // printUsage prints the top level usage string. func printUsage() { usage := `Usage: parser ... Available commands: %s %s %s %s ` log.Printf(usage, initCmd.Name(), initDescription, parseCmd.Name(), parseDescription) } func debugLog(msg string, args ...interface{}) { if *debug { log.Printf(msg, args...) } }