veyron/runtimes/google/vtrace: Add start and end times to spans.

Change-Id: I0a4a48d3918a369b4260f1faf469d41ddc2a6bc0
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 135100c..b2bd087 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -511,7 +511,7 @@
 func (fc *flowClient) Finish(resultptrs ...interface{}) error {
 	defer vlog.LogCall()()
 	err := fc.finish(resultptrs...)
-	vtrace.FromContext(fc.ctx).Annotate("Finished")
+	vtrace.FromContext(fc.ctx).Finish()
 	return err
 }
 
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index fd213bc..dba781d 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -709,6 +709,8 @@
 
 	results, err := fs.processRequest()
 
+	ivtrace.FromContext(fs).Finish()
+
 	var traceResponse vtrace.Response
 	if fs.allowDebug {
 		traceResponse = ivtrace.Response(fs)
@@ -769,6 +771,9 @@
 
 	req, verr := fs.readIPCRequest()
 	if verr != nil {
+		// We don't know what the ipc call was supposed to be, but we'll create
+		// a placeholder span so we can capture annotations.
+		fs.T, _ = ivtrace.WithNewSpan(fs, "Failed IPC Call")
 		return nil, verr
 	}
 	fs.method = req.Method
diff --git a/runtimes/google/vtrace/collector.go b/runtimes/google/vtrace/collector.go
index 956b2d2..3313384 100644
--- a/runtimes/google/vtrace/collector.go
+++ b/runtimes/google/vtrace/collector.go
@@ -9,6 +9,17 @@
 	"veyron.io/veyron/veyron2/vtrace"
 )
 
+func copySpanRecord(in *vtrace.SpanRecord) *vtrace.SpanRecord {
+	return &vtrace.SpanRecord{
+		ID:          in.ID,
+		Parent:      in.Parent,
+		Name:        in.Name,
+		Start:       in.Start,
+		End:         in.End,
+		Annotations: append([]vtrace.Annotation{}, in.Annotations...),
+	}
+}
+
 // collectors collect spans and annotations for output or analysis.
 // collectors are safe to use from multiple goroutines simultaneously.
 // TODO(mattr): collector should support log-based collection
@@ -44,18 +55,53 @@
 	}
 }
 
