import (
"container/heap"
tracev2 "internal/trace/v2"
- "io"
"math"
"sort"
"strings"
//
// If the UtilPerProc flag is not given, this always returns a single
// utilization function. Otherwise, it returns one function per P.
-func MutatorUtilizationV2(trace io.Reader, flags UtilFlags) ([][]MutatorUtil, error) {
- // Create a reader.
- r, err := tracev2.NewReader(trace)
- if err != nil {
- return nil, err
- }
-
+func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUtil {
// Set up a bunch of analysis state.
type perP struct {
// gc > 0 indicates that GC is active on this P.
}
// Iterate through the trace, tracking mutator utilization.
- var lastEv tracev2.Event
- for {
- // Read a single event.
- ev, err := r.ReadEvent()
- if err == io.EOF {
- break
- }
- if err != nil {
- return nil, err
- }
+ var lastEv *tracev2.Event
+ for i := range events {
+ ev := &events[i]
lastEv = ev
// Process the event.
}
// No events in the stream.
- if lastEv.Kind() == tracev2.EventBad {
- return nil, nil
+ if lastEv == nil {
+ return nil
}
// Add final 0 utilization event to any remaining series. This
for i := range ps {
out[ps[i].series] = addUtil(out[ps[i].series], mu)
}
- return out, nil
+ return out
}
func addUtil(util []MutatorUtil, mu MutatorUtil) []MutatorUtil {
-// Copyright 2017 The Go Authors. All rights reserved.
+// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// could potentially put confidence intervals on these estimates and
// render this progressively as we refine the distributions.
-package main
+package traceviewer
import (
"encoding/json"
"fmt"
"internal/trace"
- "internal/trace/traceviewer"
"log"
"math"
"net/http"
"time"
)
-func init() {
- http.HandleFunc("/mmu", httpMMU)
- http.HandleFunc("/mmuPlot", httpMMUPlot)
- http.HandleFunc("/mmuDetails", httpMMUDetails)
+type MutatorUtilFunc func(trace.UtilFlags) ([][]trace.MutatorUtil, error)
+
+func InstallMMUHandlers(mux *http.ServeMux, ranges []Range, f MutatorUtilFunc) {
+ mmu := &mmu{
+ cache: make(map[trace.UtilFlags]*mmuCacheEntry),
+ f: f,
+ ranges: ranges,
+ }
+ mux.HandleFunc("/mmu", func(w http.ResponseWriter, r *http.Request) {
+ // N.B. templMMU has Javascript that implicitly relies upon the existence
+ // of /mmuPlot and /mmuDetails on the same server.
+ http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU))
+ })
+ mux.HandleFunc("/mmuPlot", mmu.HandlePlot)
+ mux.HandleFunc("/mmuDetails", mmu.HandleDetails)
}
var utilFlagNames = map[string]trace.UtilFlags{
"sweep": trace.UtilSweep,
}
+func requestUtilFlags(r *http.Request) trace.UtilFlags {
+ var flags trace.UtilFlags
+ for _, flagStr := range strings.Split(r.FormValue("flags"), "|") {
+ flags |= utilFlagNames[flagStr]
+ }
+ return flags
+}
+
type mmuCacheEntry struct {
init sync.Once
util [][]trace.MutatorUtil
err error
}
-var mmuCache struct {
- m map[trace.UtilFlags]*mmuCacheEntry
- lock sync.Mutex
+type mmu struct {
+ mu sync.Mutex
+ cache map[trace.UtilFlags]*mmuCacheEntry
+ f MutatorUtilFunc
+ ranges []Range
}
-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]
+func (m *mmu) get(flags trace.UtilFlags) ([][]trace.MutatorUtil, *trace.MMUCurve, error) {
+ m.mu.Lock()
+ entry := m.cache[flags]
+ if entry == nil {
+ entry = new(mmuCacheEntry)
+ m.cache[flags] = entry
}
+ m.mu.Unlock()
- mmuCache.lock.Lock()
- c := mmuCache.m[flags]
- if c == nil {
- c = new(mmuCacheEntry)
- mmuCache.m[flags] = c
- }
- mmuCache.lock.Unlock()
-
- c.init.Do(func() {
- events, err := parseEvents()
+ entry.init.Do(func() {
+ util, err := m.f(flags)
if err != nil {
- c.err = err
+ entry.err = err
} else {
- c.util = trace.MutatorUtilization(events, flags)
- c.mmuCurve = trace.NewMMUCurve(c.util)
+ entry.util = util
+ entry.mmuCurve = trace.NewMMUCurve(util)
}
})
- return c.util, c.mmuCurve, c.err
-}
-
-// httpMMU serves the MMU plot page.
-func httpMMU(w http.ResponseWriter, r *http.Request) {
- http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU))
+ return entry.util, entry.mmuCurve, entry.err
}
-// httpMMUPlot serves the JSON data for the MMU plot.
-func httpMMUPlot(w http.ResponseWriter, r *http.Request) {
- mu, mmuCurve, err := getMMUCurve(r)
+// HandlePlot serves the JSON data for the MMU plot.
+func (m *mmu) HandlePlot(w http.ResponseWriter, r *http.Request) {
+ mu, mmuCurve, err := m.get(requestUtilFlags(r))
if err != nil {
- http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError)
+ http.Error(w, fmt.Sprintf("failed to produce MMU data: %v", err), http.StatusInternalServerError)
return
}
</html>
`
-// httpMMUDetails serves details of an MMU graph at a particular window.
-func httpMMUDetails(w http.ResponseWriter, r *http.Request) {
- _, mmuCurve, err := getMMUCurve(r)
+// HandleDetails serves details of an MMU graph at a particular window.
+func (m *mmu) HandleDetails(w http.ResponseWriter, r *http.Request) {
+ _, mmuCurve, err := m.get(requestUtilFlags(r))
if err != nil {
- http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError)
+ http.Error(w, fmt.Sprintf("failed to produce MMU data: %v", err), http.StatusInternalServerError)
return
}
// Construct a link for each window.
var links []linkedUtilWindow
for _, ui := range worst {
- links = append(links, newLinkedUtilWindow(ui, time.Duration(window)))
+ links = append(links, m.newLinkedUtilWindow(ui, time.Duration(window)))
}
err = json.NewEncoder(w).Encode(links)
URL string
}
-func newLinkedUtilWindow(ui trace.UtilWindow, window time.Duration) linkedUtilWindow {
+func (m *mmu) newLinkedUtilWindow(ui trace.UtilWindow, window time.Duration) linkedUtilWindow {
// Find the range containing this window.
- var r traceviewer.Range
- for _, r = range ranges {
+ var r Range
+ for _, r = range m.ranges {
if r.EndTime > ui.Time {
break
}