github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/Godeps/_workspace/src/golang.org/x/net/trace/events.go (about)

     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  package trace
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"html/template"
    11  	"io"
    12  	"log"
    13  	"net/http"
    14  	"runtime"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"sync/atomic"
    20  	"text/tabwriter"
    21  	"time"
    22  )
    23  
    24  var eventsTmpl = template.Must(template.New("events").Funcs(template.FuncMap{
    25  	"elapsed":   elapsed,
    26  	"trimSpace": strings.TrimSpace,
    27  }).Parse(eventsHTML))
    28  
    29  const maxEventsPerLog = 100
    30  
    31  type bucket struct {
    32  	MaxErrAge time.Duration
    33  	String    string
    34  }
    35  
    36  var buckets = []bucket{
    37  	{0, "total"},
    38  	{10 * time.Second, "errs<10s"},
    39  	{1 * time.Minute, "errs<1m"},
    40  	{10 * time.Minute, "errs<10m"},
    41  	{1 * time.Hour, "errs<1h"},
    42  	{10 * time.Hour, "errs<10h"},
    43  	{24000 * time.Hour, "errors"},
    44  }
    45  
    46  // RenderEvents renders the HTML page typically served at /debug/events.
    47  // It does not do any auth checking; see AuthRequest for the default auth check
    48  // used by the handler registered on http.DefaultServeMux.
    49  // req may be nil.
    50  func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
    51  	now := time.Now()
    52  	data := &struct {
    53  		Families []string // family names
    54  		Buckets  []bucket
    55  		Counts   [][]int // eventLog count per family/bucket
    56  
    57  		// Set when a bucket has been selected.
    58  		Family    string
    59  		Bucket    int
    60  		EventLogs eventLogs
    61  		Expanded  bool
    62  	}{
    63  		Buckets: buckets,
    64  	}
    65  
    66  	data.Families = make([]string, 0, len(families))
    67  	famMu.RLock()
    68  	for name := range families {
    69  		data.Families = append(data.Families, name)
    70  	}
    71  	famMu.RUnlock()
    72  	sort.Strings(data.Families)
    73  
    74  	// Count the number of eventLogs in each family for each error age.
    75  	data.Counts = make([][]int, len(data.Families))
    76  	for i, name := range data.Families {
    77  		// TODO(sameer): move this loop under the family lock.
    78  		f := getEventFamily(name)
    79  		data.Counts[i] = make([]int, len(data.Buckets))
    80  		for j, b := range data.Buckets {
    81  			data.Counts[i][j] = f.Count(now, b.MaxErrAge)
    82  		}
    83  	}
    84  
    85  	if req != nil {
    86  		var ok bool
    87  		data.Family, data.Bucket, ok = parseEventsArgs(req)
    88  		if !ok {
    89  			// No-op
    90  		} else {
    91  			data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge)
    92  		}
    93  		if data.EventLogs != nil {
    94  			defer data.EventLogs.Free()
    95  			sort.Sort(data.EventLogs)
    96  		}
    97  		if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
    98  			data.Expanded = exp
    99  		}
   100  	}
   101  
   102  	famMu.RLock()
   103  	defer famMu.RUnlock()
   104  	if err := eventsTmpl.Execute(w, data); err != nil {
   105  		log.Printf("net/trace: Failed executing template: %v", err)
   106  	}
   107  }
   108  
   109  func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) {
   110  	fam, bStr := req.FormValue("fam"), req.FormValue("b")
   111  	if fam == "" || bStr == "" {
   112  		return "", 0, false
   113  	}
   114  	b, err := strconv.Atoi(bStr)
   115  	if err != nil || b < 0 || b >= len(buckets) {
   116  		return "", 0, false
   117  	}
   118  	return fam, b, true
   119  }
   120  
   121  // An EventLog provides a log of events associated with a specific object.
   122  type EventLog interface {
   123  	// Printf formats its arguments with fmt.Sprintf and adds the
   124  	// result to the event log.
   125  	Printf(format string, a ...interface{})
   126  
   127  	// Errorf is like Printf, but it marks this event as an error.
   128  	Errorf(format string, a ...interface{})
   129  
   130  	// Finish declares that this event log is complete.
   131  	// The event log should not be used after calling this method.
   132  	Finish()
   133  }
   134  
   135  // NewEventLog returns a new EventLog with the specified family name
   136  // and title.
   137  func NewEventLog(family, title string) EventLog {
   138  	el := newEventLog()
   139  	el.ref()
   140  	el.Family, el.Title = family, title
   141  	el.Start = time.Now()
   142  	el.events = make([]logEntry, 0, maxEventsPerLog)
   143  	el.stack = make([]uintptr, 32)
   144  	n := runtime.Callers(2, el.stack)
   145  	el.stack = el.stack[:n]
   146  
   147  	getEventFamily(family).add(el)
   148  	return el
   149  }
   150  
   151  func (el *eventLog) Finish() {
   152  	getEventFamily(el.Family).remove(el)
   153  	el.unref() // matches ref in New
   154  }
   155  
   156  var (
   157  	famMu    sync.RWMutex
   158  	families = make(map[string]*eventFamily) // family name => family
   159  )
   160  
   161  func getEventFamily(fam string) *eventFamily {
   162  	famMu.Lock()
   163  	defer famMu.Unlock()
   164  	f := families[fam]
   165  	if f == nil {
   166  		f = &eventFamily{}
   167  		families[fam] = f
   168  	}
   169  	return f
   170  }
   171  
   172  type eventFamily struct {
   173  	mu        sync.RWMutex
   174  	eventLogs eventLogs
   175  }
   176  
   177  func (f *eventFamily) add(el *eventLog) {
   178  	f.mu.Lock()
   179  	f.eventLogs = append(f.eventLogs, el)
   180  	f.mu.Unlock()
   181  }
   182  
   183  func (f *eventFamily) remove(el *eventLog) {
   184  	f.mu.Lock()
   185  	defer f.mu.Unlock()
   186  	for i, el0 := range f.eventLogs {
   187  		if el == el0 {
   188  			copy(f.eventLogs[i:], f.eventLogs[i+1:])
   189  			f.eventLogs = f.eventLogs[:len(f.eventLogs)-1]
   190  			return
   191  		}
   192  	}
   193  }
   194  
   195  func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) {
   196  	f.mu.RLock()
   197  	defer f.mu.RUnlock()
   198  	for _, el := range f.eventLogs {
   199  		if el.hasRecentError(now, maxErrAge) {
   200  			n++
   201  		}
   202  	}
   203  	return
   204  }
   205  
   206  func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) {
   207  	f.mu.RLock()
   208  	defer f.mu.RUnlock()
   209  	els = make(eventLogs, 0, len(f.eventLogs))
   210  	for _, el := range f.eventLogs {
   211  		if el.hasRecentError(now, maxErrAge) {
   212  			el.ref()
   213  			els = append(els, el)
   214  		}
   215  	}
   216  	return
   217  }
   218  
   219  type eventLogs []*eventLog
   220  
   221  // Free calls unref on each element of the list.
   222  func (els eventLogs) Free() {
   223  	for _, el := range els {
   224  		el.unref()
   225  	}
   226  }
   227  
   228  // eventLogs may be sorted in reverse chronological order.
   229  func (els eventLogs) Len() int           { return len(els) }
   230  func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) }
   231  func (els eventLogs) Swap(i, j int)      { els[i], els[j] = els[j], els[i] }
   232  
   233  // A logEntry is a timestamped log entry in an event log.
   234  type logEntry struct {
   235  	When    time.Time
   236  	Elapsed time.Duration // since previous event in log
   237  	NewDay  bool          // whether this event is on a different day to the previous event
   238  	What    string
   239  	IsErr   bool
   240  }
   241  
   242  // WhenString returns a string representation of the elapsed time of the event.
   243  // It will include the date if midnight was crossed.
   244  func (e logEntry) WhenString() string {
   245  	if e.NewDay {
   246  		return e.When.Format("2006/01/02 15:04:05.000000")
   247  	}
   248  	return e.When.Format("15:04:05.000000")
   249  }
   250  
   251  // An eventLog represents an active event log.
   252  type eventLog struct {
   253  	// Family is the top-level grouping of event logs to which this belongs.
   254  	Family string
   255  
   256  	// Title is the title of this event log.
   257  	Title string
   258  
   259  	// Timing information.
   260  	Start time.Time
   261  
   262  	// Call stack where this event log was created.
   263  	stack []uintptr
   264  
   265  	// Append-only sequence of events.
   266  	//
   267  	// TODO(sameer): change this to a ring buffer to avoid the array copy
   268  	// when we hit maxEventsPerLog.
   269  	mu            sync.RWMutex
   270  	events        []logEntry
   271  	LastErrorTime time.Time
   272  	discarded     int
   273  
   274  	refs int32 // how many buckets this is in
   275  }
   276  
   277  func (el *eventLog) reset() {
   278  	// Clear all but the mutex. Mutexes may not be copied, even when unlocked.
   279  	el.Family = ""
   280  	el.Title = ""
   281  	el.Start = time.Time{}
   282  	el.stack = nil
   283  	el.events = nil
   284  	el.LastErrorTime = time.Time{}
   285  	el.discarded = 0
   286  	el.refs = 0
   287  }
   288  
   289  func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool {
   290  	if maxErrAge == 0 {
   291  		return true
   292  	}
   293  	el.mu.RLock()
   294  	defer el.mu.RUnlock()
   295  	return now.Sub(el.LastErrorTime) < maxErrAge
   296  }
   297  
   298  // delta returns the elapsed time since the last event or the log start,
   299  // and whether it spans midnight.
   300  // L >= el.mu
   301  func (el *eventLog) delta(t time.Time) (time.Duration, bool) {
   302  	if len(el.events) == 0 {
   303  		return t.Sub(el.Start), false
   304  	}
   305  	prev := el.events[len(el.events)-1].When
   306  	return t.Sub(prev), prev.Day() != t.Day()
   307  
   308  }
   309  
   310  func (el *eventLog) Printf(format string, a ...interface{}) {
   311  	el.printf(false, format, a...)
   312  }
   313  
   314  func (el *eventLog) Errorf(format string, a ...interface{}) {
   315  	el.printf(true, format, a...)
   316  }
   317  
   318  func (el *eventLog) printf(isErr bool, format string, a ...interface{}) {
   319  	e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)}
   320  	el.mu.Lock()
   321  	e.Elapsed, e.NewDay = el.delta(e.When)
   322  	if len(el.events) < maxEventsPerLog {
   323  		el.events = append(el.events, e)
   324  	} else {
   325  		// Discard the oldest event.
   326  		if el.discarded == 0 {
   327  			// el.discarded starts at two to count for the event it
   328  			// is replacing, plus the next one that we are about to
   329  			// drop.
   330  			el.discarded = 2
   331  		} else {
   332  			el.discarded++
   333  		}
   334  		// TODO(sameer): if this causes allocations on a critical path,
   335  		// change eventLog.What to be a fmt.Stringer, as in trace.go.
   336  		el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded)
   337  		// The timestamp of the discarded meta-event should be
   338  		// the time of the last event it is representing.
   339  		el.events[0].When = el.events[1].When
   340  		copy(el.events[1:], el.events[2:])
   341  		el.events[maxEventsPerLog-1] = e
   342  	}
   343  	if e.IsErr {
   344  		el.LastErrorTime = e.When
   345  	}
   346  	el.mu.Unlock()
   347  }
   348  
   349  func (el *eventLog) ref() {
   350  	atomic.AddInt32(&el.refs, 1)
   351  }
   352  
   353  func (el *eventLog) unref() {
   354  	if atomic.AddInt32(&el.refs, -1) == 0 {
   355  		freeEventLog(el)
   356  	}
   357  }
   358  
   359  func (el *eventLog) When() string {
   360  	return el.Start.Format("2006/01/02 15:04:05.000000")
   361  }
   362  
   363  func (el *eventLog) ElapsedTime() string {
   364  	elapsed := time.Since(el.Start)
   365  	return fmt.Sprintf("%.6f", elapsed.Seconds())
   366  }
   367  
   368  func (el *eventLog) Stack() string {
   369  	buf := new(bytes.Buffer)
   370  	tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
   371  	printStackRecord(tw, el.stack)
   372  	tw.Flush()
   373  	return buf.String()
   374  }
   375  
   376  // printStackRecord prints the function + source line information
   377  // for a single stack trace.
   378  // Adapted from runtime/pprof/pprof.go.
   379  func printStackRecord(w io.Writer, stk []uintptr) {
   380  	for _, pc := range stk {
   381  		f := runtime.FuncForPC(pc)
   382  		if f == nil {
   383  			continue
   384  		}
   385  		file, line := f.FileLine(pc)
   386  		name := f.Name()
   387  		// Hide runtime.goexit and any runtime functions at the beginning.
   388  		if strings.HasPrefix(name, "runtime.") {
   389  			continue
   390  		}
   391  		fmt.Fprintf(w, "#   %s\t%s:%d\n", name, file, line)
   392  	}
   393  }
   394  
   395  func (el *eventLog) Events() []logEntry {
   396  	el.mu.RLock()
   397  	defer el.mu.RUnlock()
   398  	return el.events
   399  }
   400  
   401  // freeEventLogs is a freelist of *eventLog
   402  var freeEventLogs = make(chan *eventLog, 1000)
   403  
   404  // newEventLog returns a event log ready to use.
   405  func newEventLog() *eventLog {
   406  	select {
   407  	case el := <-freeEventLogs:
   408  		return el
   409  	default:
   410  		return new(eventLog)
   411  	}
   412  }
   413  
   414  // freeEventLog adds el to freeEventLogs if there's room.
   415  // This is non-blocking.
   416  func freeEventLog(el *eventLog) {
   417  	el.reset()
   418  	select {
   419  	case freeEventLogs <- el:
   420  	default:
   421  	}
   422  }
   423  
   424  const eventsHTML = `
   425  <html>
   426  	<head>
   427  		<title>events</title>
   428  	</head>
   429  	<style type="text/css">
   430  		body {
   431  			font-family: sans-serif;
   432  		}
   433  		table#req-status td.family {
   434  			padding-right: 2em;
   435  		}
   436  		table#req-status td.active {
   437  			padding-right: 1em;
   438  		}
   439  		table#req-status td.empty {
   440  			color: #aaa;
   441  		}
   442  		table#reqs {
   443  			margin-top: 1em;
   444  		}
   445  		table#reqs tr.first {
   446  			{{if $.Expanded}}font-weight: bold;{{end}}
   447  		}
   448  		table#reqs td {
   449  			font-family: monospace;
   450  		}
   451  		table#reqs td.when {
   452  			text-align: right;
   453  			white-space: nowrap;
   454  		}
   455  		table#reqs td.elapsed {
   456  			padding: 0 0.5em;
   457  			text-align: right;
   458  			white-space: pre;
   459  			width: 10em;
   460  		}
   461  		address {
   462  			font-size: smaller;
   463  			margin-top: 5em;
   464  		}
   465  	</style>
   466  	<body>
   467  
   468  <h1>/debug/events</h1>
   469  
   470  <table id="req-status">
   471  	{{range $i, $fam := .Families}}
   472  	<tr>
   473  		<td class="family">{{$fam}}</td>
   474  
   475  	        {{range $j, $bucket := $.Buckets}}
   476  	        {{$n := index $.Counts $i $j}}
   477  		<td class="{{if not $bucket.MaxErrAge}}active{{end}}{{if not $n}}empty{{end}}">
   478  	                {{if $n}}<a href="?fam={{$fam}}&b={{$j}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
   479  		        [{{$n}} {{$bucket.String}}]
   480  			{{if $n}}</a>{{end}}
   481  		</td>
   482                  {{end}}
   483  
   484  	</tr>{{end}}
   485  </table>
   486  
   487  {{if $.EventLogs}}
   488  <hr />
   489  <h3>Family: {{$.Family}}</h3>
   490  
   491  {{if $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}">{{end}}
   492  [Summary]{{if $.Expanded}}</a>{{end}}
   493  
   494  {{if not $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">{{end}}
   495  [Expanded]{{if not $.Expanded}}</a>{{end}}
   496  
   497  <table id="reqs">
   498  	<tr><th>When</th><th>Elapsed</th></tr>
   499  	{{range $el := $.EventLogs}}
   500  	<tr class="first">
   501  		<td class="when">{{$el.When}}</td>
   502  		<td class="elapsed">{{$el.ElapsedTime}}</td>
   503  		<td>{{$el.Title}}
   504  	</tr>
   505  	{{if $.Expanded}}
   506  	<tr>
   507  		<td class="when"></td>
   508  		<td class="elapsed"></td>
   509  		<td><pre>{{$el.Stack|trimSpace}}</pre></td>
   510  	</tr>
   511  	{{range $el.Events}}
   512  	<tr>
   513  		<td class="when">{{.WhenString}}</td>
   514  		<td class="elapsed">{{elapsed .Elapsed}}</td>
   515  		<td>.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}</td>
   516  	</tr>
   517  	{{end}}
   518  	{{end}}
   519  	{{end}}
   520  </table>
   521  {{end}}
   522  	</body>
   523  </html>
   524  `