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

Commit 984db51f authored by Patrice Arruda's avatar Patrice Arruda Committed by Automerger Merge Worker
Browse files

Run the metrics uploader in the background. am: de44afac

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/12463371

Change-Id: Iaad1cf2c1142d0a95140f479bd479195d6c67418
parents e2eadc44 de44afac
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -171,7 +171,7 @@ func main() {
	buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
	defer build.UploadMetrics(buildCtx, config, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)
	defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)

	os.MkdirAll(logsDir, 0777)
	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
+39 −6
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import (
	"path/filepath"
	"time"

	"android/soong/ui/metrics"
	"github.com/golang/protobuf/proto"

	upload_proto "android/soong/ui/metrics/upload_proto"
@@ -32,11 +33,21 @@ const (
	uploadPbFilename = ".uploader.pb"
)

var (
	// For testing purpose
	getTmpDir = ioutil.TempDir
)

// UploadMetrics uploads a set of metrics files to a server for analysis. An
// uploader full path is required to be specified in order to upload the set
// of metrics files. This is accomplished by defining the ANDROID_ENABLE_METRICS_UPLOAD
// environment variable.
func UploadMetrics(ctx Context, config Config, buildStartedMilli int64, files ...string) {
// environment variable. The metrics files are copied to a temporary directory
// and the uploader is then executed in the background to allow the user to continue
// working.
func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStartedMilli int64, files ...string) {
	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
	defer ctx.EndTrace()

	uploader := config.MetricsUploaderApp()
	// No metrics to upload if the path to the uploader was not specified.
	if uploader == "" {
@@ -56,6 +67,22 @@ func UploadMetrics(ctx Context, config Config, buildStartedMilli int64, files ..
		return
	}

	// The temporary directory cannot be deleted as the metrics uploader is started
	// in the background and requires to exist until the operation is done. The
	// uploader can delete the directory as it is specified in the upload proto.
	tmpDir, err := getTmpDir("", "upload_metrics")
	if err != nil {
		ctx.Fatalf("failed to create a temporary directory to store the list of metrics files: %v\n", err)
	}

	for i, src := range metricsFiles {
		dst := filepath.Join(tmpDir, filepath.Base(src))
		if _, err := copyFile(src, dst); err != nil {
			ctx.Fatalf("failed to copy %q to %q: %v\n", src, dst, err)
		}
		metricsFiles[i] = dst
	}

	// For platform builds, the branch and target name is hardcoded to specific
	// values for later extraction of the metrics in the data metrics pipeline.
	data, err := proto.Marshal(&upload_proto.Upload{
@@ -64,17 +91,23 @@ func UploadMetrics(ctx Context, config Config, buildStartedMilli int64, files ..
		BranchName:            proto.String("developer-metrics"),
		TargetName:            proto.String("platform-build-systems-metrics"),
		MetricsFiles:          metricsFiles,
		DirectoriesToDelete:   []string{tmpDir},
	})
	if err != nil {
		ctx.Fatalf("failed to marshal metrics upload proto buffer message: %v\n", err)
	}

	pbFile := filepath.Join(config.OutDir(), uploadPbFilename)
	pbFile := filepath.Join(tmpDir, uploadPbFilename)
	if err := ioutil.WriteFile(pbFile, data, 0644); err != nil {
		ctx.Fatalf("failed to write the marshaled metrics upload protobuf to %q: %v\n", pbFile, err)
	}
	// Remove the upload file as it's not longer needed after it has been processed by the uploader.
	defer os.Remove(pbFile)

	Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile).RunAndStreamOrFatal()
	// Start the uploader in the background as it takes several milliseconds to start the uploader
	// and prepare the metrics for upload. This affects small commands like "lunch".
	cmd := Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile)
	if forceDumbOutput {
		cmd.RunOrFatal()
	} else {
		cmd.RunAndStreamOrFatal()
	}
}
+67 −29
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package build

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
@@ -61,6 +62,22 @@ func TestUploadMetrics(t *testing.T) {
			}
			defer os.RemoveAll(outDir)

			// Supply our own getTmpDir to delete the temp dir once the test is done.
			orgGetTmpDir := getTmpDir
			getTmpDir = func(string, string) (string, error) {
				retDir := filepath.Join(outDir, "tmp_upload_dir")
				if err := os.Mkdir(retDir, 0755); err != nil {
					t.Fatalf("failed to create temporary directory %q: %v", retDir, err)
				}
				return retDir, nil
			}
			defer func() { getTmpDir = orgGetTmpDir }()

			metricsUploadDir := filepath.Join(outDir, ".metrics_uploader")
			if err := os.Mkdir(metricsUploadDir, 0755); err != nil {
				t.Fatalf("failed to create %q directory for oauth valid check: %v", metricsUploadDir, err)
			}

			var metricsFiles []string
			if tt.createFiles {
				for _, f := range tt.files {
@@ -80,21 +97,34 @@ func TestUploadMetrics(t *testing.T) {
				buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
			}}

			UploadMetrics(ctx, config, 1591031903, metricsFiles...)

			if _, err := os.Stat(filepath.Join(outDir, uploadPbFilename)); err == nil {
				t.Error("got true, want false for upload protobuf file to exist")
			}
			UploadMetrics(ctx, config, false, 1591031903, metricsFiles...)
		})
	}
}

