diff --git a/ui/build/config.go b/ui/build/config.go index 2b22012de..5cf6ea838 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -1529,6 +1529,10 @@ func (c *configImpl) SoongMakeVarsMk() string { return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk") } +func (c *configImpl) SoongBuildMetrics() string { + return filepath.Join(c.OutDir(), "soong_build_metrics.pb") +} + func (c *configImpl) ProductOut() string { return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice()) } diff --git a/ui/build/soong.go b/ui/build/soong.go index ac9bf3a60..782a1b3bd 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -23,9 +23,11 @@ import ( "strings" "sync" "sync/atomic" + "time" "android/soong/bazel" "android/soong/ui/metrics" + "android/soong/ui/metrics/metrics_proto" "android/soong/ui/status" "android/soong/shared" @@ -33,6 +35,8 @@ import ( "github.com/google/blueprint" "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/microfactory" + + "google.golang.org/protobuf/proto" ) const ( @@ -717,8 +721,12 @@ func runSoong(ctx Context, config Config) { targets = append(targets, config.SoongNinjaFile()) } + beforeSoongTimestamp := time.Now() + ninja(targets...) + loadSoongBuildMetrics(ctx, config, beforeSoongTimestamp) + distGzipFile(ctx, config, config.SoongNinjaFile(), "soong") distFile(ctx, config, config.SoongVarsFile(), "soong") @@ -732,6 +740,42 @@ func runSoong(ctx Context, config Config) { } } +// loadSoongBuildMetrics reads out/soong_build_metrics.pb if it was generated by soong_build and copies the +// events stored in it into the soong_ui trace to provide introspection into how long the different phases of +// soong_build are taking. +func loadSoongBuildMetrics(ctx Context, config Config, oldTimestamp time.Time) { + soongBuildMetricsFile := config.SoongBuildMetrics() + if metricsStat, err := os.Stat(soongBuildMetricsFile); err != nil { + ctx.Verbosef("Failed to stat %s: %s", soongBuildMetricsFile, err) + return + } else if !metricsStat.ModTime().After(oldTimestamp) { + ctx.Verbosef("%s timestamp not later after running soong, expected %s > %s", + soongBuildMetricsFile, metricsStat.ModTime(), oldTimestamp) + return + } + + metricsData, err := os.ReadFile(config.SoongBuildMetrics()) + if err != nil { + ctx.Verbosef("Failed to read %s: %s", soongBuildMetricsFile, err) + return + } + + soongBuildMetrics := metrics_proto.SoongBuildMetrics{} + err = proto.Unmarshal(metricsData, &soongBuildMetrics) + if err != nil { + ctx.Verbosef("Failed to unmarshal %s: %s", soongBuildMetricsFile, err) + return + } + for _, event := range soongBuildMetrics.Events { + desc := event.GetDescription() + if dot := strings.LastIndexByte(desc, '.'); dot >= 0 { + desc = desc[dot+1:] + } + ctx.Tracer.Complete(desc, ctx.Thread, + event.GetStartTime(), event.GetStartTime()+event.GetRealTime()) + } +} + func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) { ctx.BeginTrace(metrics.RunSoong, name) defer ctx.EndTrace()