Commit ad638fbf authored by Hana Kim's avatar Hana Kim Committed by Hyang-Ah Hana Kim

internal/trace: fix double counting in span analysis

When snapshotting the execution time stats of a goroutine
we take into account the intermediate values kepts in the GDesc's
gdesc field. At the end of goroutine analysis, we go through all
goroutines and replace the GExecutionStat with the new snapshot.
Here the mistake was that we replaced the GExecutionStat with
the value that reflects the intermediate values, but did clear
the intermediate values. So, when the following finalizeActiveSpans
runs, it trieds to add the intermediate values again (double-counting)
when taking a snapshot.

Fix the issue by using the finalized GExecutionStat instead of
recomputing the snapshot. While we are here, refactor the finalization
logic so it can be used when processing GoEnd, GoStop events.

Change-Id: Ibdb342214c29b65d4ea37e52b1f8b83f1fb20143
Reviewed-on: https://go-review.googlesource.com/103157Reviewed-by: default avatarPeter Weinberger <pjw@google.com>
parent dafca7de
...@@ -81,8 +81,14 @@ func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionSta ...@@ -81,8 +81,14 @@ func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionSta
return ret // finalized GDesc. No pending state. return ret // finalized GDesc. No pending state.
} }
if activeGCStartTime != 0 { if activeGCStartTime != 0 { // terminating while GC is active
ret.GCTime += lastTs - activeGCStartTime if g.CreationTime < activeGCStartTime {
ret.GCTime += lastTs - activeGCStartTime
} else {
// The goroutine's lifetime completely overlaps
// with a GC.
ret.GCTime += lastTs - g.CreationTime
}
} }
if g.TotalTime == 0 { if g.TotalTime == 0 {
...@@ -110,15 +116,22 @@ func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionSta ...@@ -110,15 +116,22 @@ func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionSta
return ret return ret
} }
// finalizeActiveSpans is called when processing a goroutine end event // finalize is called when processing a goroutine end event or at
// to finalize any active spans in the goroutine. // the end of trace processing. This finalizes the execution stat
func (g *GDesc) finalizeActiveSpans(lastTs, activeGCStartTime int64, trigger *Event) { // and any active spans in the goroutine, in which case trigger is nil.
func (g *GDesc) finalize(lastTs, activeGCStartTime int64, trigger *Event) {
if trigger != nil {
g.EndTime = trigger.Ts
}
finalStat := g.snapshotStat(lastTs, activeGCStartTime)
g.GExecutionStat = finalStat
for _, s := range g.activeSpans { for _, s := range g.activeSpans {
s.End = trigger s.End = trigger
s.GExecutionStat = g.snapshotStat(lastTs, activeGCStartTime).sub(s.GExecutionStat) s.GExecutionStat = finalStat.sub(s.GExecutionStat)
g.Spans = append(g.Spans, s) g.Spans = append(g.Spans, s)
} }
g.activeSpans = nil *(g.gdesc) = gdesc{}
} }
// gdesc is a private part of GDesc that is required only during analysis. // gdesc is a private part of GDesc that is required only during analysis.
...@@ -175,20 +188,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { ...@@ -175,20 +188,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc {
} }
case EvGoEnd, EvGoStop: case EvGoEnd, EvGoStop:
g := gs[ev.G] g := gs[ev.G]
g.ExecTime += ev.Ts - g.lastStartTime g.finalize(ev.Ts, gcStartTime, ev)
g.lastStartTime = 0
g.TotalTime = ev.Ts - g.CreationTime
g.EndTime = ev.Ts
if gcStartTime != 0 { // terminating while GC is active
if g.CreationTime < gcStartTime {
g.GCTime += ev.Ts - gcStartTime
} else {
// The goroutine's lifetime overlaps
// with a GC completely.
g.GCTime += ev.Ts - g.CreationTime
}
}
g.finalizeActiveSpans(lastTs, gcStartTime, ev)
case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
EvGoBlockSync, EvGoBlockCond: EvGoBlockSync, EvGoBlockCond:
g := gs[ev.G] g := gs[ev.G]
...@@ -294,8 +294,8 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { ...@@ -294,8 +294,8 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc {
} }
for _, g := range gs { for _, g := range gs {
g.GExecutionStat = g.snapshotStat(lastTs, gcStartTime) g.finalize(lastTs, gcStartTime, nil)
g.finalizeActiveSpans(lastTs, gcStartTime, nil)
// sort based on span start time // sort based on span start time
sort.Slice(g.Spans, func(i, j int) bool { sort.Slice(g.Spans, func(i, j int) bool {
x := g.Spans[i].Start x := g.Spans[i].Start
...@@ -308,6 +308,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { ...@@ -308,6 +308,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc {
} }
return x.Ts < y.Ts return x.Ts < y.Ts
}) })
g.gdesc = nil g.gdesc = nil
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment