d888b6b4fc
Propagate the subprocess stats from ninja to build.trace.gz. There is too much data here to put into separate counters for each statistic, so put them into args on each duration event for now. We would like to track max RSS for each subprocess, but the Linux kernel inherits the max RSS of the ninja process in each subprocess, which sets a lower bound on the measurable max RSS to the size of the ninja process. The ninja process is large due to the multi-GB build.ninja files. Bug: 170701554 Test: examine build.trace.gz Change-Id: I8aaaafe627a57f1a500af098c097c6381c583ba5
115 lines
3.2 KiB
Go
115 lines
3.2 KiB
Go
// 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"
|
|
"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) 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,
|
|
},
|
|
})
|
|
}
|
|
|
|
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"`
|
|
}
|
|
|
|
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
|
|
}
|