// Copyright 2018 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tracer import ( "android/soong/ui/status" "strings" "time" ) func (t *tracerImpl) StatusTracer() status.StatusOutput { return &statusOutput{ tracer: t, running: map[*status.Action]actionStatus{}, } } type actionStatus struct { cpu int start time.Time } type statusOutput struct { tracer *tracerImpl cpus []bool running map[*status.Action]actionStatus } func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) { cpu := -1 for i, busy := range s.cpus { if !busy { cpu = i s.cpus[i] = true break } } if cpu == -1 { cpu = len(s.cpus) s.cpus = append(s.cpus, true) } s.running[action] = actionStatus{ cpu: cpu, start: time.Now(), } } func (s *statusOutput) parseTags(rawTags string) map[string]string { if rawTags == "" { return nil } tags := map[string]string{} for _, pair := range strings.Split(rawTags, ";") { if pair == "" { // Ignore empty tag pairs. It's hard to generate these cleanly from // make so some tag strings might be something like ";key=value". continue } parts := strings.SplitN(pair, "=", 2) tags[parts[0]] = parts[1] } return tags } func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) { start, ok := s.running[result.Action] if !ok { return } delete(s.running, result.Action) s.cpus[start.cpu] = false str := result.Action.Description if len(result.Action.Outputs) > 0 { str = result.Action.Outputs[0] } s.tracer.writeEvent(&viewerEvent{ Name: str, Phase: "X", Time: uint64(start.start.UnixNano()) / 1000, Dur: uint64(time.Since(start.start).Nanoseconds()) / 1000, Pid: 1, Tid: uint64(start.cpu), Arg: &statsArg{ UserTime: result.Stats.UserTime, SystemTime: result.Stats.SystemTime, MaxRssKB: result.Stats.MaxRssKB, MinorPageFaults: result.Stats.MinorPageFaults, MajorPageFaults: result.Stats.MajorPageFaults, IOInputKB: result.Stats.IOInputKB, IOOutputKB: result.Stats.IOOutputKB, VoluntaryContextSwitches: result.Stats.VoluntaryContextSwitches, InvoluntaryContextSwitches: result.Stats.InvoluntaryContextSwitches, Tags: s.parseTags(result.Stats.Tags), }, }) } type statsArg struct { UserTime uint32 `json:"user_time"` SystemTime uint32 `json:"system_time_ms"` MaxRssKB uint64 `json:"max_rss_kb"` MinorPageFaults uint64 `json:"minor_page_faults"` MajorPageFaults uint64 `json:"major_page_faults"` IOInputKB uint64 `json:"io_input_kb"` IOOutputKB uint64 `json:"io_output_kb"` VoluntaryContextSwitches uint64 `json:"voluntary_context_switches"` InvoluntaryContextSwitches uint64 `json:"involuntary_context_switches"` Tags map[string]string `json:"tags"` } func (s *statusOutput) Flush() {} func (s *statusOutput) Message(level status.MsgLevel, message string) {} func (s *statusOutput) Write(p []byte) (int, error) { // Discard writes return len(p), nil }