1// Copyright 2018 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 tracer 16 17import ( 18 "strings" 19 "time" 20 21 "android/soong/ui/status" 22) 23 24func (t *tracerImpl) StatusTracer() status.StatusOutput { 25 return &statusOutput{ 26 tracer: t, 27 28 running: map[*status.Action]actionStatus{}, 29 } 30} 31 32type actionStatus struct { 33 cpu int 34 start time.Time 35} 36 37type statusOutput struct { 38 tracer *tracerImpl 39 40 cpus []bool 41 running map[*status.Action]actionStatus 42} 43 44func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) { 45 cpu := -1 46 for i, busy := range s.cpus { 47 if !busy { 48 cpu = i 49 s.cpus[i] = true 50 break 51 } 52 } 53 54 if cpu == -1 { 55 cpu = len(s.cpus) 56 s.cpus = append(s.cpus, true) 57 } 58 59 s.running[action] = actionStatus{ 60 cpu: cpu, 61 start: time.Now(), 62 } 63} 64 65func (s *statusOutput) parseTags(rawTags string) map[string]string { 66 if rawTags == "" { 67 return nil 68 } 69 70 tags := map[string]string{} 71 for _, pair := range strings.Split(rawTags, ";") { 72 if pair == "" { 73 // Ignore empty tag pairs. It's hard to generate these cleanly from 74 // make so some tag strings might be something like ";key=value". 75 continue 76 } 77 parts := strings.SplitN(pair, "=", 2) 78 tags[parts[0]] = parts[1] 79 } 80 return tags 81} 82 83func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) { 84 start, ok := s.running[result.Action] 85 if !ok { 86 return 87 } 88 delete(s.running, result.Action) 89 s.cpus[start.cpu] = false 90 91 str := result.Action.Description 92 if len(result.Action.Outputs) > 0 { 93 str = result.Action.Outputs[0] 94 } 95 96 s.tracer.writeEvent(&viewerEvent{ 97 Name: str, 98 Phase: "X", 99 Time: uint64(start.start.UnixNano()) / 1000, 100 Dur: uint64(time.Since(start.start).Nanoseconds()) / 1000, 101 Pid: 1, 102 Tid: uint64(start.cpu), 103 Arg: &statsArg{ 104 UserTime: result.Stats.UserTime, 105 SystemTime: result.Stats.SystemTime, 106 MaxRssKB: result.Stats.MaxRssKB, 107 MinorPageFaults: result.Stats.MinorPageFaults, 108 MajorPageFaults: result.Stats.MajorPageFaults, 109 IOInputKB: result.Stats.IOInputKB, 110 IOOutputKB: result.Stats.IOOutputKB, 111 VoluntaryContextSwitches: result.Stats.VoluntaryContextSwitches, 112 InvoluntaryContextSwitches: result.Stats.InvoluntaryContextSwitches, 113 Tags: s.parseTags(result.Stats.Tags), 114 ChangedInputs: result.Action.ChangedInputs, 115 }, 116 }) 117} 118 119type statsArg struct { 120 UserTime uint32 `json:"user_time"` 121 SystemTime uint32 `json:"system_time_ms"` 122 MaxRssKB uint64 `json:"max_rss_kb"` 123 MinorPageFaults uint64 `json:"minor_page_faults"` 124 MajorPageFaults uint64 `json:"major_page_faults"` 125 IOInputKB uint64 `json:"io_input_kb"` 126 IOOutputKB uint64 `json:"io_output_kb"` 127 VoluntaryContextSwitches uint64 `json:"voluntary_context_switches"` 128 InvoluntaryContextSwitches uint64 `json:"involuntary_context_switches"` 129 Tags map[string]string `json:"tags"` 130 ChangedInputs []string `json:"changed_inputs"` 131} 132 133func (s *statusOutput) Flush() {} 134func (s *statusOutput) Message(level status.MsgLevel, message string) {} 135 136func (s *statusOutput) Write(p []byte) (int, error) { 137 // Discard writes 138 return len(p), nil 139} 140