trace.go

   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/*
   6Package trace implements tracing of requests and long-lived objects.
   7It exports HTTP interfaces on /debug/requests and /debug/events.
   8
   9A trace.Trace provides tracing for short-lived objects, usually requests.
  10A 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
  24The /debug/requests HTTP endpoint organizes the traces by family,
  25errors, and duration.  It also provides histogram of request duration
  26for each family.
  27
  28A trace.EventLog provides tracing for long-lived objects, such as RPC
  29connections.
  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
  59The /debug/events HTTP endpoint organizes the event logs by family and
  60by time since the last error.  The expanded view displays recent log
  61entries and the log's call stack.
  62*/
  63package trace // import "golang.org/x/net/trace"
  64
  65import (
  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.
  87var DebugUseAfterFinish = false
  88
  89// HTTP ServeMux paths.
  90const (
  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].
 105var 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
 120func 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.
 136func 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.
 141func 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.
 151func 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.
 166func 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.
 180func 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
 288func 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
 304func 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
 312type contextKeyT string
 313
 314var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
 315
 316// Trace represents an active request.
 317type 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
 351type lazySprintf struct {
 352	format string
 353	a      []interface{}
 354}
 355
 356func (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.
 361func 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
 397func (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
 433const (
 434	bucketsPerFamily    = 9
 435	tracesPerBucket     = 10
 436	maxActiveTraces     = 20 // Maximum number of active traces to show.
 437	maxEventsPerTrace   = 10
 438	numHistogramBuckets = 38
 439)
 440
 441var (
 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
 451type 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
 462func (ts *traceSet) Len() int {
 463	ts.mu.RLock()
 464	defer ts.mu.RUnlock()
 465	return len(ts.m)
 466}
 467
 468func (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
 477func (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.
 484func (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
 532func 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
 542func 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
 552func 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.
 564type 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
 573func 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.
 592type 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
 602func (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.
 630func (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
 649func (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.
 656type cond interface {
 657	match(t *trace) bool
 658	String() string
 659}
 660
 661type minCond time.Duration
 662
 663func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) }
 664func (m minCond) String() string      { return fmt.Sprintf("≥%gs", time.Duration(m).Seconds()) }
 665
 666type errorCond struct{}
 667
 668func (e errorCond) match(t *trace) bool { return t.IsError }
 669func (e errorCond) String() string      { return "errors" }
 670
 671type traceList []*trace
 672
 673// Free calls unref on each element of the list.
 674func (trl traceList) Free() {
 675	for _, t := range trl {
 676		t.unref()
 677	}
 678}
 679
 680// traceList may be sorted in reverse chronological order.
 681func (trl traceList) Len() int           { return len(trl) }
 682func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) }
 683func (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.
 686type 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.
 697func (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.
 706type discarded int
 707
 708func (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.
 714type 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
 741func (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
 768func (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
 776func (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
 829func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) {
 830	tr.addEvent(x, true, sensitive)
 831}
 832
 833func (tr *trace) LazyPrintf(format string, a ...interface{}) {
 834	tr.addEvent(&lazySprintf{format, a}, false, false)
 835}
 836
 837func (tr *trace) SetError() {
 838	tr.mu.Lock()
 839	tr.IsError = true
 840	tr.mu.Unlock()
 841}
 842
 843func (tr *trace) SetRecycler(f func(interface{})) {
 844	tr.mu.Lock()
 845	tr.recycler = f
 846	tr.mu.Unlock()
 847}
 848
 849func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
 850	tr.mu.Lock()
 851	tr.traceID, tr.spanID = traceID, spanID
 852	tr.mu.Unlock()
 853}
 854
 855func (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
 864func (tr *trace) ref() {
 865	atomic.AddInt32(&tr.refs, 1)
 866}
 867
 868func (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
 887func (tr *trace) When() string {
 888	return tr.Start.Format("2006/01/02 15:04:05.000000")
 889}
 890
 891func (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
 903func (tr *trace) Events() []event {
 904	tr.mu.RLock()
 905	defer tr.mu.RUnlock()
 906	return tr.events
 907}
 908
 909var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool?
 910
 911// newTrace returns a trace ready to use.
 912func 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.
 923func 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
 934func 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
 956var pageTmplCache *template.Template
 957var pageTmplOnce sync.Once
 958
 959func 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
 969const 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`