1
2
3
4
5 package traceviewer
6
7 import (
8 "fmt"
9 "html/template"
10 "math"
11 "strings"
12 "time"
13 )
14
15
16 type TimeHistogram struct {
17 Count int
18 Buckets []int
19 MinBucket, MaxBucket int
20 }
21
22
23 var logDiv = math.Log(math.Pow(10, 1.0/5))
24
25
26 func (h *TimeHistogram) Add(d time.Duration) {
27 var bucket int
28 if d > 0 {
29 bucket = int(math.Log(float64(d)) / logDiv)
30 }
31 if len(h.Buckets) <= bucket {
32 h.Buckets = append(h.Buckets, make([]int, bucket-len(h.Buckets)+1)...)
33 h.Buckets = h.Buckets[:cap(h.Buckets)]
34 }
35 h.Buckets[bucket]++
36 if bucket < h.MinBucket || h.MaxBucket == 0 {
37 h.MinBucket = bucket
38 }
39 if bucket > h.MaxBucket {
40 h.MaxBucket = bucket
41 }
42 h.Count++
43 }
44
45
46 func (h *TimeHistogram) BucketMin(bucket int) time.Duration {
47 return time.Duration(math.Exp(float64(bucket) * logDiv))
48 }
49
50
51 func (h *TimeHistogram) ToHTML(urlmaker func(min, max time.Duration) string) template.HTML {
52 if h == nil || h.Count == 0 {
53 return template.HTML("")
54 }
55
56 const barWidth = 400
57
58 maxCount := 0
59 for _, count := range h.Buckets {
60 if count > maxCount {
61 maxCount = count
62 }
63 }
64
65 w := new(strings.Builder)
66 fmt.Fprintf(w, `<table>`)
67 for i := h.MinBucket; i <= h.MaxBucket; i++ {
68
69 if h.Buckets[i] > 0 {
70 fmt.Fprintf(w, `<tr><td class="histoTime" align="right"><a href=%s>%s</a></td>`, urlmaker(h.BucketMin(i), h.BucketMin(i+1)), h.BucketMin(i))
71 } else {
72 fmt.Fprintf(w, `<tr><td class="histoTime" align="right">%s</td>`, h.BucketMin(i))
73 }
74
75 width := h.Buckets[i] * barWidth / maxCount
76 fmt.Fprintf(w, `<td><div style="width:%dpx;background:blue;position:relative"> </div></td>`, width)
77
78 fmt.Fprintf(w, `<td align="right"><div style="position:relative">%d</div></td>`, h.Buckets[i])
79 fmt.Fprintf(w, "</tr>\n")
80
81 }
82
83 fmt.Fprintf(w, `<tr><td align="right">%s</td></tr>`, h.BucketMin(h.MaxBucket+1))
84 fmt.Fprintf(w, `</table>`)
85 return template.HTML(w.String())
86 }
87
View as plain text