~elementary-os/elementaryos/os-patch-aptly-xenial

« back to all changes in this revision

Viewing changes to src/github.com/smira/aptly/_vendor/src/golang.org/x/net/trace/events.go

  • Committer: RabbitBot
  • Date: 2016-07-19 12:29:41 UTC
  • Revision ID: rabbitbot@elementary.io-20160719122941-z6gaxs7nzgmratry
Initial import, version 0.9.6-1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
`