Report more information about queried servers.

This change causes the Go client to report the timespan and query
duration for each server queried.

Change-Id: I5ddb8e3fe7a7877dbc184f64f82d9d1f5236344e
diff --git a/go/client/client.go b/go/client/client.go
index 004f829..6ecf332 100644
--- a/go/client/client.go
+++ b/go/client/client.go
@@ -190,6 +190,10 @@
 	// max is the maximum real-time (in Roughtime UTC microseconds) that
 	// could correspond to |base| (i.e. midpoint + radius + query time).
 	max *big.Int
+
+	// queryDuration contains the amount of time that the server took to
+	// answer the query.
+	queryDuration time.Duration
 }
 
 // midpoint returns the average of the min and max times.
@@ -298,7 +302,7 @@
 		Reply:         reply,
 	})
 
-	queryDurationBig := new(big.Int).SetInt64(int64(queryDuration/time.Microsecond))
+	queryDurationBig := new(big.Int).SetInt64(int64(queryDuration / time.Microsecond))
 	bigRadius := new(big.Int).SetUint64(uint64(radius))
 	min := new(big.Int).SetUint64(midpoint)
 	min.Sub(min, bigRadius)
@@ -308,10 +312,11 @@
 	max.Add(max, bigRadius)
 
 	return &timeSample{
-		server: server,
-		base:   new(big.Int).SetInt64(int64(baseTime)),
-		min:    min,
-		max:    max,
+		server:        server,
+		base:          new(big.Int).SetInt64(int64(baseTime)),
+		min:           min,
+		max:           max,
+		queryDuration: queryDuration,
 	}, nil
 }
 
@@ -463,12 +468,26 @@
 	// ServerErrors maps from server name to query error.
 	ServerErrors map[string]error
 
+	// ServerInfo contains information about each server that was queried.
+	ServerInfo map[string]ServerInfo
+
 	// OutOfRangeAnswer is true if one or more of the queries contained a
 	// significantly incorrect time, as defined by MaxDifference. In this
 	// case, the reply will have been recorded in the chain.
 	OutOfRangeAnswer bool
 }
 
+// ServerInfo contains information from a specific server.
+type ServerInfo struct {
+	// QueryDuration is the amount of time that the server took to answer.
+	QueryDuration time.Duration
+
+	// Min and Max specify the time window given by the server. These
+	// values have been adjusted so that they are comparible across
+	// servers, even though they are queried at different times.
+	Min, Max *big.Int
+}
+
 // EstablishTime queries a number of servers until it has a quorum of
 // overlapping results, or it runs out of servers. Results from the querying
 // the servers are appended to chain.
@@ -496,6 +515,15 @@
 		}
 		samples = append(samples, sample)
 
+		if result.ServerInfo == nil {
+			result.ServerInfo = make(map[string]ServerInfo)
+		}
+		result.ServerInfo[server.Name] = ServerInfo{
+			QueryDuration: sample.queryDuration,
+			Min:           sample.min,
+			Max:           sample.max,
+		}
+
 		var ok bool
 		if intersection, ok = findNOverlapping(samples, quorum); ok {
 			break
diff --git a/go/client/main.go b/go/client/main.go
index b74e95b..a410f97 100644
--- a/go/client/main.go
+++ b/go/client/main.go
@@ -21,6 +21,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 	"time"
 
 	"roughtime.googlesource.com/go/client/monotime"
@@ -81,6 +82,17 @@
 		fmt.Fprintf(os.Stderr, "Failed to query %q: %s\n", serverName, err)
 	}
 
+	maxLenServerName := 0
+	for name := range result.ServerInfo {
+		if len(name) > maxLenServerName {
+			maxLenServerName = len(name)
+		}
+	}
+
+	for name, info := range result.ServerInfo {
+		fmt.Printf("%s:%s %d–%d (answered in %s)\n", name, strings.Repeat(" ", maxLenServerName-len(name)), info.Min, info.Max, info.QueryDuration)
+	}
+
 	if result.MonoUTCDelta == nil {
 		fmt.Fprintf(os.Stderr, "Failed to get %d servers to agree on the time.\n", quorum)
 	} else {