xref: /aosp_15_r20/build/soong/ui/status/critical_path.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2019 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 status
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"android/soong/ui/metrics"
19*333d2b36SAndroid Build Coastguard Worker
20*333d2b36SAndroid Build Coastguard Worker	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
21*333d2b36SAndroid Build Coastguard Worker	"time"
22*333d2b36SAndroid Build Coastguard Worker
23*333d2b36SAndroid Build Coastguard Worker	"google.golang.org/protobuf/proto"
24*333d2b36SAndroid Build Coastguard Worker)
25*333d2b36SAndroid Build Coastguard Worker
26*333d2b36SAndroid Build Coastguard Workerfunc NewCriticalPath() *CriticalPath {
27*333d2b36SAndroid Build Coastguard Worker	return &CriticalPath{
28*333d2b36SAndroid Build Coastguard Worker		running: make(map[*Action]time.Time),
29*333d2b36SAndroid Build Coastguard Worker		nodes:   make(map[string]*node),
30*333d2b36SAndroid Build Coastguard Worker		clock:   osClock{},
31*333d2b36SAndroid Build Coastguard Worker	}
32*333d2b36SAndroid Build Coastguard Worker}
33*333d2b36SAndroid Build Coastguard Worker
34*333d2b36SAndroid Build Coastguard Workertype CriticalPath struct {
35*333d2b36SAndroid Build Coastguard Worker	nodes   map[string]*node
36*333d2b36SAndroid Build Coastguard Worker	running map[*Action]time.Time
37*333d2b36SAndroid Build Coastguard Worker
38*333d2b36SAndroid Build Coastguard Worker	start, end time.Time
39*333d2b36SAndroid Build Coastguard Worker
40*333d2b36SAndroid Build Coastguard Worker	clock clock
41*333d2b36SAndroid Build Coastguard Worker}
42*333d2b36SAndroid Build Coastguard Worker
43*333d2b36SAndroid Build Coastguard Workertype clock interface {
44*333d2b36SAndroid Build Coastguard Worker	Now() time.Time
45*333d2b36SAndroid Build Coastguard Worker}
46*333d2b36SAndroid Build Coastguard Worker
47*333d2b36SAndroid Build Coastguard Workertype osClock struct{}
48*333d2b36SAndroid Build Coastguard Worker
49*333d2b36SAndroid Build Coastguard Workerfunc (osClock) Now() time.Time { return time.Now() }
50*333d2b36SAndroid Build Coastguard Worker
51*333d2b36SAndroid Build Coastguard Worker// A critical path node stores the critical path (the minimum time to build the node and all of its dependencies given
52*333d2b36SAndroid Build Coastguard Worker// perfect parallelism) for an node.
53*333d2b36SAndroid Build Coastguard Workertype node struct {
54*333d2b36SAndroid Build Coastguard Worker	action             *Action
55*333d2b36SAndroid Build Coastguard Worker	cumulativeDuration time.Duration
56*333d2b36SAndroid Build Coastguard Worker	duration           time.Duration
57*333d2b36SAndroid Build Coastguard Worker	input              *node
58*333d2b36SAndroid Build Coastguard Worker}
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Workerfunc (cp *CriticalPath) StartAction(action *Action) {
61*333d2b36SAndroid Build Coastguard Worker	start := cp.clock.Now()
62*333d2b36SAndroid Build Coastguard Worker	if cp.start.IsZero() {
63*333d2b36SAndroid Build Coastguard Worker		cp.start = start
64*333d2b36SAndroid Build Coastguard Worker	}
65*333d2b36SAndroid Build Coastguard Worker	cp.running[action] = start
66*333d2b36SAndroid Build Coastguard Worker}
67*333d2b36SAndroid Build Coastguard Worker
68*333d2b36SAndroid Build Coastguard Workerfunc (cp *CriticalPath) FinishAction(action *Action) {
69*333d2b36SAndroid Build Coastguard Worker	if start, ok := cp.running[action]; ok {
70*333d2b36SAndroid Build Coastguard Worker		delete(cp.running, action)
71*333d2b36SAndroid Build Coastguard Worker
72*333d2b36SAndroid Build Coastguard Worker		// Determine the input to this edge with the longest cumulative duration
73*333d2b36SAndroid Build Coastguard Worker		var criticalPathInput *node
74*333d2b36SAndroid Build Coastguard Worker		for _, input := range action.Inputs {
75*333d2b36SAndroid Build Coastguard Worker			if x := cp.nodes[input]; x != nil {
76*333d2b36SAndroid Build Coastguard Worker				if criticalPathInput == nil || x.cumulativeDuration > criticalPathInput.cumulativeDuration {
77*333d2b36SAndroid Build Coastguard Worker					criticalPathInput = x
78*333d2b36SAndroid Build Coastguard Worker				}
79*333d2b36SAndroid Build Coastguard Worker			}
80*333d2b36SAndroid Build Coastguard Worker		}
81*333d2b36SAndroid Build Coastguard Worker
82*333d2b36SAndroid Build Coastguard Worker		end := cp.clock.Now()
83*333d2b36SAndroid Build Coastguard Worker		duration := end.Sub(start)
84*333d2b36SAndroid Build Coastguard Worker
85*333d2b36SAndroid Build Coastguard Worker		cumulativeDuration := duration
86*333d2b36SAndroid Build Coastguard Worker		if criticalPathInput != nil {
87*333d2b36SAndroid Build Coastguard Worker			cumulativeDuration += criticalPathInput.cumulativeDuration
88*333d2b36SAndroid Build Coastguard Worker		}
89*333d2b36SAndroid Build Coastguard Worker
90*333d2b36SAndroid Build Coastguard Worker		node := &node{
91*333d2b36SAndroid Build Coastguard Worker			action:             action,
92*333d2b36SAndroid Build Coastguard Worker			cumulativeDuration: cumulativeDuration,
93*333d2b36SAndroid Build Coastguard Worker			duration:           duration,
94*333d2b36SAndroid Build Coastguard Worker			input:              criticalPathInput,
95*333d2b36SAndroid Build Coastguard Worker		}
96*333d2b36SAndroid Build Coastguard Worker
97*333d2b36SAndroid Build Coastguard Worker		for _, output := range action.Outputs {
98*333d2b36SAndroid Build Coastguard Worker			cp.nodes[output] = node
99*333d2b36SAndroid Build Coastguard Worker		}
100*333d2b36SAndroid Build Coastguard Worker
101*333d2b36SAndroid Build Coastguard Worker		cp.end = end
102*333d2b36SAndroid Build Coastguard Worker	}
103*333d2b36SAndroid Build Coastguard Worker}
104*333d2b36SAndroid Build Coastguard Worker
105*333d2b36SAndroid Build Coastguard Workerfunc (cp *CriticalPath) criticalPath() (path []*node, elapsedTime time.Duration, criticalTime time.Duration) {
106*333d2b36SAndroid Build Coastguard Worker	var max *node
107*333d2b36SAndroid Build Coastguard Worker
108*333d2b36SAndroid Build Coastguard Worker	// Find the node with the longest critical path
109*333d2b36SAndroid Build Coastguard Worker	for _, node := range cp.nodes {
110*333d2b36SAndroid Build Coastguard Worker		if max == nil || node.cumulativeDuration > max.cumulativeDuration {
111*333d2b36SAndroid Build Coastguard Worker			max = node
112*333d2b36SAndroid Build Coastguard Worker		}
113*333d2b36SAndroid Build Coastguard Worker	}
114*333d2b36SAndroid Build Coastguard Worker
115*333d2b36SAndroid Build Coastguard Worker	node := max
116*333d2b36SAndroid Build Coastguard Worker	for node != nil {
117*333d2b36SAndroid Build Coastguard Worker		path = append(path, node)
118*333d2b36SAndroid Build Coastguard Worker		node = node.input
119*333d2b36SAndroid Build Coastguard Worker	}
120*333d2b36SAndroid Build Coastguard Worker	if len(path) > 0 {
121*333d2b36SAndroid Build Coastguard Worker		// Log the critical path to the verbose log
122*333d2b36SAndroid Build Coastguard Worker		criticalTime = path[0].cumulativeDuration
123*333d2b36SAndroid Build Coastguard Worker		if !cp.start.IsZero() {
124*333d2b36SAndroid Build Coastguard Worker			elapsedTime = cp.end.Sub(cp.start)
125*333d2b36SAndroid Build Coastguard Worker		}
126*333d2b36SAndroid Build Coastguard Worker	}
127*333d2b36SAndroid Build Coastguard Worker	return
128*333d2b36SAndroid Build Coastguard Worker}
129*333d2b36SAndroid Build Coastguard Worker
130*333d2b36SAndroid Build Coastguard Workerfunc (cp *CriticalPath) longRunningJobs() (nodes []*node) {
131*333d2b36SAndroid Build Coastguard Worker	threshold := time.Second * 30
132*333d2b36SAndroid Build Coastguard Worker	for _, node := range cp.nodes {
133*333d2b36SAndroid Build Coastguard Worker		if node != nil && node.duration > threshold {
134*333d2b36SAndroid Build Coastguard Worker			nodes = append(nodes, node)
135*333d2b36SAndroid Build Coastguard Worker		}
136*333d2b36SAndroid Build Coastguard Worker	}
137*333d2b36SAndroid Build Coastguard Worker	return
138*333d2b36SAndroid Build Coastguard Worker}
139*333d2b36SAndroid Build Coastguard Worker
140*333d2b36SAndroid Build Coastguard Workerfunc addJobInfos(jobInfos *[]*soong_metrics_proto.JobInfo, sources []*node) {
141*333d2b36SAndroid Build Coastguard Worker	for _, job := range sources {
142*333d2b36SAndroid Build Coastguard Worker		jobInfo := soong_metrics_proto.JobInfo{}
143*333d2b36SAndroid Build Coastguard Worker		jobInfo.ElapsedTimeMicros = proto.Uint64(uint64(job.duration.Microseconds()))
144*333d2b36SAndroid Build Coastguard Worker		jobInfo.JobDescription = &job.action.Description
145*333d2b36SAndroid Build Coastguard Worker		*jobInfos = append(*jobInfos, &jobInfo)
146*333d2b36SAndroid Build Coastguard Worker	}
147*333d2b36SAndroid Build Coastguard Worker}
148*333d2b36SAndroid Build Coastguard Worker
149*333d2b36SAndroid Build Coastguard Workerfunc (cp *CriticalPath) WriteToMetrics(met *metrics.Metrics) {
150*333d2b36SAndroid Build Coastguard Worker	criticalPathInfo := soong_metrics_proto.CriticalPathInfo{}
151*333d2b36SAndroid Build Coastguard Worker	path, elapsedTime, criticalTime := cp.criticalPath()
152*333d2b36SAndroid Build Coastguard Worker	criticalPathInfo.ElapsedTimeMicros = proto.Uint64(uint64(elapsedTime.Microseconds()))
153*333d2b36SAndroid Build Coastguard Worker	criticalPathInfo.CriticalPathTimeMicros = proto.Uint64(uint64(criticalTime.Microseconds()))
154*333d2b36SAndroid Build Coastguard Worker	addJobInfos(&criticalPathInfo.LongRunningJobs, cp.longRunningJobs())
155*333d2b36SAndroid Build Coastguard Worker	addJobInfos(&criticalPathInfo.CriticalPath, path)
156*333d2b36SAndroid Build Coastguard Worker	met.SetCriticalPathInfo(criticalPathInfo)
157*333d2b36SAndroid Build Coastguard Worker}
158