veyron/lib/stats: Add Delete function

The new Delete function is used to delete a stats object from the tree,
along with any children below it.

Use this function to clean up the ipc server stats when the server is
stopped.

Change-Id: I84eec2a015ad7affead9d73dbbcbc8536ee98ccc
diff --git a/lib/stats/stats.go b/lib/stats/stats.go
index 335098c..e55947c 100644
--- a/lib/stats/stats.go
+++ b/lib/stats/stats.go
@@ -67,6 +67,24 @@
 	return obj.Value(), nil
 }
 
+// Delete deletes a StatsObject and all its children, if any.
+func Delete(name string) error {
+	if name == "" {
+		return ErrNotFound
+	}
+	elems := strings.Split(name, "/")
+	last := len(elems) - 1
+	dirname, basename := strings.Join(elems[:last], "/"), elems[last]
+	lock.Lock()
+	defer lock.Unlock()
+	parent := findNodeLocked(dirname, false)
+	if parent == nil {
+		return ErrNotFound
+	}
+	delete(parent.children, basename)
+	return nil
+}
+
 func newNode() *node {
 	return &node{children: make(map[string]*node)}
 }
diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go
index 17e0f61..dc2c8c6 100644
--- a/lib/stats/stats_test.go
+++ b/lib/stats/stats_test.go
@@ -272,3 +272,25 @@
 		t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
 	}
 }
+
+func TestDelete(t *testing.T) {
+	_ = libstats.NewInteger("a/b/c/d")
+	if _, err := libstats.GetStatsObject("a/b/c/d"); err != nil {
+		t.Errorf("unexpected error value: %v", err)
+	}
+	if err := libstats.Delete("a/b/c/d"); err != nil {
+		t.Errorf("unexpected error value: %v", err)
+	}
+	if _, err := libstats.GetStatsObject("a/b/c/d"); err != libstats.ErrNotFound {
+		t.Errorf("unexpected error value: Got %v, want %v", err, libstats.ErrNotFound)
+	}
+	if err := libstats.Delete("a/b"); err != nil {
+		t.Errorf("unexpected error value: %v", err)
+	}
+	if _, err := libstats.GetStatsObject("a/b"); err != libstats.ErrNotFound {
+		t.Errorf("unexpected error value: Got %v, want %v", err, libstats.ErrNotFound)
+	}
+	if _, err := libstats.GetStatsObject("a/b/c"); err != libstats.ErrNotFound {
+		t.Errorf("unexpected error value: Got %v, want %v", err, libstats.ErrNotFound)
+	}
+}
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 7665b83..a8ff3b3 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -499,6 +499,9 @@
 	close(s.stoppedChan)
 	s.Unlock()
 
+	// Delete the stats object.
+	s.stats.stop()
+
 	// Note, It's safe to Stop/WaitForStop on the publisher outside of the
 	// server lock, since publisher is safe for concurrent access.
 
diff --git a/runtimes/google/ipc/stats.go b/runtimes/google/ipc/stats.go
index a04e932..5f6f2ed 100644
--- a/runtimes/google/ipc/stats.go
+++ b/runtimes/google/ipc/stats.go
@@ -24,6 +24,10 @@
 	latency *histogram.Histogram
 }
 
+func (s *ipcStats) stop() {
+	stats.Delete(s.prefix)
+}
+
 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