]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/trace: expose MMU analysis flags in web UI
authorAustin Clements <austin@google.com>
Fri, 28 Jul 2017 20:30:05 +0000 (16:30 -0400)
committerAustin Clements <austin@google.com>
Mon, 5 Nov 2018 19:10:25 +0000 (19:10 +0000)
Change-Id: I672240487172380c9eef61837b41698021aaf834
Reviewed-on: https://go-review.googlesource.com/c/60798
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
src/cmd/trace/mmu.go

index d3b6768686a2d8663b6699bae102239387eaab38..3fae3d6645b175aa32c5a70da8b9f618bc51cd14 100644 (file)
@@ -25,24 +25,54 @@ func init() {
        http.HandleFunc("/mmuDetails", httpMMUDetails)
 }
 
-var mmuCache struct {
+var utilFlagNames = map[string]trace.UtilFlags{
+       "perProc":    trace.UtilPerProc,
+       "stw":        trace.UtilSTW,
+       "background": trace.UtilBackground,
+       "assist":     trace.UtilAssist,
+       "sweep":      trace.UtilSweep,
+}
+
+type mmuCacheEntry struct {
        init     sync.Once
        util     [][]trace.MutatorUtil
        mmuCurve *trace.MMUCurve
        err      error
 }
 
-func getMMUCurve() ([][]trace.MutatorUtil, *trace.MMUCurve, error) {
-       mmuCache.init.Do(func() {
+var mmuCache struct {
+       m    map[trace.UtilFlags]*mmuCacheEntry
+       lock sync.Mutex
+}
+
+func init() {
+       mmuCache.m = make(map[trace.UtilFlags]*mmuCacheEntry)
+}
+
+func getMMUCurve(r *http.Request) ([][]trace.MutatorUtil, *trace.MMUCurve, error) {
+       var flags trace.UtilFlags
+       for _, flagStr := range strings.Split(r.FormValue("flags"), "|") {
+               flags |= utilFlagNames[flagStr]
+       }
+
+       mmuCache.lock.Lock()
+       c := mmuCache.m[flags]
+       if c == nil {
+               c = new(mmuCacheEntry)
+               mmuCache.m[flags] = c
+       }
+       mmuCache.lock.Unlock()
+
+       c.init.Do(func() {
                tr, err := parseTrace()
                if err != nil {
-                       mmuCache.err = err
+                       c.err = err
                } else {
-                       mmuCache.util = tr.MutatorUtilization(trace.UtilSTW | trace.UtilBackground | trace.UtilAssist)
-                       mmuCache.mmuCurve = trace.NewMMUCurve(mmuCache.util)
+                       c.util = tr.MutatorUtilization(flags)
+                       c.mmuCurve = trace.NewMMUCurve(c.util)
                }
        })
-       return mmuCache.util, mmuCache.mmuCurve, mmuCache.err
+       return c.util, c.mmuCurve, c.err
 }
 
 // httpMMU serves the MMU plot page.
@@ -52,7 +82,7 @@ func httpMMU(w http.ResponseWriter, r *http.Request) {
 
 // httpMMUPlot serves the JSON data for the MMU plot.
 func httpMMUPlot(w http.ResponseWriter, r *http.Request) {
-       mu, mmuCurve, err := getMMUCurve()
+       mu, mmuCurve, err := getMMUCurve(r)
        if err != nil {
                http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError)
                return
@@ -107,7 +137,8 @@ var templMMU = `<!doctype html>
     <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
     <script type="text/javascript">
       google.charts.load('current', {'packages':['corechart']});
-      google.charts.setOnLoadCallback(refreshChart);
+      var chartsReady = false;
+      google.charts.setOnLoadCallback(function() { chartsReady = true; refreshChart(); });
 
       var chart;
       var curve;
@@ -119,13 +150,31 @@ var templMMU = `<!doctype html>
           else { return ns / 1e9 + 's'; }
       }
 
+      function mmuFlags() {
+        var flags = "";
+        $("#options input").each(function(i, elt) {
+          if (elt.checked)
+            flags += "|" + elt.id;
+        });
+        return flags.substr(1);
+      }
+
       function refreshChart() {
-        $.getJSON('/mmuPlot')
+        if (!chartsReady) return;
+        var container = $('#mmu_chart');
+        container.css('opacity', '.5');
+        refreshChart.count++;
+        var seq = refreshChart.count;
+        $.getJSON('/mmuPlot?flags=' + mmuFlags())
          .fail(function(xhr, status, error) {
            alert('failed to load plot: ' + status);
          })
-         .done(drawChart);
+         .done(function(result) {
+           if (refreshChart.count === seq)
+             drawChart(result);
+         });
       }
+      refreshChart.count = 0;
 
       function drawChart(plotData) {
         curve = plotData.curve;
@@ -162,11 +211,13 @@ var templMMU = `<!doctype html>
 
         var container = $('#mmu_chart');
         container.empty();
+        container.css('opacity', '');
         chart = new google.visualization.LineChart(container[0]);
         chart = new google.visualization.LineChart(document.getElementById('mmu_chart'));
         chart.draw(data, options);
 
         google.visualization.events.addListener(chart, 'select', selectHandler);
+        $('#details').empty();
       }
 
       function selectHandler() {
@@ -177,7 +228,7 @@ var templMMU = `<!doctype html>
         var details = $('#details');
         details.empty();
         var windowNS = curve[items[0].row][0];
-        var url = '/mmuDetails?window=' + windowNS;
+        var url = '/mmuDetails?window=' + windowNS + '&flags=' + mmuFlags();
         $.getJSON(url)
          .fail(function(xhr, status, error) {
             details.text(status + ': ' + url + ' could not be loaded');
@@ -191,10 +242,64 @@ var templMMU = `<!doctype html>
             }
          });
       }
+
+      $.when($.ready).then(function() {
+        $("#options input").click(refreshChart);
+      });
     </script>
+    <style>
+      .help {
+        display: inline-block;
+        position: relative;
+        width: 1em;
+        height: 1em;
+        border-radius: 50%;
+        color: #fff;
+        background: #555;
+        text-align: center;
+        cursor: help;
+      }
+      .help > span {
+        display: none;
+      }
+      .help:hover > span {
+        display: block;
+        position: absolute;
+        left: 1.1em;
+        top: 1.1em;
+        background: #555;
+        text-align: left;
+        width: 20em;
+        padding: 0.5em;
+        border-radius: 0.5em;
+        z-index: 5;
+      }
+    </style>
   </head>
   <body>
-    <div id="mmu_chart" style="width: 900px; height: 500px">Loading plot...</div>
+    <div style="position: relative">
+      <div id="mmu_chart" style="width: 900px; height: 500px; display: inline-block; vertical-align: top">Loading plot...</div>
+      <div id="options" style="display: inline-block; vertical-align: top">
+        <p>
+          <b>View</b><br/>
+          <input type="radio" name="view" id="system" checked><label for="system">System</label>
+          <span class="help">?<span>Consider whole system utilization. For example, if one of four procs is available to the mutator, mutator utilization will be 0.25. This is the standard definition of an MMU.</span></span><br/>
+          <input type="radio" name="view" id="perProc"><label for="perProc">Per-goroutine</label>
+          <span class="help">?<span>Consider per-goroutine utilization. When even one goroutine is interrupted by GC, mutator utilization is 0.</span></span><br/>
+        </p>
+        <p>
+          <b>Include</b><br/>
+          <input type="checkbox" id="stw" checked><label for="stw">STW</label>
+          <span class="help">?<span>Stop-the-world stops all goroutines simultaneously.</span></span><br/>
+          <input type="checkbox" id="background" checked><label for="background">Background workers</label>
+          <span class="help">?<span>Background workers are GC-specific goroutines. 25% of the CPU is dedicated to background workers during GC.</span></span><br/>
+          <input type="checkbox" id="assist" checked><label for="assist">Mark assist</label>
+          <span class="help">?<span>Mark assists are performed by allocation to prevent the mutator from outpacing GC.</span></span><br/>
+          <input type="checkbox" id="sweep"><label for="sweep">Sweep</label>
+          <span class="help">?<span>Sweep reclaims unused memory between GCs. (Enabling this may be very slow.).</span></span><br/>
+        </p>
+      </div>
+    </div>
     <div id="details">Select a point for details.</div>
   </body>
 </html>
@@ -202,7 +307,7 @@ var templMMU = `<!doctype html>
 
 // httpMMUDetails serves details of an MMU graph at a particular window.
 func httpMMUDetails(w http.ResponseWriter, r *http.Request) {
-       _, mmuCurve, err := getMMUCurve()
+       _, mmuCurve, err := getMMUCurve(r)
        if err != nil {
                http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError)
                return