blob: 72914e7c77e7c21a60ce9a2e13153bb8d5ab8d16 [file] [log] [blame]
Colin Cross7b624532019-06-21 15:08:30 -07001// Copyright 2019 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
15package status
16
17import (
Jeongik Cha28c1fe52023-03-07 15:19:44 +090018 "android/soong/ui/metrics"
19
20 soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
Colin Cross7b624532019-06-21 15:08:30 -070021 "time"
22
Jeongik Cha28c1fe52023-03-07 15:19:44 +090023 "google.golang.org/protobuf/proto"
Colin Cross7b624532019-06-21 15:08:30 -070024)
25
Jeongik Cha28c1fe52023-03-07 15:19:44 +090026func NewCriticalPath() *CriticalPath {
27 return &CriticalPath{
Colin Cross7b624532019-06-21 15:08:30 -070028 running: make(map[*Action]time.Time),
29 nodes: make(map[string]*node),
30 clock: osClock{},
31 }
32}
33
Jeongik Cha28c1fe52023-03-07 15:19:44 +090034type CriticalPath struct {
Colin Cross7b624532019-06-21 15:08:30 -070035 nodes map[string]*node
36 running map[*Action]time.Time
37
38 start, end time.Time
39
40 clock clock
41}
42
43type clock interface {
44 Now() time.Time
45}
46
47type osClock struct{}
48
49func (osClock) Now() time.Time { return time.Now() }
50
51// A critical path node stores the critical path (the minimum time to build the node and all of its dependencies given
52// perfect parallelism) for an node.
53type node struct {
54 action *Action
55 cumulativeDuration time.Duration
56 duration time.Duration
57 input *node
58}
59
Jeongik Cha28c1fe52023-03-07 15:19:44 +090060func (cp *CriticalPath) StartAction(action *Action) {
Colin Cross7b624532019-06-21 15:08:30 -070061 start := cp.clock.Now()
62 if cp.start.IsZero() {
63 cp.start = start
64 }
65 cp.running[action] = start
66}
67
Jeongik Cha28c1fe52023-03-07 15:19:44 +090068func (cp *CriticalPath) FinishAction(action *Action) {
69 if start, ok := cp.running[action]; ok {
70 delete(cp.running, action)
Colin Cross7b624532019-06-21 15:08:30 -070071
72 // Determine the input to this edge with the longest cumulative duration
73 var criticalPathInput *node
Jeongik Cha28c1fe52023-03-07 15:19:44 +090074 for _, input := range action.Inputs {
Colin Cross7b624532019-06-21 15:08:30 -070075 if x := cp.nodes[input]; x != nil {
76 if criticalPathInput == nil || x.cumulativeDuration > criticalPathInput.cumulativeDuration {
77 criticalPathInput = x
78 }
79 }
80 }
81
82 end := cp.clock.Now()
83 duration := end.Sub(start)
84
85 cumulativeDuration := duration
86 if criticalPathInput != nil {
87 cumulativeDuration += criticalPathInput.cumulativeDuration
88 }
89
90 node := &node{
Jeongik Cha28c1fe52023-03-07 15:19:44 +090091 action: action,
Colin Cross7b624532019-06-21 15:08:30 -070092 cumulativeDuration: cumulativeDuration,
93 duration: duration,
94 input: criticalPathInput,
95 }
96
Jeongik Cha28c1fe52023-03-07 15:19:44 +090097 for _, output := range action.Outputs {
Colin Cross7b624532019-06-21 15:08:30 -070098 cp.nodes[output] = node
99 }
100
101 cp.end = end
102 }
103}
104
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900105func (cp *CriticalPath) criticalPath() (path []*node, elapsedTime time.Duration, criticalTime time.Duration) {
Colin Cross7b624532019-06-21 15:08:30 -0700106 var max *node
107
108 // Find the node with the longest critical path
109 for _, node := range cp.nodes {
110 if max == nil || node.cumulativeDuration > max.cumulativeDuration {
111 max = node
112 }
113 }
114
Colin Cross7b624532019-06-21 15:08:30 -0700115 node := max
116 for node != nil {
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900117 path = append(path, node)
Colin Cross7b624532019-06-21 15:08:30 -0700118 node = node.input
119 }
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900120 if len(path) > 0 {
121 // Log the critical path to the verbose log
122 criticalTime = path[0].cumulativeDuration
123 if !cp.start.IsZero() {
124 elapsedTime = cp.end.Sub(cp.start)
125 }
126 }
127 return
128}
Colin Cross7b624532019-06-21 15:08:30 -0700129
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900130func (cp *CriticalPath) WriteToMetrics(met *metrics.Metrics) {
131 criticalPathInfo := soong_metrics_proto.CriticalPathInfo{}
132 path, elapsedTime, criticalTime := cp.criticalPath()
133 criticalPathInfo.ElapsedTime = proto.Uint64(uint64(elapsedTime.Microseconds()))
134 criticalPathInfo.CriticalPathTime = proto.Uint64(uint64(criticalTime.Microseconds()))
135 for _, job := range path {
136 jobInfo := soong_metrics_proto.JobInfo{}
137 jobInfo.ElapsedTime = proto.Uint64(uint64(job.duration.Microseconds()))
138 jobInfo.JobDescription = &job.action.Description
139 criticalPathInfo.CriticalPath = append(criticalPathInfo.CriticalPath, &jobInfo)
140 }
141 met.SetCriticalPathInfo(criticalPathInfo)
Colin Cross7b624532019-06-21 15:08:30 -0700142}