1*333d2b36SAndroid Build Coastguard Worker// Copyright 2020 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage android 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "bytes" 19*333d2b36SAndroid Build Coastguard Worker "io/ioutil" 20*333d2b36SAndroid Build Coastguard Worker "os" 21*333d2b36SAndroid Build Coastguard Worker "runtime" 22*333d2b36SAndroid Build Coastguard Worker "strconv" 23*333d2b36SAndroid Build Coastguard Worker "time" 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/metrics" 26*333d2b36SAndroid Build Coastguard Worker "google.golang.org/protobuf/proto" 27*333d2b36SAndroid Build Coastguard Worker 28*333d2b36SAndroid Build Coastguard Worker soong_metrics_proto "android/soong/ui/metrics/metrics_proto" 29*333d2b36SAndroid Build Coastguard Worker) 30*333d2b36SAndroid Build Coastguard Worker 31*333d2b36SAndroid Build Coastguard Workervar soongMetricsOnceKey = NewOnceKey("soong metrics") 32*333d2b36SAndroid Build Coastguard Worker 33*333d2b36SAndroid Build Coastguard Workertype soongMetrics struct { 34*333d2b36SAndroid Build Coastguard Worker modules int 35*333d2b36SAndroid Build Coastguard Worker variants int 36*333d2b36SAndroid Build Coastguard Worker perfCollector perfCollector 37*333d2b36SAndroid Build Coastguard Worker} 38*333d2b36SAndroid Build Coastguard Worker 39*333d2b36SAndroid Build Coastguard Workertype perfCollector struct { 40*333d2b36SAndroid Build Coastguard Worker events []*soong_metrics_proto.PerfCounters 41*333d2b36SAndroid Build Coastguard Worker stop chan<- bool 42*333d2b36SAndroid Build Coastguard Worker} 43*333d2b36SAndroid Build Coastguard Worker 44*333d2b36SAndroid Build Coastguard Workerfunc getSoongMetrics(config Config) *soongMetrics { 45*333d2b36SAndroid Build Coastguard Worker return config.Once(soongMetricsOnceKey, func() interface{} { 46*333d2b36SAndroid Build Coastguard Worker return &soongMetrics{} 47*333d2b36SAndroid Build Coastguard Worker }).(*soongMetrics) 48*333d2b36SAndroid Build Coastguard Worker} 49*333d2b36SAndroid Build Coastguard Worker 50*333d2b36SAndroid Build Coastguard Workerfunc init() { 51*333d2b36SAndroid Build Coastguard Worker RegisterParallelSingletonType("soong_metrics", soongMetricsSingletonFactory) 52*333d2b36SAndroid Build Coastguard Worker} 53*333d2b36SAndroid Build Coastguard Worker 54*333d2b36SAndroid Build Coastguard Workerfunc soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} } 55*333d2b36SAndroid Build Coastguard Worker 56*333d2b36SAndroid Build Coastguard Workertype soongMetricsSingleton struct{} 57*333d2b36SAndroid Build Coastguard Worker 58*333d2b36SAndroid Build Coastguard Workerfunc (soongMetricsSingleton) GenerateBuildActions(ctx SingletonContext) { 59*333d2b36SAndroid Build Coastguard Worker metrics := getSoongMetrics(ctx.Config()) 60*333d2b36SAndroid Build Coastguard Worker ctx.VisitAllModules(func(m Module) { 61*333d2b36SAndroid Build Coastguard Worker if ctx.PrimaryModule(m) == m { 62*333d2b36SAndroid Build Coastguard Worker metrics.modules++ 63*333d2b36SAndroid Build Coastguard Worker } 64*333d2b36SAndroid Build Coastguard Worker metrics.variants++ 65*333d2b36SAndroid Build Coastguard Worker }) 66*333d2b36SAndroid Build Coastguard Worker} 67*333d2b36SAndroid Build Coastguard Worker 68*333d2b36SAndroid Build Coastguard Workerfunc collectMetrics(config Config, eventHandler *metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics { 69*333d2b36SAndroid Build Coastguard Worker metrics := &soong_metrics_proto.SoongBuildMetrics{} 70*333d2b36SAndroid Build Coastguard Worker 71*333d2b36SAndroid Build Coastguard Worker soongMetrics := getSoongMetrics(config) 72*333d2b36SAndroid Build Coastguard Worker if soongMetrics.modules > 0 { 73*333d2b36SAndroid Build Coastguard Worker metrics.Modules = proto.Uint32(uint32(soongMetrics.modules)) 74*333d2b36SAndroid Build Coastguard Worker metrics.Variants = proto.Uint32(uint32(soongMetrics.variants)) 75*333d2b36SAndroid Build Coastguard Worker } 76*333d2b36SAndroid Build Coastguard Worker 77*333d2b36SAndroid Build Coastguard Worker soongMetrics.perfCollector.stop <- true 78*333d2b36SAndroid Build Coastguard Worker metrics.PerfCounters = soongMetrics.perfCollector.events 79*333d2b36SAndroid Build Coastguard Worker 80*333d2b36SAndroid Build Coastguard Worker memStats := runtime.MemStats{} 81*333d2b36SAndroid Build Coastguard Worker runtime.ReadMemStats(&memStats) 82*333d2b36SAndroid Build Coastguard Worker metrics.MaxHeapSize = proto.Uint64(memStats.HeapSys) 83*333d2b36SAndroid Build Coastguard Worker metrics.TotalAllocCount = proto.Uint64(memStats.Mallocs) 84*333d2b36SAndroid Build Coastguard Worker metrics.TotalAllocSize = proto.Uint64(memStats.TotalAlloc) 85*333d2b36SAndroid Build Coastguard Worker 86*333d2b36SAndroid Build Coastguard Worker for _, event := range eventHandler.CompletedEvents() { 87*333d2b36SAndroid Build Coastguard Worker perfInfo := soong_metrics_proto.PerfInfo{ 88*333d2b36SAndroid Build Coastguard Worker Description: proto.String(event.Id), 89*333d2b36SAndroid Build Coastguard Worker Name: proto.String("soong_build"), 90*333d2b36SAndroid Build Coastguard Worker StartTime: proto.Uint64(uint64(event.Start.UnixNano())), 91*333d2b36SAndroid Build Coastguard Worker RealTime: proto.Uint64(event.RuntimeNanoseconds()), 92*333d2b36SAndroid Build Coastguard Worker } 93*333d2b36SAndroid Build Coastguard Worker metrics.Events = append(metrics.Events, &perfInfo) 94*333d2b36SAndroid Build Coastguard Worker } 95*333d2b36SAndroid Build Coastguard Worker 96*333d2b36SAndroid Build Coastguard Worker return metrics 97*333d2b36SAndroid Build Coastguard Worker} 98*333d2b36SAndroid Build Coastguard Worker 99*333d2b36SAndroid Build Coastguard Workerfunc StartBackgroundMetrics(config Config) { 100*333d2b36SAndroid Build Coastguard Worker perfCollector := &getSoongMetrics(config).perfCollector 101*333d2b36SAndroid Build Coastguard Worker stop := make(chan bool) 102*333d2b36SAndroid Build Coastguard Worker perfCollector.stop = stop 103*333d2b36SAndroid Build Coastguard Worker 104*333d2b36SAndroid Build Coastguard Worker previousTime := time.Now() 105*333d2b36SAndroid Build Coastguard Worker previousCpuTime := readCpuTime() 106*333d2b36SAndroid Build Coastguard Worker 107*333d2b36SAndroid Build Coastguard Worker ticker := time.NewTicker(time.Second) 108*333d2b36SAndroid Build Coastguard Worker 109*333d2b36SAndroid Build Coastguard Worker go func() { 110*333d2b36SAndroid Build Coastguard Worker for { 111*333d2b36SAndroid Build Coastguard Worker select { 112*333d2b36SAndroid Build Coastguard Worker case <-stop: 113*333d2b36SAndroid Build Coastguard Worker ticker.Stop() 114*333d2b36SAndroid Build Coastguard Worker return 115*333d2b36SAndroid Build Coastguard Worker case <-ticker.C: 116*333d2b36SAndroid Build Coastguard Worker // carry on 117*333d2b36SAndroid Build Coastguard Worker } 118*333d2b36SAndroid Build Coastguard Worker 119*333d2b36SAndroid Build Coastguard Worker currentTime := time.Now() 120*333d2b36SAndroid Build Coastguard Worker 121*333d2b36SAndroid Build Coastguard Worker var memStats runtime.MemStats 122*333d2b36SAndroid Build Coastguard Worker runtime.ReadMemStats(&memStats) 123*333d2b36SAndroid Build Coastguard Worker 124*333d2b36SAndroid Build Coastguard Worker currentCpuTime := readCpuTime() 125*333d2b36SAndroid Build Coastguard Worker 126*333d2b36SAndroid Build Coastguard Worker interval := currentTime.Sub(previousTime) 127*333d2b36SAndroid Build Coastguard Worker intervalCpuTime := currentCpuTime - previousCpuTime 128*333d2b36SAndroid Build Coastguard Worker intervalCpuPercent := intervalCpuTime * 100 / interval 129*333d2b36SAndroid Build Coastguard Worker 130*333d2b36SAndroid Build Coastguard Worker // heapAlloc is the memory that has been allocated on the heap but not yet GC'd. It may be referenced, 131*333d2b36SAndroid Build Coastguard Worker // or unrefenced but not yet GC'd. 132*333d2b36SAndroid Build Coastguard Worker heapAlloc := memStats.HeapAlloc 133*333d2b36SAndroid Build Coastguard Worker // heapUnused is the memory that was previously used by the heap, but is currently not used. It does not 134*333d2b36SAndroid Build Coastguard Worker // count memory that was used and then returned to the OS. 135*333d2b36SAndroid Build Coastguard Worker heapUnused := memStats.HeapIdle - memStats.HeapReleased 136*333d2b36SAndroid Build Coastguard Worker // heapOverhead is the memory used by the allocator and GC 137*333d2b36SAndroid Build Coastguard Worker heapOverhead := memStats.MSpanSys + memStats.MCacheSys + memStats.GCSys 138*333d2b36SAndroid Build Coastguard Worker // otherMem is the memory used outside of the heap. 139*333d2b36SAndroid Build Coastguard Worker otherMem := memStats.Sys - memStats.HeapSys - heapOverhead 140*333d2b36SAndroid Build Coastguard Worker 141*333d2b36SAndroid Build Coastguard Worker perfCollector.events = append(perfCollector.events, &soong_metrics_proto.PerfCounters{ 142*333d2b36SAndroid Build Coastguard Worker Time: proto.Uint64(uint64(currentTime.UnixNano())), 143*333d2b36SAndroid Build Coastguard Worker Groups: []*soong_metrics_proto.PerfCounterGroup{ 144*333d2b36SAndroid Build Coastguard Worker { 145*333d2b36SAndroid Build Coastguard Worker Name: proto.String("cpu"), 146*333d2b36SAndroid Build Coastguard Worker Counters: []*soong_metrics_proto.PerfCounter{ 147*333d2b36SAndroid Build Coastguard Worker {Name: proto.String("cpu_percent"), Value: proto.Int64(int64(intervalCpuPercent))}, 148*333d2b36SAndroid Build Coastguard Worker }, 149*333d2b36SAndroid Build Coastguard Worker }, { 150*333d2b36SAndroid Build Coastguard Worker Name: proto.String("memory"), 151*333d2b36SAndroid Build Coastguard Worker Counters: []*soong_metrics_proto.PerfCounter{ 152*333d2b36SAndroid Build Coastguard Worker {Name: proto.String("heap_alloc"), Value: proto.Int64(int64(heapAlloc))}, 153*333d2b36SAndroid Build Coastguard Worker {Name: proto.String("heap_unused"), Value: proto.Int64(int64(heapUnused))}, 154*333d2b36SAndroid Build Coastguard Worker {Name: proto.String("heap_overhead"), Value: proto.Int64(int64(heapOverhead))}, 155*333d2b36SAndroid Build Coastguard Worker {Name: proto.String("other"), Value: proto.Int64(int64(otherMem))}, 156*333d2b36SAndroid Build Coastguard Worker }, 157*333d2b36SAndroid Build Coastguard Worker }, 158*333d2b36SAndroid Build Coastguard Worker }, 159*333d2b36SAndroid Build Coastguard Worker }) 160*333d2b36SAndroid Build Coastguard Worker 161*333d2b36SAndroid Build Coastguard Worker previousTime = currentTime 162*333d2b36SAndroid Build Coastguard Worker previousCpuTime = currentCpuTime 163*333d2b36SAndroid Build Coastguard Worker } 164*333d2b36SAndroid Build Coastguard Worker }() 165*333d2b36SAndroid Build Coastguard Worker} 166*333d2b36SAndroid Build Coastguard Worker 167*333d2b36SAndroid Build Coastguard Workerfunc readCpuTime() time.Duration { 168*333d2b36SAndroid Build Coastguard Worker if runtime.GOOS != "linux" { 169*333d2b36SAndroid Build Coastguard Worker return 0 170*333d2b36SAndroid Build Coastguard Worker } 171*333d2b36SAndroid Build Coastguard Worker 172*333d2b36SAndroid Build Coastguard Worker stat, err := os.ReadFile("/proc/self/stat") 173*333d2b36SAndroid Build Coastguard Worker if err != nil { 174*333d2b36SAndroid Build Coastguard Worker return 0 175*333d2b36SAndroid Build Coastguard Worker } 176*333d2b36SAndroid Build Coastguard Worker 177*333d2b36SAndroid Build Coastguard Worker endOfComm := bytes.LastIndexByte(stat, ')') 178*333d2b36SAndroid Build Coastguard Worker if endOfComm < 0 || endOfComm > len(stat)-2 { 179*333d2b36SAndroid Build Coastguard Worker return 0 180*333d2b36SAndroid Build Coastguard Worker } 181*333d2b36SAndroid Build Coastguard Worker 182*333d2b36SAndroid Build Coastguard Worker stat = stat[endOfComm+2:] 183*333d2b36SAndroid Build Coastguard Worker 184*333d2b36SAndroid Build Coastguard Worker statFields := bytes.Split(stat, []byte{' '}) 185*333d2b36SAndroid Build Coastguard Worker // This should come from sysconf(_SC_CLK_TCK), but there's no way to call that from Go. Assume it's 100, 186*333d2b36SAndroid Build Coastguard Worker // which is the value for all platforms we support. 187*333d2b36SAndroid Build Coastguard Worker const HZ = 100 188*333d2b36SAndroid Build Coastguard Worker const MS_PER_HZ = 1e3 / HZ * time.Millisecond 189*333d2b36SAndroid Build Coastguard Worker 190*333d2b36SAndroid Build Coastguard Worker const STAT_UTIME_FIELD = 14 - 2 191*333d2b36SAndroid Build Coastguard Worker const STAT_STIME_FIELD = 15 - 2 192*333d2b36SAndroid Build Coastguard Worker if len(statFields) < STAT_STIME_FIELD { 193*333d2b36SAndroid Build Coastguard Worker return 0 194*333d2b36SAndroid Build Coastguard Worker } 195*333d2b36SAndroid Build Coastguard Worker userCpuTicks, err := strconv.ParseUint(string(statFields[STAT_UTIME_FIELD]), 10, 64) 196*333d2b36SAndroid Build Coastguard Worker if err != nil { 197*333d2b36SAndroid Build Coastguard Worker return 0 198*333d2b36SAndroid Build Coastguard Worker } 199*333d2b36SAndroid Build Coastguard Worker kernelCpuTicks, _ := strconv.ParseUint(string(statFields[STAT_STIME_FIELD]), 10, 64) 200*333d2b36SAndroid Build Coastguard Worker if err != nil { 201*333d2b36SAndroid Build Coastguard Worker return 0 202*333d2b36SAndroid Build Coastguard Worker } 203*333d2b36SAndroid Build Coastguard Worker return time.Duration(userCpuTicks+kernelCpuTicks) * MS_PER_HZ 204*333d2b36SAndroid Build Coastguard Worker} 205*333d2b36SAndroid Build Coastguard Worker 206*333d2b36SAndroid Build Coastguard Workerfunc WriteMetrics(config Config, eventHandler *metrics.EventHandler, metricsFile string) error { 207*333d2b36SAndroid Build Coastguard Worker metrics := collectMetrics(config, eventHandler) 208*333d2b36SAndroid Build Coastguard Worker 209*333d2b36SAndroid Build Coastguard Worker buf, err := proto.Marshal(metrics) 210*333d2b36SAndroid Build Coastguard Worker if err != nil { 211*333d2b36SAndroid Build Coastguard Worker return err 212*333d2b36SAndroid Build Coastguard Worker } 213*333d2b36SAndroid Build Coastguard Worker err = ioutil.WriteFile(absolutePath(metricsFile), buf, 0666) 214*333d2b36SAndroid Build Coastguard Worker if err != nil { 215*333d2b36SAndroid Build Coastguard Worker return err 216*333d2b36SAndroid Build Coastguard Worker } 217*333d2b36SAndroid Build Coastguard Worker 218*333d2b36SAndroid Build Coastguard Worker return nil 219*333d2b36SAndroid Build Coastguard Worker} 220