include symlink metrics in bp2build_metrics.pb

Bug: b/256212479
Test: Prior to thi CL bp2build.symlink_forest event was missing in bp2build_metrics.pb after a clean mixed build
Change-Id: I53bfc4114a383c0d1f9c4c7945e7d4c69bc50b0c
This commit is contained in:
usta
2022-10-28 23:32:01 -04:00
parent 9b019134c2
commit 4f5d2c1e97
7 changed files with 98 additions and 75 deletions

View File

@@ -26,7 +26,7 @@ import (
// Codegen is the backend of bp2build. The code generator is responsible for // Codegen is the backend of bp2build. The code generator is responsible for
// writing .bzl files that are equivalent to Android.bp files that are capable // writing .bzl files that are equivalent to Android.bp files that are capable
// of being built with Bazel. // of being built with Bazel.
func Codegen(ctx *CodegenContext) CodegenMetrics { func Codegen(ctx *CodegenContext) *CodegenMetrics {
// This directory stores BUILD files that could be eventually checked-in. // This directory stores BUILD files that could be eventually checked-in.
bp2buildDir := android.PathForOutput(ctx, "bp2build") bp2buildDir := android.PathForOutput(ctx, "bp2build")
if err := android.RemoveAllOutputDir(bp2buildDir); err != nil { if err := android.RemoveAllOutputDir(bp2buildDir); err != nil {
@@ -48,7 +48,7 @@ func Codegen(ctx *CodegenContext) CodegenMetrics {
soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName) soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics)) writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
return res.metrics return &res.metrics
} }
// Get the output directory and create it if it doesn't exist. // Get the output directory and create it if it doesn't exist.

View File

@@ -28,7 +28,6 @@ import (
"android/soong/android" "android/soong/android"
"android/soong/bazel" "android/soong/bazel"
"android/soong/starlark_fmt" "android/soong/starlark_fmt"
"github.com/google/blueprint" "github.com/google/blueprint"
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
) )
@@ -245,12 +244,7 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers
buildFileToTargets := make(map[string]BazelTargets) buildFileToTargets := make(map[string]BazelTargets)
// Simple metrics tracking for bp2build // Simple metrics tracking for bp2build
metrics := CodegenMetrics{ metrics := CreateCodegenMetrics()
ruleClassCount: make(map[string]uint64),
convertedModulePathMap: make(map[string]string),
convertedModuleTypeCount: make(map[string]uint64),
totalModuleTypeCount: make(map[string]uint64),
}
dirs := make(map[string]bool) dirs := make(map[string]bool)

View File

