summaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/BUILD4
-rw-r--r--tools/bazel.mk5
-rw-r--r--tools/bazeldefs/defs.bzl3
-rw-r--r--tools/bigquery/BUILD3
-rw-r--r--tools/bigquery/bigquery.go31
-rw-r--r--tools/checkescape/checkescape.go124
-rw-r--r--tools/defs.bzl12
-rw-r--r--tools/go_marshal/gomarshal/generator.go8
-rw-r--r--tools/go_stateify/main.go114
-rw-r--r--tools/nogo/BUILD14
-rw-r--r--tools/nogo/build.go3
-rw-r--r--tools/nogo/config.go428
-rw-r--r--tools/nogo/defs.bzl77
-rwxr-xr-xtools/nogo/gentest.sh47
-rw-r--r--tools/nogo/matchers.go23
-rw-r--r--tools/parsers/BUILD27
-rw-r--r--tools/parsers/go_parser.go151
-rw-r--r--tools/parsers/go_parser_test.go171
18 files changed, 1068 insertions, 177 deletions
diff --git a/tools/BUILD b/tools/BUILD
index da83877b1..faf310676 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -5,5 +5,7 @@ package(licenses = ["notice"])
bzl_library(
name = "defs_bzl",
srcs = ["defs.bzl"],
- visibility = ["//visibility:private"],
+ visibility = [
+ "//:sandbox",
+ ],
)
diff --git a/tools/bazel.mk b/tools/bazel.mk
index 5e129b2ed..25575c02c 100644
--- a/tools/bazel.mk
+++ b/tools/bazel.mk
@@ -152,8 +152,9 @@ build_cmd = docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) sh -o pipefai
build_paths = $(build_cmd) 2>&1 \
| tee /proc/self/fd/2 \
- | grep -E "^ bazel-bin/" \
- | tr -d '\r' \
+ | grep " bazel-bin/" \
+ | sed "s/ /\n/g" \
+ | strings -n 10 \
| awk '{$$1=$$1};1' \
| xargs -n 1 -I {} sh -c "$(1)"
diff --git a/tools/bazeldefs/defs.bzl b/tools/bazeldefs/defs.bzl
index dad5fc3b2..cf5b1dc0d 100644
--- a/tools/bazeldefs/defs.bzl
+++ b/tools/bazeldefs/defs.bzl
@@ -191,3 +191,6 @@ def default_installer():
def default_net_util():
return [] # Nothing needed.
+
+def coreutil():
+ return [] # Nothing needed.
diff --git a/tools/bigquery/BUILD b/tools/bigquery/BUILD
index 5748fb390..2b0062a63 100644
--- a/tools/bigquery/BUILD
+++ b/tools/bigquery/BUILD
@@ -6,5 +6,8 @@ go_library(
name = "bigquery",
testonly = 1,
srcs = ["bigquery.go"],
+ visibility = [
+ "//:sandbox",
+ ],
deps = ["@com_google_cloud_go_bigquery//:go_default_library"],
)
diff --git a/tools/bigquery/bigquery.go b/tools/bigquery/bigquery.go
index 56f0dc5c9..5f1a882de 100644
--- a/tools/bigquery/bigquery.go
+++ b/tools/bigquery/bigquery.go
@@ -30,11 +30,20 @@ import (
// Benchmark is the top level structure of recorded benchmark data. BigQuery
// will infer the schema from this.
type Benchmark struct {
- Name string `bq:"name"`
- Timestamp time.Time `bq:"timestamp"`
- Official bool `bq:"official"`
- Metric []*Metric `bq:"metric"`
- Metadata *Metadata `bq:"metadata"`
+ Name string `bq:"name"`
+ Condition []*Condition `bq:"condition"`
+ Timestamp time.Time `bq:"timestamp"`
+ Official bool `bq:"official"`
+ Metric []*Metric `bq:"metric"`
+ Metadata *Metadata `bq:"metadata"`
+}
+
+// Condition represents qualifiers for the benchmark. For example:
+// Get_Pid/1/real_time would have Benchmark Name "Get_Pid" with "1"
+// and "real_time" parameters as conditions.
+type Condition struct {
+ Name string `bq:"name"`
+ Value string `bq:"value"`
}
// Metric holds the actual metric data and unit information for this benchmark.
@@ -79,6 +88,14 @@ func InitBigQuery(ctx context.Context, projectID, datasetID, tableID string) err
return nil
}
+// AddCondition adds a condition to an existing Benchmark.
+func (bm *Benchmark) AddCondition(name, value string) {
+ bm.Condition = append(bm.Condition, &Condition{
+ Name: name,
+ Value: value,
+ })
+}
+
// AddMetric adds a metric to an existing Benchmark.
func (bm *Benchmark) AddMetric(metricName, unit string, sample float64) {
m := &Metric{
@@ -90,7 +107,7 @@ func (bm *Benchmark) AddMetric(metricName, unit string, sample float64) {
}
// NewBenchmark initializes a new benchmark.
-func NewBenchmark(name string, official bool) *Benchmark {
+func NewBenchmark(name string, iters int, official bool) *Benchmark {
return &Benchmark{
Name: name,
Timestamp: time.Now().UTC(),
@@ -103,7 +120,7 @@ func NewBenchmark(name string, official bool) *Benchmark {
func SendBenchmarks(ctx context.Context, benchmarks []*Benchmark, projectID, datasetID, tableID string) error {
client, err := bq.NewClient(ctx, projectID)
if err != nil {
- return fmt.Errorf("Failed to initialize client on project: %s: %v", projectID, err)
+ return fmt.Errorf("failed to initialize client on project: %s: %v", projectID, err)
}
defer client.Close()
diff --git a/tools/checkescape/checkescape.go b/tools/checkescape/checkescape.go
index d98f5c3a1..523a42692 100644
--- a/tools/checkescape/checkescape.go
+++ b/tools/checkescape/checkescape.go
@@ -102,8 +102,8 @@ var (
// This may be set instead of Binary.
Reader io.Reader
- // Tool is the tool used to dump a binary.
- tool = flag.String("dump_tool", "", "tool used to dump a binary")
+ // objdumpTool is the tool used to dump a binary.
+ objdumpTool = flag.String("objdump_tool", "", "tool used to dump a binary")
)
// EscapeReason is an escape reason.
@@ -387,7 +387,7 @@ func loadObjdump() (map[string][]string, error) {
}
// Construct our command.
- cmd := exec.Command(*tool, args...)
+ cmd := exec.Command(*objdumpTool, args...)
cmd.Stdin = stdin
cmd.Stderr = os.Stderr
out, err := cmd.StdoutPipe()
@@ -398,7 +398,37 @@ func loadObjdump() (map[string][]string, error) {
return nil, err
}
+ // Identify calls by address or name. Note that this is also
+ // constructed dynamically below, as we encounted the addresses.
+ // This is because some of the functions (duffzero) may have
+ // jump targets in the middle of the function itself.
+ funcsAllowed := map[string]struct{}{
+ "runtime.duffzero": struct{}{},
+ "runtime.duffcopy": struct{}{},
+ "runtime.racefuncenter": struct{}{},
+ "runtime.gcWriteBarrier": struct{}{},
+ "runtime.retpolineAX": struct{}{},
+ "runtime.retpolineBP": struct{}{},
+ "runtime.retpolineBX": struct{}{},
+ "runtime.retpolineCX": struct{}{},
+ "runtime.retpolineDI": struct{}{},
+ "runtime.retpolineDX": struct{}{},
+ "runtime.retpolineR10": struct{}{},
+ "runtime.retpolineR11": struct{}{},
+ "runtime.retpolineR12": struct{}{},
+ "runtime.retpolineR13": struct{}{},
+ "runtime.retpolineR14": struct{}{},
+ "runtime.retpolineR15": struct{}{},
+ "runtime.retpolineR8": struct{}{},
+ "runtime.retpolineR9": struct{}{},
+ "runtime.retpolineSI": struct{}{},
+ "runtime.stackcheck": struct{}{},
+ "runtime.settls": struct{}{},
+ }
+ addrsAllowed := make(map[string]struct{})
+
// Build the map.
+ nextFunc := "" // For funcsAllowed.
m := make(map[string][]string)
r := bufio.NewReader(out)
NextLine:
@@ -407,6 +437,19 @@ NextLine:
if err != nil && err != io.EOF {
return nil, err
}
+ fields := strings.Fields(line)
+
+ // Is this an "allowed" function definition?
+ if len(fields) >= 2 && fields[0] == "TEXT" {
+ nextFunc = strings.TrimSuffix(fields[1], "(SB)")
+ if _, ok := funcsAllowed[nextFunc]; !ok {
+ nextFunc = "" // Don't record addresses.
+ }
+ }
+ if nextFunc != "" && len(fields) > 2 {
+ // Save the given address (in hex form, as it appears).
+ addrsAllowed[fields[1]] = struct{}{}
+ }
// We recognize lines corresponding to actual code (not the
// symbol name or other metadata) and annotate them if they
@@ -416,53 +459,31 @@ NextLine:
//
// Lines look like this (including the first space):
// gohacks_unsafe.go:33 0xa39 488b442408 MOVQ 0x8(SP), AX
- if len(line) > 0 && line[0] == ' ' {
- fields := strings.Fields(line)
+ if len(fields) >= 5 && line[0] == ' ' {
if !strings.Contains(fields[3], "CALL") {
continue
}
- site := strings.TrimSpace(fields[0])
- var callStr string // Friendly string.
- if len(fields) > 5 {
- callStr = strings.Join(fields[5:], " ")
- }
- if len(callStr) == 0 {
- // Just a raw call? is this asm?
- callStr = strings.Join(fields[3:], " ")
- }
-
- // Ignore strings containing duffzero, which is just
- // used by stack allocations for types that are large
- // enough to warrant Duff's device.
- if strings.Contains(callStr, "runtime.duffzero") ||
- strings.Contains(callStr, "runtime.duffcopy") {
- continue
- }
-
- // Ignore the racefuncenter call, which is used for
- // race builds. This does not escape.
- if strings.Contains(callStr, "runtime.racefuncenter") {
- continue
- }
+ site := fields[0]
+ target := strings.TrimSuffix(fields[4], "(SB)")
- // Ignore the write barriers.
- if strings.Contains(callStr, "runtime.gcWriteBarrier") {
+ // Ignore strings containing allowed functions.
+ if _, ok := funcsAllowed[target]; ok {
continue
}
-
- // Ignore retpolines.
- if strings.Contains(callStr, "runtime.retpoline") {
+ if _, ok := addrsAllowed[target]; ok {
continue
}
-
- // Ignore stack sanity check (does not split).
- if strings.Contains(callStr, "runtime.stackcheck") {
- continue
- }
-
- // Ignore tls functions.
- if strings.Contains(callStr, "runtime.settls") {
- continue
+ if len(fields) > 5 {
+ // This may be a future relocation. Some
+ // objdump versions describe this differently.
+ // If it contains any of the functions allowed
+ // above as a string, we let it go.
+ softTarget := strings.Join(fields[5:], " ")
+ for name := range funcsAllowed {
+ if strings.Contains(softTarget, name) {
+ continue NextLine
+ }
+ }
}
// Does this exist already?
@@ -471,11 +492,11 @@ NextLine:
existing = make([]string, 0, 1)
}
for _, other := range existing {
- if callStr == other {
+ if target == other {
continue NextLine
}
}
- existing = append(existing, callStr)
+ existing = append(existing, target)
m[site] = existing // Update.
}
if err == io.EOF {
@@ -483,12 +504,25 @@ NextLine:
}
}
+ // Zap any accidental false positives.
+ final := make(map[string][]string)
+ for site, calls := range m {
+ filteredCalls := make([]string, 0, len(calls))
+ for _, call := range calls {
+ if _, ok := addrsAllowed[call]; ok {
+ continue // Omit this call.
+ }
+ filteredCalls = append(filteredCalls, call)
+ }
+ final[site] = filteredCalls
+ }
+
// Wait for the dump to finish.
if err := cmd.Wait(); err != nil {
return nil, err
}
- return m, nil
+ return final, nil
}
// poser is a type that implements Pos.
diff --git a/tools/defs.bzl b/tools/defs.bzl
index 60c9c9d8c..e2c72a1f6 100644
--- a/tools/defs.bzl
+++ b/tools/defs.bzl
@@ -7,7 +7,7 @@ change for Google-internal and bazel-compatible rules.
load("//tools/go_stateify:defs.bzl", "go_stateify")
load("//tools/go_marshal:defs.bzl", "go_marshal", "marshal_deps", "marshal_test_deps")
-load("//tools/bazeldefs:defs.bzl", _build_test = "build_test", _bzl_library = "bzl_library", _cc_binary = "cc_binary", _cc_flags_supplier = "cc_flags_supplier", _cc_grpc_library = "cc_grpc_library", _cc_library = "cc_library", _cc_proto_library = "cc_proto_library", _cc_test = "cc_test", _cc_toolchain = "cc_toolchain", _default_installer = "default_installer", _default_net_util = "default_net_util", _gazelle = "gazelle", _gbenchmark = "gbenchmark", _go_binary = "go_binary", _go_embed_data = "go_embed_data", _go_grpc_and_proto_libraries = "go_grpc_and_proto_libraries", _go_library = "go_library", _go_path = "go_path", _go_proto_library = "go_proto_library", _go_test = "go_test", _grpcpp = "grpcpp", _gtest = "gtest", _loopback = "loopback", _pkg_deb = "pkg_deb", _pkg_tar = "pkg_tar", _proto_library = "proto_library", _py_binary = "py_binary", _rbe_platform = "rbe_platform", _rbe_toolchain = "rbe_toolchain", _select_arch = "select_arch", _select_system = "select_system", _short_path = "short_path", _vdso_linker_option = "vdso_linker_option")
+load("//tools/bazeldefs:defs.bzl", _build_test = "build_test", _bzl_library = "bzl_library", _cc_binary = "cc_binary", _cc_flags_supplier = "cc_flags_supplier", _cc_grpc_library = "cc_grpc_library", _cc_library = "cc_library", _cc_proto_library = "cc_proto_library", _cc_test = "cc_test", _cc_toolchain = "cc_toolchain", _coreutil = "coreutil", _default_installer = "default_installer", _default_net_util = "default_net_util", _gazelle = "gazelle", _gbenchmark = "gbenchmark", _go_binary = "go_binary", _go_embed_data = "go_embed_data", _go_grpc_and_proto_libraries = "go_grpc_and_proto_libraries", _go_library = "go_library", _go_path = "go_path", _go_proto_library = "go_proto_library", _go_test = "go_test", _grpcpp = "grpcpp", _gtest = "gtest", _loopback = "loopback", _pkg_deb = "pkg_deb", _pkg_tar = "pkg_tar", _proto_library = "proto_library", _py_binary = "py_binary", _rbe_platform = "rbe_platform", _rbe_toolchain = "rbe_toolchain", _select_arch = "select_arch", _select_system = "select_system", _short_path = "short_path", _vdso_linker_option = "vdso_linker_option")
load("//tools/bazeldefs:platforms.bzl", _default_platform = "default_platform", _platforms = "platforms")
load("//tools/bazeldefs:tags.bzl", "go_suffixes")
load("//tools/nogo:defs.bzl", "nogo_test")
@@ -39,6 +39,7 @@ short_path = _short_path
rbe_platform = _rbe_platform
rbe_toolchain = _rbe_toolchain
vdso_linker_option = _vdso_linker_option
+coreutil = _coreutil
# Platform options.
default_platform = _default_platform
@@ -71,7 +72,8 @@ def go_binary(name, nogo = True, pure = False, static = False, x_defs = None, **
)
nogo_test(
name = name + "_nogo",
- deps = [":" + name + "_nogo_library"],
+ srcs = kwargs.get("srcs", []),
+ library = ":" + name + "_nogo_library",
)
def calculate_sets(srcs):
@@ -202,7 +204,8 @@ def go_library(name, srcs, deps = [], imports = [], stateify = True, marshal = F
if nogo:
nogo_test(
name = name + "_nogo",
- deps = [":" + name],
+ srcs = all_srcs,
+ library = ":" + name,
)
if marshal:
@@ -238,7 +241,8 @@ def go_test(name, nogo = True, **kwargs):
if nogo:
nogo_test(
name = name + "_nogo",
- deps = [":" + name],
+ srcs = kwargs.get("srcs", []),
+ library = ":" + name,
)
def proto_library(name, srcs, deps = None, has_services = 0, **kwargs):
diff --git a/tools/go_marshal/gomarshal/generator.go b/tools/go_marshal/gomarshal/generator.go
index 56fbcb5d2..4a53d25be 100644
--- a/tools/go_marshal/gomarshal/generator.go
+++ b/tools/go_marshal/gomarshal/generator.go
@@ -80,15 +80,15 @@ type Generator struct {
func NewGenerator(srcs []string, out, outTest, outTestUnconditional, pkg string, imports []string) (*Generator, error) {
f, err := os.OpenFile(out, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
- return nil, fmt.Errorf("Couldn't open output file %q: %v", out, err)
+ return nil, fmt.Errorf("couldn't open output file %q: %w", out, err)
}
fTest, err := os.OpenFile(outTest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
- return nil, fmt.Errorf("Couldn't open test output file %q: %v", out, err)
+ return nil, fmt.Errorf("couldn't open test output file %q: %w", out, err)
}
fTestUC, err := os.OpenFile(outTestUnconditional, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
- return nil, fmt.Errorf("Couldn't open unconditional test output file %q: %v", out, err)
+ return nil, fmt.Errorf("couldn't open unconditional test output file %q: %w", out, err)
}
g := Generator{
inputs: srcs,
@@ -181,7 +181,7 @@ func (g *Generator) parse() ([]*ast.File, []*token.FileSet, error) {
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
// Not a valid input file?
- return nil, nil, fmt.Errorf("Input %q can't be parsed: %v", path, err)
+ return nil, nil, fmt.Errorf("input %q can't be parsed: %w", path, err)
}
if debugEnabled() {
diff --git a/tools/go_stateify/main.go b/tools/go_stateify/main.go
index 4f6ed208a..e1de12e25 100644
--- a/tools/go_stateify/main.go
+++ b/tools/go_stateify/main.go
@@ -39,7 +39,7 @@ var (
)
// resolveTypeName returns a qualified type name.
-func resolveTypeName(name string, typ ast.Expr) (field string, qualified string) {
+func resolveTypeName(typ ast.Expr) (field string, qualified string) {
for done := false; !done; {
// Resolve star expressions.
switch rs := typ.(type) {
@@ -69,11 +69,7 @@ func resolveTypeName(name string, typ ast.Expr) (field string, qualified string)
}
// Figure out actual type name.
- ident, ok := typ.(*ast.Ident)
- if !ok {
- panic(fmt.Sprintf("type not supported: %s (involves anonymous types?)", name))
- }
- field = ident.Name
+ field = typ.(*ast.Ident).Name
qualified = qualified + field
return
}
@@ -119,7 +115,7 @@ func scanFields(ss *ast.StructType, prefix string, fn scanFunctions) {
} else {
// Anonymous types can't be embedded, so we don't need
// to worry about providing a useful name here.
- name, _ = resolveTypeName("", field.Type)
+ name, _ = resolveTypeName(field.Type)
}
// Skip _ fields.
@@ -214,9 +210,6 @@ func main() {
emitRegister := func(name string) {
initCalls = append(initCalls, fmt.Sprintf("%sRegister((*%s)(nil))", statePrefix, name))
}
- emitZeroCheck := func(name string) {
- fmt.Fprintf(outputFile, " if !%sIsZeroValue(&x.%s) { %sFailf(\"%s is %%#v, expected zero\", &x.%s) }\n", statePrefix, name, statePrefix, name, name)
- }
// Automated warning.
fmt.Fprint(outputFile, "// automatically generated by stateify.\n\n")
@@ -265,52 +258,39 @@ func main() {
}
type method struct {
- receiver string
- name string
+ typeName string
+ methodName string
}
- // Search for and add all methods with a pointer receiver and no other
- // arguments to a set. We support auto-detecting the existence of
- // several different methods with this signature.
- simpleMethods := map[method]struct{}{}
+ // Search for and add all method to a set. We auto-detecting several
+ // different methods (and insert them if we don't find them, in order
+ // to ensure that expectations match reality).
+ //
+ // While we do this, figure out the right receiver name. If there are
+ // multiple distinct receivers, then we will just pick the last one.
+ simpleMethods := make(map[method]struct{})
+ receiverNames := make(map[string]string)
for _, f := range files {
-
// Go over all functions.
for _, decl := range f.Decls {
d, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
- if d.Name == nil || d.Recv == nil || d.Type == nil {
+ if d.Recv == nil || len(d.Recv.List) != 1 {
// Not a named method.
continue
}
- if len(d.Recv.List) != 1 {
- // Wrong number of receivers?
- continue
- }
- if d.Type.Params != nil && len(d.Type.Params.List) != 0 {
- // Has argument(s).
- continue
- }
- if d.Type.Results != nil && len(d.Type.Results.List) != 0 {
- // Has return(s).
- continue
- }
- pt, ok := d.Recv.List[0].Type.(*ast.StarExpr)
- if !ok {
- // Not a pointer receiver.
- continue
+ // Save the method and the receiver.
+ name, _ := resolveTypeName(d.Recv.List[0].Type)
+ simpleMethods[method{
+ typeName: name,
+ methodName: d.Name.Name,
+ }] = struct{}{}
+ if len(d.Recv.List[0].Names) > 0 {
+ receiverNames[name] = d.Recv.List[0].Names[0].Name
}
-
- t, ok := pt.X.(*ast.Ident)
- if !ok {
- // This shouldn't happen with valid Go.
- continue
- }
-
- simpleMethods[method{t.Name, d.Name.Name}] = struct{}{}
}
}
@@ -349,6 +329,11 @@ func main() {
for _, gs := range d.Specs {
ts := gs.(*ast.TypeSpec)
+ recv, ok := receiverNames[ts.Name.Name]
+ if !ok {
+ // Maybe no methods were defined?
+ recv = strings.ToLower(ts.Name.Name[:1])
+ }
switch x := ts.Type.(type) {
case *ast.StructType:
maybeEmitImports()
@@ -365,29 +350,32 @@ func main() {
emitField(name)
}
emitLoadValue := func(name, typName string) {
- fmt.Fprintf(outputFile, " m.LoadValue(%d, new(%s), func(y interface{}) { x.load%s(y.(%s)) })\n", fields[name], typName, camelCased(name), typName)
+ fmt.Fprintf(outputFile, " stateSourceObject.LoadValue(%d, new(%s), func(y interface{}) { %s.load%s(y.(%s)) })\n", fields[name], typName, recv, camelCased(name), typName)
}
emitLoad := func(name string) {
- fmt.Fprintf(outputFile, " m.Load(%d, &x.%s)\n", fields[name], name)
+ fmt.Fprintf(outputFile, " stateSourceObject.Load(%d, &%s.%s)\n", fields[name], recv, name)
}
emitLoadWait := func(name string) {
- fmt.Fprintf(outputFile, " m.LoadWait(%d, &x.%s)\n", fields[name], name)
+ fmt.Fprintf(outputFile, " stateSourceObject.LoadWait(%d, &%s.%s)\n", fields[name], recv, name)
}
emitSaveValue := func(name, typName string) {
- fmt.Fprintf(outputFile, " var %s %s = x.save%s()\n", name, typName, camelCased(name))
- fmt.Fprintf(outputFile, " m.SaveValue(%d, %s)\n", fields[name], name)
+ fmt.Fprintf(outputFile, " var %sValue %s = %s.save%s()\n", name, typName, recv, camelCased(name))
+ fmt.Fprintf(outputFile, " stateSinkObject.SaveValue(%d, %sValue)\n", fields[name], name)
}
emitSave := func(name string) {
- fmt.Fprintf(outputFile, " m.Save(%d, &x.%s)\n", fields[name], name)
+ fmt.Fprintf(outputFile, " stateSinkObject.Save(%d, &%s.%s)\n", fields[name], recv, name)
+ }
+ emitZeroCheck := func(name string) {
+ fmt.Fprintf(outputFile, " if !%sIsZeroValue(&%s.%s) { %sFailf(\"%s is %%#v, expected zero\", &%s.%s) }\n", statePrefix, recv, name, statePrefix, name, recv, name)
}
// Generate the type name method.
- fmt.Fprintf(outputFile, "func (x *%s) StateTypeName() string {\n", ts.Name.Name)
+ fmt.Fprintf(outputFile, "func (%s *%s) StateTypeName() string {\n", recv, ts.Name.Name)
fmt.Fprintf(outputFile, " return \"%s.%s\"\n", *fullPkg, ts.Name.Name)
fmt.Fprintf(outputFile, "}\n\n")
// Generate the fields method.
- fmt.Fprintf(outputFile, "func (x *%s) StateFields() []string {\n", ts.Name.Name)
+ fmt.Fprintf(outputFile, "func (%s *%s) StateFields() []string {\n", recv, ts.Name.Name)
fmt.Fprintf(outputFile, " return []string{\n")
scanFields(x, "", scanFunctions{
normal: emitField,
@@ -401,8 +389,11 @@ func main() {
// the code from compiling if a custom beforeSave was defined in a
// file not provided to this binary and prevents inherited methods
// from being called multiple times by overriding them.
- if _, ok := simpleMethods[method{ts.Name.Name, "beforeSave"}]; !ok && generateSaverLoader {
- fmt.Fprintf(outputFile, "func (x *%s) beforeSave() {}\n\n", ts.Name.Name)
+ if _, ok := simpleMethods[method{
+ typeName: ts.Name.Name,
+ methodName: "beforeSave",
+ }]; !ok && generateSaverLoader {
+ fmt.Fprintf(outputFile, "func (%s *%s) beforeSave() {}\n\n", recv, ts.Name.Name)
}
// Generate the save method.
@@ -412,8 +403,8 @@ func main() {
// on this specific behavior, but the ability to specify slots
// allows a manual implementation to be order-dependent.
if generateSaverLoader {
- fmt.Fprintf(outputFile, "func (x *%s) StateSave(m %sSink) {\n", ts.Name.Name, statePrefix)
- fmt.Fprintf(outputFile, " x.beforeSave()\n")
+ fmt.Fprintf(outputFile, "func (%s *%s) StateSave(stateSinkObject %sSink) {\n", recv, ts.Name.Name, statePrefix)
+ fmt.Fprintf(outputFile, " %s.beforeSave()\n", recv)
scanFields(x, "", scanFunctions{zerovalue: emitZeroCheck})
scanFields(x, "", scanFunctions{value: emitSaveValue})
scanFields(x, "", scanFunctions{normal: emitSave, wait: emitSave})
@@ -422,16 +413,19 @@ func main() {
// Define afterLoad if a definition was not found. We do this for
// the same reason that we do it for beforeSave.
- _, hasAfterLoad := simpleMethods[method{ts.Name.Name, "afterLoad"}]
+ _, hasAfterLoad := simpleMethods[method{
+ typeName: ts.Name.Name,
+ methodName: "afterLoad",
+ }]
if !hasAfterLoad && generateSaverLoader {
- fmt.Fprintf(outputFile, "func (x *%s) afterLoad() {}\n\n", ts.Name.Name)
+ fmt.Fprintf(outputFile, "func (%s *%s) afterLoad() {}\n\n", recv, ts.Name.Name)
}
// Generate the load method.
//
// N.B. See the comment above for the save method.
if generateSaverLoader {
- fmt.Fprintf(outputFile, "func (x *%s) StateLoad(m %sSource) {\n", ts.Name.Name, statePrefix)
+ fmt.Fprintf(outputFile, "func (%s *%s) StateLoad(stateSourceObject %sSource) {\n", recv, ts.Name.Name, statePrefix)
scanFields(x, "", scanFunctions{normal: emitLoad, wait: emitLoadWait})
scanFields(x, "", scanFunctions{value: emitLoadValue})
if hasAfterLoad {
@@ -439,7 +433,7 @@ func main() {
// AfterLoad is called, the object encodes a dependency on
// referred objects (i.e. fields). This means that afterLoad
// will not be called until the other afterLoads are called.
- fmt.Fprintf(outputFile, " m.AfterLoad(x.afterLoad)\n")
+ fmt.Fprintf(outputFile, " stateSourceObject.AfterLoad(%s.afterLoad)\n", recv)
}
fmt.Fprintf(outputFile, "}\n\n")
}
@@ -451,10 +445,10 @@ func main() {
maybeEmitImports()
// Generate the info methods.
- fmt.Fprintf(outputFile, "func (x *%s) StateTypeName() string {\n", ts.Name.Name)
+ fmt.Fprintf(outputFile, "func (%s *%s) StateTypeName() string {\n", recv, ts.Name.Name)
fmt.Fprintf(outputFile, " return \"%s.%s\"\n", *fullPkg, ts.Name.Name)
fmt.Fprintf(outputFile, "}\n\n")
- fmt.Fprintf(outputFile, "func (x *%s) StateFields() []string {\n", ts.Name.Name)
+ fmt.Fprintf(outputFile, "func (%s *%s) StateFields() []string {\n", recv, ts.Name.Name)
fmt.Fprintf(outputFile, " return nil\n")
fmt.Fprintf(outputFile, "}\n\n")
diff --git a/tools/nogo/BUILD b/tools/nogo/BUILD
index 9f1fcd9c7..dd4b46f58 100644
--- a/tools/nogo/BUILD
+++ b/tools/nogo/BUILD
@@ -1,10 +1,10 @@
load("//tools:defs.bzl", "bzl_library", "go_library")
-load("//tools/nogo:defs.bzl", "nogo_dump_tool", "nogo_stdlib")
+load("//tools/nogo:defs.bzl", "nogo_objdump_tool", "nogo_stdlib")
package(licenses = ["notice"])
-nogo_dump_tool(
- name = "dump_tool",
+nogo_objdump_tool(
+ name = "objdump_tool",
visibility = ["//visibility:public"],
)
@@ -13,6 +13,12 @@ nogo_stdlib(
visibility = ["//visibility:public"],
)
+sh_binary(
+ name = "gentest",
+ srcs = ["gentest.sh"],
+ visibility = ["//visibility:public"],
+)
+
go_library(
name = "nogo",
srcs = [
@@ -27,6 +33,8 @@ go_library(
deps = [
"//tools/checkescape",
"//tools/checkunsafe",
+ "@co_honnef_go_tools//staticcheck:go_default_library",
+ "@co_honnef_go_tools//stylecheck:go_default_library",
"@org_golang_x_tools//go/analysis:go_tool_library",
"@org_golang_x_tools//go/analysis/internal/facts:go_tool_library",
"@org_golang_x_tools//go/analysis/passes/asmdecl:go_tool_library",
diff --git a/tools/nogo/build.go b/tools/nogo/build.go
index 39c2ae418..55d34760f 100644
--- a/tools/nogo/build.go
+++ b/tools/nogo/build.go
@@ -29,6 +29,9 @@ var (
// internalDefault is applied when no paths are provided.
internalDefault = fmt.Sprintf("%s/.*", notPath("external"))
+ // generatedPrefix is a regex for generated files.
+ generatedPrefix = "^(.*/)?(bazel-genfiles|bazel-out|bazel-bin)/"
+
// externalPrefix is external workspace packages.
externalPrefix = "^external/"
)
diff --git a/tools/nogo/config.go b/tools/nogo/config.go
index cfe7b4aa4..8079618ab 100644
--- a/tools/nogo/config.go
+++ b/tools/nogo/config.go
@@ -41,6 +41,8 @@ import (
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
+ "honnef.co/go/tools/staticcheck"
+ "honnef.co/go/tools/stylecheck"
"gvisor.dev/gvisor/tools/checkescape"
"gvisor.dev/gvisor/tools/checkunsafe"
@@ -123,6 +125,432 @@ var analyzerConfig = map[*analysis.Analyzer]matcher{
checkunsafe.Analyzer: internalMatches(),
}
+func init() {
+ staticMatcher := and(
+ // Only match internal, non-generated files.
+ internalMatches(),
+ generatedExcluded(),
+
+ // We use ALL_CAPS for system definitions,
+ // which are common enough in the code base
+ // that we shouldn't annotate exceptions.
+ //
+ // Same story for underscores.
+ resultExcluded([]string{
+ "should not use ALL_CAPS in Go names",
+ "should not use underscores in Go names",
+ }),
+
+ // Exclude existing matches.
+ internalExcluded(
+ "pkg/abi/linux/fuse.go:22",
+ "pkg/abi/linux/fuse.go:25",
+ "pkg/abi/linux/socket.go:113",
+ "pkg/abi/linux/tty.go:73",
+ "pkg/bpf/decoder.go:112",
+ "pkg/cpuid/cpuid_x86.go:675",
+ "pkg/eventchannel/event.go:193",
+ "pkg/eventchannel/event.go:27",
+ "pkg/eventchannel/event_test.go:22",
+ "pkg/eventchannel/rate.go:19",
+ "pkg/gohacks/gohacks_unsafe.go:33",
+ "pkg/log/json.go:30",
+ "pkg/log/log.go:359",
+ "pkg/merkletree/merkletree.go:230",
+ "pkg/merkletree/merkletree.go:243",
+ "pkg/merkletree/merkletree.go:249",
+ "pkg/merkletree/merkletree.go:266",
+ "pkg/merkletree/merkletree.go:355",
+ "pkg/merkletree/merkletree.go:369",
+ "pkg/metric/metric_test.go:20",
+ "pkg/p9/p9test/client_test.go:687",
+ "pkg/p9/transport_test.go:196",
+ "pkg/pool/pool.go:15",
+ "pkg/refs/refcounter.go:510",
+ "pkg/refs/refcounter_test.go:169",
+ "pkg/refs_vfs2/refs.go:16",
+ "pkg/safemem/block_unsafe.go:89",
+ "pkg/seccomp/seccomp.go:82",
+ "pkg/segment/test/set_functions.go:15",
+ "pkg/sentry/arch/signal.go:166",
+ "pkg/sentry/arch/signal.go:171",
+ "pkg/sentry/control/pprof.go:196",
+ "pkg/sentry/devices/memdev/full.go:58",
+ "pkg/sentry/devices/memdev/null.go:59",
+ "pkg/sentry/devices/memdev/random.go:68",
+ "pkg/sentry/devices/memdev/zero.go:86",
+ "pkg/sentry/fdimport/fdimport.go:15",
+ "pkg/sentry/fs/attr.go:257",
+ "pkg/sentry/fsbridge/fs.go:116",
+ "pkg/sentry/fsbridge/vfs.go:124",
+ "pkg/sentry/fsbridge/vfs.go:70",
+ "pkg/sentry/fs/copy_up.go:365",
+ "pkg/sentry/fs/copy_up_test.go:65",
+ "pkg/sentry/fs/dev/net_tun.go:161",
+ "pkg/sentry/fs/dev/net_tun.go:63",
+ "pkg/sentry/fs/dev/null.go:97",
+ "pkg/sentry/fs/dirent_cache.go:64",
+ "pkg/sentry/fs/file_overlay.go:327",
+ "pkg/sentry/fs/file_overlay.go:524",
+ "pkg/sentry/fs/filetest/filetest.go:55",
+ "pkg/sentry/fs/filetest/filetest.go:60",
+ "pkg/sentry/fs/fs.go:77",
+ "pkg/sentry/fs/fsutil/file.go:290",
+ "pkg/sentry/fs/fsutil/file.go:346",
+ "pkg/sentry/fs/fsutil/host_file_mapper.go:105",
+ "pkg/sentry/fs/fsutil/inode_cached.go:676",
+ "pkg/sentry/fs/fsutil/inode_cached.go:772",
+ "pkg/sentry/fs/gofer/attr.go:120",
+ "pkg/sentry/fs/gofer/fifo.go:33",
+ "pkg/sentry/fs/gofer/inode.go:410",
+ "pkg/sentry/fsimpl/devpts/devpts.go:110",
+ "pkg/sentry/fsimpl/devpts/devpts.go:246",
+ "pkg/sentry/fsimpl/devpts/devpts.go:50",
+ "pkg/sentry/fsimpl/devpts/master.go:110",
+ "pkg/sentry/fsimpl/devpts/master.go:55",
+ "pkg/sentry/fsimpl/devpts/replica.go:113",
+ "pkg/sentry/fsimpl/devpts/replica.go:57",
+ "pkg/sentry/fsimpl/devtmpfs/devtmpfs.go:54",
+ "pkg/sentry/fsimpl/ext/disklayout/superblock_64.go:97",
+ "pkg/sentry/fsimpl/ext/disklayout/superblock_old.go:92",
+ "pkg/sentry/fsimpl/ext/disklayout/block_group_32.go:44",
+ "pkg/sentry/fsimpl/ext/disklayout/inode_new.go:91",
+ "pkg/sentry/fsimpl/ext/disklayout/inode_old.go:93",
+ "pkg/sentry/fsimpl/ext/disklayout/superblock_32.go:66",
+ "pkg/sentry/fsimpl/ext/disklayout/block_group_64.go:53",
+ "pkg/sentry/fsimpl/eventfd/eventfd.go:268",
+ "pkg/sentry/fsimpl/ext/directory.go:163",
+ "pkg/sentry/fsimpl/ext/directory.go:164",
+ "pkg/sentry/fsimpl/ext/extent_file.go:142",
+ "pkg/sentry/fsimpl/ext/extent_file.go:143",
+ "pkg/sentry/fsimpl/ext/ext.go:105",
+ "pkg/sentry/fsimpl/ext/filesystem.go:287",
+ "pkg/sentry/fsimpl/ext/regular_file.go:153",
+ "pkg/sentry/fsimpl/ext/symlink.go:113",
+ "pkg/sentry/fsimpl/fuse/connection_control.go:194",
+ "pkg/sentry/fsimpl/fuse/dev.go:387",
+ "pkg/sentry/fsimpl/fuse/dev_test.go:318",
+ "pkg/sentry/fsimpl/fuse/fusefs.go:102",
+ "pkg/sentry/fsimpl/fuse/read_write.go:129",
+ "pkg/sentry/fsimpl/fuse/request_response.go:71",
+ "pkg/sentry/fsimpl/gofer/directory.go:135",
+ "pkg/sentry/fsimpl/gofer/filesystem.go:679",
+ "pkg/sentry/fsimpl/gofer/gofer.go:1694",
+ "pkg/sentry/fsimpl/gofer/gofer.go:276",
+ "pkg/sentry/fsimpl/gofer/regular_file.go:81",
+ "pkg/sentry/fsimpl/gofer/special_file.go:141",
+ "pkg/sentry/fsimpl/host/host.go:184",
+ "pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go:50",
+ "pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go:90",
+ "pkg/sentry/fsimpl/kernfs/fd_impl_util.go:273",
+ "pkg/sentry/fsimpl/kernfs/filesystem.go:247",
+ "pkg/sentry/fsimpl/kernfs/inode_impl_util.go:320",
+ "pkg/sentry/fsimpl/kernfs/inode_impl_util.go:497",
+ "pkg/sentry/fsimpl/kernfs/synthetic_directory.go:52",
+ "pkg/sentry/fsimpl/overlay/directory.go:119",
+ "pkg/sentry/fsimpl/overlay/filesystem.go:527",
+ "pkg/sentry/fsimpl/overlay/non_directory.go:152",
+ "pkg/sentry/fsimpl/overlay/overlay.go:115",
+ "pkg/sentry/fsimpl/overlay/overlay.go:719",
+ "pkg/sentry/fsimpl/pipefs/pipefs.go:74",
+ "pkg/sentry/fsimpl/proc/filesystem.go:52",
+ "pkg/sentry/fsimpl/proc/filesystem.go:81",
+ "pkg/sentry/fsimpl/proc/subtasks.go:126",
+ "pkg/sentry/fsimpl/proc/subtasks.go:189",
+ "pkg/sentry/fsimpl/proc/task_fds.go:168",
+ "pkg/sentry/fsimpl/proc/task_fds.go:228",
+ "pkg/sentry/fsimpl/proc/task_fds.go:301",
+ "pkg/sentry/fsimpl/proc/task_fds.go:318",
+ "pkg/sentry/fsimpl/proc/task_fds.go:67",
+ "pkg/sentry/fsimpl/proc/task_files.go:112",
+ "pkg/sentry/fsimpl/proc/task_files.go:158",
+ "pkg/sentry/fsimpl/proc/task_files.go:259",
+ "pkg/sentry/fsimpl/proc/task_files.go:285",
+ "pkg/sentry/fsimpl/proc/task_files.go:305",
+ "pkg/sentry/fsimpl/proc/task_files.go:384",
+ "pkg/sentry/fsimpl/proc/task_files.go:403",
+ "pkg/sentry/fsimpl/proc/task_files.go:428",
+ "pkg/sentry/fsimpl/proc/task_files.go:691",
+ "pkg/sentry/fsimpl/proc/task_files.go:770",
+ "pkg/sentry/fsimpl/proc/task_files.go:797",
+ "pkg/sentry/fsimpl/proc/task_files.go:828",
+ "pkg/sentry/fsimpl/proc/task_files.go:879",
+ "pkg/sentry/fsimpl/proc/task_files.go:910",
+ "pkg/sentry/fsimpl/proc/task_files.go:961",
+ "pkg/sentry/fsimpl/proc/task.go:127",
+ "pkg/sentry/fsimpl/proc/task.go:193",
+ "pkg/sentry/fsimpl/proc/task_net.go:134",
+ "pkg/sentry/fsimpl/proc/task_net.go:475",
+ "pkg/sentry/fsimpl/proc/task_net.go:491",
+ "pkg/sentry/fsimpl/proc/task_net.go:508",
+ "pkg/sentry/fsimpl/proc/task_net.go:665",
+ "pkg/sentry/fsimpl/proc/task_net.go:715",
+ "pkg/sentry/fsimpl/proc/task_net.go:779",
+ "pkg/sentry/fsimpl/proc/tasks_files.go:113",
+ "pkg/sentry/fsimpl/proc/tasks_files.go:388",
+ "pkg/sentry/fsimpl/proc/tasks.go:232",
+ "pkg/sentry/fsimpl/proc/tasks_sys.go:145",
+ "pkg/sentry/fsimpl/proc/tasks_sys.go:181",
+ "pkg/sentry/fsimpl/proc/tasks_sys.go:239",
+ "pkg/sentry/fsimpl/proc/tasks_sys.go:291",
+ "pkg/sentry/fsimpl/proc/tasks_sys.go:375",
+ "pkg/sentry/fsimpl/signalfd/signalfd.go:124",
+ "pkg/sentry/fsimpl/signalfd/signalfd.go:15",
+ "pkg/sentry/fsimpl/signalfd/signalfd.go:126",
+ "pkg/sentry/fsimpl/sockfs/sockfs.go:36",
+ "pkg/sentry/fsimpl/sockfs/sockfs.go:79",
+ "pkg/sentry/fsimpl/sys/kcov.go:49",
+ "pkg/sentry/fsimpl/sys/kcov.go:99",
+ "pkg/sentry/fsimpl/sys/sys.go:118",
+ "pkg/sentry/fsimpl/sys/sys.go:56",
+ "pkg/sentry/fsimpl/testutil/testutil.go:257",
+ "pkg/sentry/fsimpl/testutil/testutil.go:260",
+ "pkg/sentry/fsimpl/timerfd/timerfd.go:87",
+ "pkg/sentry/fsimpl/tmpfs/directory.go:112",
+ "pkg/sentry/fsimpl/tmpfs/filesystem.go:195",
+ "pkg/sentry/fsimpl/tmpfs/regular_file.go:226",
+ "pkg/sentry/fsimpl/tmpfs/regular_file.go:346",
+ "pkg/sentry/fsimpl/tmpfs/tmpfs.go:103",
+ "pkg/sentry/fsimpl/tmpfs/tmpfs.go:733",
+ "pkg/sentry/fsimpl/verity/filesystem.go:490",
+ "pkg/sentry/fsimpl/verity/verity.go:156",
+ "pkg/sentry/fsimpl/verity/verity.go:629",
+ "pkg/sentry/fsimpl/verity/verity.go:672",
+ "pkg/sentry/fs/mount.go:162",
+ "pkg/sentry/fs/mount.go:256",
+ "pkg/sentry/fs/mount_overlay.go:144",
+ "pkg/sentry/fs/mounts.go:432",
+ "pkg/sentry/fs/proc/exec_args.go:104",
+ "pkg/sentry/fs/proc/exec_args.go:73",
+ "pkg/sentry/fs/proc/fds.go:269",
+ "pkg/sentry/fs/proc/loadavg.go:33",
+ "pkg/sentry/fs/proc/meminfo.go:39",
+ "pkg/sentry/fs/proc/mounts.go:193",
+ "pkg/sentry/fs/proc/mounts.go:84",
+ "pkg/sentry/fs/proc/net.go:125",
+ "pkg/sentry/fs/proc/proc.go:146",
+ "pkg/sentry/fs/proc/proc.go:204",
+ "pkg/sentry/fs/proc/seqfile/seqfile.go:210",
+ "pkg/sentry/fs/proc/sys.go:146",
+ "pkg/sentry/fs/proc/sys.go:43",
+ "pkg/sentry/fs/proc/sys_net.go:113",
+ "pkg/sentry/fs/proc/sys_net.go:205",
+ "pkg/sentry/fs/proc/sys_net.go:233",
+ "pkg/sentry/fs/proc/sys_net.go:307",
+ "pkg/sentry/fs/proc/sys_net.go:335",
+ "pkg/sentry/fs/proc/sys_net.go:446",
+ "pkg/sentry/fs/proc/sys_net.go:456",
+ "pkg/sentry/fs/proc/sys_net.go:89",
+ "pkg/sentry/fs/proc/task.go:170",
+ "pkg/sentry/fs/proc/task.go:322",
+ "pkg/sentry/fs/proc/task.go:427",
+ "pkg/sentry/fs/proc/task.go:467",
+ "pkg/sentry/fs/proc/task.go:500",
+ "pkg/sentry/fs/proc/task.go:784",
+ "pkg/sentry/fs/proc/task.go:839",
+ "pkg/sentry/fs/proc/task.go:920",
+ "pkg/sentry/fs/proc/uid_gid_map.go:108",
+ "pkg/sentry/fs/proc/uid_gid_map.go:79",
+ "pkg/sentry/fs/proc/uptime.go:75",
+ "pkg/sentry/fs/ramfs/dir.go:447",
+ "pkg/sentry/fs/tmpfs/inode_file.go:436",
+ "pkg/sentry/fs/tmpfs/inode_file.go:537",
+ "pkg/sentry/fs/tty/dir.go:313",
+ "pkg/sentry/fs/tty/master.go:131",
+ "pkg/sentry/fs/tty/master.go:91",
+ "pkg/sentry/fs/tty/replica.go:116",
+ "pkg/sentry/fs/tty/replica.go:88",
+ "pkg/sentry/kernel/auth/id_map.go:269",
+ "pkg/sentry/kernel/fasync/fasync.go:67",
+ "pkg/sentry/kernel/kcov.go:209",
+ "pkg/sentry/kernel/kcov.go:223",
+ "pkg/sentry/kernel/kernel.go:343",
+ "pkg/sentry/kernel/kernel.go:368",
+ "pkg/sentry/kernel/pipe/node_test.go:112",
+ "pkg/sentry/kernel/pipe/node_test.go:119",
+ "pkg/sentry/kernel/pipe/node_test.go:130",
+ "pkg/sentry/kernel/pipe/node_test.go:137",
+ "pkg/sentry/kernel/pipe/node_test.go:149",
+ "pkg/sentry/kernel/pipe/node_test.go:150",
+ "pkg/sentry/kernel/pipe/node_test.go:158",
+ "pkg/sentry/kernel/pipe/node_test.go:174",
+ "pkg/sentry/kernel/pipe/node_test.go:180",
+ "pkg/sentry/kernel/pipe/node_test.go:193",
+ "pkg/sentry/kernel/pipe/node_test.go:202",
+ "pkg/sentry/kernel/pipe/node_test.go:205",
+ "pkg/sentry/kernel/pipe/node_test.go:216",
+ "pkg/sentry/kernel/pipe/node_test.go:219",
+ "pkg/sentry/kernel/pipe/node_test.go:271",
+ "pkg/sentry/kernel/pipe/node_test.go:290",
+ "pkg/sentry/kernel/pipe/pipe_test.go:93",
+ "pkg/sentry/kernel/pipe/reader_writer.go:65",
+ "pkg/sentry/kernel/posixtimer.go:157",
+ "pkg/sentry/kernel/ptrace.go:218",
+ "pkg/sentry/kernel/semaphore/semaphore.go:323",
+ "pkg/sentry/kernel/sessions.go:123",
+ "pkg/sentry/kernel/sessions.go:508",
+ "pkg/sentry/kernel/signal_handlers.go:57",
+ "pkg/sentry/kernel/task_context.go:72",
+ "pkg/sentry/kernel/task_exit.go:67",
+ "pkg/sentry/kernel/task_sched.go:255",
+ "pkg/sentry/kernel/task_sched.go:280",
+ "pkg/sentry/kernel/task_sched.go:323",
+ "pkg/sentry/kernel/task_stop.go:192",
+ "pkg/sentry/kernel/thread_group.go:530",
+ "pkg/sentry/kernel/timekeeper.go:316",
+ "pkg/sentry/kernel/vdso.go:106",
+ "pkg/sentry/kernel/vdso.go:118",
+ "pkg/sentry/memmap/memmap.go:103",
+ "pkg/sentry/memmap/memmap.go:163",
+ "pkg/sentry/mm/address_space.go:42",
+ "pkg/sentry/mm/address_space.go:42",
+ "pkg/sentry/mm/aio_context.go:208",
+ "pkg/sentry/mm/aio_context.go:288",
+ "pkg/sentry/mm/pma.go:683",
+ "pkg/sentry/mm/special_mappable.go:80",
+ "pkg/sentry/platform/systrap/subprocess.go:370",
+ "pkg/sentry/platform/systrap/usertrap/usertrap_amd64.go:124",
+ "pkg/sentry/socket/control/control.go:260",
+ "pkg/sentry/socket/control/control.go:94",
+ "pkg/sentry/socket/control/control_vfs2.go:37",
+ "pkg/sentry/socket/hostinet/stack.go:433",
+ "pkg/sentry/socket/hostinet/stack.go:438",
+ "pkg/sentry/socket/hostinet/stack.go:444",
+ "pkg/sentry/socket/hostinet/stack.go:460",
+ "pkg/sentry/socket/netfilter/tcp_matcher.go:74",
+ "pkg/sentry/socket/netfilter/udp_matcher.go:71",
+ "pkg/sentry/socket/netlink/route/protocol.go:38",
+ "pkg/sentry/socket/socket.go:332",
+ "pkg/sentry/socket/unix/transport/connectioned.go:394",
+ "pkg/sentry/socket/unix/transport/connectionless.go:152",
+ "pkg/sentry/socket/unix/transport/unix.go:436",
+ "pkg/sentry/socket/unix/transport/unix.go:490",
+ "pkg/sentry/socket/unix/transport/unix.go:685",
+ "pkg/sentry/socket/unix/transport/unix.go:795",
+ "pkg/sentry/syscalls/linux/sys_sem.go:62",
+ "pkg/sentry/syscalls/linux/sys_time.go:189",
+ "pkg/sentry/usage/cpu.go:42",
+ "pkg/sentry/vfs/anonfs.go:302",
+ "pkg/sentry/vfs/anonfs.go:99",
+ "pkg/sentry/vfs/dentry.go:214",
+ "pkg/sentry/vfs/epoll.go:168",
+ "pkg/sentry/vfs/epoll.go:314",
+ "pkg/sentry/vfs/file_description.go:549",
+ "pkg/sentry/vfs/file_description_impl_util.go:304",
+ "pkg/sentry/vfs/file_description_impl_util.go:412",
+ "pkg/sentry/vfs/filesystem.go:76",
+ "pkg/sentry/vfs/lock.go:15",
+ "pkg/sentry/vfs/lock.go:47",
+ "pkg/sentry/vfs/memxattr/xattr.go:37",
+ "pkg/sentry/vfs/mount.go:510",
+ "pkg/sentry/vfs/mount.go:667",
+ "pkg/sentry/vfs/mount_test.go:106",
+ "pkg/sentry/vfs/mount_test.go:160",
+ "pkg/sentry/vfs/mount_test.go:215",
+ "pkg/sentry/vfs/mount_unsafe.go:153",
+ "pkg/sentry/vfs/resolving_path.go:228",
+ "pkg/sentry/vfs/vfs.go:897",
+ "pkg/shim/runsc/runsc.go:16",
+ "pkg/shim/runsc/utils.go:16",
+ "pkg/shim/v1/proc/deleted_state.go:16",
+ "pkg/shim/v1/proc/exec.go:16",
+ "pkg/shim/v1/proc/exec_state.go:16",
+ "pkg/shim/v1/proc/init.go:16",
+ "pkg/shim/v1/proc/init_state.go:16",
+ "pkg/shim/v1/proc/io.go:16",
+ "pkg/shim/v1/proc/process.go:16",
+ "pkg/shim/v1/proc/types.go:16",
+ "pkg/shim/v1/proc/utils.go:16",
+ "pkg/shim/v1/shim/api.go:16",
+ "pkg/shim/v1/shim/platform.go:16",
+ "pkg/shim/v1/shim/service.go:16",
+ "pkg/shim/v1/utils/annotations.go:15",
+ "pkg/shim/v1/utils/utils.go:15",
+ "pkg/shim/v1/utils/volumes.go:15",
+ "pkg/shim/v2/api.go:16",
+ "pkg/shim/v2/epoll.go:18",
+ "pkg/shim/v2/options/options.go:15",
+ "pkg/shim/v2/options/options.go:24",
+ "pkg/shim/v2/options/options.go:26",
+ "pkg/shim/v2/runtimeoptions/runtimeoptions.go:16",
+ "pkg/shim/v2/runtimeoptions/runtimeoptions_test.go:22",
+ "pkg/shim/v2/service.go:15",
+ "pkg/shim/v2/service_linux.go:18",
+ "pkg/state/tests/integer_test.go:23",
+ "pkg/state/tests/integer_test.go:28",
+ "pkg/sync/rwmutex_test.go:105",
+ "pkg/syserr/host_linux.go:35",
+ "pkg/tcpip/adapters/gonet/gonet_test.go:144",
+ "pkg/tcpip/adapters/gonet/gonet_test.go:415",
+ "pkg/tcpip/adapters/gonet/gonet_test.go:99",
+ "pkg/tcpip/buffer/view.go:238",
+ "pkg/tcpip/buffer/view.go:238",
+ "pkg/tcpip/buffer/view.go:246",
+ "pkg/tcpip/header/tcp.go:151",
+ "pkg/tcpip/link/sharedmem/pipe/pipe_test.go:493",
+ "pkg/tcpip/stack/iptables.go:293",
+ "pkg/tcpip/stack/iptables_types.go:277",
+ "pkg/tcpip/stack/stack.go:553",
+ "pkg/tcpip/stack/transport_test.go:30",
+ "pkg/tcpip/transport/packet/endpoint.go:126",
+ "pkg/tcpip/transport/raw/endpoint.go:145",
+ "pkg/tcpip/transport/tcp/sack_scoreboard.go:167",
+ "pkg/unet/unet_test.go:634",
+ "pkg/unet/unet_test.go:662",
+ "pkg/unet/unet_test.go:703",
+ "pkg/unet/unet_test.go:98",
+ "pkg/usermem/addr.go:34",
+ "pkg/usermem/usermem.go:171",
+ "pkg/usermem/usermem.go:170",
+ "runsc/boot/compat.go:22",
+ "runsc/boot/compat.go:56",
+ "runsc/boot/loader.go:1115",
+ "runsc/boot/loader.go:1120",
+ "runsc/cmd/checkpoint.go:151",
+ "runsc/config/flags.go:32",
+ "runsc/container/container.go:641",
+ "runsc/container/container.go:988",
+ "runsc/specutils/specutils.go:172",
+ "runsc/specutils/specutils.go:428",
+ "runsc/specutils/specutils.go:436",
+ "runsc/specutils/specutils.go:442",
+ "runsc/specutils/specutils.go:447",
+ "runsc/specutils/specutils.go:454",
+ "test/cmd/test_app/fds.go:171",
+ "test/iptables/filter_output.go:251",
+ "test/packetimpact/testbench/connections.go:77",
+ "tools/bigquery/bigquery.go:106",
+ "tools/checkescape/test1/test1.go:108",
+ "tools/checkescape/test1/test1.go:122",
+ "tools/checkescape/test1/test1.go:137",
+ "tools/checkescape/test1/test1.go:151",
+ "tools/checkescape/test1/test1.go:170",
+ "tools/checkescape/test1/test1.go:39",
+ "tools/checkescape/test1/test1.go:45",
+ "tools/checkescape/test1/test1.go:50",
+ "tools/checkescape/test1/test1.go:64",
+ "tools/checkescape/test1/test1.go:80",
+ "tools/checkescape/test1/test1.go:94",
+ "tools/go_generics/imports.go:51",
+ "tools/go_generics/imports.go:75",
+ "tools/go_marshal/gomarshal/generator.go:177",
+ "tools/go_marshal/gomarshal/generator.go:81",
+ "tools/go_marshal/gomarshal/generator.go:85",
+ "tools/go_marshal/test/escape/escape.go:15",
+ "tools/go_marshal/test/test.go:164",
+ ),
+ )
+
+ // Add all staticcheck analyzers; internal only.
+ for _, a := range staticcheck.Analyzers {
+ analyzerConfig[a] = staticMatcher
+ }
+ // Add all stylecheck analyzers; internal only.
+ for _, a := range stylecheck.Analyzers {
+ analyzerConfig[a] = staticMatcher
+ }
+}
+
var escapesConfig = map[*analysis.Analyzer]matcher{
// Informational only: include all packages.
checkescape.EscapeAnalyzer: alwaysMatches(),
diff --git a/tools/nogo/defs.bzl b/tools/nogo/defs.bzl
index 480438047..c6fcfd402 100644
--- a/tools/nogo/defs.bzl
+++ b/tools/nogo/defs.bzl
@@ -2,8 +2,7 @@
load("//tools/bazeldefs:defs.bzl", "go_context", "go_importpath", "go_rule", "go_test_library")
-def _nogo_dump_tool_impl(ctx):
- # Extract the Go context.
+def _nogo_objdump_tool_impl(ctx):
go_ctx = go_context(ctx)
# Construct the magic dump command.
@@ -40,9 +39,9 @@ def _nogo_dump_tool_impl(ctx):
executable = dumper,
)]
-nogo_dump_tool = go_rule(
+nogo_objdump_tool = go_rule(
rule,
- implementation = _nogo_dump_tool_impl,
+ implementation = _nogo_objdump_tool_impl,
)
# NogoStdlibInfo is the set of standard library facts.
@@ -55,7 +54,6 @@ NogoStdlibInfo = provider(
)
def _nogo_stdlib_impl(ctx):
- # Extract the Go context.
go_ctx = go_context(ctx)
# Build the standard library facts.
@@ -72,12 +70,12 @@ def _nogo_stdlib_impl(ctx):
ctx.actions.run(
inputs = [config_file] + go_ctx.stdlib_srcs,
outputs = [facts, findings],
- tools = depset(go_ctx.runfiles.to_list() + ctx.files._dump_tool),
+ tools = depset(go_ctx.runfiles.to_list() + ctx.files._objdump_tool),
executable = ctx.files._nogo[0],
mnemonic = "GoStandardLibraryAnalysis",
progress_message = "Analyzing Go Standard Library",
arguments = go_ctx.nogo_args + [
- "-dump_tool=%s" % ctx.files._dump_tool[0].path,
+ "-objdump_tool=%s" % ctx.files._objdump_tool[0].path,
"-stdlib=%s" % config_file.path,
"-findings=%s" % findings.path,
"-facts=%s" % facts.path,
@@ -97,8 +95,8 @@ nogo_stdlib = go_rule(
"_nogo": attr.label(
default = "//tools/nogo/check:check",
),
- "_dump_tool": attr.label(
- default = "//tools/nogo:dump_tool",
+ "_objdump_tool": attr.label(
+ default = "//tools/nogo:objdump_tool",
),
},
)
@@ -121,6 +119,8 @@ NogoInfo = provider(
)
def _nogo_aspect_impl(target, ctx):
+ go_ctx = go_context(ctx)
+
# If this is a nogo rule itself (and not the shadow of a go_library or
# go_binary rule created by such a rule), then we simply return nothing.
# All work is done in the shadow properties for go rules. For a proto
@@ -135,9 +135,6 @@ def _nogo_aspect_impl(target, ctx):
else:
return [NogoInfo()]
- # Extract the Go context.
- go_ctx = go_context(ctx)
-
# If we're using the "library" attribute, then we need to aggregate the
# original library sources and dependencies into this target to perform
# proper type analysis.
@@ -227,13 +224,13 @@ def _nogo_aspect_impl(target, ctx):
ctx.actions.run(
inputs = inputs,
outputs = [facts, findings, escapes],
- tools = depset(go_ctx.runfiles.to_list() + ctx.files._dump_tool),
+ tools = depset(go_ctx.runfiles.to_list() + ctx.files._objdump_tool),
executable = ctx.files._nogo[0],
mnemonic = "GoStaticAnalysis",
progress_message = "Analyzing %s" % target.label,
arguments = go_ctx.nogo_args + [
"-binary=%s" % target_objfile.path,
- "-dump_tool=%s" % ctx.files._dump_tool[0].path,
+ "-objdump_tool=%s" % ctx.files._objdump_tool[0].path,
"-package=%s" % config_file.path,
"-findings=%s" % findings.path,
"-facts=%s" % facts.path,
@@ -271,58 +268,48 @@ nogo_aspect = go_rule(
attrs = {
"_nogo": attr.label(default = "//tools/nogo/check:check"),
"_nogo_stdlib": attr.label(default = "//tools/nogo:stdlib"),
- "_dump_tool": attr.label(default = "//tools/nogo:dump_tool"),
+ "_objdump_tool": attr.label(default = "//tools/nogo:objdump_tool"),
},
)
def _nogo_test_impl(ctx):
"""Check nogo findings."""
- # Build a runner that checks for the existence of the facts file. Note that
- # the actual build will fail in the case of a broken analysis. We things
- # this way so that any test applied is effectively pushed down to all
- # upstream dependencies through the aspect.
- inputs = []
- findings = []
- runner = ctx.actions.declare_file("%s-executer" % ctx.label.name)
- runner_content = ["#!/bin/bash"]
- for dep in ctx.attr.deps:
- # Extract the findings.
- info = dep[NogoInfo]
- inputs.append(info.findings)
- findings.append(info.findings)
-
- # Include all source files, transitively. This will make this target
- # "directly affected" for the purpose of build analysis.
- inputs += info.srcs
-
- # If there are findings, dump them and fail.
- runner_content.append("if [[ -s \"%s\" ]]; then cat \"%s\" && exit 1; fi" % (
- info.findings.short_path,
- info.findings.short_path,
- ))
-
- # Otherwise, draw a sweet unicode checkmark with the package name (in green).
- runner_content.append("echo -e \"\\033[0;32m\\xE2\\x9C\\x94\\033[0;31m\\033[0m %s\"" % info.importpath)
- runner_content.append("exit 0\n")
- ctx.actions.write(runner, "\n".join(runner_content), is_executable = True)
+ # Build a runner that checks the facts files.
+ findings = [dep[NogoInfo].findings for dep in ctx.attr.deps]
+ runner = ctx.actions.declare_file(ctx.label.name)
+ ctx.actions.run(
+ inputs = findings + ctx.files.srcs,
+ outputs = [runner],
+ tools = depset(ctx.files._gentest),
+ executable = ctx.files._gentest[0],
+ mnemonic = "Gentest",
+ progress_message = "Generating %s" % ctx.label,
+ arguments = [runner.path] + [f.path for f in findings],
+ )
return [DefaultInfo(
- runfiles = ctx.runfiles(files = inputs),
executable = runner,
)]
_nogo_test = rule(
implementation = _nogo_test_impl,
attrs = {
+ # deps should have only a single element.
"deps": attr.label_list(aspects = [nogo_aspect]),
+ # srcs exist here only to ensure that this target is
+ # directly affected by changes to the source files.
+ "srcs": attr.label_list(allow_files = True),
+ "_gentest": attr.label(default = "//tools/nogo:gentest"),
},
test = True,
)
-def nogo_test(name, **kwargs):
+def nogo_test(name, srcs, library, **kwargs):
tags = kwargs.pop("tags", []) + ["nogo"]
_nogo_test(
name = name,
+ srcs = srcs,
+ deps = [library],
tags = tags,
**kwargs
)
diff --git a/tools/nogo/gentest.sh b/tools/nogo/gentest.sh
new file mode 100755
index 000000000..033da11ad
--- /dev/null
+++ b/tools/nogo/gentest.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+# Copyright 2019 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.
+
+set -euo pipefail
+
+if [[ "$#" -lt 2 ]]; then
+ echo "usage: $0 <output> <findings...>"
+ exit 2
+fi
+declare violations=0
+declare output=$1
+shift
+
+# Start the script.
+echo "#!/bin/sh" > "${output}"
+
+# Read a list of findings files.
+declare filename
+declare line
+for filename in "$@"; do
+ if [[ -z "${filename}" ]]; then
+ continue
+ fi
+ while read -r line; do
+ violations=$((${violations}+1));
+ echo "echo -e '\\033[0;31m${line}\\033[0;31m\\033[0m'" >> "${output}"
+ done < "${filename}"
+done
+
+# Show violations.
+if [[ "${violations}" -eq 0 ]]; then
+ echo "echo -e '\\033[0;32mPASS\\033[0;31m\\033[0m'" >> "${output}"
+else
+ echo "exit 1" >> "${output}"
+fi
diff --git a/tools/nogo/matchers.go b/tools/nogo/matchers.go
index 5c39be630..b7b73fa27 100644
--- a/tools/nogo/matchers.go
+++ b/tools/nogo/matchers.go
@@ -102,6 +102,14 @@ func internalMatches() *pathRegexps {
}
}
+// generatedExcluded excludes all generated code.
+func generatedExcluded() *pathRegexps {
+ return &pathRegexps{
+ expr: buildRegexps(generatedPrefix, ".*"),
+ include: false,
+ }
+}
+
// resultExcluded excludes explicit message contents.
type resultExcluded []string
@@ -117,20 +125,23 @@ func (r resultExcluded) ShouldReport(d analysis.Diagnostic, _ *token.FileSet) bo
// andMatcher is a composite matcher.
type andMatcher struct {
- first matcher
- second matcher
+ all []matcher
}
// ShouldReport implements matcher.ShouldReport.
func (a *andMatcher) ShouldReport(d analysis.Diagnostic, fs *token.FileSet) bool {
- return a.first.ShouldReport(d, fs) && a.second.ShouldReport(d, fs)
+ for _, m := range a.all {
+ if !m.ShouldReport(d, fs) {
+ return false
+ }
+ }
+ return true
}
// and is a syntactic convension for andMatcher.
-func and(first matcher, second matcher) *andMatcher {
+func and(ms ...matcher) *andMatcher {
return &andMatcher{
- first: first,
- second: second,
+ all: ms,
}
}
diff --git a/tools/parsers/BUILD b/tools/parsers/BUILD
new file mode 100644
index 000000000..7d9c9a3fb
--- /dev/null
+++ b/tools/parsers/BUILD
@@ -0,0 +1,27 @@
+load("//tools:defs.bzl", "go_library", "go_test")
+
+package(licenses = ["notice"])
+
+go_test(
+ name = "parsers_test",
+ size = "small",
+ srcs = ["go_parser_test.go"],
+ library = ":parsers",
+ deps = [
+ "//tools/bigquery",
+ "@com_github_google_go_cmp//cmp:go_default_library",
+ ],
+)
+
+go_library(
+ name = "parsers",
+ testonly = 1,
+ srcs = [
+ "go_parser.go",
+ ],
+ visibility = ["//:sandbox"],
+ deps = [
+ "//test/benchmarks/tools",
+ "//tools/bigquery",
+ ],
+)
diff --git a/tools/parsers/go_parser.go b/tools/parsers/go_parser.go
new file mode 100644
index 000000000..2cf74c883
--- /dev/null
+++ b/tools/parsers/go_parser.go
@@ -0,0 +1,151 @@
+// 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.
+
+// Package parsers holds parsers to parse Benchmark test output.
+//
+// Parsers parse Benchmark test output and place it in BigQuery
+// structs for sending to BigQuery databases.
+package parsers
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "gvisor.dev/gvisor/test/benchmarks/tools"
+ "gvisor.dev/gvisor/tools/bigquery"
+)
+
+// parseOutput expects golang benchmark output returns a Benchmark struct formatted for BigQuery.
+func parseOutput(output string, metadata *bigquery.Metadata, official bool) ([]*bigquery.Benchmark, error) {
+ var benchmarks []*bigquery.Benchmark
+ lines := strings.Split(output, "\n")
+ for _, line := range lines {
+ bm, err := parseLine(line, metadata, official)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse line '%s': %v", line, err)
+ }
+ if bm != nil {
+ benchmarks = append(benchmarks, bm)
+ }
+ }
+ return benchmarks, nil
+}
+
+// parseLine handles parsing a benchmark line into a bigquery.Benchmark.
+//
+// Example: "BenchmarkRuby/server_threads.1-6 1 1397875880 ns/op 140 requests_per_second.QPS"
+//
+// This function will return the following benchmark:
+// *bigquery.Benchmark{
+// Name: BenchmarkRuby
+// []*bigquery.Condition{
+// {Name: GOMAXPROCS, 6}
+// {Name: server_threads, 1}
+// }
+// []*bigquery.Metric{
+// {Name: ns/op, Unit: ns/op, Sample: 1397875880}
+// {Name: requests_per_second, Unit: QPS, Sample: 140 }
+// }
+// Metadata: metadata
+//}
+func parseLine(line string, metadata *bigquery.Metadata, official bool) (*bigquery.Benchmark, error) {
+ fields := strings.Fields(line)
+
+ // Check if this line is a Benchmark line. Otherwise ignore the line.
+ if len(fields) < 2 || !strings.HasPrefix(fields[0], "Benchmark") {
+ return nil, nil
+ }
+
+ iters, err := strconv.Atoi(fields[1])
+ if err != nil {
+ return nil, fmt.Errorf("expecting number of runs, got %s: %v", fields[1], err)
+ }
+
+ name, params, err := parseNameParams(fields[0])
+ if err != nil {
+ return nil, fmt.Errorf("parse name/params: %v", err)
+ }
+
+ bm := bigquery.NewBenchmark(name, iters, official)
+ bm.Metadata = metadata
+ for _, p := range params {
+ bm.AddCondition(p.Name, p.Value)
+ }
+
+ for i := 1; i < len(fields)/2; i++ {
+ value := fields[2*i]
+ metric := fields[2*i+1]
+ if err := makeMetric(bm, value, metric); err != nil {
+ return nil, fmt.Errorf("makeMetric on metric %q value: %s: %v", metric, value, err)
+ }
+ }
+ return bm, nil
+}
+
+// parseNameParams parses the Name, GOMAXPROCS, and Params from the test.
+// Field here should be of the format TESTNAME/PARAMS-GOMAXPROCS.
+// Parameters will be separated by a "/" with individual params being
+// "name.value".
+func parseNameParams(field string) (string, []*tools.Parameter, error) {
+ var params []*tools.Parameter
+ // Remove GOMAXPROCS from end.
+ maxIndex := strings.LastIndex(field, "-")
+ if maxIndex < 0 {
+ return "", nil, fmt.Errorf("GOMAXPROCS not found: %s", field)
+ }
+ maxProcs := field[maxIndex+1:]
+ params = append(params, &tools.Parameter{
+ Name: "GOMAXPROCS",
+ Value: maxProcs,
+ })
+
+ remainder := field[0:maxIndex]
+ index := strings.Index(remainder, "/")
+ if index == -1 {
+ return remainder, params, nil
+ }
+
+ name := remainder[0:index]
+ p := remainder[index+1:]
+
+ ps, err := tools.NameToParameters(p)
+ if err != nil {
+ return "", nil, fmt.Errorf("NameToParameters %s: %v", field, err)
+ }
+ params = append(params, ps...)
+ return name, params, nil
+}
+
+// makeMetric parses metrics and adds them to the passed Benchmark.
+func makeMetric(bm *bigquery.Benchmark, value, metric string) error {
+ switch metric {
+ // Ignore most output from golang benchmarks.
+ case "MB/s", "B/op", "allocs/op":
+ return nil
+ case "ns/op":
+ val, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ return fmt.Errorf("ParseFloat %s: %v", value, err)
+ }
+ bm.AddMetric(metric /*metric name*/, metric /*unit*/, val /*sample*/)
+ default:
+ m, err := tools.ParseCustomMetric(value, metric)
+ if err != nil {
+ return fmt.Errorf("ParseCustomMetric %s: %v ", metric, err)
+ }
+ bm.AddMetric(m.Name, m.Unit, m.Sample)
+ }
+ return nil
+}
diff --git a/tools/parsers/go_parser_test.go b/tools/parsers/go_parser_test.go
new file mode 100644
index 000000000..36996b7c8
--- /dev/null
+++ b/tools/parsers/go_parser_test.go
@@ -0,0 +1,171 @@
+// 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.
+
+package parsers
+
+import (
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "gvisor.dev/gvisor/tools/bigquery"
+)
+
+func TestParseLine(t *testing.T) {
+ testCases := []struct {
+ name string
+ data string
+ want *bigquery.Benchmark
+ }{
+ {
+ name: "Iperf",
+ data: "BenchmarkIperf/Upload-6 1 11094914892 ns/op 4751711232 bandwidth.bytes_per_second",
+ want: &bigquery.Benchmark{
+ Name: "BenchmarkIperf",
+ Condition: []*bigquery.Condition{
+ {
+ Name: "GOMAXPROCS",
+ Value: "6",
+ },
+ {
+ Name: "Upload",
+ Value: "Upload",
+ },
+ },
+ Metric: []*bigquery.Metric{
+ {
+ Name: "ns/op",
+ Unit: "ns/op",
+ Sample: 11094914892.0,
+ },
+ {
+ Name: "bandwidth",
+ Unit: "bytes_per_second",
+ Sample: 4751711232.0,
+ },
+ },
+ },
+ },
+ {
+ name: "Ruby",
+ data: "BenchmarkRuby/server_threads.1-6 1 1397875880 ns/op 0.00710 average_latency.s 140 requests_per_second.QPS",
+ want: &bigquery.Benchmark{
+ Name: "BenchmarkRuby",
+ Condition: []*bigquery.Condition{
+ {
+ Name: "GOMAXPROCS",
+ Value: "6",
+ },
+ {
+ Name: "server_threads",
+ Value: "1",
+ },
+ },
+ Metric: []*bigquery.Metric{
+ {
+ Name: "ns/op",
+ Unit: "ns/op",
+ Sample: 1397875880.0,
+ },
+ {
+ Name: "average_latency",
+ Unit: "s",
+ Sample: 0.00710,
+ },
+ {
+ Name: "requests_per_second",
+ Unit: "QPS",
+ Sample: 140.0,
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ got, err := parseLine(tc.data, nil, false)
+ if err != nil {
+ t.Fatalf("parseLine failed with: %v", err)
+ }
+
+ tc.want.Timestamp = got.Timestamp
+
+ if !cmp.Equal(tc.want, got, nil) {
+ for _, c := range got.Condition {
+ t.Logf("Cond: %+v", c)
+ }
+ for _, m := range got.Metric {
+ t.Logf("Metric: %+v", m)
+ }
+ t.Fatalf("Compare failed want: %+v got: %+v", tc.want, got)
+ }
+ })
+
+ }
+}
+
+func TestParseOutput(t *testing.T) {
+ testCases := []struct {
+ name string
+ data string
+ numBenchmarks int
+ numMetrics int
+ numConditions int
+ }{
+ {
+ name: "Startup",
+ data: `
+ BenchmarkStartupEmpty
+ BenchmarkStartupEmpty-6 2 766377884 ns/op 1 allocs/op
+ BenchmarkStartupNode
+ BenchmarkStartupNode-6 1 1752158409 ns/op 1 allocs/op
+ `,
+ numBenchmarks: 2,
+ numMetrics: 1,
+ numConditions: 1,
+ },
+ {
+ name: "Ruby",
+ data: `BenchmarkRuby
+BenchmarkRuby/server_threads.1
+BenchmarkRuby/server_threads.1-6 1 1397875880 ns/op 0.00710 average_latency.s 140 requests_per_second.QPS
+BenchmarkRuby/server_threads.5
+BenchmarkRuby/server_threads.5-6 1 1416003331 ns/op 0.00950 average_latency.s 465 requests_per_second.QPS`,
+ numBenchmarks: 2,
+ numMetrics: 3,
+ numConditions: 2,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ bms, err := parseOutput(tc.data, nil, false)
+ if err != nil {
+ t.Fatalf("parseOutput failed: %v", err)
+ } else if len(bms) != tc.numBenchmarks {
+ t.Fatalf("NumBenchmarks failed want: %d got: %d %+v", tc.numBenchmarks, len(bms), bms)
+ }
+
+ for _, bm := range bms {
+ if len(bm.Metric) != tc.numMetrics {
+ t.Fatalf("NumMetrics failed want: %d got: %d %+v", tc.numMetrics, len(bm.Metric), bm.Metric)
+ }
+
+ if len(bm.Condition) != tc.numConditions {
+ t.Fatalf("NumConditions failed want: %d got: %d %+v", tc.numConditions, len(bm.Condition), bm.Condition)
+ }
+ }
+ })
+ }
+}