// 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. package main import ( "cmp" "fmt" "html/template" "internal/trace" "internal/trace/traceviewer" "net/http" "net/url" "slices" "sort" "strconv" "strings" "time" ) // UserRegionsHandlerFunc returns a HandlerFunc that reports all regions found in the trace. func UserRegionsHandlerFunc(t *parsedTrace) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Summarize all the regions. summary := make(map[regionFingerprint]regionStats) for _, g := range t.summary.Goroutines { for _, r := range g.Regions { id := fingerprintRegion(r) stats, ok := summary[id] if !ok { stats.regionFingerprint = id } stats.add(t, r) summary[id] = stats } } // Sort regions by PC and name. userRegions := make([]regionStats, 0, len(summary)) for _, stats := range summary { userRegions = append(userRegions, stats) } slices.SortFunc(userRegions, func(a, b regionStats) int { if c := cmp.Compare(a.Type, b.Type); c != 0 { return c } return cmp.Compare(a.Frame.PC, b.Frame.PC) }) // Emit table. err := templUserRegionTypes.Execute(w, userRegions) if err != nil { http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) return } } } // regionFingerprint is a way to categorize regions that goes just one step beyond the region's Type // by including the top stack frame. type regionFingerprint struct { Frame trace.StackFrame Type string } func fingerprintRegion(r *trace.UserRegionSummary) regionFingerprint { return regionFingerprint{ Frame: regionTopStackFrame(r), Type: r.Name, } } func regionTopStackFrame(r *trace.UserRegionSummary) trace.StackFrame { var frame trace.StackFrame if r.Start != nil && r.Start.Stack() != trace.NoStack { r.Start.Stack().Frames(func(f trace.StackFrame) bool { frame = f return false }) } return frame } type regionStats struct { regionFingerprint Histogram traceviewer.TimeHistogram } func (s *regionStats) UserRegionURL() func(min, max time.Duration) string { return func(min, max time.Duration) string { return fmt.Sprintf("/userregion?type=%s&pc=%x&latmin=%v&latmax=%v", template.URLQueryEscaper(s.Type), s.Frame.PC, template.URLQueryEscaper(min), template.URLQueryEscaper(max)) } } func (s *regionStats) add(t *parsedTrace, region *trace.UserRegionSummary) { s.Histogram.Add(regionInterval(t, region).duration()) } var templUserRegionTypes = template.Must(template.New("").Parse(`
Region type | Count | Duration distribution (complete tasks) |
---|---|---|
{{printf "%q" .Type}} |
{{.Histogram.Count}} | {{.Histogram.ToHTML (.UserRegionURL)}} |