// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package trace // GDesc contains statistics about execution of a single goroutine. type GDesc struct { ID uint64 Name string PC uint64 CreationTime int64 StartTime int64 EndTime int64 ExecTime int64 SchedWaitTime int64 IOTime int64 BlockTime int64 SyscallTime int64 GCTime int64 SweepTime int64 TotalTime int64 *gdesc // private part } // gdesc is a private part of GDesc that is required only during analysis. type gdesc struct { lastStartTime int64 blockNetTime int64 blockSyncTime int64 blockSyscallTime int64 blockSweepTime int64 blockGCTime int64 blockSchedTime int64 } // GoroutineStats generates statistics for all goroutines in the trace. func GoroutineStats(events []*Event) map[uint64]*GDesc { gs := make(map[uint64]*GDesc) var lastTs int64 var gcStartTime int64 for _, ev := range events { lastTs = ev.Ts switch ev.Type { case EvGoCreate: g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)} g.blockSchedTime = ev.Ts gs[g.ID] = g case EvGoStart: g := gs[ev.G] if g.PC == 0 { g.PC = ev.Stk[0].PC g.Name = ev.Stk[0].Fn } g.lastStartTime = ev.Ts if g.StartTime == 0 { g.StartTime = ev.Ts } if g.blockSchedTime != 0 { g.SchedWaitTime += ev.Ts - g.blockSchedTime g.blockSchedTime = 0 } case EvGoEnd, EvGoStop: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime g.TotalTime = ev.Ts - g.CreationTime g.EndTime = ev.Ts case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime g.blockSyncTime = ev.Ts case EvGoSched, EvGoPreempt: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime g.blockSchedTime = ev.Ts case EvGoSleep, EvGoBlock: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime case EvGoBlockNet: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime g.blockNetTime = ev.Ts case EvGoUnblock: g := gs[ev.Args[0]] if g.blockNetTime != 0 { g.IOTime += ev.Ts - g.blockNetTime g.blockNetTime = 0 } if g.blockSyncTime != 0 { g.BlockTime += ev.Ts - g.blockSyncTime g.blockSyncTime = 0 } g.blockSchedTime = ev.Ts case EvGoSysBlock: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime g.blockSyscallTime = ev.Ts case EvGoSysExit: g := gs[ev.G] if g.blockSyscallTime != 0 { g.SyscallTime += ev.Ts - g.blockSyscallTime g.blockSyscallTime = 0 } g.blockSchedTime = ev.Ts case EvGCSweepStart: g := gs[ev.G] if g != nil { // Sweep can happen during GC on system goroutine. g.blockSweepTime = ev.Ts } case EvGCSweepDone: g := gs[ev.G] if g != nil && g.blockSweepTime != 0 { g.SweepTime += ev.Ts - g.blockSweepTime g.blockSweepTime = 0 } case EvGCStart: gcStartTime = ev.Ts case EvGCDone: for _, g := range gs { if g.EndTime == 0 { g.GCTime += ev.Ts - gcStartTime } } } } for _, g := range gs { if g.TotalTime == 0 { g.TotalTime = lastTs - g.CreationTime } if g.EndTime == 0 { g.EndTime = lastTs } if g.blockNetTime != 0 { g.IOTime += lastTs - g.blockNetTime g.blockNetTime = 0 } if g.blockSyncTime != 0 { g.BlockTime += lastTs - g.blockSyncTime g.blockSyncTime = 0 } if g.blockSyscallTime != 0 { g.SyscallTime += lastTs - g.blockSyscallTime g.blockSyscallTime = 0 } if g.blockSchedTime != 0 { g.SchedWaitTime += lastTs - g.blockSchedTime g.blockSchedTime = 0 } g.gdesc = nil } return gs } // RelatedGoroutines finds a set of goroutines related to goroutine goid. func RelatedGoroutines(events []*Event, goid uint64) map[uint64]bool { // BFS of depth 2 over "unblock" edges // (what goroutines unblock goroutine goid?). gmap := make(map[uint64]bool) gmap[goid] = true for i := 0; i < 2; i++ { gmap1 := make(map[uint64]bool) for g := range gmap { gmap1[g] = true } for _, ev := range events { if ev.Type == EvGoUnblock && gmap[ev.Args[0]] { gmap1[ev.G] = true } } gmap = gmap1 } gmap[0] = true // for GC events return gmap }