func TestUploadMetricsErrors(t *testing.T) {
	expectedErr := "failed to write the marshaled"
	ctx := testContext()
	tests := []struct {
		description string
		tmpDir      string
		tmpDirErr   error
		expectedErr string
	}{{
		description: "getTmpDir returned error",
		tmpDirErr:   errors.New("getTmpDir failed"),
		expectedErr: "getTmpDir failed",
	}, {
		description: "copyFile operation error",
		tmpDir:      "/fake_dir",
		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, expectedErr) {
			t.Errorf("got %q, want %q to be contained in error", got, expectedErr)
				if !strings.Contains(got, tt.expectedErr) {
					t.Errorf("got %q, want %q to be contained in error", got, tt.expectedErr)
				}
			})

@@ -104,6 +134,12 @@ func TestUploadMetricsErrors(t *testing.T) {
			}
			defer os.RemoveAll(outDir)

			orgGetTmpDir := getTmpDir
			getTmpDir = func(string, string) (string, error) {
				return tt.tmpDir, tt.tmpDirErr
			}
			defer func() { getTmpDir = orgGetTmpDir }()

			metricsFile := filepath.Join(outDir, "metrics_file_1")
			if err := ioutil.WriteFile(metricsFile, []byte("test file"), 0644); err != nil {
				t.Fatalf("failed to create a fake metrics file %q for uploading: %v", metricsFile, err)
@@ -115,6 +151,8 @@ func TestUploadMetricsErrors(t *testing.T) {
					"OUT_DIR=/bad",
				}}}

	UploadMetrics(testContext(), config, 1591031903, metricsFile)
	t.Errorf("got nil, expecting %q as a failure", expectedErr)
			UploadMetrics(ctx, config, true, 1591031903, metricsFile)
			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
		})
	}
}
+18 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package build

import (
	"io"
	"os"
	"path/filepath"
	"strings"
@@ -124,3 +125,20 @@ func decodeKeyValue(str string) (string, string, bool) {
	}
	return str[:idx], str[idx+1:], true
}

// copyFile copies a file from src to dst. filepath.Dir(dst) must exist.
func copyFile(src, dst string) (int64, error) {
	source, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer source.Close()

	destination, err := os.Create(dst)
	if err != nil {
		return 0, err
	}
	defer destination.Close()

	return io.Copy(destination, source)
}
+70 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package build

import (
	"bytes"
	"io/ioutil"
	"os"
	"path/filepath"
@@ -49,3 +50,72 @@ func TestEnsureEmptyDirs(t *testing.T) {

	ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a"))
}

func TestCopyFile(t *testing.T) {
	tmpDir, err := ioutil.TempDir("", "test_copy_file")
	if err != nil {
		t.Fatalf("failed to create temporary directory to hold test text files: %v", err)
	}
	defer os.Remove(tmpDir)

	data := []byte("fake data")
	src := filepath.Join(tmpDir, "src.txt")
	if err := ioutil.WriteFile(src, data, 0755); err != nil {
		t.Fatalf("failed to create a src file %q for copying: %v", src, err)
	}

	dst := filepath.Join(tmpDir, "dst.txt")

	l, err := copyFile(src, dst)
	if err != nil {
		t.Fatalf("got %v, expecting nil error on copyFile operation", err)
	}

	if l != int64(len(data)) {
		t.Errorf("got %d, expecting %d for copied bytes", l, len(data))
	}

	dstData, err := ioutil.ReadFile(dst)
	if err != nil {
		t.Fatalf("got %v, expecting nil error reading dst %q file", err, dst)
	}

	if bytes.Compare(data, dstData) != 0 {
		t.Errorf("got %q, expecting data %q from dst %q text file", string(data), string(dstData), dst)
	}
}

func TestCopyFileErrors(t *testing.T) {
	tmpDir, err := ioutil.TempDir("", "test_copy_file_errors")
	if err != nil {
		t.Fatalf("failed to create temporary directory to hold test text files: %v", err)
	}
	defer os.Remove(tmpDir)

	srcExists := filepath.Join(tmpDir, "src_exist.txt")
	if err := ioutil.WriteFile(srcExists, []byte("fake data"), 0755); err != nil {
		t.Fatalf("failed to create a src file %q for copying: %v", srcExists, err)
	}

	tests := []struct {
		description string
		src         string
		dst         string
	}{{
		description: "src file does not exist",
		src:         "/src/not/exist",
		dst:         "/dst/not/exist",
	}, {
		description: "dst directory does not exist",
		src:         srcExists,
		dst:         "/dst/not/exist",
	}}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			if _, err := copyFile(tt.src, tt.dst); err == nil {
				t.Errorf("got nil, expecting error")
			}
		})
	}
}
Loading