@@ -32,7 +32,7 @@ func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []Baz
files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package. files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
files = append(files, newFile("apex_toolchain", "constants.bzl", apex.BazelApexToolchainVars())) files = append(files, newFile("apex_toolchain", "constants.bzl", apex.BazelApexToolchainVars()))
files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n"))) files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t") convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t")
if err != nil { if err != nil {
@@ -55,10 +55,6 @@ func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []Baz
return files return files
} }
func convertedModules(convertedModules []string) string {
return strings.Join(convertedModules, "\n")
}
func CreateBazelFiles( func CreateBazelFiles(
cfg android.Config, cfg android.Config,
ruleShims map[string]RuleShim, ruleShims map[string]RuleShim,

View File

@@ -84,7 +84,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte)) testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
files := CreateSoongInjectionFiles(testConfig, CodegenMetrics{}) files := CreateSoongInjectionFiles(testConfig, CreateCodegenMetrics())
expectedFilePaths := []bazelFilepath{ expectedFilePaths := []bazelFilepath{
{ {

View File

@@ -9,25 +9,16 @@ import (
"android/soong/android" "android/soong/android"
"android/soong/shared" "android/soong/shared"
"android/soong/ui/metrics/bp2build_metrics_proto" "android/soong/ui/metrics/bp2build_metrics_proto"
"google.golang.org/protobuf/proto"
"github.com/google/blueprint" "github.com/google/blueprint"
) )
// Simple metrics struct to collect information about a Blueprint to BUILD // CodegenMetrics represents information about the Blueprint-to-BUILD
// conversion process. // conversion process.
// Use CreateCodegenMetrics() to get a properly initialized instance
type CodegenMetrics struct { type CodegenMetrics struct {
// Total number of Soong modules converted to generated targets serialized *bp2build_metrics_proto.Bp2BuildMetrics
generatedModuleCount uint64
// Total number of Soong modules converted to handcrafted targets
handCraftedModuleCount uint64
// Total number of unconverted Soong modules
unconvertedModuleCount uint64
// Counts of generated Bazel targets per Bazel rule class
ruleClassCount map[string]uint64
// List of modules with unconverted deps // List of modules with unconverted deps
// NOTE: NOT in the .proto // NOTE: NOT in the .proto
moduleWithUnconvertedDepsMsgs []string moduleWithUnconvertedDepsMsgs []string
@@ -36,40 +27,32 @@ type CodegenMetrics struct {
// NOTE: NOT in the .proto // NOTE: NOT in the .proto
moduleWithMissingDepsMsgs []string moduleWithMissingDepsMsgs []string
// List of converted modules
convertedModules []string
// Map of converted modules and paths to call // Map of converted modules and paths to call
// NOTE: NOT in the .proto
convertedModulePathMap map[string]string convertedModulePathMap map[string]string
}
// Counts of converted modules by module type. func CreateCodegenMetrics() CodegenMetrics {
convertedModuleTypeCount map[string]uint64 return CodegenMetrics{
serialized: &bp2build_metrics_proto.Bp2BuildMetrics{
// Counts of total modules by module type. RuleClassCount: make(map[string]uint64),
totalModuleTypeCount map[string]uint64 ConvertedModuleTypeCount: make(map[string]uint64),
TotalModuleTypeCount: make(map[string]uint64),
Events []*bp2build_metrics_proto.Event },
convertedModulePathMap: make(map[string]string),
}
} }
// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics // Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics
func (metrics *CodegenMetrics) Serialize() bp2build_metrics_proto.Bp2BuildMetrics { func (metrics *CodegenMetrics) Serialize() *bp2build_metrics_proto.Bp2BuildMetrics {
return bp2build_metrics_proto.Bp2BuildMetrics{ return metrics.serialized
GeneratedModuleCount: metrics.generatedModuleCount,
HandCraftedModuleCount: metrics.handCraftedModuleCount,
UnconvertedModuleCount: metrics.unconvertedModuleCount,
RuleClassCount: metrics.ruleClassCount,
ConvertedModules: metrics.convertedModules,
ConvertedModuleTypeCount: metrics.convertedModuleTypeCount,
TotalModuleTypeCount: metrics.totalModuleTypeCount,
Events: metrics.Events,
}
} }
// Print the codegen metrics to stdout. // Print the codegen metrics to stdout.
func (metrics *CodegenMetrics) Print() { func (metrics *CodegenMetrics) Print() {
generatedTargetCount := uint64(0) generatedTargetCount := uint64(0)
for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) { for _, ruleClass := range android.SortedStringKeys(metrics.serialized.RuleClassCount) {
count := metrics.ruleClassCount[ruleClass] count := metrics.serialized.RuleClassCount[ruleClass]
fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count) fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
generatedTargetCount += count generatedTargetCount += count
} }
@@ -80,9 +63,9 @@ func (metrics *CodegenMetrics) Print() {
%d converted modules have missing deps: %d converted modules have missing deps:
%s %s
`, `,
metrics.generatedModuleCount, metrics.serialized.GeneratedModuleCount,
generatedTargetCount, generatedTargetCount,
metrics.handCraftedModuleCount, metrics.serialized.HandCraftedModuleCount,
metrics.TotalModuleCount(), metrics.TotalModuleCount(),
len(metrics.moduleWithUnconvertedDepsMsgs), len(metrics.moduleWithUnconvertedDepsMsgs),
strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"), strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"),
@@ -119,29 +102,67 @@ func (metrics *CodegenMetrics) Write(dir string) {
fail(err, "Error outputting %s", metricsFile) fail(err, "Error outputting %s", metricsFile)
} }
if _, err := os.Stat(metricsFile); err != nil { if _, err := os.Stat(metricsFile); err != nil {
fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile) if os.IsNotExist(err) {
fail(err, "MISSING BP2BUILD METRICS OUTPUT: %s", metricsFile)
} else {
fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile)
}
}
}
// ReadCodegenMetrics loads CodegenMetrics from `dir`
// returns a nil pointer if the file doesn't exist
func ReadCodegenMetrics(dir string) *CodegenMetrics {
metricsFile := filepath.Join(dir, bp2buildMetricsFilename)
if _, err := os.Stat(metricsFile); err != nil {
if os.IsNotExist(err) {
return nil
} else {
fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile)
panic("unreachable after fail")
}
}
if buf, err := os.ReadFile(metricsFile); err != nil {
fail(err, "FAILED TO READ BP2BUILD METRICS OUTPUT: %s", metricsFile)
panic("unreachable after fail")
} else {
bp2BuildMetrics := bp2build_metrics_proto.Bp2BuildMetrics{
RuleClassCount: make(map[string]uint64),
ConvertedModuleTypeCount: make(map[string]uint64),
TotalModuleTypeCount: make(map[string]uint64),
}
if err := proto.Unmarshal(buf, &bp2BuildMetrics); err != nil {
fail(err, "FAILED TO PARSE BP2BUILD METRICS OUTPUT: %s", metricsFile)
}
return &CodegenMetrics{
serialized: &bp2BuildMetrics,
convertedModulePathMap: make(map[string]string),
}
} }
} }
func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) { func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
metrics.ruleClassCount[ruleClass] += 1 metrics.serialized.RuleClassCount[ruleClass] += 1
} }
func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) {
metrics.serialized.Events = append(metrics.serialized.Events, event)
}
func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) { func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
metrics.unconvertedModuleCount += 1 metrics.serialized.UnconvertedModuleCount += 1
metrics.totalModuleTypeCount[moduleType] += 1 metrics.serialized.TotalModuleTypeCount[moduleType] += 1
} }
func (metrics *CodegenMetrics) TotalModuleCount() uint64 { func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
return metrics.handCraftedModuleCount + return metrics.serialized.HandCraftedModuleCount +
metrics.generatedModuleCount + metrics.serialized.GeneratedModuleCount +
metrics.unconvertedModuleCount metrics.serialized.UnconvertedModuleCount
} }
// Dump serializes the metrics to the given filename // Dump serializes the metrics to the given filename
func (metrics *CodegenMetrics) dump(filename string) (err error) { func (metrics *CodegenMetrics) dump(filename string) (err error) {
ser := metrics.Serialize() ser := metrics.Serialize()
return shared.Save(&ser, filename) return shared.Save(ser, filename)
} }
type ConversionType int type ConversionType int
@@ -154,14 +175,14 @@ const (
func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) { func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
// Undo prebuilt_ module name prefix modifications // Undo prebuilt_ module name prefix modifications
moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name()) moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
metrics.convertedModules = append(metrics.convertedModules, moduleName) metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
metrics.convertedModulePathMap[moduleName] = "//" + dir metrics.convertedModulePathMap[moduleName] = "//" + dir
metrics.convertedModuleTypeCount[moduleType] += 1 metrics.serialized.ConvertedModuleTypeCount[moduleType] += 1
metrics.totalModuleTypeCount[moduleType] += 1 metrics.serialized.TotalModuleTypeCount[moduleType] += 1
if conversionType == Handcrafted { if conversionType == Handcrafted {
metrics.handCraftedModuleCount += 1 metrics.serialized.HandCraftedModuleCount += 1
} else if conversionType == Generated { } else if conversionType == Generated {
metrics.generatedModuleCount += 1 metrics.serialized.GeneratedModuleCount += 1
} }
} }

