github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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  // charecters 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  }