diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go index 030d9325..65d563be 100644 --- a/cmd/mbf/main.go +++ b/cmd/mbf/main.go @@ -93,7 +93,7 @@ func main() { if !flagLTO { flags |= rosa.OptLLVMNoLTO } - rosa.DropCaches(flags) + rosa.DropCaches("", flags) return nil }).Flag( diff --git a/cmd/mbf/main_test.go b/cmd/mbf/main_test.go index 8f0d0894..a5b33e90 100644 --- a/cmd/mbf/main_test.go +++ b/cmd/mbf/main_test.go @@ -9,7 +9,7 @@ import ( ) func TestMain(m *testing.M) { - rosa.DropCaches(rosa.OptLLVMNoLTO) + rosa.DropCaches("", rosa.OptLLVMNoLTO) os.Exit(m.Run()) } diff --git a/cmd/sharefs/test/configuration.nix b/cmd/sharefs/test/configuration.nix index e1f43b72..05d67dcb 100644 --- a/cmd/sharefs/test/configuration.nix +++ b/cmd/sharefs/test/configuration.nix @@ -20,11 +20,14 @@ }; virtualisation = { + # Hopefully reduces spurious test failures: + memorySize = if pkgs.stdenv.hostPlatform.is32bit then 2046 else 8192; + diskSize = 6 * 1024; qemu.options = [ # Increase test performance: - "-smp 8" + "-smp 16" ]; }; diff --git a/cmd/sharefs/test/default.nix b/cmd/sharefs/test/default.nix index 53ab8fc2..e963eaa6 100644 --- a/cmd/sharefs/test/default.nix +++ b/cmd/sharefs/test/default.nix @@ -28,7 +28,7 @@ testers.nixosTest { # For go tests: (pkgs.writeShellScriptBin "sharefs-workload-hakurei-tests" '' cp -r "${self.packages.${system}.hakurei.src}" "/sdcard/hakurei" && cd "/sdcard/hakurei" - ${fhs}/bin/hakurei-fhs -c 'CC="clang -O3 -Werror" go test ./...' + ${fhs}/bin/hakurei-fhs -c 'ROSA_SKIP_BINFMT=1 CC="clang -O3 -Werror" go test ./...' '') ]; diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index d0d0b366..b91299dd 100644 --- a/internal/pkg/exec.go +++ b/internal/pkg/exec.go @@ -9,8 +9,10 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "slices" "strconv" + "sync" "syscall" "time" "unique" @@ -94,6 +96,32 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath { return ExecPath{check.MustAbs(pathname), a, writable} } +var ( + binfmt map[string]container.BinfmtEntry + binfmtMu sync.RWMutex +) + +// RegisterArch arranges for [KindExec] and [KindExecNet] to support a new +// architecture via a binfmt_misc entry. Each architecture must be registered +// at most once. +func RegisterArch(arch string, e container.BinfmtEntry) { + if arch == "" { + panic(UnsupportedArchError(arch)) + } + + binfmtMu.Lock() + defer binfmtMu.Unlock() + + if binfmt == nil { + binfmt = make(map[string]container.BinfmtEntry) + } + + if _, ok := binfmt[arch]; ok { + panic("attempting to register " + strconv.Quote(arch) + " twice") + } + binfmt[arch] = e +} + const ( // ExecTimeoutDefault replaces out of range [NewExec] timeout values. ExecTimeoutDefault = 15 * time.Minute @@ -110,6 +138,8 @@ type execArtifact struct { // Caller-supplied user-facing reporting name, guaranteed to be nonzero // during initialisation. name string + // Target architecture. + arch string // Caller-supplied inner mount points. paths []ExecPath @@ -178,7 +208,7 @@ func (a *execNetArtifact) Cure(f *FContext) error { // container and does not affect curing outcome. Because of this, it is omitted // from parameter data for computing identifier. func NewExec( - name string, + name, arch string, checksum *Checksum, timeout time.Duration, exclusive bool, @@ -193,13 +223,16 @@ func NewExec( if name == "" { name = "exec-" + filepath.Base(pathname.String()) } + if arch == "" { + arch = runtime.GOARCH + } if timeout <= 0 { timeout = ExecTimeoutDefault } if timeout > ExecTimeoutMax { timeout = ExecTimeoutMax } - a := execArtifact{name, paths, dir, env, pathname, args, timeout, exclusive} + a := execArtifact{name, arch, paths, dir, env, pathname, args, timeout, exclusive} if checksum == nil { return &a } @@ -211,6 +244,7 @@ func (*execArtifact) Kind() Kind { return KindExec } // Params writes paths, executable pathname and args. func (a *execArtifact) Params(ctx *IContext) { + ctx.WriteString(a.arch) ctx.WriteString(a.name) ctx.WriteUint32(uint32(len(a.paths))) @@ -257,11 +291,26 @@ func (a *execArtifact) Params(ctx *IContext) { } } +// UnsupportedArchError describes an unsupported or invalid architecture. +type UnsupportedArchError string + +func (e UnsupportedArchError) Error() string { + if e == "" { + return "invalid architecture name" + } + return "unsupported architecture " + string(e) +} + // readExecArtifact interprets IR values and returns the address of execArtifact // or execNetArtifact. func readExecArtifact(r *IRReader, net bool) Artifact { r.DiscardAll() + arch := r.ReadString() + if arch == "" { + panic(UnsupportedArchError(arch)) + } + name := r.ReadString() sz := r.ReadUint32() @@ -327,7 +376,7 @@ func readExecArtifact(r *IRReader, net bool) Artifact { } return NewExec( - name, checksumP, timeout, exclusive, dir, env, pathname, args, paths..., + name, arch, checksumP, timeout, exclusive, dir, env, pathname, args, paths..., ) } @@ -442,6 +491,17 @@ func (a *execArtifact) makeContainer( z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)}) z.Grow(len(a.paths) + 4) + if a.arch != runtime.GOARCH { + binfmtMu.RLock() + e, ok := binfmt[a.arch] + binfmtMu.RUnlock() + if !ok { + return nil, UnsupportedArchError(a.arch) + } + z.Binfmt = []container.BinfmtEntry{e} + z.InitAsRoot = true + } + for i, b := range a.paths { if i == overlayWorkIndex { if err = os.MkdirAll(work.String(), 0700); err != nil { diff --git a/internal/pkg/exec_test.go b/internal/pkg/exec_test.go index 5d2905d4..73249a56 100644 --- a/internal/pkg/exec_test.go +++ b/internal/pkg/exec_test.go @@ -8,12 +8,15 @@ import ( "net" "os" "os/exec" + "path/filepath" "slices" "testing" "unique" "hakurei.app/check" + "hakurei.app/container" "hakurei.app/hst" + "hakurei.app/internal/info" "hakurei.app/internal/pkg" "hakurei.app/internal/stub" @@ -27,6 +30,17 @@ import ( //go:embed internal/testtool/testtool var testtoolBin []byte +func init() { + pathname, err := filepath.Abs("internal/testtool/testtool") + if err != nil { + panic(err) + } + pkg.RegisterArch("cafe", container.BinfmtEntry{ + Magic: expected.Magic, + Interpreter: check.MustAbs(pathname), + }) +} + func TestExec(t *testing.T) { t.Parallel() @@ -43,7 +57,7 @@ func TestExec(t *testing.T) { cureMany(t, c, []cureStep{ {"container", pkg.NewExec( - "exec-offline", nil, 0, false, + "exec-offline", "", nil, 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), @@ -66,7 +80,7 @@ func TestExec(t *testing.T) { ), ignorePathname, wantOffline, nil}, {"error passthrough", pkg.NewExec( - "", nil, 0, true, + "", "", nil, 0, true, pkg.AbsWork, []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), @@ -89,7 +103,7 @@ func TestExec(t *testing.T) { }}, {"invalid paths", pkg.NewExec( - "", nil, 0, false, + "", "", nil, 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), @@ -102,7 +116,7 @@ func TestExec(t *testing.T) { // check init failure passthrough var exitError *exec.ExitError if _, _, err := c.Cure(pkg.NewExec( - "", nil, 0, false, + "", "", nil, 0, false, pkg.AbsWork, nil, check.MustAbs("/opt/bin/testtool"), @@ -141,7 +155,7 @@ func TestExec(t *testing.T) { } cureMany(t, c, []cureStep{ {"container", pkg.NewExec( - "exec-net", new(wantNet.hash()), 0, false, + "exec-net", "", new(wantNet.hash()), 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), @@ -188,7 +202,7 @@ func TestExec(t *testing.T) { cureMany(t, c, []cureStep{ {"container", pkg.NewExec( - "exec-overlay-root", nil, 0, false, + "exec-overlay-root", "", nil, 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, check.MustAbs("/opt/bin/testtool"), @@ -227,7 +241,7 @@ func TestExec(t *testing.T) { cureMany(t, c, []cureStep{ {"container", pkg.NewExec( - "exec-overlay-work", nil, 0, false, + "exec-overlay-work", "", nil, 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, check.MustAbs("/work/bin/testtool"), @@ -271,7 +285,7 @@ func TestExec(t *testing.T) { cureMany(t, c, []cureStep{ {"container", pkg.NewExec( - "exec-multiple-layers", nil, 0, false, + "exec-multiple-layers", "", nil, 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, check.MustAbs("/opt/bin/testtool"), @@ -342,7 +356,7 @@ func TestExec(t *testing.T) { cureMany(t, c, []cureStep{ {"container", pkg.NewExec( - "exec-layer-promotion", nil, 0, true, + "exec-layer-promotion", "", nil, 0, true, pkg.AbsWork, []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, check.MustAbs("/opt/bin/testtool"), @@ -382,6 +396,69 @@ func TestExec(t *testing.T) { "temp": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700}, }}, + + {"binfmt", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { + if info.CanDegrade && os.Getenv("ROSA_SKIP_BINFMT") != "" { + t.Skip("binfmt_misc test explicitly skipped") + } + + cureMany(t, c, []cureStep{ + {"container", pkg.NewExec( + "exec-binfmt", "cafe", nil, 0, true, + pkg.AbsWork, + []string{"HAKUREI_TEST=1", "HAKUREI_BINFMT=1"}, + check.MustAbs("/opt/bin/sample"), + []string{"sample"}, + + pkg.MustPath("/", true, &stubArtifact{ + kind: pkg.KindTar, + params: []byte("empty directory"), + cure: func(t *pkg.TContext) error { + return os.MkdirAll(t.GetWorkDir().String(), 0700) + }, + }), + pkg.MustPath("/opt", false, overrideIdent{pkg.ID{0xfe, 0xff}, &stubArtifact{ + kind: pkg.KindTar, + cure: func(t *pkg.TContext) error { + work := t.GetWorkDir() + if err := os.MkdirAll( + work.Append("bin").String(), + 0700, + ); err != nil { + return err + } + + return os.WriteFile(t.GetWorkDir().Append( + "bin", + "sample", + ).String(), []byte(expected.Full), 0500) + }, + }}), + ), ignorePathname, expectsFS{ + ".": {Mode: fs.ModeDir | 0500}, + + "check": {Mode: 0400, Data: []byte("binfmt")}, + }, nil}, + }) + }, expectsFS{ + ".": {Mode: fs.ModeDir | 0700}, + + "checksum": {Mode: fs.ModeDir | 0700}, + "checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV": {Mode: fs.ModeDir | 0500}, + "checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV/bin": {Mode: fs.ModeDir | 0700}, + "checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV/bin/sample": {Mode: 0500, Data: []byte("\xca\xfe\xba\xbe\xfd\xfd:3")}, + "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500}, + "checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3": {Mode: fs.ModeDir | 0500}, + "checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3/check": {Mode: 0400, Data: []byte("binfmt")}, + + "identifier": {Mode: fs.ModeDir | 0700}, + "identifier/6VQTJ1lI5BmVuI1YFYJ8ClO3MRORvTTrcWFDcUU-l5Ga8EofxCxGlSTYN-u8dKj_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3")}, + "identifier/_v8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV")}, + "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, + + "temp": {Mode: fs.ModeDir | 0700}, + "work": {Mode: fs.ModeDir | 0700}, + }}, }) } diff --git a/internal/pkg/internal/testtool/expected/doc.go b/internal/pkg/internal/testtool/expected/doc.go deleted file mode 100644 index 53978d26..00000000 --- a/internal/pkg/internal/testtool/expected/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package expected contains expected identifiers for exec artifact tests. -package expected diff --git a/internal/pkg/internal/testtool/expected/expected.go b/internal/pkg/internal/testtool/expected/expected.go new file mode 100644 index 00000000..c0e8e4a2 --- /dev/null +++ b/internal/pkg/internal/testtool/expected/expected.go @@ -0,0 +1,9 @@ +// Package expected contains data shared between test helper and test harness. +package expected + +const ( + // Magic are magic bytes in the binfmt test case. + Magic = "\xca\xfe\xba\xbe\xfd\xfd" + // Full is the full content of the binfmt test case executable. + Full = Magic + ":3" +) diff --git a/internal/pkg/internal/testtool/expected/sum_amd64.go b/internal/pkg/internal/testtool/expected/sum_amd64.go index a9baee09..7ac5d2d5 100644 --- a/internal/pkg/internal/testtool/expected/sum_amd64.go +++ b/internal/pkg/internal/testtool/expected/sum_amd64.go @@ -1,10 +1,10 @@ package expected const ( - Offline = "dztPS6jRjiZtCF4_p8AzfnxGp6obkhrgFVsxdodbKWUoAEVtDz3MykepJB4kI_ks" - OvlRoot = "RdMA-mubnrHuu3Ky1wWyxauSYCO0ZH_zCPUj3uDHqkfwv5sGcByoF_g5PjlGiClb" - Layers = "p1t_drXr34i-jZNuxDMLaMOdL6tZvQqhavNafGynGqxOZoXAUTSn7kqNh3Ovv3DT" - Net = "G8qPxD9puvvoOVV7lrT80eyDeIl3G_CCFoKw12c8mCjMdG1zF7NEPkwYpNubClK3" - Promote = "xXTIYcXmgJWNLC91c417RRrNM9cjELwEZHpGvf8Fk_GNP5agRJp_SicD0w9aMeLJ" - Work = "5hlaukCirnXE4W_RSLJFOZN47Z5RiHnacXzdFp_70cLgiJUGR6cSb_HaFftkzi0-" + Offline = "oe7Uv1u5BwxcuX3HLQzZRg1Q5oetJo6jWiKGMOeqLiqBkaVgyKzvx82N81_IzUAz" + OvlRoot = "NacZGXwuRkTvcHaG08a22ujJ8qCWN0RSoFlRSR5FSt0ZcBbJ28FRvkYsHEtX7G8i" + Layers = "WBJDrATtX6rIE5yAu8ePX3WmDF0Tt9kFiue0m3cRnyRoVx1my8a67fh3CAW486oP" + Net = "CmYtj2sNB3LHtqiDuck_Lz3MjLLIiwyP8N4NDitQ1Icvv__LVP9p8tm-sHeQaKKp" + Promote = "TX3eCloaQFkV-SZIH6Jg6E5WKH--rcXY1P0jnZKmLFKWrNqnOzd4G9eIBh6i5ywN" + Work = "OuNiLSC68pZhAOr1YQ4WbV1tzASA0nxLEBcK7lO7MqxDY_j8dmP_C612RTuF23Lu" ) diff --git a/internal/pkg/internal/testtool/main.go b/internal/pkg/internal/testtool/main.go index 6b665d9d..cc8f9e49 100644 --- a/internal/pkg/internal/testtool/main.go +++ b/internal/pkg/internal/testtool/main.go @@ -24,6 +24,19 @@ func main() { log.SetFlags(0) log.SetPrefix("testtool: ") + if os.Getenv("HAKUREI_BINFMT") == "1" { + wantArgs := []string{"/interpreter", "/opt/bin/sample"} + if !slices.Equal(os.Args, wantArgs) { + log.Fatalf("Args: %q, want %q", os.Args, wantArgs) + } + + if err := os.WriteFile("check", []byte("binfmt"), 0400); err != nil { + log.Fatal(err) + } + + return + } + environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool { return s == "CURE_JOBS="+strconv.Itoa(runtime.NumCPU()) }) diff --git a/internal/pkg/ir_test.go b/internal/pkg/ir_test.go index 5c6c3a2e..15c8b634 100644 --- a/internal/pkg/ir_test.go +++ b/internal/pkg/ir_test.go @@ -39,7 +39,7 @@ func TestIRRoundtrip(t *testing.T) { )}, {"exec offline", pkg.NewExec( - "exec-offline", nil, 0, false, + "exec-offline", "", nil, 0, false, pkg.AbsWork, []string{"HAKUREI_TEST=1"}, check.MustAbs("/opt/bin/testtool"), @@ -59,7 +59,7 @@ func TestIRRoundtrip(t *testing.T) { )}, {"exec net", pkg.NewExec( - "exec-net", + "exec-net", "", (*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))), 0, false, pkg.AbsWork, diff --git a/internal/pkg/pkg_test.go b/internal/pkg/pkg_test.go index 04bd665f..75f14a0e 100644 --- a/internal/pkg/pkg_test.go +++ b/internal/pkg/pkg_test.go @@ -1289,6 +1289,11 @@ func TestErrors(t *testing.T) { {"UnsupportedVariantError", pkg.UnsupportedVariantError( "rosa", ), `unsupported variant "rosa"`}, + + {"UnsupportedArchError zero", pkg.UnsupportedArchError(""), + "invalid architecture name"}, + {"UnsupportedArchError", pkg.UnsupportedArchError("riscv64"), + "unsupported architecture riscv64"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/rosa/all.go b/internal/rosa/all.go index 8fee7867..c8eddd7f 100644 --- a/internal/rosa/all.go +++ b/internal/rosa/all.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "runtime" "strconv" "sync" @@ -347,6 +348,9 @@ var ( // artifactsOnce is for lazy initialisation of artifacts. artifactsOnce [_toolchainEnd][len(artifactsM)]sync.Once + // arch is the target architecture. + arch = runtime.GOARCH + // presetOpts globally modifies behaviour of presets. presetOpts int ) @@ -358,6 +362,9 @@ const ( OptLLVMNoLTO ) +// Arch returns the target architecture. +func Arch() string { return arch } + // Flags returns the current preset flags func Flags() int { return presetOpts } @@ -367,7 +374,12 @@ func zero[T any](p *T) { var v T; *p = v } // DropCaches arranges for all cached [pkg.Artifact] to be freed some time after // it returns. Must not be used concurrently with any other function from this // package. -func DropCaches(flags int) { +func DropCaches(targetArch string, flags int) { + if targetArch == "" { + targetArch = runtime.GOARCH + } + + arch = targetArch presetOpts = flags zero(&artifacts) zero(&artifactsOnce) diff --git a/internal/rosa/all_test.go b/internal/rosa/all_test.go index 52eabf39..8f86201a 100644 --- a/internal/rosa/all_test.go +++ b/internal/rosa/all_test.go @@ -20,8 +20,8 @@ func TestLoad(t *testing.T) { } func BenchmarkAll(b *testing.B) { - flags := rosa.Flags() - b.Cleanup(func() { rosa.DropCaches(flags) }) + arch, flags := rosa.Arch(), rosa.Flags() + b.Cleanup(func() { rosa.DropCaches(arch, flags) }) for b.Loop() { for i := range rosa.PresetEnd { @@ -29,7 +29,7 @@ func BenchmarkAll(b *testing.B) { } b.StopTimer() - rosa.DropCaches(0) + rosa.DropCaches("", 0) b.StartTimer() } } diff --git a/internal/rosa/busybox.go b/internal/rosa/busybox.go index 14247f05..437e05e4 100644 --- a/internal/rosa/busybox.go +++ b/internal/rosa/busybox.go @@ -105,7 +105,7 @@ func newBusyboxBin() pkg.Artifact { } return pkg.NewExec( - "busybox-bin-"+version, nil, pkg.ExecTimeoutMax, false, + "busybox-bin-"+version, arch, nil, pkg.ExecTimeoutMax, false, fhs.AbsRoot, []string{ "PATH=/system/bin", }, diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go index e1d20407..fb4e8a4b 100644 --- a/internal/rosa/rosa.go +++ b/internal/rosa/rosa.go @@ -327,7 +327,7 @@ mkdir -vp /work/system/bin } return pkg.NewExec( - name, knownChecksum, pkg.ExecTimeoutMax, flag&TExclusive != 0, + name, arch, knownChecksum, pkg.ExecTimeoutMax, flag&TExclusive != 0, fhs.AbsRoot, env, AbsSystem.Append("bin", "sh"), []string{"sh", absCureScript.String()}, diff --git a/internal/rosa/rosa_test.go b/internal/rosa/rosa_test.go index b9f8206a..0eef8334 100644 --- a/internal/rosa/rosa_test.go +++ b/internal/rosa/rosa_test.go @@ -28,7 +28,7 @@ var ( ) func TestMain(m *testing.M) { - rosa.DropCaches(rosa.OptLLVMNoLTO) + rosa.DropCaches("", rosa.OptLLVMNoLTO) container.TryArgv0(nil) code := m.Run() @@ -94,14 +94,14 @@ func TestCureAll(t *testing.T) { } func BenchmarkStage3(b *testing.B) { - flags := rosa.Flags() - b.Cleanup(func() { rosa.DropCaches(flags) }) + arch, flags := rosa.Arch(), rosa.Flags() + b.Cleanup(func() { rosa.DropCaches(arch, flags) }) for b.Loop() { rosa.Std.Load(rosa.LLVM) b.StopTimer() - rosa.DropCaches(0) + rosa.DropCaches("", 0) b.StartTimer() } }