1// Copyright 2018 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package metrics represents the metrics system for Android Platform Build Systems. 16package metrics 17 18// This is the main heart of the metrics system for Android Platform Build Systems. 19// The starting of the soong_ui (cmd/soong_ui/main.go), the metrics system is 20// initialized by the invocation of New and is then stored in the context 21// (ui/build/context.go) to be used throughout the system. During the build 22// initialization phase, several functions in this file are invoked to store 23// information such as the environment, build configuration and build metadata. 24// There are several scoped code that has Begin() and defer End() functions 25// that captures the metrics and is them added as a perfInfo into the set 26// of the collected metrics. Finally, when soong_ui has finished the build, 27// the defer Dump function is invoked to store the collected metrics to the 28// raw protobuf file in the $OUT directory and this raw protobuf file will be 29// uploaded to the destination. See ui/build/upload.go for more details. The 30// filename of the raw protobuf file and the list of files to be uploaded is 31// defined in cmd/soong_ui/main.go. See ui/metrics/event.go for the explanation 32// of what an event is and how the metrics system is a stack based system. 33 34import ( 35 "fmt" 36 "os" 37 "runtime" 38 "strings" 39 "time" 40 41 "android/soong/shared" 42 43 "google.golang.org/protobuf/proto" 44 45 soong_metrics_proto "android/soong/ui/metrics/metrics_proto" 46 mk_metrics_proto "android/soong/ui/metrics/mk_metrics_proto" 47) 48 49const ( 50 // Below is a list of names passed in to the Begin tracing functions. These 51 // names are used to group a set of metrics. 52 53 // Setup and tear down of the build systems. 54 RunSetupTool = "setup" 55 RunShutdownTool = "shutdown" 56 TestRun = "test" 57 58 // List of build system tools. 59 RunSoong = "soong" 60 PrimaryNinja = "ninja" 61 RunKati = "kati" 62 RunBazel = "bazel" 63 64 // Overall build from building the graph to building the target. 65 Total = "total" 66) 67 68// Metrics is a struct that stores collected metrics during the course of a 69// build. It is later dumped to protobuf files. See underlying metrics protos 70// for further details on what information is collected. 71type Metrics struct { 72 // Protobuf containing various top-level build metrics. These include: 73 // 1. Build identifiers (ex: branch ID, requested product, hostname, 74 // originating command) 75 // 2. Per-subprocess top-level metrics (ex: ninja process IO and runtime). 76 // Note that, since these metrics are reported by soong_ui, there is little 77 // insight that can be provided into performance breakdowns of individual 78 // subprocesses. 79 metrics soong_metrics_proto.MetricsBase 80 81 // Protobuf containing metrics pertaining to number of makefiles in a build. 82 mkMetrics mk_metrics_proto.MkMetrics 83 84 // A list of pending build events. 85 EventTracer *EventTracer 86} 87 88// New returns a pointer of Metrics to store a set of metrics. 89func New() (metrics *Metrics) { 90 m := &Metrics{ 91 metrics: soong_metrics_proto.MetricsBase{}, 92 mkMetrics: mk_metrics_proto.MkMetrics{}, 93 EventTracer: &EventTracer{}, 94 } 95 return m 96} 97 98func (m *Metrics) SetTotalMakefiles(total int) { 99 m.mkMetrics.TotalMakefiles = uint32(total) 100} 101 102func (m *Metrics) SetToplevelMakefiles(total int) { 103 m.mkMetrics.ToplevelMakefiles = uint32(total) 104} 105 106func (m *Metrics) DumpMkMetrics(outPath string) { 107 shared.Save(&m.mkMetrics, outPath) 108} 109 110// SetTimeMetrics stores performance information from an executed block of 111// code. 112func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) { 113 switch perf.GetName() { 114 case RunKati: 115 m.metrics.KatiRuns = append(m.metrics.KatiRuns, &perf) 116 case RunSoong: 117 m.metrics.SoongRuns = append(m.metrics.SoongRuns, &perf) 118 case RunBazel: 119 m.metrics.BazelRuns = append(m.metrics.BazelRuns, &perf) 120 case PrimaryNinja: 121 m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf) 122 case RunSetupTool: 123 m.metrics.SetupTools = append(m.metrics.SetupTools, &perf) 124 case Total: 125 m.metrics.Total = &perf 126 } 127} 128 129func (m *Metrics) SetCriticalPathInfo(criticalPathInfo soong_metrics_proto.CriticalPathInfo) { 130 m.metrics.CriticalPathInfo = &criticalPathInfo 131} 132 133// SetFatalOrPanicMessage stores a non-zero exit and the relevant message in the latest event if 134// available or the metrics base. 135func (m *Metrics) SetFatalOrPanicMessage(errMsg string) { 136 if m == nil { 137 return 138 } 139 if event := m.EventTracer.peek(); event != nil { 140 event.nonZeroExitCode = true 141 event.errorMsg = &errMsg 142 } else { 143 m.metrics.ErrorMessage = proto.String(errMsg) 144 } 145 m.metrics.NonZeroExit = proto.Bool(true) 146} 147 148// BuildConfig stores information about the build configuration. 149func (m *Metrics) BuildConfig(b *soong_metrics_proto.BuildConfig) { 150 m.metrics.BuildConfig = b 151} 152 153// SystemResourceInfo stores information related to the host system such 154// as total CPU and memory. 155func (m *Metrics) SystemResourceInfo(b *soong_metrics_proto.SystemResourceInfo) { 156 m.metrics.SystemResourceInfo = b 157} 158 159// ExpConfigFetcher stores information about the expconfigfetcher. 160func (m *Metrics) ExpConfigFetcher(b *soong_metrics_proto.ExpConfigFetcher) { 161 m.metrics.ExpConfigFetcher = b 162} 163 164// SetMetadataMetrics sets information about the build such as the target 165// product, host architecture and out directory. 166func (m *Metrics) SetMetadataMetrics(metadata map[string]string) { 167 for k, v := range metadata { 168 switch k { 169 case "BUILD_ID": 170 m.metrics.BuildId = proto.String(v) 171 case "PLATFORM_VERSION_CODENAME": 172 m.metrics.PlatformVersionCodename = proto.String(v) 173 case "TARGET_PRODUCT": 174 m.metrics.TargetProduct = proto.String(v) 175 case "TARGET_BUILD_VARIANT": 176 switch v { 177 case "user": 178 m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_USER.Enum() 179 case "userdebug": 180 m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_USERDEBUG.Enum() 181 case "eng": 182 m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_ENG.Enum() 183 } 184 case "TARGET_ARCH": 185 m.metrics.TargetArch = arch(v) 186 case "TARGET_ARCH_VARIANT": 187 m.metrics.TargetArchVariant = proto.String(v) 188 case "TARGET_CPU_VARIANT": 189 m.metrics.TargetCpuVariant = proto.String(v) 190 case "HOST_ARCH": 191 m.metrics.HostArch = arch(v) 192 case "HOST_2ND_ARCH": 193 m.metrics.Host_2NdArch = arch(v) 194 case "HOST_OS_EXTRA": 195 m.metrics.HostOsExtra = proto.String(v) 196 case "HOST_CROSS_OS": 197 m.metrics.HostCrossOs = proto.String(v) 198 case "HOST_CROSS_ARCH": 199 m.metrics.HostCrossArch = proto.String(v) 200 case "HOST_CROSS_2ND_ARCH": 201 m.metrics.HostCross_2NdArch = proto.String(v) 202 case "OUT_DIR": 203 m.metrics.OutDir = proto.String(v) 204 } 205 } 206} 207 208// arch returns the corresponding MetricsBase_Arch based on the string 209// parameter. 210func arch(a string) *soong_metrics_proto.MetricsBase_Arch { 211 switch a { 212 case "arm": 213 return soong_metrics_proto.MetricsBase_ARM.Enum() 214 case "arm64": 215 return soong_metrics_proto.MetricsBase_ARM64.Enum() 216 case "x86": 217 return soong_metrics_proto.MetricsBase_X86.Enum() 218 case "x86_64": 219 return soong_metrics_proto.MetricsBase_X86_64.Enum() 220 default: 221 return soong_metrics_proto.MetricsBase_UNKNOWN.Enum() 222 } 223} 224 225// SetBuildDateTime sets the build date and time. The value written 226// to the protobuf file is in seconds. 227func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) { 228 m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second)) 229} 230 231func (m *Metrics) UpdateTotalRealTime(data []byte) error { 232 if err := proto.Unmarshal(data, &m.metrics); err != nil { 233 return fmt.Errorf("Failed to unmarshal proto", err) 234 } 235 startTime := *m.metrics.Total.StartTime 236 endTime := uint64(time.Now().UnixNano()) 237 238 *m.metrics.Total.RealTime = *proto.Uint64(endTime - startTime) 239 return nil 240} 241 242// SetBuildCommand adds the build command specified by the user to the 243// list of collected metrics. 244func (m *Metrics) SetBuildCommand(cmd []string) { 245 m.metrics.BuildCommand = proto.String(strings.Join(cmd, " ")) 246} 247 248// Dump exports the collected metrics from the executed build to the file at 249// out path. 250func (m *Metrics) Dump(out string) error { 251 // ignore the error if the hostname could not be retrieved as it 252 // is not a critical metric to extract. 253 if hostname, err := os.Hostname(); err == nil { 254 m.metrics.Hostname = proto.String(hostname) 255 } 256 m.metrics.HostOs = proto.String(runtime.GOOS) 257 258 return shared.Save(&m.metrics, out) 259} 260 261// SetSoongBuildMetrics sets the metrics collected from the soong_build 262// execution. 263func (m *Metrics) SetSoongBuildMetrics(metrics *soong_metrics_proto.SoongBuildMetrics) { 264 m.metrics.SoongBuildMetrics = metrics 265} 266 267// A CriticalUserJourneysMetrics is a struct that contains critical user journey 268// metrics. These critical user journeys are defined under cuj/cuj.go file. 269type CriticalUserJourneysMetrics struct { 270 // A list of collected CUJ metrics. 271 cujs soong_metrics_proto.CriticalUserJourneysMetrics 272} 273 274// NewCriticalUserJourneyMetrics returns a pointer of CriticalUserJourneyMetrics 275// to capture CUJs metrics. 276func NewCriticalUserJourneysMetrics() *CriticalUserJourneysMetrics { 277 return &CriticalUserJourneysMetrics{} 278} 279 280// Add adds a set of collected metrics from an executed critical user journey. 281func (c *CriticalUserJourneysMetrics) Add(name string, metrics *Metrics) { 282 c.cujs.Cujs = append(c.cujs.Cujs, &soong_metrics_proto.CriticalUserJourneyMetrics{ 283 Name: proto.String(name), 284 Metrics: &metrics.metrics, 285 }) 286} 287 288// Dump saves the collected CUJs metrics to the raw protobuf file. 289func (c *CriticalUserJourneysMetrics) Dump(filename string) (err error) { 290 return shared.Save(&c.cujs, filename) 291} 292