gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/benchmarks/tools/parser_util.go (about) 1 // Copyright 2020 The gVisor Authors. 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 tools 16 17 import ( 18 "fmt" 19 "regexp" 20 "strconv" 21 "strings" 22 "testing" 23 ) 24 25 // Parameter is a test parameter. 26 type Parameter struct { 27 Name string 28 Value string 29 } 30 31 // Output is parsed and split by these values. Make them illegal in input methods. 32 // We are constrained on what characters these can be by 1) docker's allowable 33 // container names, 2) golang allowable benchmark names, and 3) golangs allowable 34 // characters in b.ReportMetric calls. 35 var illegalChars = regexp.MustCompile(`[/\.]`) 36 37 // ParametersToName joins parameters into a string format for parsing. 38 // It is meant to be used for t.Run() calls in benchmark tools. 39 func ParametersToName(params ...Parameter) (string, error) { 40 var strs []string 41 for _, param := range params { 42 if illegalChars.MatchString(param.Name) || illegalChars.MatchString(param.Value) { 43 return "", fmt.Errorf("params Name: %q and Value: %q cannot container '.' or '/'", param.Name, param.Value) 44 } 45 strs = append(strs, strings.Join([]string{param.Name, param.Value}, ".")) 46 } 47 return strings.Join(strs, "/"), nil 48 } 49 50 // NameToParameters parses the string created by ParametersToName and returns 51 // it as a set of Parameters. 52 // Example: BenchmarkRuby/server_threads.1/doc_size.16KB-6 53 // The parameter part of this benchmark is: 54 // "server_threads.1/doc_size.16KB" (BenchmarkRuby is the name, and 6 is GOMAXPROCS) 55 // This function will return a slice with two parameters -> 56 // {Name: server_threads, Value: 1}, {Name: doc_size, Value: 16KB} 57 func NameToParameters(name string) ([]*Parameter, error) { 58 var params []*Parameter 59 for _, cond := range strings.Split(name, "/") { 60 cs := strings.Split(cond, ".") 61 switch len(cs) { 62 case 1: 63 params = append(params, &Parameter{Name: cond, Value: cond}) 64 case 2: 65 params = append(params, &Parameter{Name: cs[0], Value: cs[1]}) 66 default: 67 return nil, fmt.Errorf("failed to parse param: %s", cond) 68 } 69 } 70 return params, nil 71 } 72 73 // ReportCustomMetric reports a metric in a set format for parsing. 74 func ReportCustomMetric(b *testing.B, value float64, name, unit string) { 75 if illegalChars.MatchString(name) || illegalChars.MatchString(unit) { 76 b.Fatalf("name: %q and unit: %q cannot contain '/' or '.'", name, unit) 77 } 78 nameUnit := strings.Join([]string{name, unit}, ".") 79 b.ReportMetric(value, nameUnit) 80 } 81 82 // Metric holds metric data parsed from a string based on the format 83 // ReportMetric. 84 type Metric struct { 85 Name string 86 Unit string 87 Sample float64 88 } 89 90 // ParseCustomMetric parses a metric reported with ReportCustomMetric. 91 func ParseCustomMetric(value, metric string) (*Metric, error) { 92 sample, err := strconv.ParseFloat(value, 64) 93 if err != nil { 94 return nil, fmt.Errorf("failed to parse value: %v", err) 95 } 96 nameUnit := strings.Split(metric, ".") 97 if len(nameUnit) != 2 { 98 return nil, fmt.Errorf("failed to parse metric: %s", metric) 99 } 100 return &Metric{Name: nameUnit[0], Unit: nameUnit[1], Sample: sample}, nil 101 }