Complete: task.complete(),
Events: events,
Start: time.Duration(task.firstTimestamp()) * time.Nanosecond,
- End: time.Duration(task.lastTimestamp()) * time.Nanosecond,
+ End: time.Duration(task.endTimestamp()) * time.Nanosecond,
GCTime: task.overlappingGCDuration(res.gcEvents),
})
}
}
wb := new(bytes.Buffer)
fmt.Fprintf(wb, "task %d:\t%s\n", task.id, task.name)
- fmt.Fprintf(wb, "\tstart: %v end: %v complete: %t\n", task.firstTimestamp(), task.lastTimestamp(), task.complete())
+ fmt.Fprintf(wb, "\tstart: %v end: %v complete: %t\n", task.firstTimestamp(), task.endTimestamp(), task.complete())
fmt.Fprintf(wb, "\t%d goroutines\n", len(task.goroutines))
fmt.Fprintf(wb, "\t%d regions:\n", len(task.regions))
for _, s := range task.regions {
// trace. If the trace does not contain the task end event, the last
// timestamp of the trace will be returned.
func (task *taskDesc) lastTimestamp() int64 {
+ endTs := task.endTimestamp()
+ if last := task.lastEvent(); last != nil && last.Ts > endTs {
+ return last.Ts
+ }
+ return endTs
+}
+
+// endTimestamp returns the timestamp of this task's end event.
+// If the trace does not contain the task end event, the last
+// timestamp of the trace will be returned.
+func (task *taskDesc) endTimestamp() int64 {
if task != nil && task.end != nil {
return task.end.Ts
}
}
func (task *taskDesc) duration() time.Duration {
- return time.Duration(task.lastTimestamp()-task.firstTimestamp()) * time.Nanosecond
+ return time.Duration(task.endTimestamp()-task.firstTimestamp()) * time.Nanosecond
}
func (region *regionDesc) duration() time.Duration {
// any of the task's region if ev is a goroutine-local event, or overlaps with the
// task's lifetime if ev is a global event.
func (task *taskDesc) overlappingInstant(ev *trace.Event) bool {
- if isUserAnnotationEvent(ev) && task.id != ev.Args[0] {
+ if _, ok := isUserAnnotationEvent(ev); ok && task.id != ev.Args[0] {
return false // not this task's user event.
}
ts := ev.Ts
taskStart := task.firstTimestamp()
- taskEnd := task.lastTimestamp()
+ taskEnd := task.endTimestamp()
if ts < taskStart || taskEnd < ts {
return false
}
// This event is a global GC event
if ev.P == trace.GCP {
taskStart := task.firstTimestamp()
- taskEnd := task.lastTimestamp()
+ taskEnd := task.endTimestamp()
o := overlappingDuration(taskStart, taskEnd, start, end)
return o, o > 0
}
// If non-zero depth is provided, this searches all events with BFS and includes
// goroutines unblocked any of related goroutines to the result.
func (task *taskDesc) RelatedGoroutines(events []*trace.Event, depth int) map[uint64]bool {
- start, end := task.firstTimestamp(), task.lastTimestamp()
+ start, end := task.firstTimestamp(), task.endTimestamp()
gmap := map[uint64]bool{}
for k := range task.goroutines {
return ""
}
-func isUserAnnotationEvent(ev *trace.Event) bool {
+func isUserAnnotationEvent(ev *trace.Event) (taskID uint64, ok bool) {
switch ev.Type {
case trace.EvUserLog, trace.EvUserRegion, trace.EvUserTaskCreate, trace.EvUserTaskEnd:
- return true
+ return ev.Args[0], true
}
- return false
+ return 0, false
}
var templUserRegionType = template.Must(template.New("").Funcs(template.FuncMap{
}
ctx.emit(tBegin)
- tEnd := &ViewerEvent{Category: "task", Name: taskName, Phase: "e", Time: float64(task.lastTimestamp()) / 1e3, Tid: taskRow, ID: task.id, Cname: colorBlue}
+ tEnd := &ViewerEvent{Category: "task", Name: taskName, Phase: "e", Time: float64(task.endTimestamp()) / 1e3, Tid: taskRow, ID: task.id, Cname: colorBlue}
if task.end != nil {
tEnd.Stack = ctx.stack(task.end.Stk)
}
func (ctx *traceContext) emitInstant(ev *trace.Event, name, category string) {
cname := ""
if ctx.mode&modeTaskOriented != 0 {
- overlapping := false
+ taskID, isUserAnnotation := isUserAnnotationEvent(ev)
+
+ show := false
for _, task := range ctx.tasks {
- if task.overlappingInstant(ev) {
- overlapping = true
+ if isUserAnnotation && task.id == taskID || task.overlappingInstant(ev) {
+ show = true
break
}
}
// grey out or skip if non-overlapping instant.
- if !overlapping {
- if isUserAnnotationEvent(ev) {
- return // don't display unrelated task events.
+ if !show {
+ if isUserAnnotation {
+ return // don't display unrelated user annotation events.
}
cname = colorLightGrey
}
package main
import (
+ "context"
"internal/trace"
"io/ioutil"
+ rtrace "runtime/trace"
"strings"
"testing"
)
t.Errorf("Got %v MARK ASSIST events, want %v", marks, 2)
}
}
+
+func TestFoo(t *testing.T) {
+ prog0 := func() {
+ ctx, task := rtrace.NewTask(context.Background(), "ohHappyDay")
+ rtrace.Log(ctx, "", "log before task ends")
+ task.End()
+ rtrace.Log(ctx, "", "log after task ends") // log after task ends
+ }
+ if err := traceProgram(t, prog0, "TestFoo"); err != nil {
+ t.Fatalf("failed to trace the program: %v", err)
+ }
+ res, err := parseTrace()
+ if err != nil {
+ t.Fatalf("failed to parse the trace: %v", err)
+ }
+ annotRes, _ := analyzeAnnotations()
+ var task *taskDesc
+ for _, t := range annotRes.tasks {
+ if t.name == "ohHappyDay" {
+ task = t
+ break
+ }
+ }
+ if task == nil {
+ t.Fatal("failed to locate expected task event")
+ }
+
+ params := &traceParams{
+ parsed: res,
+ mode: modeTaskOriented,
+ startTime: task.firstTimestamp() - 1,
+ endTime: task.lastTimestamp() + 1,
+ tasks: []*taskDesc{task},
+ }
+
+ c := viewerDataTraceConsumer(ioutil.Discard, 0, 1<<63-1)
+
+ var logBeforeTaskEnd, logAfterTaskEnd bool
+ c.consumeViewerEvent = func(ev *ViewerEvent, _ bool) {
+ if ev.Name == "log before task ends" {
+ logBeforeTaskEnd = true
+ }
+ if ev.Name == "log after task ends" {
+ logAfterTaskEnd = true
+ }
+ }
+ if err := generateTrace(params, c); err != nil {
+ t.Fatalf("generateTrace failed: %v", err)
+ }
+ if !logBeforeTaskEnd {
+ t.Error("failed to find 'log before task ends'")
+ }
+ if !logAfterTaskEnd {
+ t.Error("failed to find 'log after task ends'")
+ }
+
+}