metrics.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package bp2build
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "android/soong/android"
  8. "android/soong/shared"
  9. "android/soong/ui/metrics/bp2build_metrics_proto"
  10. "google.golang.org/protobuf/proto"
  11. "github.com/google/blueprint"
  12. )
  13. // CodegenMetrics represents information about the Blueprint-to-BUILD
  14. // conversion process.
  15. // Use CreateCodegenMetrics() to get a properly initialized instance
  16. type CodegenMetrics struct {
  17. serialized *bp2build_metrics_proto.Bp2BuildMetrics
  18. // List of modules with unconverted deps
  19. // NOTE: NOT in the .proto
  20. moduleWithUnconvertedDepsMsgs []string
  21. // List of modules with missing deps
  22. // NOTE: NOT in the .proto
  23. moduleWithMissingDepsMsgs []string
  24. // Map of converted modules and paths to call
  25. // NOTE: NOT in the .proto
  26. convertedModulePathMap map[string]string
  27. }
  28. func CreateCodegenMetrics() CodegenMetrics {
  29. return CodegenMetrics{
  30. serialized: &bp2build_metrics_proto.Bp2BuildMetrics{
  31. RuleClassCount: make(map[string]uint64),
  32. ConvertedModuleTypeCount: make(map[string]uint64),
  33. TotalModuleTypeCount: make(map[string]uint64),
  34. UnconvertedModules: make(map[string]*bp2build_metrics_proto.UnconvertedReason),
  35. },
  36. convertedModulePathMap: make(map[string]string),
  37. }
  38. }
  39. // Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics
  40. func (metrics *CodegenMetrics) Serialize() *bp2build_metrics_proto.Bp2BuildMetrics {
  41. return metrics.serialized
  42. }
  43. // Print the codegen metrics to stdout.
  44. func (metrics *CodegenMetrics) Print() {
  45. generatedTargetCount := uint64(0)
  46. for _, ruleClass := range android.SortedKeys(metrics.serialized.RuleClassCount) {
  47. count := metrics.serialized.RuleClassCount[ruleClass]
  48. fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
  49. generatedTargetCount += count
  50. }
  51. fmt.Printf(
  52. `[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules.
  53. %d converted modules have unconverted deps:
  54. %s
  55. %d converted modules have missing deps:
  56. %s
  57. `,
  58. metrics.serialized.GeneratedModuleCount,
  59. generatedTargetCount,
  60. metrics.serialized.HandCraftedModuleCount,
  61. metrics.TotalModuleCount(),
  62. len(metrics.moduleWithUnconvertedDepsMsgs),
  63. strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"),
  64. len(metrics.moduleWithMissingDepsMsgs),
  65. strings.Join(metrics.moduleWithMissingDepsMsgs, "\n\t"),
  66. )
  67. }
  68. const bp2buildMetricsFilename = "bp2build_metrics.pb"
  69. // fail prints $PWD to stderr, followed by the given printf string and args (vals),
  70. // then the given alert, and then exits with 1 for failure
  71. func fail(err error, alertFmt string, vals ...interface{}) {
  72. cwd, wderr := os.Getwd()
  73. if wderr != nil {
  74. cwd = "FAILED TO GET $PWD: " + wderr.Error()
  75. }
  76. fmt.Fprintf(os.Stderr, "\nIn "+cwd+":\n"+alertFmt+"\n"+err.Error()+"\n", vals...)
  77. os.Exit(1)
  78. }
  79. // Write the bp2build-protoized codegen metrics into the given directory
  80. func (metrics *CodegenMetrics) Write(dir string) {
  81. if _, err := os.Stat(dir); os.IsNotExist(err) {
  82. // The metrics dir doesn't already exist, so create it (and parents)
  83. if err := os.MkdirAll(dir, 0755); err != nil { // rx for all; w for user
  84. fail(err, "Failed to `mkdir -p` %s", dir)
  85. }
  86. } else if err != nil {
  87. fail(err, "Failed to `stat` %s", dir)
  88. }
  89. metricsFile := filepath.Join(dir, bp2buildMetricsFilename)
  90. if err := metrics.dump(metricsFile); err != nil {
  91. fail(err, "Error outputting %s", metricsFile)
  92. }
  93. if _, err := os.Stat(metricsFile); err != nil {
  94. if os.IsNotExist(err) {
  95. fail(err, "MISSING BP2BUILD METRICS OUTPUT: %s", metricsFile)
  96. } else {
  97. fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile)
  98. }
  99. }
  100. }
  101. // ReadCodegenMetrics loads CodegenMetrics from `dir`
  102. // returns a nil pointer if the file doesn't exist
  103. func ReadCodegenMetrics(dir string) *CodegenMetrics {
  104. metricsFile := filepath.Join(dir, bp2buildMetricsFilename)
  105. if _, err := os.Stat(metricsFile); err != nil {
  106. if os.IsNotExist(err) {
  107. return nil
  108. } else {
  109. fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile)
  110. panic("unreachable after fail")
  111. }
  112. }
  113. if buf, err := os.ReadFile(metricsFile); err != nil {
  114. fail(err, "FAILED TO READ BP2BUILD METRICS OUTPUT: %s", metricsFile)
  115. panic("unreachable after fail")
  116. } else {
  117. bp2BuildMetrics := bp2build_metrics_proto.Bp2BuildMetrics{
  118. RuleClassCount: make(map[string]uint64),
  119. ConvertedModuleTypeCount: make(map[string]uint64),
  120. TotalModuleTypeCount: make(map[string]uint64),
  121. }
  122. if err := proto.Unmarshal(buf, &bp2BuildMetrics); err != nil {
  123. fail(err, "FAILED TO PARSE BP2BUILD METRICS OUTPUT: %s", metricsFile)
  124. }
  125. return &CodegenMetrics{
  126. serialized: &bp2BuildMetrics,
  127. convertedModulePathMap: make(map[string]string),
  128. }
  129. }
  130. }
  131. func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
  132. metrics.serialized.RuleClassCount[ruleClass] += 1
  133. }
  134. func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) {
  135. metrics.serialized.Events = append(metrics.serialized.Events, event)
  136. }
  137. func (metrics *CodegenMetrics) SetSymlinkCount(n uint64) {
  138. if m := metrics.serialized.WorkspaceSymlinkCount; m != 0 {
  139. fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceSymlinkCount of %d", m)
  140. }
  141. metrics.serialized.WorkspaceSymlinkCount = n
  142. }
  143. func (metrics *CodegenMetrics) SetMkDirCount(n uint64) {
  144. if m := metrics.serialized.WorkspaceMkDirCount; m != 0 {
  145. fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceDirCount of %d", m)
  146. }
  147. metrics.serialized.WorkspaceMkDirCount = n
  148. }
  149. func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
  150. return metrics.serialized.HandCraftedModuleCount +
  151. metrics.serialized.GeneratedModuleCount +
  152. metrics.serialized.UnconvertedModuleCount
  153. }
  154. // Dump serializes the metrics to the given filename
  155. func (metrics *CodegenMetrics) dump(filename string) (err error) {
  156. ser := metrics.Serialize()
  157. return shared.Save(ser, filename)
  158. }
  159. type ConversionType int
  160. const (
  161. Generated ConversionType = iota
  162. Handcrafted
  163. )
  164. func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string) {
  165. //a package module has empty name
  166. if moduleType == "package" {
  167. return
  168. }
  169. // Undo prebuilt_ module name prefix modifications
  170. moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
  171. metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
  172. metrics.convertedModulePathMap[moduleName] = "//" + dir
  173. metrics.serialized.ConvertedModuleTypeCount[moduleType] += 1
  174. metrics.serialized.TotalModuleTypeCount[moduleType] += 1
  175. metrics.serialized.GeneratedModuleCount += 1
  176. }
  177. func (metrics *CodegenMetrics) AddUnconvertedModule(m blueprint.Module, moduleType string, dir string,
  178. reason android.UnconvertedReason) {
  179. //a package module has empty name
  180. if moduleType == "package" {
  181. return
  182. }
  183. // Undo prebuilt_ module name prefix modifications
  184. moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
  185. metrics.serialized.UnconvertedModules[moduleName] = &bp2build_metrics_proto.UnconvertedReason{
  186. Type: bp2build_metrics_proto.UnconvertedReasonType(reason.ReasonType),
  187. Detail: reason.Detail,
  188. }
  189. metrics.serialized.UnconvertedModuleCount += 1
  190. metrics.serialized.TotalModuleTypeCount[moduleType] += 1
  191. if reason.ReasonType == int(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE) {
  192. metrics.serialized.HandCraftedModuleCount += 1
  193. }
  194. }