veyron/runtimes/google/ipc: Adding per-method stats

This change adds two per-method stats to server calls: the number of
calls, and the latency distribution.

It is intended as a proof of concept.

Change-Id: Id0e9abb8b744c74b71a4db88941ba234114cff56
diff --git a/runtimes/google/ipc/stats.go b/runtimes/google/ipc/stats.go
new file mode 100644
index 0000000..1b8344d
--- /dev/null
+++ b/runtimes/google/ipc/stats.go
@@ -0,0 +1,59 @@
+package ipc
+
+import (
+	"sync"
+	"time"
+
+	"veyron/lib/stats"
+	"veyron/lib/stats/histogram"
+
+	"veyron2/naming"
+)
+
+type ipcStats struct {
+	mu      sync.RWMutex
+	prefix  string
+	methods map[string]*perMethodStats
+}
+
+func newIPCStats(prefix string) *ipcStats {
+	return &ipcStats{prefix: prefix, methods: make(map[string]*perMethodStats)}
+}
+
+type perMethodStats struct {
+	latency *histogram.Histogram
+}
+
+func (s *ipcStats) record(method string, latency time.Duration) {
+	// Try first with a read lock. This will succeed in the most common
+	// case. If it fails, try again with a write lock and create the stats
+	// objects if they are still not there.
+	s.mu.RLock()
+	m, ok := s.methods[method]
+	s.mu.RUnlock()
+	if !ok {
+		m = s.newPerMethodStats(method)
+	}
+	m.latency.Add(int64(latency / time.Millisecond))
+}
+
+// newPerMethodStats creates a new perMethodStats object if one doesn't exist
+// already. It returns the newly created object, or the already existing one.
+func (s *ipcStats) newPerMethodStats(method string) *perMethodStats {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	m, ok := s.methods[method]
+	if !ok {
+		name := naming.Join(s.prefix, method, "latency-ms")
+		s.methods[method] = &perMethodStats{
+			latency: stats.NewHistogram(name, histogram.Options{
+				NumBuckets:         25,
+				GrowthFactor:       1,
+				SmallestBucketSize: 1,
+				MinValue:           0,
+			}),
+		}
+		m = s.methods[method]
+	}
+	return m
+}