return ret // finalized GDesc. No pending state.
        }
 
-       if activeGCStartTime != 0 {
-               ret.GCTime += lastTs - activeGCStartTime
+       if activeGCStartTime != 0 { // terminating while GC is active
+               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 {
        return ret
 }
 
-// finalizeActiveSpans is called when processing a goroutine end event
-// to finalize any active spans in the goroutine.
-func (g *GDesc) finalizeActiveSpans(lastTs, activeGCStartTime int64, trigger *Event) {
+// finalize is called when processing a goroutine end event or at
+// the end of trace processing. This finalizes the execution stat
+// 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 {
                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.activeSpans = nil
+       *(g.gdesc) = gdesc{}
 }
 
 // gdesc is a private part of GDesc that is required only during analysis.
                        }
                case EvGoEnd, EvGoStop:
                        g := gs[ev.G]
-                       g.ExecTime += ev.Ts - g.lastStartTime
-                       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)
+                       g.finalize(ev.Ts, gcStartTime, ev)
                case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
                        EvGoBlockSync, EvGoBlockCond:
                        g := gs[ev.G]
        }
 
        for _, g := range gs {
-               g.GExecutionStat = g.snapshotStat(lastTs, gcStartTime)
-               g.finalizeActiveSpans(lastTs, gcStartTime, nil)
+               g.finalize(lastTs, gcStartTime, nil)
+
                // sort based on span start time
                sort.Slice(g.Spans, func(i, j int) bool {
                        x := g.Spans[i].Start
                        }
                        return x.Ts < y.Ts
                })
+
                g.gdesc = nil
        }