mirror of
https://github.com/divan/expvarmon.git
synced 2025-04-27 13:48:55 +08:00
Add stats for GC pauses
This commit is contained in:
parent
b93e309f19
commit
0091befb37
47
histogram.go
47
histogram.go
@ -1,9 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
type Histogram struct {
|
||||
Bins []Bin
|
||||
Maxbins int
|
||||
Total uint64
|
||||
|
||||
min, max uint64
|
||||
}
|
||||
|
||||
type Bin struct {
|
||||
@ -16,15 +22,24 @@ type Bin struct {
|
||||
// should be sufficient.
|
||||
func NewHistogram(n int) *Histogram {
|
||||
return &Histogram{
|
||||
Bins: make([]Bin, 0),
|
||||
Bins: make([]Bin, 0, n),
|
||||
Maxbins: n,
|
||||
Total: 0,
|
||||
|
||||
min: math.MaxUint64,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Histogram) Add(n uint64) {
|
||||
defer h.trim()
|
||||
h.Total++
|
||||
|
||||
if n > h.max {
|
||||
h.max = n
|
||||
}
|
||||
if n < h.min {
|
||||
h.min = n
|
||||
}
|
||||
|
||||
defer h.trim()
|
||||
for i := range h.Bins {
|
||||
if h.Bins[i].Value == n {
|
||||
h.Bins[i].Count++
|
||||
@ -45,17 +60,17 @@ func (h *Histogram) Add(n uint64) {
|
||||
h.Bins = append(h.Bins, Bin{Count: 1, Value: n})
|
||||
}
|
||||
|
||||
func (h *Histogram) Quantile(q uint64) int64 {
|
||||
count := q * h.Total
|
||||
func (h *Histogram) Quantile(q float64) int64 {
|
||||
count := q * float64(h.Total)
|
||||
for i := range h.Bins {
|
||||
count -= h.Bins[i].Count
|
||||
count -= float64(h.Bins[i].Count)
|
||||
|
||||
if count <= 0 {
|
||||
return int64(h.Bins[i].Value)
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
return 0
|
||||
}
|
||||
|
||||
// CDF returns the value of the cumulative distribution function
|
||||
@ -86,6 +101,24 @@ func (h *Histogram) Mean() float64 {
|
||||
return sum / float64(h.Total)
|
||||
}
|
||||
|
||||
// Min returns the minimal recorder value.
|
||||
func (h *Histogram) Min() uint64 {
|
||||
if h.Total == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return h.min
|
||||
}
|
||||
|
||||
// Max returns the maximum recorder value.
|
||||
func (h *Histogram) Max() uint64 {
|
||||
if h.Total == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return h.max
|
||||
}
|
||||
|
||||
// Variance returns the variance of the distribution
|
||||
func (h *Histogram) Variance() float64 {
|
||||
if h.Total == 0 {
|
||||
|
24
ui_single.go
24
ui_single.go
@ -15,6 +15,7 @@ type TermUISingle struct {
|
||||
Sparkline *termui.Sparklines
|
||||
Pars []*termui.Par
|
||||
BarChart *termui.BarChart
|
||||
GCStats *termui.Par
|
||||
BarChart2 *termui.BarChart
|
||||
|
||||
bins int // histograms' bins count
|
||||
@ -85,6 +86,14 @@ func (t *TermUISingle) Init(data UIData) error {
|
||||
bc.NumColor = termui.ColorBlack
|
||||
return bc
|
||||
}()
|
||||
t.GCStats = func() *termui.Par {
|
||||
p := termui.NewPar("")
|
||||
p.Height = 4
|
||||
p.Width = len("Max: 123ms") // example
|
||||
p.HasBorder = false
|
||||
p.TextFgColor = termui.ColorGreen
|
||||
return p
|
||||
}()
|
||||
}
|
||||
|
||||
if data.HasGCIntervals {
|
||||
@ -150,13 +159,19 @@ func (t *TermUISingle) Update(data UIData) {
|
||||
labels := make([]string, 0, len(counts))
|
||||
for i := 0; i < len(counts); i++ {
|
||||
vals = append(vals, int(counts[i]))
|
||||
d := roundDuration(time.Duration(values[i]))
|
||||
d := round(time.Duration(values[i]))
|
||||
labels = append(labels, d.String())
|
||||
}
|
||||
t.BarChart.Data = vals
|
||||
t.BarChart.DataLabels = labels
|
||||
t.BarChart.Border.Label = "GC Pauses (last 256)"
|
||||
|
||||
t.GCStats.Text = fmt.Sprintf("Min: %v\nAvg: %v\n95p: %v\nMax: %v",
|
||||
round(time.Duration(hist.Min())),
|
||||
round(time.Duration(hist.Mean())),
|
||||
round(time.Duration(hist.Quantile(0.95))),
|
||||
round(time.Duration(hist.Max())),
|
||||
)
|
||||
}
|
||||
|
||||
if data.HasGCIntervals {
|
||||
@ -173,7 +188,7 @@ func (t *TermUISingle) Update(data UIData) {
|
||||
labels := make([]string, 0, len(counts))
|
||||
for i := 0; i < len(counts); i++ {
|
||||
vals = append(vals, int(counts[i]))
|
||||
d := roundDuration(time.Duration(values[i]))
|
||||
d := round(time.Duration(values[i]))
|
||||
labels = append(labels, d.String())
|
||||
}
|
||||
t.BarChart2.Data = vals
|
||||
@ -186,7 +201,7 @@ func (t *TermUISingle) Update(data UIData) {
|
||||
var widgets []termui.Bufferer
|
||||
widgets = append(widgets, t.Title, t.Status, t.Sparkline)
|
||||
if data.HasGCPauses {
|
||||
widgets = append(widgets, t.BarChart)
|
||||
widgets = append(widgets, t.BarChart, t.GCStats)
|
||||
}
|
||||
if data.HasGCIntervals {
|
||||
widgets = append(widgets, t.BarChart2)
|
||||
@ -263,6 +278,9 @@ func (t *TermUISingle) Relayout() {
|
||||
t.BarChart.Height = h - calcHeight
|
||||
t.BarChart.Y = th - t.BarChart.Height
|
||||
t.BarChart.BarWidth = binWidth
|
||||
|
||||
t.GCStats.X = barchartWidth - t.GCStats.Width - 1
|
||||
t.GCStats.Y = t.BarChart.Y + 1
|
||||
}
|
||||
|
||||
if t.BarChart2 != nil {
|
||||
|
6
var.go
6
var.go
@ -129,7 +129,7 @@ type Duration struct {
|
||||
|
||||
func (v *Duration) Kind() VarKind { return KindDuration }
|
||||
func (v *Duration) String() string {
|
||||
return fmt.Sprintf("%s", roundDuration(time.Duration(v.dur)))
|
||||
return fmt.Sprintf("%s", round(time.Duration(v.dur)))
|
||||
}
|
||||
|
||||
func (v *Duration) Set(j *jason.Value) {
|
||||
@ -331,8 +331,8 @@ func (v VarName) Kind() VarKind {
|
||||
return KindDefault
|
||||
}
|
||||
|
||||
// roundDuration removes unneeded precision from the String() output for time.Duration.
|
||||
func roundDuration(d time.Duration) time.Duration {
|
||||
// round removes unneeded precision from the String() output for time.Duration.
|
||||
func round(d time.Duration) time.Duration {
|
||||
r := time.Second
|
||||
if d < time.Second {
|
||||
r = time.Millisecond
|
||||
|
Loading…
x
Reference in New Issue
Block a user