From c11e18226270a0ca13c40532a005d6dc07084d3d Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Wed, 29 Nov 2023 19:42:13 -0800 Subject: [PATCH] `runsc`: Do not precompile seccomp-bpf filters in `fastbuild` mode. While precompilation is pretty fast, its presence in the build graph removes concurrency from the `runsc` build process. This is because the precompilation generation binary is dependency-heavy (e.g. it needs all platform implementations), yet must be built before the `//runsc/boot/filter` package is built, thus slowing down the `runsc` build process. This change creates a low-dependency "stubbed" version of this generation binary when running in `fastbuild` mode. The generated code in this mode contains no precompiled seccomp programs; they are instead all built from scratch on container startup. This trades off build speed vs container startup speed. PiperOrigin-RevId: 586520033 --- pkg/seccomp/precompiledseccomp/defs.bzl | 119 +++++++++++++----- .../precompiledseccomp/precompile_gen.go | 17 ++- runsc/boot/filter/BUILD | 1 + 3 files changed, 104 insertions(+), 33 deletions(-) diff --git a/pkg/seccomp/precompiledseccomp/defs.bzl b/pkg/seccomp/precompiledseccomp/defs.bzl index 70ed8d72f..232d72009 100644 --- a/pkg/seccomp/precompiledseccomp/defs.bzl +++ b/pkg/seccomp/precompiledseccomp/defs.bzl @@ -7,7 +7,8 @@ def precompiled_seccomp_rules( programs_to_compile_go_library, programs_to_compile_go_import, out, - out_package_name): + out_package_name, + exclude_in_fastbuild = False): """Generates a Go source file containing precompiled seccomp-bpf programs. Args: @@ -24,7 +25,16 @@ def precompiled_seccomp_rules( function: GetPrecompiled(programName string) (precompiledseccomp.Program, bool) out_package_name: Go package name that `out` belongs to. + exclude_in_fastbuild: Whether to skip precompilation in fastbuild mode. + The auto-generated `GetPrecompiled` function will fail all lookups. """ + if exclude_in_fastbuild: + native.config_setting( + name = name + "_fastbuild_cond", + values = { + "compilation_mode": "fastbuild", + }, + ) # This genrule copies precompiled_lib.tmpl.go to the directory of wherever # `precompiled_seccomp_rules` is called. @@ -41,49 +51,102 @@ def precompiled_seccomp_rules( # This genrule generates the Go file of the binary that, when run, # precompiles rules and writes them to a designated file. + gen_cmd_template = ( + " while IFS= read -r line; do" + + " if echo \"$$line\" | grep -q 'REPLACED_IMPORT_THIS_IS_A_LOAD_BEARING_COMMENT'; then" + + " {rules_import_echo}" + + " elif echo \"$$line\" | grep -q 'PROGRAMS_FUNC_THIS_IS_A_LOAD_BEARING_COMMENT'; then" + + " {load_programs_fn_echo}" + + " elif echo \"$$line\" | grep -q 'go:embed precompiled_lib.tmpl.go'; then" + + " echo -e \"//go:embed " + out + ".gen.lib.tmpl.go\";" + + " else" + + " echo \"$$line\";" + + " fi;" + + " done" + + " < $(SRCS)" + + " > $@" + ) + gen_cmd = gen_cmd_template.format( + rules_import_echo = ( + "echo -e \"\\\\trules \\\"" + + programs_to_compile_go_import + + "\\\"\";" + ), + load_programs_fn_echo = ( + "echo -e \"var loadProgramsFn = rules.PrecompiledPrograms\";" + ), + ) native.genrule( name = name + "_gen", outs = [out + ".gen.go"], - cmd = ( - " while IFS= read -r line; do" + - " if echo \"$$line\" | grep -q 'REPLACED_IMPORT_THIS_IS_A_LOAD_BEARING_COMMENT'; then" + - " echo -e \"\\\\trules \\\"" + programs_to_compile_go_import + "\\\"\";" + - " elif echo \"$$line\" | grep -q 'PROGRAMS_FUNC_THIS_IS_A_LOAD_BEARING_COMMENT'; then" + - " echo -e \"var loadProgramsFn = rules.PrecompiledPrograms\";" + - " elif echo \"$$line\" | grep -q 'go:embed precompiled_lib.tmpl.go'; then" + - " echo -e \"//go:embed " + out + ".gen.lib.tmpl.go\";" + - " else" + - " echo \"$$line\";" + - " fi;" + - " done" + - " < $(SRCS)" + - " > $@" - ), + cmd = gen_cmd, srcs = [ "//pkg/seccomp/precompiledseccomp:precompile_gen.go", ], ) + if exclude_in_fastbuild: + native.genrule( + name = name + "_gen_stubbed", + outs = [out + ".gen_stubbed.go"], + cmd = gen_cmd_template.format( + rules_import_echo = "true;", + load_programs_fn_echo = ( + "echo -e \"var loadProgramsFn func() ([]precompiledseccomp.Program, error) = nil\";" + ), + ), + srcs = [ + "//pkg/seccomp/precompiledseccomp:precompile_gen.go", + ], + ) # This defines the go_binary for the Go file we just generated. + base_gen_bin_deps = [ + "//pkg/seccomp/precompiledseccomp", + "//runsc/flag", + ] + gen_bin_deps = [programs_to_compile_go_library] + base_gen_bin_deps go_binary( name = name + "_gen_bin", srcs = [out + ".gen.go"], - deps = [ - programs_to_compile_go_library, - "//runsc/flag", - ], + deps = gen_bin_deps, embedsrcs = [ ":" + out + ".gen.lib.tmpl.go", ], ) + if exclude_in_fastbuild: + go_binary( + name = name + "_gen_stubbed_bin", + srcs = [out + ".gen_stubbed.go"], + deps = base_gen_bin_deps, + embedsrcs = [ + ":" + out + ".gen.lib.tmpl.go", + ], + ) # This genrule actually runs the go_binary we just declared, and writes # its output (containing the precompiled rules) to the desired `out` file. - native.genrule( - name = name, - outs = [out], - cmd = ( - "$(location :" + name + "_gen_bin) --package='" + out_package_name + "' --out=$@" - ), - tools = [":" + name + "_gen_bin"], - ) + out_cmd = "$(location :" + name + "_gen_bin) --package='" + out_package_name + "' --out=$@" + if exclude_in_fastbuild: + native.genrule( + name = name, + outs = [out], + cmd = select({ + ":" + name + "_fastbuild_cond": ( + "$(location :" + name + "_gen_stubbed_bin) --package='" + out_package_name + "' --out=$@" + ), + "//conditions:default": out_cmd, + }), + tools = select({ + ":" + name + "_fastbuild_cond": [":" + name + "_gen_stubbed_bin"], + "//conditions:default": [":" + name + "_gen_bin"], + }), + ) + else: + native.genrule( + name = name, + outs = [out], + cmd = ( + "$(location :" + name + "_gen_bin) --package='" + out_package_name + "' --out=$@" + ), + tools = [":" + name + "_gen_bin"], + ) diff --git a/pkg/seccomp/precompiledseccomp/precompile_gen.go b/pkg/seccomp/precompiledseccomp/precompile_gen.go index eab2ca030..7331f6b17 100644 --- a/pkg/seccomp/precompiledseccomp/precompile_gen.go +++ b/pkg/seccomp/precompiledseccomp/precompile_gen.go @@ -23,9 +23,11 @@ import ( "sort" "strings" + "gvisor.dev/gvisor/pkg/seccomp/precompiledseccomp" "gvisor.dev/gvisor/runsc/flag" - // This import will be replaced by the one specified in the genrule. + // This import will be replaced by the one specified in the genrule, + // or removed if stubbed out in fastbuild mode. "gvisor.dev/gvisor/pkg/seccomp/precompiledseccomp/example" // REPLACED_IMPORT_THIS_IS_A_LOAD_BEARING_COMMENT ) @@ -47,16 +49,21 @@ var ( ) // loadProgramsFn loads seccomp programs to be precompiled. +// It may be nil when it is stubbed out in fastbuild mode. var loadProgramsFn = example.PrecompiledPrograms // PROGRAMS_FUNC_THIS_IS_A_LOAD_BEARING_COMMENT func main() { flag.Parse() // Get a sorted list of programs. - programs, err := loadProgramsFn() - if err != nil { - fmt.Fprintf(os.Stderr, "Cannot get list of programs to precompile: %v\n", err) - os.Exit(1) + var programs []precompiledseccomp.Program + if loadProgramsFn != nil { + var err error + programs, err = loadProgramsFn() + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot get list of programs to precompile: %v\n", err) + os.Exit(1) + } } programNames := make(map[string]struct{}, len(programs)) for _, program := range programs { diff --git a/runsc/boot/filter/BUILD b/runsc/boot/filter/BUILD index a28221ddc..fb2e40e17 100644 --- a/runsc/boot/filter/BUILD +++ b/runsc/boot/filter/BUILD @@ -10,6 +10,7 @@ package( precompiled_seccomp_rules( name = "filter_precompiled", out = "filter_precompiled.go", + exclude_in_fastbuild = True, out_package_name = "filter", programs_to_compile_go_import = "gvisor.dev/gvisor/runsc/boot/filter/config", programs_to_compile_go_library = "//runsc/boot/filter/config",