gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

trace.go (28786B)


      1 // Copyright 2015 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 /*
      6 Package trace implements tracing of requests and long-lived objects.
      7 It exports HTTP interfaces on /debug/requests and /debug/events.
      8 
      9 A trace.Trace provides tracing for short-lived objects, usually requests.
     10 A request handler might be implemented like this:
     11 
     12 	func fooHandler(w http.ResponseWriter, req *http.Request) {
     13 		tr := trace.New("mypkg.Foo", req.URL.Path)
     14 		defer tr.Finish()
     15 		...
     16 		tr.LazyPrintf("some event %q happened", str)
     17 		...
     18 		if err := somethingImportant(); err != nil {
     19 			tr.LazyPrintf("somethingImportant failed: %v", err)
     20 			tr.SetError()
     21 		}
     22 	}
     23 
     24 The /debug/requests HTTP endpoint organizes the traces by family,
     25 errors, and duration.  It also provides histogram of request duration
     26 for each family.
     27 
     28 A trace.EventLog provides tracing for long-lived objects, such as RPC
     29 connections.
     30 
     31 	// A Fetcher fetches URL paths for a single domain.
     32 	type Fetcher struct {
     33 		domain string
     34 		events trace.EventLog
     35 	}
     36 
     37 	func NewFetcher(domain string) *Fetcher {
     38 		return &Fetcher{
     39 			domain,
     40 			trace.NewEventLog("mypkg.Fetcher", domain),
     41 		}
     42 	}
     43 
     44 	func (f *Fetcher) Fetch(path string) (string, error) {
     45 		resp, err := http.Get("http://" + f.domain + "/" + path)
     46 		if err != nil {
     47 			f.events.Errorf("Get(%q) = %v", path, err)
     48 			return "", err
     49 		}
     50 		f.events.Printf("Get(%q) = %s", path, resp.Status)
     51 		...
     52 	}
     53 
     54 	func (f *Fetcher) Close() error {
     55 		f.events.Finish()
     56 		return nil
     57 	}
     58 
     59 The /debug/events HTTP endpoint organizes the event logs by family and
     60 by time since the last error.  The expanded view displays recent log
     61 entries and the log's call stack.
     62 */
     63 package trace // import "golang.org/x/net/trace"
     64 
     65 import (
     66 	"bytes"
     67 	"context"
     68 	"fmt"
     69 	"html/template"
     70 	"io"
     71 	"log"
     72 	"net"
     73 	"net/http"
     74 	"net/url"
     75 	"runtime"
     76 	"sort"
     77 	"strconv"
     78 	"sync"
     79 	"sync/atomic"
     80 	"time"
     81 
     82 	"golang.org/x/net/internal/timeseries"
     83 )
     84 
     85 // DebugUseAfterFinish controls whether to debug uses of Trace values after finishing.
     86 // FOR DEBUGGING ONLY. This will slow down the program.
     87 var DebugUseAfterFinish = false
     88 
     89 // HTTP ServeMux paths.
     90 const (
     91 	debugRequestsPath = "/debug/requests"
     92 	debugEventsPath   = "/debug/events"
     93 )
     94 
     95 // AuthRequest determines whether a specific request is permitted to load the
     96 // /debug/requests or /debug/events pages.
     97 //
     98 // It returns two bools; the first indicates whether the page may be viewed at all,
     99 // and the second indicates whether sensitive events will be shown.
    100 //
    101 // AuthRequest may be replaced by a program to customize its authorization requirements.
    102 //
    103 // The default AuthRequest function returns (true, true) if and only if the request
    104 // comes from localhost/127.0.0.1/[::1].
    105 var AuthRequest = func(req *http.Request) (any, sensitive bool) {
    106 	// RemoteAddr is commonly in the form "IP" or "IP:port".
    107 	// If it is in the form "IP:port", split off the port.
    108 	host, _, err := net.SplitHostPort(req.RemoteAddr)
    109 	if err != nil {
    110 		host = req.RemoteAddr
    111 	}
    112 	switch host {
    113 	case "localhost", "127.0.0.1", "::1":
    114 		return true, true
    115 	default:
    116 		return false, false
    117 	}
    118 }
    119 
    120 func init() {
    121 	_, pat := http.DefaultServeMux.Handler(&http.Request{URL: &url.URL{Path: debugRequestsPath}})
    122 	if pat == debugRequestsPath {
    123 		panic("/debug/requests is already registered. You may have two independent copies of " +
    124 			"golang.org/x/net/trace in your binary, trying to maintain separate state. This may " +
    125 			"involve a vendored copy of golang.org/x/net/trace.")
    126 	}
    127 
    128 	// TODO(jbd): Serve Traces from /debug/traces in the future?
    129 	// There is no requirement for a request to be present to have traces.
    130 	http.HandleFunc(debugRequestsPath, Traces)
    131 	http.HandleFunc(debugEventsPath, Events)
    132 }
    133 
    134 // NewContext returns a copy of the parent context
    135 // and associates it with a Trace.
    136 func NewContext(ctx context.Context, tr Trace) context.Context {
    137 	return context.WithValue(ctx, contextKey, tr)
    138 }
    139 
    140 // FromContext returns the Trace bound to the context, if any.
    141 func FromContext(ctx context.Context) (tr Trace, ok bool) {
    142 	tr, ok = ctx.Value(contextKey).(Trace)
    143 	return
    144 }
    145 
    146 // Traces responds with traces from the program.
    147 // The package initialization registers it in http.DefaultServeMux
    148 // at /debug/requests.
    149 //
    150 // It performs authorization by running AuthRequest.
    151 func Traces(w http.ResponseWriter, req *http.Request) {
    152 	any, sensitive := AuthRequest(req)
    153 	if !any {
    154 		http.Error(w, "not allowed", http.StatusUnauthorized)
    155 		return
    156 	}
    157 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
    158 	Render(w, req, sensitive)
    159 }
    160 
    161 // Events responds with a page of events collected by EventLogs.
    162 // The package initialization registers it in http.DefaultServeMux
    163 // at /debug/events.
    164 //
    165 // It performs authorization by running AuthRequest.
    166 func Events(w http.ResponseWriter, req *http.Request) {
    167 	any, sensitive := AuthRequest(req)
    168 	if !any {
    169 		http.Error(w, "not allowed", http.StatusUnauthorized)
    170 		return
    171 	}
    172 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
    173 	RenderEvents(w, req, sensitive)
    174 }
    175 
    176 // Render renders the HTML page typically served at /debug/requests.
    177 // It does not do any auth checking. The request may be nil.
    178 //
    179 // Most users will use the Traces handler.
    180 func Render(w io.Writer, req *http.Request, sensitive bool) {
    181 	data := &struct {
    182 		Families         []string
    183 		ActiveTraceCount map[string]int
    184 		CompletedTraces  map[string]*family
    185 
    186 		// Set when a bucket has been selected.
    187 		Traces        traceList
    188 		Family        string
    189 		Bucket        int
    190 		Expanded      bool
    191 		Traced        bool
    192 		Active        bool
    193 		ShowSensitive bool // whether to show sensitive events
    194 
    195 		Histogram       template.HTML
    196 		HistogramWindow string // e.g. "last minute", "last hour", "all time"
    197 
    198 		// If non-zero, the set of traces is a partial set,
    199 		// and this is the total number.
    200 		Total int
    201 	}{
    202 		CompletedTraces: completedTraces,
    203 	}
    204 
    205 	data.ShowSensitive = sensitive
    206 	if req != nil {
    207 		// Allow show_sensitive=0 to force hiding of sensitive data for testing.
    208 		// This only goes one way; you can't use show_sensitive=1 to see things.
    209 		if req.FormValue("show_sensitive") == "0" {
    210 			data.ShowSensitive = false
    211 		}
    212 
    213 		if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
    214 			data.Expanded = exp
    215 		}
    216 		if exp, err := strconv.ParseBool(req.FormValue("rtraced")); err == nil {
    217 			data.Traced = exp
    218 		}
    219 	}
    220 
    221 	completedMu.RLock()
    222 	data.Families = make([]string, 0, len(completedTraces))
    223 	for fam := range completedTraces {
    224 		data.Families = append(data.Families, fam)
    225 	}
    226 	completedMu.RUnlock()
    227 	sort.Strings(data.Families)
    228 
    229 	// We are careful here to minimize the time spent locking activeMu,
    230 	// since that lock is required every time an RPC starts and finishes.
    231 	data.ActiveTraceCount = make(map[string]int, len(data.Families))
    232 	activeMu.RLock()
    233 	for fam, s := range activeTraces {
    234 		data.ActiveTraceCount[fam] = s.Len()
    235 	}
    236 	activeMu.RUnlock()
    237 
    238 	var ok bool
    239 	data.Family, data.Bucket, ok = parseArgs(req)
    240 	switch {
    241 	case !ok:
    242 		// No-op
    243 	case data.Bucket == -1:
    244 		data.Active = true
    245 		n := data.ActiveTraceCount[data.Family]
    246 		data.Traces = getActiveTraces(data.Family)
    247 		if len(data.Traces) < n {
    248 			data.Total = n
    249 		}
    250 	case data.Bucket < bucketsPerFamily:
    251 		if b := lookupBucket(data.Family, data.Bucket); b != nil {
    252 			data.Traces = b.Copy(data.Traced)
    253 		}
    254 	default:
    255 		if f := getFamily(data.Family, false); f != nil {
    256 			var obs timeseries.Observable
    257 			f.LatencyMu.RLock()
    258 			switch o := data.Bucket - bucketsPerFamily; o {
    259 			case 0:
    260 				obs = f.Latency.Minute()
    261 				data.HistogramWindow = "last minute"
    262 			case 1:
    263 				obs = f.Latency.Hour()
    264 				data.HistogramWindow = "last hour"
    265 			case 2:
    266 				obs = f.Latency.Total()
    267 				data.HistogramWindow = "all time"
    268 			}
    269 			f.LatencyMu.RUnlock()
    270 			if obs != nil {
    271 				data.Histogram = obs.(*histogram).html()
    272 			}
    273 		}
    274 	}
    275 
    276 	if data.Traces != nil {
    277 		defer data.Traces.Free()
    278 		sort.Sort(data.Traces)
    279 	}
    280 
    281 	completedMu.RLock()
    282 	defer completedMu.RUnlock()
    283 	if err := pageTmpl().ExecuteTemplate(w, "Page", data); err != nil {
    284 		log.Printf("net/trace: Failed executing template: %v", err)
    285 	}
    286 }
    287 
    288 func parseArgs(req *http.Request) (fam string, b int, ok bool) {
    289 	if req == nil {
    290 		return "", 0, false
    291 	}
    292 	fam, bStr := req.FormValue("fam"), req.FormValue("b")
    293 	if fam == "" || bStr == "" {
    294 		return "", 0, false
    295 	}
    296 	b, err := strconv.Atoi(bStr)
    297 	if err != nil || b < -1 {
    298 		return "", 0, false
    299 	}
    300 
    301 	return fam, b, true
    302 }
    303 
    304 func lookupBucket(fam string, b int) *traceBucket {
    305 	f := getFamily(fam, false)
    306 	if f == nil || b < 0 || b >= len(f.Buckets) {
    307 		return nil
    308 	}
    309 	return f.Buckets[b]
    310 }
    311 
    312 type contextKeyT string
    313 
    314 var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
    315 
    316 // Trace represents an active request.
    317 type Trace interface {
    318 	// LazyLog adds x to the event log. It will be evaluated each time the
    319 	// /debug/requests page is rendered. Any memory referenced by x will be
    320 	// pinned until the trace is finished and later discarded.
    321 	LazyLog(x fmt.Stringer, sensitive bool)
    322 
    323 	// LazyPrintf evaluates its arguments with fmt.Sprintf each time the
    324 	// /debug/requests page is rendered. Any memory referenced by a will be
    325 	// pinned until the trace is finished and later discarded.
    326 	LazyPrintf(format string, a ...interface{})
    327 
    328 	// SetError declares that this trace resulted in an error.
    329 	SetError()
    330 
    331 	// SetRecycler sets a recycler for the trace.
    332 	// f will be called for each event passed to LazyLog at a time when
    333 	// it is no longer required, whether while the trace is still active
    334 	// and the event is discarded, or when a completed trace is discarded.
    335 	SetRecycler(f func(interface{}))
    336 
    337 	// SetTraceInfo sets the trace info for the trace.
    338 	// This is currently unused.
    339 	SetTraceInfo(traceID, spanID uint64)
    340 
    341 	// SetMaxEvents sets the maximum number of events that will be stored
    342 	// in the trace. This has no effect if any events have already been
    343 	// added to the trace.
    344 	SetMaxEvents(m int)
    345 
    346 	// Finish declares that this trace is complete.
    347 	// The trace should not be used after calling this method.
    348 	Finish()
    349 }
    350 
    351 type lazySprintf struct {
    352 	format string
    353 	a      []interface{}
    354 }
    355 
    356 func (l *lazySprintf) String() string {
    357 	return fmt.Sprintf(l.format, l.a...)
    358 }
    359 
    360 // New returns a new Trace with the specified family and title.
    361 func New(family, title string) Trace {
    362 	tr := newTrace()
    363 	tr.ref()
    364 	tr.Family, tr.Title = family, title
    365 	tr.Start = time.Now()
    366 	tr.maxEvents = maxEventsPerTrace
    367 	tr.events = tr.eventsBuf[:0]
    368 
    369 	activeMu.RLock()
    370 	s := activeTraces[tr.Family]
    371 	activeMu.RUnlock()
    372 	if s == nil {
    373 		activeMu.Lock()
    374 		s = activeTraces[tr.Family] // check again
    375 		if s == nil {
    376 			s = new(traceSet)
    377 			activeTraces[tr.Family] = s
    378 		}
    379 		activeMu.Unlock()
    380 	}
    381 	s.Add(tr)
    382 
    383 	// Trigger allocation of the completed trace structure for this family.
    384 	// This will cause the family to be present in the request page during
    385 	// the first trace of this family. We don't care about the return value,
    386 	// nor is there any need for this to run inline, so we execute it in its
    387 	// own goroutine, but only if the family isn't allocated yet.
    388 	completedMu.RLock()
    389 	if _, ok := completedTraces[tr.Family]; !ok {
    390 		go allocFamily(tr.Family)
    391 	}
    392 	completedMu.RUnlock()
    393 
    394 	return tr
    395 }
    396 
    397 func (tr *trace) Finish() {
    398 	elapsed := time.Since(tr.Start)
    399 	tr.mu.Lock()
    400 	tr.Elapsed = elapsed
    401 	tr.mu.Unlock()
    402 
    403 	if DebugUseAfterFinish {
    404 		buf := make([]byte, 4<<10) // 4 KB should be enough
    405 		n := runtime.Stack(buf, false)
    406 		tr.finishStack = buf[:n]
    407 	}
    408 
    409 	activeMu.RLock()
    410 	m := activeTraces[tr.Family]
    411 	activeMu.RUnlock()
    412 	m.Remove(tr)
    413 
    414 	f := getFamily(tr.Family, true)
    415 	tr.mu.RLock() // protects tr fields in Cond.match calls
    416 	for _, b := range f.Buckets {
    417 		if b.Cond.match(tr) {
    418 			b.Add(tr)
    419 		}
    420 	}
    421 	tr.mu.RUnlock()
    422 
    423 	// Add a sample of elapsed time as microseconds to the family's timeseries
    424 	h := new(histogram)
    425 	h.addMeasurement(elapsed.Nanoseconds() / 1e3)
    426 	f.LatencyMu.Lock()
    427 	f.Latency.Add(h)
    428 	f.LatencyMu.Unlock()
    429 
    430 	tr.unref() // matches ref in New
    431 }
    432 
    433 const (
    434 	bucketsPerFamily    = 9
    435 	tracesPerBucket     = 10
    436 	maxActiveTraces     = 20 // Maximum number of active traces to show.
    437 	maxEventsPerTrace   = 10
    438 	numHistogramBuckets = 38
    439 )
    440 
    441 var (
    442 	// The active traces.
    443 	activeMu     sync.RWMutex
    444 	activeTraces = make(map[string]*traceSet) // family -> traces
    445 
    446 	// Families of completed traces.
    447 	completedMu     sync.RWMutex
    448 	completedTraces = make(map[string]*family) // family -> traces
    449 )
    450 
    451 type traceSet struct {
    452 	mu sync.RWMutex
    453 	m  map[*trace]bool
    454 
    455 	// We could avoid the entire map scan in FirstN by having a slice of all the traces
    456 	// ordered by start time, and an index into that from the trace struct, with a periodic
    457 	// repack of the slice after enough traces finish; we could also use a skip list or similar.
    458 	// However, that would shift some of the expense from /debug/requests time to RPC time,
    459 	// which is probably the wrong trade-off.
    460 }
    461 
    462 func (ts *traceSet) Len() int {
    463 	ts.mu.RLock()
    464 	defer ts.mu.RUnlock()
    465 	return len(ts.m)
    466 }
    467 
    468 func (ts *traceSet) Add(tr *trace) {
    469 	ts.mu.Lock()
    470 	if ts.m == nil {
    471 		ts.m = make(map[*trace]bool)
    472 	}
    473 	ts.m[tr] = true
    474 	ts.mu.Unlock()
    475 }
    476 
    477 func (ts *traceSet) Remove(tr *trace) {
    478 	ts.mu.Lock()
    479 	delete(ts.m, tr)
    480 	ts.mu.Unlock()
    481 }
    482 
    483 // FirstN returns the first n traces ordered by time.
    484 func (ts *traceSet) FirstN(n int) traceList {
    485 	ts.mu.RLock()
    486 	defer ts.mu.RUnlock()
    487 
    488 	if n > len(ts.m) {
    489 		n = len(ts.m)
    490 	}
    491 	trl := make(traceList, 0, n)
    492 
    493 	// Fast path for when no selectivity is needed.
    494 	if n == len(ts.m) {
    495 		for tr := range ts.m {
    496 			tr.ref()
    497 			trl = append(trl, tr)
    498 		}
    499 		sort.Sort(trl)
    500 		return trl
    501 	}
    502 
    503 	// Pick the oldest n traces.
    504 	// This is inefficient. See the comment in the traceSet struct.
    505 	for tr := range ts.m {
    506 		// Put the first n traces into trl in the order they occur.
    507 		// When we have n, sort trl, and thereafter maintain its order.
    508 		if len(trl) < n {
    509 			tr.ref()
    510 			trl = append(trl, tr)
    511 			if len(trl) == n {
    512 				// This is guaranteed to happen exactly once during this loop.
    513 				sort.Sort(trl)
    514 			}
    515 			continue
    516 		}
    517 		if tr.Start.After(trl[n-1].Start) {
    518 			continue
    519 		}
    520 
    521 		// Find where to insert this one.
    522 		tr.ref()
    523 		i := sort.Search(n, func(i int) bool { return trl[i].Start.After(tr.Start) })
    524 		trl[n-1].unref()
    525 		copy(trl[i+1:], trl[i:])
    526 		trl[i] = tr
    527 	}
    528 
    529 	return trl
    530 }
    531 
    532 func getActiveTraces(fam string) traceList {
    533 	activeMu.RLock()
    534 	s := activeTraces[fam]
    535 	activeMu.RUnlock()
    536 	if s == nil {
    537 		return nil
    538 	}
    539 	return s.FirstN(maxActiveTraces)
    540 }
    541 
    542 func getFamily(fam string, allocNew bool) *family {
    543 	completedMu.RLock()
    544 	f := completedTraces[fam]
    545 	completedMu.RUnlock()
    546 	if f == nil && allocNew {
    547 		f = allocFamily(fam)
    548 	}
    549 	return f
    550 }
    551 
    552 func allocFamily(fam string) *family {
    553 	completedMu.Lock()
    554 	defer completedMu.Unlock()
    555 	f := completedTraces[fam]
    556 	if f == nil {
    557 		f = newFamily()
    558 		completedTraces[fam] = f
    559 	}
    560 	return f
    561 }
    562 
    563 // family represents a set of trace buckets and associated latency information.
    564 type family struct {
    565 	// traces may occur in multiple buckets.
    566 	Buckets [bucketsPerFamily]*traceBucket
    567 
    568 	// latency time series
    569 	LatencyMu sync.RWMutex
    570 	Latency   *timeseries.MinuteHourSeries
    571 }
    572 
    573 func newFamily() *family {
    574 	return &family{
    575 		Buckets: [bucketsPerFamily]*traceBucket{
    576 			{Cond: minCond(0)},
    577 			{Cond: minCond(50 * time.Millisecond)},
    578 			{Cond: minCond(100 * time.Millisecond)},
    579 			{Cond: minCond(200 * time.Millisecond)},
    580 			{Cond: minCond(500 * time.Millisecond)},
    581 			{Cond: minCond(1 * time.Second)},
    582 			{Cond: minCond(10 * time.Second)},
    583 			{Cond: minCond(100 * time.Second)},
    584 			{Cond: errorCond{}},
    585 		},
    586 		Latency: timeseries.NewMinuteHourSeries(func() timeseries.Observable { return new(histogram) }),
    587 	}
    588 }
    589 
    590 // traceBucket represents a size-capped bucket of historic traces,
    591 // along with a condition for a trace to belong to the bucket.
    592 type traceBucket struct {
    593 	Cond cond
    594 
    595 	// Ring buffer implementation of a fixed-size FIFO queue.
    596 	mu     sync.RWMutex
    597 	buf    [tracesPerBucket]*trace
    598 	start  int // < tracesPerBucket
    599 	length int // <= tracesPerBucket
    600 }
    601 
    602 func (b *traceBucket) Add(tr *trace) {
    603 	b.mu.Lock()
    604 	defer b.mu.Unlock()
    605 
    606 	i := b.start + b.length
    607 	if i >= tracesPerBucket {
    608 		i -= tracesPerBucket
    609 	}
    610 	if b.length == tracesPerBucket {
    611 		// "Remove" an element from the bucket.
    612 		b.buf[i].unref()
    613 		b.start++
    614 		if b.start == tracesPerBucket {
    615 			b.start = 0
    616 		}
    617 	}
    618 	b.buf[i] = tr
    619 	if b.length < tracesPerBucket {
    620 		b.length++
    621 	}
    622 	tr.ref()
    623 }
    624 
    625 // Copy returns a copy of the traces in the bucket.
    626 // If tracedOnly is true, only the traces with trace information will be returned.
    627 // The logs will be ref'd before returning; the caller should call
    628 // the Free method when it is done with them.
    629 // TODO(dsymonds): keep track of traced requests in separate buckets.
    630 func (b *traceBucket) Copy(tracedOnly bool) traceList {
    631 	b.mu.RLock()
    632 	defer b.mu.RUnlock()
    633 
    634 	trl := make(traceList, 0, b.length)
    635 	for i, x := 0, b.start; i < b.length; i++ {
    636 		tr := b.buf[x]
    637 		if !tracedOnly || tr.spanID != 0 {
    638 			tr.ref()
    639 			trl = append(trl, tr)
    640 		}
    641 		x++
    642 		if x == b.length {
    643 			x = 0
    644 		}
    645 	}
    646 	return trl
    647 }
    648 
    649 func (b *traceBucket) Empty() bool {
    650 	b.mu.RLock()
    651 	defer b.mu.RUnlock()
    652 	return b.length == 0
    653 }
    654 
    655 // cond represents a condition on a trace.
    656 type cond interface {
    657 	match(t *trace) bool
    658 	String() string
    659 }
    660 
    661 type minCond time.Duration
    662 
    663 func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) }
    664 func (m minCond) String() string      { return fmt.Sprintf("≥%gs", time.Duration(m).Seconds()) }
    665 
    666 type errorCond struct{}
    667 
    668 func (e errorCond) match(t *trace) bool { return t.IsError }
    669 func (e errorCond) String() string      { return "errors" }
    670 
    671 type traceList []*trace
    672 
    673 // Free calls unref on each element of the list.
    674 func (trl traceList) Free() {
    675 	for _, t := range trl {
    676 		t.unref()
    677 	}
    678 }
    679 
    680 // traceList may be sorted in reverse chronological order.
    681 func (trl traceList) Len() int           { return len(trl) }
    682 func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) }
    683 func (trl traceList) Swap(i, j int)      { trl[i], trl[j] = trl[j], trl[i] }
    684 
    685 // An event is a timestamped log entry in a trace.
    686 type event struct {
    687 	When       time.Time
    688 	Elapsed    time.Duration // since previous event in trace
    689 	NewDay     bool          // whether this event is on a different day to the previous event
    690 	Recyclable bool          // whether this event was passed via LazyLog
    691 	Sensitive  bool          // whether this event contains sensitive information
    692 	What       interface{}   // string or fmt.Stringer
    693 }
    694 
    695 // WhenString returns a string representation of the elapsed time of the event.
    696 // It will include the date if midnight was crossed.
    697 func (e event) WhenString() string {
    698 	if e.NewDay {
    699 		return e.When.Format("2006/01/02 15:04:05.000000")
    700 	}
    701 	return e.When.Format("15:04:05.000000")
    702 }
    703 
    704 // discarded represents a number of discarded events.
    705 // It is stored as *discarded to make it easier to update in-place.
    706 type discarded int
    707 
    708 func (d *discarded) String() string {
    709 	return fmt.Sprintf("(%d events discarded)", int(*d))
    710 }
    711 
    712 // trace represents an active or complete request,
    713 // either sent or received by this program.
    714 type trace struct {
    715 	// Family is the top-level grouping of traces to which this belongs.
    716 	Family string
    717 
    718 	// Title is the title of this trace.
    719 	Title string
    720 
    721 	// Start time of the this trace.
    722 	Start time.Time
    723 
    724 	mu        sync.RWMutex
    725 	events    []event // Append-only sequence of events (modulo discards).
    726 	maxEvents int
    727 	recycler  func(interface{})
    728 	IsError   bool          // Whether this trace resulted in an error.
    729 	Elapsed   time.Duration // Elapsed time for this trace, zero while active.
    730 	traceID   uint64        // Trace information if non-zero.
    731 	spanID    uint64
    732 
    733 	refs int32     // how many buckets this is in
    734 	disc discarded // scratch space to avoid allocation
    735 
    736 	finishStack []byte // where finish was called, if DebugUseAfterFinish is set
    737 
    738 	eventsBuf [4]event // preallocated buffer in case we only log a few events
    739 }
    740 
    741 func (tr *trace) reset() {
    742 	// Clear all but the mutex. Mutexes may not be copied, even when unlocked.
    743 	tr.Family = ""
    744 	tr.Title = ""
    745 	tr.Start = time.Time{}
    746 
    747 	tr.mu.Lock()
    748 	tr.Elapsed = 0
    749 	tr.traceID = 0
    750 	tr.spanID = 0
    751 	tr.IsError = false
    752 	tr.maxEvents = 0
    753 	tr.events = nil
    754 	tr.recycler = nil
    755 	tr.mu.Unlock()
    756 
    757 	tr.refs = 0
    758 	tr.disc = 0
    759 	tr.finishStack = nil
    760 	for i := range tr.eventsBuf {
    761 		tr.eventsBuf[i] = event{}
    762 	}
    763 }
    764 
    765 // delta returns the elapsed time since the last event or the trace start,
    766 // and whether it spans midnight.
    767 // L >= tr.mu
    768 func (tr *trace) delta(t time.Time) (time.Duration, bool) {
    769 	if len(tr.events) == 0 {
    770 		return t.Sub(tr.Start), false
    771 	}
    772 	prev := tr.events[len(tr.events)-1].When
    773 	return t.Sub(prev), prev.Day() != t.Day()
    774 }
    775 
    776 func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
    777 	if DebugUseAfterFinish && tr.finishStack != nil {
    778 		buf := make([]byte, 4<<10) // 4 KB should be enough
    779 		n := runtime.Stack(buf, false)
    780 		log.Printf("net/trace: trace used after finish:\nFinished at:\n%s\nUsed at:\n%s", tr.finishStack, buf[:n])
    781 	}
    782 
    783 	/*
    784 		NOTE TO DEBUGGERS
    785 
    786 		If you are here because your program panicked in this code,
    787 		it is almost definitely the fault of code using this package,
    788 		and very unlikely to be the fault of this code.
    789 
    790 		The most likely scenario is that some code elsewhere is using
    791 		a trace.Trace after its Finish method is called.
    792 		You can temporarily set the DebugUseAfterFinish var
    793 		to help discover where that is; do not leave that var set,
    794 		since it makes this package much less efficient.
    795 	*/
    796 
    797 	e := event{When: time.Now(), What: x, Recyclable: recyclable, Sensitive: sensitive}
    798 	tr.mu.Lock()
    799 	e.Elapsed, e.NewDay = tr.delta(e.When)
    800 	if len(tr.events) < tr.maxEvents {
    801 		tr.events = append(tr.events, e)
    802 	} else {
    803 		// Discard the middle events.
    804 		di := int((tr.maxEvents - 1) / 2)
    805 		if d, ok := tr.events[di].What.(*discarded); ok {
    806 			(*d)++
    807 		} else {
    808 			// disc starts at two to count for the event it is replacing,
    809 			// plus the next one that we are about to drop.
    810 			tr.disc = 2
    811 			if tr.recycler != nil && tr.events[di].Recyclable {
    812 				go tr.recycler(tr.events[di].What)
    813 			}
    814 			tr.events[di].What = &tr.disc
    815 		}
    816 		// The timestamp of the discarded meta-event should be
    817 		// the time of the last event it is representing.
    818 		tr.events[di].When = tr.events[di+1].When
    819 
    820 		if tr.recycler != nil && tr.events[di+1].Recyclable {
    821 			go tr.recycler(tr.events[di+1].What)
    822 		}
    823 		copy(tr.events[di+1:], tr.events[di+2:])
    824 		tr.events[tr.maxEvents-1] = e
    825 	}
    826 	tr.mu.Unlock()
    827 }
    828 
    829 func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) {
    830 	tr.addEvent(x, true, sensitive)
    831 }
    832 
    833 func (tr *trace) LazyPrintf(format string, a ...interface{}) {
    834 	tr.addEvent(&lazySprintf{format, a}, false, false)
    835 }
    836 
    837 func (tr *trace) SetError() {
    838 	tr.mu.Lock()
    839 	tr.IsError = true
    840 	tr.mu.Unlock()
    841 }
    842 
    843 func (tr *trace) SetRecycler(f func(interface{})) {
    844 	tr.mu.Lock()
    845 	tr.recycler = f
    846 	tr.mu.Unlock()
    847 }
    848 
    849 func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
    850 	tr.mu.Lock()
    851 	tr.traceID, tr.spanID = traceID, spanID
    852 	tr.mu.Unlock()
    853 }
    854 
    855 func (tr *trace) SetMaxEvents(m int) {
    856 	tr.mu.Lock()
    857 	// Always keep at least three events: first, discarded count, last.
    858 	if len(tr.events) == 0 && m > 3 {
    859 		tr.maxEvents = m
    860 	}
    861 	tr.mu.Unlock()
    862 }
    863 
    864 func (tr *trace) ref() {
    865 	atomic.AddInt32(&tr.refs, 1)
    866 }
    867 
    868 func (tr *trace) unref() {
    869 	if atomic.AddInt32(&tr.refs, -1) == 0 {
    870 		tr.mu.RLock()
    871 		if tr.recycler != nil {
    872 			// freeTrace clears tr, so we hold tr.recycler and tr.events here.
    873 			go func(f func(interface{}), es []event) {
    874 				for _, e := range es {
    875 					if e.Recyclable {
    876 						f(e.What)
    877 					}
    878 				}
    879 			}(tr.recycler, tr.events)
    880 		}
    881 		tr.mu.RUnlock()
    882 
    883 		freeTrace(tr)
    884 	}
    885 }
    886 
    887 func (tr *trace) When() string {
    888 	return tr.Start.Format("2006/01/02 15:04:05.000000")
    889 }
    890 
    891 func (tr *trace) ElapsedTime() string {
    892 	tr.mu.RLock()
    893 	t := tr.Elapsed
    894 	tr.mu.RUnlock()
    895 
    896 	if t == 0 {
    897 		// Active trace.
    898 		t = time.Since(tr.Start)
    899 	}
    900 	return fmt.Sprintf("%.6f", t.Seconds())
    901 }
    902 
    903 func (tr *trace) Events() []event {
    904 	tr.mu.RLock()
    905 	defer tr.mu.RUnlock()
    906 	return tr.events
    907 }
    908 
    909 var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool?
    910 
    911 // newTrace returns a trace ready to use.
    912 func newTrace() *trace {
    913 	select {
    914 	case tr := <-traceFreeList:
    915 		return tr
    916 	default:
    917 		return new(trace)
    918 	}
    919 }
    920 
    921 // freeTrace adds tr to traceFreeList if there's room.
    922 // This is non-blocking.
    923 func freeTrace(tr *trace) {
    924 	if DebugUseAfterFinish {
    925 		return // never reuse
    926 	}
    927 	tr.reset()
    928 	select {
    929 	case traceFreeList <- tr:
    930 	default:
    931 	}
    932 }
    933 
    934 func elapsed(d time.Duration) string {
    935 	b := []byte(fmt.Sprintf("%.6f", d.Seconds()))
    936 
    937 	// For subsecond durations, blank all zeros before decimal point,
    938 	// and all zeros between the decimal point and the first non-zero digit.
    939 	if d < time.Second {
    940 		dot := bytes.IndexByte(b, '.')
    941 		for i := 0; i < dot; i++ {
    942 			b[i] = ' '
    943 		}
    944 		for i := dot + 1; i < len(b); i++ {
    945 			if b[i] == '0' {
    946 				b[i] = ' '
    947 			} else {
    948 				break
    949 			}
    950 		}
    951 	}
    952 
    953 	return string(b)
    954 }
    955 
    956 var pageTmplCache *template.Template
    957 var pageTmplOnce sync.Once
    958 
    959 func pageTmpl() *template.Template {
    960 	pageTmplOnce.Do(func() {
    961 		pageTmplCache = template.Must(template.New("Page").Funcs(template.FuncMap{
    962 			"elapsed": elapsed,
    963 			"add":     func(a, b int) int { return a + b },
    964 		}).Parse(pageHTML))
    965 	})
    966 	return pageTmplCache
    967 }
    968 
    969 const pageHTML = `
    970 {{template "Prolog" .}}
    971 {{template "StatusTable" .}}
    972 {{template "Epilog" .}}
    973 
    974 {{define "Prolog"}}
    975 <html>
    976 	<head>
    977 	<title>/debug/requests</title>
    978 	<style type="text/css">
    979 		body {
    980 			font-family: sans-serif;
    981 		}
    982 		table#tr-status td.family {
    983 			padding-right: 2em;
    984 		}
    985 		table#tr-status td.active {
    986 			padding-right: 1em;
    987 		}
    988 		table#tr-status td.latency-first {
    989 			padding-left: 1em;
    990 		}
    991 		table#tr-status td.empty {
    992 			color: #aaa;
    993 		}
    994 		table#reqs {
    995 			margin-top: 1em;
    996 		}
    997 		table#reqs tr.first {
    998 			{{if $.Expanded}}font-weight: bold;{{end}}
    999 		}
   1000 		table#reqs td {
   1001 			font-family: monospace;
   1002 		}
   1003 		table#reqs td.when {
   1004 			text-align: right;
   1005 			white-space: nowrap;
   1006 		}
   1007 		table#reqs td.elapsed {
   1008 			padding: 0 0.5em;
   1009 			text-align: right;
   1010 			white-space: pre;
   1011 			width: 10em;
   1012 		}
   1013 		address {
   1014 			font-size: smaller;
   1015 			margin-top: 5em;
   1016 		}
   1017 	</style>
   1018 	</head>
   1019 	<body>
   1020 
   1021 <h1>/debug/requests</h1>
   1022 {{end}} {{/* end of Prolog */}}
   1023 
   1024 {{define "StatusTable"}}
   1025 <table id="tr-status">
   1026 	{{range $fam := .Families}}
   1027 	<tr>
   1028 		<td class="family">{{$fam}}</td>
   1029 
   1030 		{{$n := index $.ActiveTraceCount $fam}}
   1031 		<td class="active {{if not $n}}empty{{end}}">
   1032 			{{if $n}}<a href="?fam={{$fam}}&b=-1{{if $.Expanded}}&exp=1{{end}}">{{end}}
   1033 			[{{$n}} active]
   1034 			{{if $n}}</a>{{end}}
   1035 		</td>
   1036 
   1037 		{{$f := index $.CompletedTraces $fam}}
   1038 		{{range $i, $b := $f.Buckets}}
   1039 		{{$empty := $b.Empty}}
   1040 		<td {{if $empty}}class="empty"{{end}}>
   1041 		{{if not $empty}}<a href="?fam={{$fam}}&b={{$i}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
   1042 		[{{.Cond}}]
   1043 		{{if not $empty}}</a>{{end}}
   1044 		</td>
   1045 		{{end}}
   1046 
   1047 		{{$nb := len $f.Buckets}}
   1048 		<td class="latency-first">
   1049 		<a href="?fam={{$fam}}&b={{$nb}}">[minute]</a>
   1050 		</td>
   1051 		<td>
   1052 		<a href="?fam={{$fam}}&b={{add $nb 1}}">[hour]</a>
   1053 		</td>
   1054 		<td>
   1055 		<a href="?fam={{$fam}}&b={{add $nb 2}}">[total]</a>
   1056 		</td>
   1057 
   1058 	</tr>
   1059 	{{end}}
   1060 </table>
   1061 {{end}} {{/* end of StatusTable */}}
   1062 
   1063 {{define "Epilog"}}
   1064 {{if $.Traces}}
   1065 <hr />
   1066 <h3>Family: {{$.Family}}</h3>
   1067 
   1068 {{if or $.Expanded $.Traced}}
   1069   <a href="?fam={{$.Family}}&b={{$.Bucket}}">[Normal/Summary]</a>
   1070 {{else}}
   1071   [Normal/Summary]
   1072 {{end}}
   1073 
   1074 {{if or (not $.Expanded) $.Traced}}
   1075   <a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">[Normal/Expanded]</a>
   1076 {{else}}
   1077   [Normal/Expanded]
   1078 {{end}}
   1079 
   1080 {{if not $.Active}}
   1081 	{{if or $.Expanded (not $.Traced)}}
   1082 	<a href="?fam={{$.Family}}&b={{$.Bucket}}&rtraced=1">[Traced/Summary]</a>
   1083 	{{else}}
   1084 	[Traced/Summary]
   1085 	{{end}}
   1086 	{{if or (not $.Expanded) (not $.Traced)}}
   1087 	<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1&rtraced=1">[Traced/Expanded]</a>
   1088         {{else}}
   1089 	[Traced/Expanded]
   1090 	{{end}}
   1091 {{end}}
   1092 
   1093 {{if $.Total}}
   1094 <p><em>Showing <b>{{len $.Traces}}</b> of <b>{{$.Total}}</b> traces.</em></p>
   1095 {{end}}
   1096 
   1097 <table id="reqs">
   1098 	<caption>
   1099 		{{if $.Active}}Active{{else}}Completed{{end}} Requests
   1100 	</caption>
   1101 	<tr><th>When</th><th>Elapsed&nbsp;(s)</th></tr>
   1102 	{{range $tr := $.Traces}}
   1103 	<tr class="first">
   1104 		<td class="when">{{$tr.When}}</td>
   1105 		<td class="elapsed">{{$tr.ElapsedTime}}</td>
   1106 		<td>{{$tr.Title}}</td>
   1107 		{{/* TODO: include traceID/spanID */}}
   1108 	</tr>
   1109 	{{if $.Expanded}}
   1110 	{{range $tr.Events}}
   1111 	<tr>
   1112 		<td class="when">{{.WhenString}}</td>
   1113 		<td class="elapsed">{{elapsed .Elapsed}}</td>
   1114 		<td>{{if or $.ShowSensitive (not .Sensitive)}}... {{.What}}{{else}}<em>[redacted]</em>{{end}}</td>
   1115 	</tr>
   1116 	{{end}}
   1117 	{{end}}
   1118 	{{end}}
   1119 </table>
   1120 {{end}} {{/* if $.Traces */}}
   1121 
   1122 {{if $.Histogram}}
   1123 <h4>Latency (&micro;s) of {{$.Family}} over {{$.HistogramWindow}}</h4>
   1124 {{$.Histogram}}
   1125 {{end}} {{/* if $.Histogram */}}
   1126 
   1127 	</body>
   1128 </html>
   1129 {{end}} {{/* end of Epilog */}}
   1130 `