-// annotate adds a span annotation to the collection.
-func (c *collector) annotate(s vtrace.Span, msg string) {
+func (c *collector) spanRecordLocked(s *span) *vtrace.SpanRecord {
+	sid := s.ID()
+	record, ok := c.spans[sid]
+	if !ok {
+		record = &vtrace.SpanRecord{
+			ID:     sid,
+			Parent: s.parent,
+			Name:   s.name,
+			Start:  s.start.UnixNano(),
+		}
+		c.spans[sid] = record
+	}
+	return record
+}
+
+// start records the fact that a given span has begun.
+func (c *collector) start(s *span) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
 	if c.method == vtrace.InMemory {
-		sid := s.ID()
-		record, ok := c.spans[sid]
-		if !ok {
-			record = &vtrace.SpanRecord{ID: sid, Parent: s.Parent(), Name: s.Name()}
-			c.spans[sid] = record
-		}
+		// Note that simply fetching the record is enough since
+		// if the record does not exist we will created it according
+		// to the start time in s.
+		c.spanRecordLocked(s)
+	}
+}
+
+// finish records the time that a span finished.
+func (c *collector) finish(s *span) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if c.method == vtrace.InMemory {
+		record := c.spanRecordLocked(s)
+		// TODO(mattr): Perhaps we should log an error if we have already been finished?
+		record.End = time.Now().UnixNano()
+	}
+}
+
+// annotate adds a span annotation to the collection.
+func (c *collector) annotate(s *span, msg string) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if c.method == vtrace.InMemory {
+		record := c.spanRecordLocked(s)
 		record.Annotations = append(record.Annotations, vtrace.Annotation{
 			When:    time.Now().UnixNano(),
 			Message: msg,
@@ -69,7 +115,7 @@
 	defer c.mu.Unlock()
 	return vtrace.Response{
 		Method: c.method,
-		Trace:  c.recordLocked(),
+		Trace:  c.traceRecordLocked(),
 	}
 }
 
@@ -78,18 +124,13 @@
 func (c *collector) Record() vtrace.TraceRecord {
 	c.mu.Lock()
 	defer c.mu.Unlock()
-	return c.recordLocked()
+	return c.traceRecordLocked()
 }
 
-func (c *collector) recordLocked() vtrace.TraceRecord {
+func (c *collector) traceRecordLocked() vtrace.TraceRecord {
 	spans := make([]vtrace.SpanRecord, 0, len(c.spans))
 	for _, span := range c.spans {
-		spans = append(spans, vtrace.SpanRecord{
-			ID:          span.ID,
-			Parent:      span.Parent,
-			Name:        span.Name,
-			Annotations: append([]vtrace.Annotation{}, span.Annotations...),
-		})
+		spans = append(spans, *copySpanRecord(span))
 	}
 	return vtrace.TraceRecord{
 		ID:    c.traceID,
@@ -110,12 +151,7 @@
 	// by assuming that children of parent need to start after parent
 	// and end before now.
 	for _, span := range t.Trace.Spans {
-		c.spans[span.ID] = &vtrace.SpanRecord{
-			ID:          span.ID,
-			Parent:      span.Parent,
-			Name:        span.Name,
-			Annotations: append([]vtrace.Annotation{}, span.Annotations...),
-		}
+		c.spans[span.ID] = copySpanRecord(&span)
 	}
 }
 
diff --git a/runtimes/google/vtrace/vtrace.go b/runtimes/google/vtrace/vtrace.go
index 0600677..3a41e41 100644
--- a/runtimes/google/vtrace/vtrace.go
+++ b/runtimes/google/vtrace/vtrace.go
@@ -4,6 +4,8 @@
 package vtrace
 
 import (
+	"time"
+
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/uniqueid"
 	"veyron.io/veyron/veyron2/vlog"
@@ -16,6 +18,7 @@
 	parent    uniqueid.ID
 	name      string
 	collector *collector
+	start     time.Time
 }
 
 func newSpan(parent uniqueid.ID, name string, collector *collector) *span {
@@ -28,8 +31,9 @@
 		parent:    parent,
 		name:      name,
 		collector: collector,
+		start:     time.Now(),
 	}
-	s.Annotate("Started")
+	collector.start(s)
 	return s
 }
 
@@ -38,6 +42,7 @@
 func (c *span) Name() string        { return c.name }
 func (c *span) Trace() vtrace.Trace { return c.collector }
 func (c *span) Annotate(msg string) { c.collector.annotate(c, msg) }
+func (c *span) Finish()             { c.collector.finish(c) }
 
 // Request generates a vtrace.Request from the active Span.
 func Request(ctx context.T) vtrace.Request {
diff --git a/runtimes/google/vtrace/vtrace_test.go b/runtimes/google/vtrace/vtrace_test.go
index 44b7d53..66c8bd7 100644
--- a/runtimes/google/vtrace/vtrace_test.go
+++ b/runtimes/google/vtrace/vtrace_test.go
@@ -122,11 +122,15 @@
 }
 
 func summary(span *vtrace.SpanRecord) string {
-	msgs := []string{}
-	for _, annotation := range span.Annotations {
-		msgs = append(msgs, annotation.Message)
+	summary := span.Name
+	if len(span.Annotations) > 0 {
+		msgs := []string{}
+		for _, annotation := range span.Annotations {
+			msgs = append(msgs, annotation.Message)
+		}
+		summary += ": " + strings.Join(msgs, ", ")
 	}
-	return span.Name + ": " + strings.Join(msgs, ",")
+	return summary
 }
 
 func expectSequence(t *testing.T, trace vtrace.TraceRecord, expectedSpans []string) {
@@ -135,16 +139,31 @@
 	}
 
 	spans := map[string]*vtrace.SpanRecord{}
+	summaries := []string{}
 	for i := range trace.Spans {
 		span := &trace.Spans[i]
+
+		// All spans should have a start.
+		if span.Start == 0 {
+			t.Errorf("span missing start: %#v", span)
+		}
+		// All spans except the root should have an end.
+		if span.Name != "" && span.End == 0 {
+			t.Errorf("span missing end: %#v", span)
+			if span.Start >= span.End {
+				t.Errorf("span end should be after start: %#v", span)
+			}
+		}
+
 		summary := summary(span)
+		summaries = append(summaries, summary)
 		spans[summary] = span
 	}
 
 	for i := range expectedSpans {
 		child, ok := spans[expectedSpans[i]]
 		if !ok {
-			t.Errorf("expected span not found: %s", expectedSpans[i])
+			t.Errorf("expected span %s not found in %#v", expectedSpans[i], summaries)
 			continue
 		}
 		if i == 0 {
@@ -152,7 +171,7 @@
 		}
 		parent, ok := spans[expectedSpans[i-1]]
 		if !ok {
-			t.Errorf("expected span not found: %s", expectedSpans[i-1])
+			t.Errorf("expected span %s not found in %#v", expectedSpans[i-1], summaries)
 			continue
 		}
 		if child.Parent != parent.ID {
@@ -207,11 +226,11 @@
 	span.Annotate("c0-end")
 
 	expectedSpans := []string{
-		": c0-begin,c0-end",
-		"Client Call: c1.Run: Started,Finished",
-		"Server Call: .Run: c1-begin,c1-end",
-		"Client Call: c2.Run: Started,Finished",
-		"Server Call: .Run: c2-begin,c2-end",
+		": c0-begin, c0-end",
+		"Client Call: c1.Run",
+		"Server Call: .Run: c1-begin, c1-end",
+		"Client Call: c2.Run",
+		"Server Call: .Run: c2-begin, c2-end",
 	}
 	expectSequence(t, span.Trace().Record(), expectedSpans)
 }
@@ -228,10 +247,10 @@
 
 	expectedSpans := []string{
 		": c0-end",
-		"Client Call: c1.Run: Finished",
+		"Client Call: c1.Run",
 		"Server Call: .Run: c1-end",
-		"Client Call: c2.Run: Finished",
-		"Server Call: .Run: c2-begin,c2-end",
+		"Client Call: c2.Run",
+		"Server Call: .Run: c2-begin, c2-end",
 	}
 	expectSequence(t, span.Trace().Record(), expectedSpans)
 }