xref: /aosp_15_r20/build/soong/android/metrics.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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