Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit afdd267f authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add RBE metrics dump in Soong UI."

parents cee7e66b 62f1bf23
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ func main() {
		config.Parallel(), config.RemoteParallel(), config.HighmemParallel())

	defer met.Dump(soongMetricsFile)
	defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)

	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
		if !strings.HasSuffix(start, "N") {
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ bootstrap_go_package {
        "cleanbuild_test.go",
        "config_test.go",
        "environment_test.go",
        "rbe_test.go",
        "upload_test.go",
        "util_test.go",
        "proc_sync_test.go",
+9 −0
Original line number Diff line number Diff line
@@ -804,6 +804,15 @@ func (c *configImpl) StartRBE() bool {
	return true
}

func (c *configImpl) RBEStatsOutputDir() string {
	for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
		if v, ok := c.environ.Get(f); ok {
			return v
		}
	}
	return ""
}

func (c *configImpl) UseRemoteBuild() bool {
	return c.UseGoma() || c.UseRBE()
}
+70 −13
Original line number Diff line number Diff line
@@ -15,14 +15,39 @@
package build

import (
	"os"
	"path/filepath"

	"android/soong/ui/metrics"
)

const bootstrapCmd = "bootstrap"
const rbeLeastNProcs = 2500
const rbeLeastNFiles = 16000
const (
	rbeLeastNProcs = 2500
	rbeLeastNFiles = 16000

	// prebuilt RBE binaries
	bootstrapCmd = "bootstrap"

	// RBE metrics proto buffer file
	rbeMetricsPBFilename = "rbe_metrics.pb"
)

func rbeCommand(ctx Context, config Config, rbeCmd string) string {
	var cmdPath string
	if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
		cmdPath = filepath.Join(rbeDir, rbeCmd)
	} else if home, ok := config.Environment().Get("HOME"); ok {
		cmdPath = filepath.Join(home, "rbe", rbeCmd)
	} else {
		ctx.Fatalf("rbe command path not found")
	}

	if _, err := os.Stat(cmdPath); err != nil && os.IsNotExist(err) {
		ctx.Fatalf("rbe command %q not found", rbeCmd)
	}

	return cmdPath
}

func startRBE(ctx Context, config Config) {
	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
@@ -35,18 +60,50 @@ func startRBE(ctx Context, config Config) {
		ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
	}

	var rbeBootstrap string
	if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
		rbeBootstrap = filepath.Join(rbeDir, bootstrapCmd)
	} else if home, ok := config.Environment().Get("HOME"); ok {
		rbeBootstrap = filepath.Join(home, "rbe", bootstrapCmd)
	} else {
		ctx.Fatalln("rbe bootstrap not found")
	}

	cmd := Command(ctx, config, "boostrap", rbeBootstrap)
	cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))

	if output, err := cmd.CombinedOutput(); err != nil {
		ctx.Fatalf("rbe bootstrap failed with: %v\n%s\n", err, output)
	}
}

func stopRBE(ctx Context, config Config) {
	cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
	if output, err := cmd.CombinedOutput(); err != nil {
		ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
	}
}

// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
// The protobuf file is created if RBE is enabled and the proxy service has
// started. The proxy service is shutdown in order to dump the RBE metrics to the
// protobuf file.
func DumpRBEMetrics(ctx Context, config Config, filename string) {
	ctx.BeginTrace(metrics.RunShutdownTool, "dump_rbe_metrics")
	defer ctx.EndTrace()

	// Remove the previous metrics file in case there is a failure or RBE has been
	// disable for this run.
	os.Remove(filename)

	// If RBE is not enabled then there are no metrics to generate.
	// If RBE does not require to start, the RBE proxy maybe started
	// manually for debugging purpose and can generate the metrics
	// afterwards.
	if !config.StartRBE() {
		return
	}

	outputDir := config.RBEStatsOutputDir()
	if outputDir == "" {
		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
	}
	metricsFile := filepath.Join(outputDir, rbeMetricsPBFilename)

	// Stop the proxy first in order to generate the RBE metrics protobuf file.
	stopRBE(ctx, config)

	if _, err := copyFile(metricsFile, filename); err != nil {
		ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
	}
}

ui/build/rbe_test.go

0 → 100644
+142 −0
Original line number Diff line number Diff line
// Copyright 2020 Google Inc. All rights reserved.
//
// 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 build

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"android/soong/ui/logger"
)

func TestDumpRBEMetrics(t *testing.T) {
	ctx := testContext()
	tests := []struct {
		description string
		env         []string
		generated   bool
	}{{
		description: "RBE disabled",
		env: []string{
			"NOSTART_RBE=true",
		},
	}, {
		description: "rbe metrics generated",
		env: []string{
			"USE_RBE=true",
		},
		generated: true,
	}}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			tmpDir := t.TempDir()

			rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
			if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(rbeBootstrapProgram), 0755); err != nil {
				t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
			}

			env := Environment(tt.env)
			env.Set("OUT_DIR", tmpDir)
			env.Set("RBE_DIR", tmpDir)
			env.Set("RBE_output_dir", t.TempDir())
			config := Config{&configImpl{
				environ: &env,
			}}

			rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
			DumpRBEMetrics(ctx, config, rbeMetricsFilename)

			// Validate that the rbe metrics file exists if RBE is enabled.
			if _, err := os.Stat(rbeMetricsFilename); err == nil {
				if !tt.generated {
					t.Errorf("got true, want false for rbe metrics file %s to exist.", rbeMetricsFilename)
				}
			} else if os.IsNotExist(err) {
				if tt.generated {
					t.Errorf("got false, want true for rbe metrics file %s to exist.", rbeMetricsFilename)
				}
			} else {
				t.Errorf("unknown error found on checking %s exists: %v", rbeMetricsFilename, err)
			}
		})
	}
}

func TestDumpRBEMetricsErrors(t *testing.T) {
	ctx := testContext()
	tests := []struct {
		description         string
		rbeOutputDirDefined bool
		bootstrapProgram    string
		expectedErr         string
	}{{
		description:      "output_dir not defined",
		bootstrapProgram: rbeBootstrapProgram,
		expectedErr:      "RBE output dir variable not defined",
	}, {
		description:         "stopRBE failed",
		rbeOutputDirDefined: true,
		bootstrapProgram:    "#!/bin/bash\nexit 1",
		expectedErr:         "shutdown failed",
	}, {
		description:         "failed to copy metrics file",
		rbeOutputDirDefined: true,
		bootstrapProgram:    "#!/bin/bash",
		expectedErr:         "failed to copy",
	}}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			defer logger.Recover(func(err error) {
				got := err.Error()
				if !strings.Contains(got, tt.expectedErr) {
					t.Errorf("got %q, want %q to be contained in error", got, tt.expectedErr)
				}
			})

			tmpDir := t.TempDir()

			rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
			if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(tt.bootstrapProgram), 0755); err != nil {
				t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
			}

			env := &Environment{}
			env.Set("USE_RBE", "true")
			env.Set("OUT_DIR", tmpDir)
			env.Set("RBE_DIR", tmpDir)

			if tt.rbeOutputDirDefined {
				env.Set("RBE_output_dir", t.TempDir())
			}

			config := Config{&configImpl{
				environ: env,
			}}

			rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
			DumpRBEMetrics(ctx, config, rbeMetricsFilename)
			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
		})
	}
}

var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s", rbeMetricsPBFilename)
Loading