forked from newrelic/go-agent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sampler.go
147 lines (128 loc) · 4.44 KB
/
sampler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2020 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package newrelic
import (
"runtime"
"time"
"github.com/Easypay/go-agent/v3/internal/sysinfo"
)
// systemSample is a system/runtime snapshot.
type systemSample struct {
when time.Time
memStats runtime.MemStats
usage sysinfo.Usage
numGoroutine int
numCPU int
}
func bytesToMebibytesFloat(bts uint64) float64 {
return float64(bts) / (1024 * 1024)
}
// getSystemSample gathers a new systemSample.
func getSystemSample(now time.Time, lg Logger) *systemSample {
s := systemSample{
when: now,
numGoroutine: runtime.NumGoroutine(),
numCPU: runtime.NumCPU(),
}
if usage, err := sysinfo.GetUsage(); err == nil {
s.usage = usage
} else {
lg.Warn("unable to usage", map[string]interface{}{
"error": err.Error(),
})
}
runtime.ReadMemStats(&s.memStats)
return &s
}
type cpuStats struct {
used time.Duration
fraction float64 // used / (elapsed * numCPU)
}
// systemStats contains system information for a period of time.
type systemStats struct {
numGoroutine int
allocBytes uint64
heapObjects uint64
user cpuStats
system cpuStats
gcPauseFraction float64
deltaNumGC uint32
deltaPauseTotal time.Duration
minPause time.Duration
maxPause time.Duration
}
// systemSamples is used as the parameter to getSystemStats to avoid mixing up the previous
// and current sample.
type systemSamples struct {
Previous *systemSample
Current *systemSample
}
// getSystemStats combines two systemSamples into a Stats.
func getSystemStats(ss systemSamples) systemStats {
cur := ss.Current
prev := ss.Previous
elapsed := cur.when.Sub(prev.when)
s := systemStats{
numGoroutine: cur.numGoroutine,
allocBytes: cur.memStats.Alloc,
heapObjects: cur.memStats.HeapObjects,
}
// CPU Utilization
totalCPUSeconds := elapsed.Seconds() * float64(cur.numCPU)
if prev.usage.User != 0 && cur.usage.User > prev.usage.User {
s.user.used = cur.usage.User - prev.usage.User
s.user.fraction = s.user.used.Seconds() / totalCPUSeconds
}
if prev.usage.System != 0 && cur.usage.System > prev.usage.System {
s.system.used = cur.usage.System - prev.usage.System
s.system.fraction = s.system.used.Seconds() / totalCPUSeconds
}
// GC Pause Fraction
deltaPauseTotalNs := cur.memStats.PauseTotalNs - prev.memStats.PauseTotalNs
frac := float64(deltaPauseTotalNs) / float64(elapsed.Nanoseconds())
s.gcPauseFraction = frac
// GC Pauses
if deltaNumGC := cur.memStats.NumGC - prev.memStats.NumGC; deltaNumGC > 0 {
// In case more than 256 pauses have happened between samples
// and we are examining a subset of the pauses, we ensure that
// the min and max are not on the same side of the average by
// using the average as the starting min and max.
maxPauseNs := deltaPauseTotalNs / uint64(deltaNumGC)
minPauseNs := deltaPauseTotalNs / uint64(deltaNumGC)
for i := prev.memStats.NumGC + 1; i <= cur.memStats.NumGC; i++ {
pause := cur.memStats.PauseNs[(i+255)%256]
if pause > maxPauseNs {
maxPauseNs = pause
}
if pause < minPauseNs {
minPauseNs = pause
}
}
s.deltaPauseTotal = time.Duration(deltaPauseTotalNs) * time.Nanosecond
s.deltaNumGC = deltaNumGC
s.minPause = time.Duration(minPauseNs) * time.Nanosecond
s.maxPause = time.Duration(maxPauseNs) * time.Nanosecond
}
return s
}
// MergeIntoHarvest implements Harvestable.
func (s systemStats) MergeIntoHarvest(h *harvest) {
h.Metrics.addValue(heapObjectsAllocated, "", float64(s.heapObjects), forced)
h.Metrics.addValue(runGoroutine, "", float64(s.numGoroutine), forced)
h.Metrics.addValueExclusive(memoryPhysical, "", bytesToMebibytesFloat(s.allocBytes), 0, forced)
h.Metrics.addValueExclusive(cpuUserUtilization, "", s.user.fraction, 0, forced)
h.Metrics.addValueExclusive(cpuSystemUtilization, "", s.system.fraction, 0, forced)
h.Metrics.addValue(cpuUserTime, "", s.user.used.Seconds(), forced)
h.Metrics.addValue(cpuSystemTime, "", s.system.used.Seconds(), forced)
h.Metrics.addValueExclusive(gcPauseFraction, "", s.gcPauseFraction, 0, forced)
if s.deltaNumGC > 0 {
h.Metrics.add(gcPauses, "", metricData{
countSatisfied: float64(s.deltaNumGC),
totalTolerated: s.deltaPauseTotal.Seconds(),
exclusiveFailed: 0,
min: s.minPause.Seconds(),
max: s.maxPause.Seconds(),
sumSquares: s.deltaPauseTotal.Seconds() * s.deltaPauseTotal.Seconds(),
}, forced)
}
}