From f67df53edb8e6a2b766ffb317eff4478c06e8406 Mon Sep 17 00:00:00 2001 From: "Matt T. Proud" Date: Sun, 13 Jul 2014 14:48:00 +0200 Subject: [PATCH] Containerize the runtime events in a ring buffer. To enable someone to use this tool to observe event streams over sliding windows of sample recency as well as prevent unbound growth of observations, the list of observations has been wrapped in a ring buffer of flag-specifiable size. This has been a tremendous help in visualizing events from particularly garbage collector-active servers. --- main.go | 115 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 37 deletions(-) diff --git a/main.go b/main.go index 84dcf01..defc394 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -// gzvis is a tool to assist you visualising the operation of +// gcvis is a tool to assist you visualising the operation of // the go runtime garbage collector. // // usage: @@ -8,6 +8,8 @@ package main import ( "bufio" + "container/ring" + "flag" "fmt" "html/template" "io" @@ -27,7 +29,7 @@ import ( func startSubprocess(w io.Writer) { env := os.Environ() env = append(env, "GODEBUG=gctrace=1") - args := os.Args[1:] + args := flag.Args() cmd := exec.Command(args[0], args[1:]...) cmd.Env = env cmd.Stdin = os.Stdin @@ -73,26 +75,76 @@ type scvgtrace struct { type graphPoints [2]int -var heapuse, scvginuse, scvgidle, scvgsys, scvgreleased, scvgconsumed []graphPoints +type gcviz struct { + heapuse, scvginuse, scvgidle, scvgsys, scvgreleased, scvgconsumed *ring.Ring -var mu sync.RWMutex + mu sync.RWMutex +} + +func newgcviz(histSz int) *gcviz { + return &gcviz{ + heapuse: ring.New(histSz), + scvginuse: ring.New(histSz), + scvgidle: ring.New(histSz), + scvgsys: ring.New(histSz), + scvgreleased: ring.New(histSz), + scvgconsumed: ring.New(histSz), + } +} + +func asFlotStream(r *ring.Ring) []graphPoints { + var strm []graphPoints + r.Do(func(v interface{}) { + if v == nil { + return + } + strm = append(strm, v.(graphPoints)) + }) + return strm +} -func index(w http.ResponseWriter, req *http.Request) { - mu.RLock() - defer mu.RUnlock() - visTmpl.Execute(w, struct { +func (g *gcviz) tmpl() interface{} { + g.mu.RLock() + defer g.mu.RUnlock() + + return struct { HeapUse, ScvgInuse, ScvgIdle, ScvgSys, ScvgReleased, ScvgConsumed []graphPoints Title string }{ - HeapUse: heapuse, - ScvgInuse: scvginuse, - ScvgIdle: scvgidle, - ScvgSys: scvgsys, - ScvgReleased: scvgreleased, - ScvgConsumed: scvgconsumed, - Title: strings.Join(os.Args[1:], " "), - }) + HeapUse: asFlotStream(g.heapuse), + ScvgInuse: asFlotStream(g.scvginuse), + ScvgIdle: asFlotStream(g.scvgidle), + ScvgSys: asFlotStream(g.scvgsys), + ScvgReleased: asFlotStream(g.scvgreleased), + ScvgConsumed: asFlotStream(g.scvgconsumed), + Title: strings.Join(flag.Args(), " "), + } +} +func observe(t, v int, r *ring.Ring) *ring.Ring { + r.Value = graphPoints{t, v} + return r.Next() +} + +func (g *gcviz) ingest(gcs chan *gctrace, scvgs chan *scvgtrace) { + for { + select { + case gc := <-gcs: + g.mu.Lock() + ts := int(time.Now().UnixNano() / 1e6) + g.heapuse = observe(ts, gc.Heap1, g.heapuse) + g.mu.Unlock() + case scvg := <-scvgs: + g.mu.Lock() + ts := int(time.Now().UnixNano() / 1e6) + g.scvginuse = observe(ts, scvg.inuse, g.scvginuse) + g.scvgidle = observe(ts, scvg.idle, g.scvgidle) + g.scvgsys = observe(ts, scvg.sys, g.scvgsys) + g.scvgreleased = observe(ts, scvg.released, g.scvgreleased) + g.scvgconsumed = observe(ts, scvg.consumed, g.scvgconsumed) + g.mu.Unlock() + } + } } var visTmpl = template.Must(template.New("vis").Parse(` @@ -173,7 +225,7 @@ var visTmpl = template.Must(template.New("vis").Parse(` $("#overview").bind("plotselected", function (event, ranges) { plot.setSelection(ranges); }); - + });