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