From 0072a5b325f8e0ddef7ebe365f8516aeb3675b57 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 26 Jan 2014 22:05:02 -0800 Subject: [PATCH] client/android: instrument memory usage. also try to GC and return memory to OS every 5 seconds, but that barely makes a dent in the real problem. more later. Change-Id: I2979e099121bdcb8578124b1c45714313cc2ec09 --- pkg/client/android/android.go | 50 ++++++++++++++++++++++++++++++++++- pkg/client/client.go | 6 ++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/pkg/client/android/android.go b/pkg/client/android/android.go index 580e670f7..7759873af 100644 --- a/pkg/client/android/android.go +++ b/pkg/client/android/android.go @@ -33,11 +33,15 @@ import ( "os/exec" "path/filepath" "regexp" + "runtime" + "runtime/debug" "strconv" "sync" + "time" "camlistore.org/pkg/blob" "camlistore.org/pkg/blobserver" + "camlistore.org/pkg/osutil" "camlistore.org/pkg/schema" ) @@ -48,6 +52,7 @@ var androidOutput, _ = strconv.ParseBool(os.Getenv("CAMPUT_ANDROID_OUTPUT")) // child process and should report its output in the form that the // Android uploader app expects. func IsChild() bool { + memOnce.Do(startMemGoroutine) return androidOutput } @@ -93,6 +98,17 @@ func (ni *namedInt) Incr(delta int64) { Printf("STAT %s %d\n", ni.name, nv) } +func (ni *namedInt) Set(v int64) { + ni.Lock() + if v == ni.val { + ni.Unlock() + return + } + ni.val = v + ni.Unlock() + Printf("STAT %s %d\n", ni.name, v) +} + var ( statDNSStart = &namedInt{name: "dns_start"} statDNSDone = &namedInt{name: "dns_done"} @@ -109,6 +125,9 @@ var ( statBlobExisted = &namedInt{name: "blob_existed"} statFileUploaded = &namedInt{name: "file_uploaded"} statFileExisted = &namedInt{name: "file_existed"} + statMemReleased = &namedInt{name: "mem_heap_released"} + statMemAlloc = &namedInt{name: "mem_alloc"} + statMemRSS = &namedInt{name: "mem_rss"} ) type statTrackingConn struct { @@ -212,7 +231,13 @@ func Dial(network, addr string) (net.Conn, error) { statTCPFail.Incr(1) return nil, fmt.Errorf("couldn't split %q", addr) } - c, err := net.Dial(network, net.JoinHostPort(androidLookupHost(host), port)) + if OnAndroid() { + // Only do the Android DNS lookups when actually + // running on a device. We also run in "Android mode" + // (IsChild) in tests and interactive debugging. + host = androidLookupHost(host) + } + c, err := net.Dial(network, net.JoinHostPort(host, port)) if err != nil { statTCPFail.Incr(1) return nil, err @@ -323,3 +348,26 @@ func (w writeUntilSliceFull) Write(p []byte) (n int, err error) { *w.s = s return len(p), nil } + +var memOnce sync.Once + +func startMemGoroutine() { + if !androidOutput { + return + } + go func() { + var ms runtime.MemStats + n := 0 + for { + runtime.ReadMemStats(&ms) + statMemReleased.Set(int64(ms.HeapReleased)) + statMemAlloc.Set(int64(ms.Alloc)) + statMemRSS.Set(osutil.MemUsage()) + time.Sleep(1 * time.Second) + n++ + if n%5 == 0 { + debug.FreeOSMemory() + } + } + }() +} diff --git a/pkg/client/client.go b/pkg/client/client.go index bfe0b4b58..8f60bfc67 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -188,7 +188,7 @@ func (c *Client) TransportForConfig(tc *TransportConfig) http.RoundTripper { httpStats.VerboseLog = tc.Verbose } transport = httpStats - if android.OnAndroid() { + if android.IsChild() { transport = &android.StatsTransport{transport} } return transport @@ -816,7 +816,7 @@ func (c *Client) DialFunc() func(network, addr string) (net.Conn, error) { trustedCerts := c.getTrustedCerts() if !c.useTLS() || (!c.InsecureTLS && len(trustedCerts) == 0) { // No TLS, or TLS with normal/full verification - if android.OnAndroid() { + if android.IsChild() { return func(network, addr string) (net.Conn, error) { return android.Dial(network, addr) } @@ -827,7 +827,7 @@ func (c *Client) DialFunc() func(network, addr string) (net.Conn, error) { return func(network, addr string) (net.Conn, error) { var conn *tls.Conn var err error - if android.OnAndroid() { + if android.IsChild() { con, err := android.Dial(network, addr) if err != nil { return nil, err