veyron/lib/flags/buildinfo: rework the buildinfo package a bit, and add stat

Change-Id: I33ba24ba8d968c4201e7ba08c2c7cce33667c333
diff --git a/lib/flags/buildinfo/buildinfo.go b/lib/flags/buildinfo/buildinfo.go
index fcc2427..f7574cb 100644
--- a/lib/flags/buildinfo/buildinfo.go
+++ b/lib/flags/buildinfo/buildinfo.go
@@ -5,25 +5,37 @@
 	"runtime"
 )
 
-// vanadiumBuildInfo is filled in at link time, using:
-//  -ldflags "-X v.io/core/veyron/lib/flags/buildinfo.vanadiumBuildInfo <value>"
-var vanadiumBuildInfo string
+// These variables are filled in at link time, using:
+//  -ldflags "-X v.io/core/veyron/lib/flags/buildinfo.<varname> <value>"
+var timestamp, username, platform string
 
-// VanadiumBinaryInfo describes binary metadata.
-type VanadiumBinaryInfo struct {
-	GoVersion, BuildInfo string
+// T describes binary metadata.
+type T struct {
+	GoVersion, BuildTimestamp, BuildUser, BuildPlatform string
 }
 
-// BinaryInfo returns the binary metadata as a JSON-encoded string, under the
-// expectation that clients may want to parse it for specific bits of metadata.
-func BinaryInfo() string {
-	info := VanadiumBinaryInfo{
-		GoVersion: runtime.Version(),
-		BuildInfo: vanadiumBuildInfo,
+var info T
+
+func init() {
+	info = T{
+		GoVersion:      runtime.Version(),
+		BuildTimestamp: timestamp,
+		BuildUser:      username,
+		BuildPlatform:  platform,
 	}
-	jsonInfo, err := json.Marshal(info)
+}
+
+// Info returns metadata about the current binary.
+func Info() *T {
+	return &info
+}
+
+// String returns the binary metadata as a JSON-encoded string, under the
+// expectation that clients may want to parse it for specific bits of metadata.
+func (t *T) String() string {
+	jsonT, err := json.Marshal(t)
 	if err != nil {
 		return ""
 	}
-	return string(jsonInfo)
+	return string(jsonT)
 }
diff --git a/lib/flags/flags.go b/lib/flags/flags.go
index 8a4930c..ce1ff24 100644
--- a/lib/flags/flags.go
+++ b/lib/flags/flags.go
@@ -393,7 +393,7 @@
 			fs.Usage = oldUsage
 		}()
 		fs.Usage = func() {
-			fmt.Fprintf(os.Stderr, "Binary info: %v\n", buildinfo.BinaryInfo())
+			fmt.Fprintf(os.Stderr, "Binary info: %s\n", buildinfo.Info())
 			if oldUsage == nil {
 				flag.Usage()
 			} else {
diff --git a/lib/stats/sysstats/sysstats.go b/lib/stats/sysstats/sysstats.go
index 9579280..1db188b 100644
--- a/lib/stats/sysstats/sysstats.go
+++ b/lib/stats/sysstats/sysstats.go
@@ -10,6 +10,7 @@
 	"strings"
 	"time"
 
+	"v.io/core/veyron/lib/flags/buildinfo"
 	"v.io/core/veyron/lib/stats"
 )
 
@@ -27,6 +28,7 @@
 	}
 	exportEnv()
 	exportMemStats()
+	exportBuildInfo()
 }
 
 func exportEnv() {
@@ -72,3 +74,12 @@
 		}
 	}()
 }
+
+func exportBuildInfo() {
+	kv := []stats.KeyValue{}
+	v := reflect.ValueOf(*buildinfo.Info())
+	for i := 0; i < v.NumField(); i++ {
+		kv = append(kv, stats.KeyValue{v.Type().Field(i).Name, v.Field(i).Interface()})
+	}
+	stats.NewMap("system/buildinfo").Set(kv)
+}
diff --git a/runtimes/google/rt/runtime.go b/runtimes/google/rt/runtime.go
index 945b77e..665b68b 100644
--- a/runtimes/google/rt/runtime.go
+++ b/runtimes/google/rt/runtime.go
@@ -72,7 +72,7 @@
 		return nil, nil, nil, err
 	}
 	// TODO(caprita): Only print this out for servers?
-	vlog.Infof("Binary info: %v", buildinfo.BinaryInfo())
+	vlog.Infof("Binary info: %s", buildinfo.Info())
 
 	// Setup the initial trace.
 	ctx, err = ivtrace.Init(ctx, flags.Vtrace)