View File

@@ -651,13 +651,23 @@ func runSymlinkForestCreation(configuration android.Config, extraNinjaDeps []str
writeDepFile(symlinkForestMarker, eventHandler, ninjaDeps) writeDepFile(symlinkForestMarker, eventHandler, ninjaDeps)
touch(shared.JoinPath(topDir, symlinkForestMarker)) touch(shared.JoinPath(topDir, symlinkForestMarker))
metricsDir := configuration.Getenv("LOG_DIR")
codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
if codegenMetrics == nil {
m := bp2build.CreateCodegenMetrics()
codegenMetrics = &m
} else {
//TODO (usta) we cannot determine if we loaded a stale file, i.e. from an unrelated prior
//invocation of codegen. We should simply use a separate .pb file
}
writeBp2BuildMetrics(codegenMetrics, configuration, eventHandler)
} }
// Run Soong in the bp2build mode. This creates a standalone context that registers // Run Soong in the bp2build mode. This creates a standalone context that registers
// an alternate pipeline of mutators and singletons specifically for generating // an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files. // Bazel BUILD files instead of Ninja files.
func runBp2Build(configuration android.Config, extraNinjaDeps []string) { func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
var codegenMetrics bp2build.CodegenMetrics var codegenMetrics *bp2build.CodegenMetrics
eventHandler := metrics.EventHandler{} eventHandler := metrics.EventHandler{}
eventHandler.Do("bp2build", func() { eventHandler.Do("bp2build", func() {
@@ -706,15 +716,14 @@ func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
if configuration.IsEnvTrue("BP2BUILD_VERBOSE") { if configuration.IsEnvTrue("BP2BUILD_VERBOSE") {
codegenMetrics.Print() codegenMetrics.Print()
} }
writeBp2BuildMetrics(&codegenMetrics, configuration, eventHandler) writeBp2BuildMetrics(codegenMetrics, configuration, eventHandler)
} }
// Write Bp2Build metrics into $LOG_DIR // Write Bp2Build metrics into $LOG_DIR
func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics, func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics,
configuration android.Config, eventHandler metrics.EventHandler) { configuration android.Config, eventHandler metrics.EventHandler) {
for _, event := range eventHandler.CompletedEvents() { for _, event := range eventHandler.CompletedEvents() {
codegenMetrics.Events = append(codegenMetrics.Events, codegenMetrics.AddEvent(&bp2build_metrics_proto.Event{
&bp2build_metrics_proto.Event{
Name: event.Id, Name: event.Id,
StartTime: uint64(event.Start.UnixNano()), StartTime: uint64(event.Start.UnixNano()),
RealTime: event.RuntimeNanoseconds(), RealTime: event.RuntimeNanoseconds(),

View File

@@ -18,6 +18,7 @@ package build
// another. // another.
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@@ -56,7 +57,9 @@ func pruneMetricsFiles(paths []string) []string {
} }
if fi.IsDir() { if fi.IsDir() {
if l, err := ioutil.ReadDir(p); err == nil { if l, err := ioutil.ReadDir(p); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to find files under %s\n", p)
} else {
files := make([]string, 0, len(l)) files := make([]string, 0, len(l))
for _, fi := range l { for _, fi := range l {
files = append(files, filepath.Join(p, fi.Name())) files = append(files, filepath.Join(p, fi.Name()))