Skip to content

Commit 665bf7f

Browse files
committed
allow modifying unit prefixes
1 parent 7eb526d commit 665bf7f

12 files changed

+222
-61
lines changed

cli/clistat/cgroup.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@ const (
4545
)
4646

4747
// ContainerCPU returns the CPU usage of the container cgroup.
48+
// This is calculated as difference of two samples of the
49+
// CPU usage of the container cgroup.
50+
// The total is read from the relevant path in /sys/fs/cgroup.
51+
// If there is no limit set, the total is assumed to be the
52+
// number of host cores multiplied by the CFS period.
4853
// If the system is not containerized, this always returns nil.
49-
func (s *Statter) ContainerCPU() (*Result, error) {
54+
func (s *Statter) ContainerCPU(m Prefix) (*Result, error) {
5055
// Firstly, check if we are containerized.
5156
if ok, err := IsContainerized(s.fs); err != nil || !ok {
5257
return nil, nil //nolint: nilnil
@@ -61,6 +66,11 @@ func (s *Statter) ContainerCPU() (*Result, error) {
6166
if err != nil {
6267
return nil, xerrors.Errorf("get cgroup CPU usage: %w", err)
6368
}
69+
70+
// The measurements in /sys/fs/cgroup are counters.
71+
// We need to wait for a bit to get a difference.
72+
// Note that someone could reset the counter in the meantime.
73+
// We can't do anything about that.
6474
s.wait(s.sampleInterval)
6575

6676
used2, err := s.cGroupCPUUsed()
@@ -69,9 +79,10 @@ func (s *Statter) ContainerCPU() (*Result, error) {
6979
}
7080

7181
r := &Result{
72-
Unit: "cores",
73-
Used: (used2 - used1),
74-
Total: ptr.To(total),
82+
Unit: "cores",
83+
Prefix: m,
84+
Used: (used2 - used1),
85+
Total: ptr.To(total),
7586
}
7687
return r, nil
7788
}
@@ -169,20 +180,20 @@ func (s *Statter) cGroupV1CPUUsed() (float64, error) {
169180

170181
// ContainerMemory returns the memory usage of the container cgroup.
171182
// If the system is not containerized, this always returns nil.
172-
func (s *Statter) ContainerMemory() (*Result, error) {
183+
func (s *Statter) ContainerMemory(m Prefix) (*Result, error) {
173184
if ok, err := IsContainerized(s.fs); err != nil || !ok {
174185
return nil, nil //nolint:nilnil
175186
}
176187

177188
if s.isCGroupV2() {
178-
return s.cGroupV2Memory()
189+
return s.cGroupV2Memory(m)
179190
}
180191

181192
// Fall back to CGroupv1
182-
return s.cGroupV1Memory()
193+
return s.cGroupV1Memory(m)
183194
}
184195

185-
func (s *Statter) cGroupV2Memory() (*Result, error) {
196+
func (s *Statter) cGroupV2Memory(m Prefix) (*Result, error) {
186197
maxUsageBytes, err := readInt64(s.fs, cgroupV2MemoryMaxBytes)
187198
if err != nil {
188199
return nil, xerrors.Errorf("read memory total: %w", err)
@@ -199,13 +210,14 @@ func (s *Statter) cGroupV2Memory() (*Result, error) {
199210
}
200211

201212
return &Result{
202-
Total: ptr.To(float64(maxUsageBytes) / 1024 / 1024 / 1024),
203-
Used: float64(currUsageBytes-inactiveFileBytes) / 1024 / 1024 / 1024,
204-
Unit: "GB",
213+
Total: ptr.To(float64(maxUsageBytes)),
214+
Used: float64(currUsageBytes - inactiveFileBytes),
215+
Unit: "B",
216+
Prefix: m,
205217
}, nil
206218
}
207219

208-
func (s *Statter) cGroupV1Memory() (*Result, error) {
220+
func (s *Statter) cGroupV1Memory(m Prefix) (*Result, error) {
209221
maxUsageBytes, err := readInt64(s.fs, cgroupV1MemoryMaxUsageBytes)
210222
if err != nil {
211223
return nil, xerrors.Errorf("read memory total: %w", err)
@@ -224,9 +236,10 @@ func (s *Statter) cGroupV1Memory() (*Result, error) {
224236

225237
// Total memory used is usage - total_inactive_file
226238
return &Result{
227-
Total: ptr.To(float64(maxUsageBytes) / 1024 / 1024 / 1024),
228-
Used: float64(usageBytes-totalInactiveFileBytes) / 1024 / 1024 / 1024,
229-
Unit: "GB",
239+
Total: ptr.To(float64(maxUsageBytes)),
240+
Used: float64(usageBytes - totalInactiveFileBytes),
241+
Unit: "B",
242+
Prefix: m,
230243
}, nil
231244
}
232245

cli/clistat/disk.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
// Disk returns the disk usage of the given path.
1212
// If path is empty, it returns the usage of the root directory.
13-
func (*Statter) Disk(path string) (*Result, error) {
13+
func (*Statter) Disk(path string, m Prefix) (*Result, error) {
1414
if path == "" {
1515
path = "/"
1616
}
@@ -19,8 +19,9 @@ func (*Statter) Disk(path string) (*Result, error) {
1919
return nil, err
2020
}
2121
var r Result
22-
r.Total = ptr.To(float64(stat.Blocks*uint64(stat.Bsize)) / 1024 / 1024 / 1024)
23-
r.Used = float64(stat.Blocks-stat.Bfree) * float64(stat.Bsize) / 1024 / 1024 / 1024
24-
r.Unit = "GB"
22+
r.Total = ptr.To(float64(stat.Blocks * uint64(stat.Bsize)))
23+
r.Used = float64(stat.Blocks-stat.Bfree) * float64(stat.Bsize)
24+
r.Unit = "B"
25+
r.Prefix = m
2526
return &r, nil
2627
}

cli/clistat/disk_windows.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
// Disk returns the disk usage of the given path.
99
// If path is empty, it defaults to C:\
10-
func (*Statter) Disk(path string) (*Result, error) {
10+
func (*Statter) Disk(path string, m Prefix) (*Result, error) {
1111
if path == "" {
1212
path = `C:\`
1313
}
@@ -28,8 +28,9 @@ func (*Statter) Disk(path string) (*Result, error) {
2828
}
2929

3030
var r Result
31-
r.Total = ptr.To(float64(totalBytes) / 1024 / 1024 / 1024)
32-
r.Used = float64(totalBytes-freeBytes) / 1024 / 1024 / 1024
33-
r.Unit = "GB"
31+
r.Total = ptr.To(float64(totalBytes))
32+
r.Used = float64(totalBytes - freeBytes)
33+
r.Unit = "B"
34+
r.Prefix = m
3435
return &r, nil
3536
}

cli/clistat/stat.go

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,77 @@ import (
1414
sysinfotypes "github.com/elastic/go-sysinfo/types"
1515
)
1616

17+
// Prefix is an SI prefix for a unit.
18+
type Prefix string
19+
20+
// Float64 returns the prefix as a float64.
21+
func (m *Prefix) Float64() (float64, error) {
22+
switch *m {
23+
case PrefixDeciShort, PrefixDeci:
24+
return 0.1, nil
25+
case PrefixCentiShort, PrefixCenti:
26+
return 0.01, nil
27+
case PrefixMilliShort, PrefixMilli:
28+
return 0.001, nil
29+
case PrefixMicroShort, PrefixMicro:
30+
return 0.000_001, nil
31+
case PrefixNanoShort, PrefixNano:
32+
return 0.000_000_001, nil
33+
case PrefixKiloShort, PrefixKilo:
34+
return 1_000.0, nil
35+
case PrefixMegaShort, PrefixMega:
36+
return 1_000_000.0, nil
37+
case PrefixGigaShort, PrefixGiga:
38+
return 1_000_000_000.0, nil
39+
case PrefixTeraShort, PrefixTera:
40+
return 1_000_000_000_000.0, nil
41+
case PrefixKibiShort, PrefixKibi:
42+
return 1024.0, nil
43+
case PrefixMebiShort, PrefixMebi:
44+
return 1_048_576.0, nil
45+
case PrefixGibiShort, PrefixGibi:
46+
return 1_073_741_824.0, nil
47+
case PrefixTebiShort, PrefixTebi:
48+
return 1_099_511_627_776.0, nil
49+
default:
50+
return 0, xerrors.Errorf("unknown prefix: %s", *m)
51+
}
52+
}
53+
54+
const (
55+
PrefixDeci Prefix = "deci"
56+
PrefixCenti Prefix = "centi"
57+
PrefixMilli Prefix = "milli"
58+
PrefixMicro Prefix = "micro"
59+
PrefixNano Prefix = "nano"
60+
61+
PrefixDeciShort Prefix = "d"
62+
PrefixCentiShort Prefix = "c"
63+
PrefixMilliShort Prefix = "m"
64+
PrefixMicroShort Prefix = "u"
65+
PrefixNanoShort Prefix = "n"
66+
67+
PrefixKilo Prefix = "kilo"
68+
PrefixMega Prefix = "mega"
69+
PrefixGiga Prefix = "giga"
70+
PrefixTera Prefix = "tera"
71+
72+
PrefixKiloShort Prefix = "K"
73+
PrefixMegaShort Prefix = "M"
74+
PrefixGigaShort Prefix = "G"
75+
PrefixTeraShort Prefix = "T"
76+
77+
PrefixKibi = "kibi"
78+
PrefixMebi = "mebi"
79+
PrefixGibi = "gibi"
80+
PrefixTebi = "tebi"
81+
82+
PrefixKibiShort Prefix = "Ki"
83+
PrefixMebiShort Prefix = "Mi"
84+
PrefixGibiShort Prefix = "Gi"
85+
PrefixTebiShort Prefix = "Ti"
86+
)
87+
1788
// Result is a generic result type for a statistic.
1889
// Total is the total amount of the resource available.
1990
// It is nil if the resource is not a finite quantity.
@@ -23,6 +94,8 @@ type Result struct {
2394
Total *float64 `json:"total"`
2495
Unit string `json:"unit"`
2596
Used float64 `json:"used"`
97+
// Prefix controls the string representation of the result.
98+
Prefix Prefix `json:"-"`
2699
}
27100

28101
// String returns a human-readable representation of the result.
@@ -31,13 +104,20 @@ func (r *Result) String() string {
31104
return "-"
32105
}
33106
var sb strings.Builder
34-
_, _ = sb.WriteString(strconv.FormatFloat(r.Used, 'f', 1, 64))
107+
scale, err := r.Prefix.Float64()
108+
prefix := string(r.Prefix)
109+
if err != nil {
110+
prefix = ""
111+
scale = 1.0
112+
}
113+
_, _ = sb.WriteString(strconv.FormatFloat(r.Used/scale, 'f', 1, 64))
35114
if r.Total != (*float64)(nil) {
36115
_, _ = sb.WriteString("/")
37-
_, _ = sb.WriteString(strconv.FormatFloat(*r.Total, 'f', 1, 64))
116+
_, _ = sb.WriteString(strconv.FormatFloat(*r.Total/scale, 'f', 1, 64))
38117
}
39118
if r.Unit != "" {
40119
_, _ = sb.WriteString(" ")
120+
_, _ = sb.WriteString(prefix)
41121
_, _ = sb.WriteString(r.Unit)
42122
}
43123
return sb.String()
@@ -96,7 +176,7 @@ func New(opts ...Option) (*Statter, error) {
96176
// This is calculated by taking the difference between the total and idle HostCPU time
97177
// and scaling it by the number of cores.
98178
// Units are in "cores".
99-
func (s *Statter) HostCPU() (*Result, error) {
179+
func (s *Statter) HostCPU(m Prefix) (*Result, error) {
100180
r := &Result{
101181
Unit: "cores",
102182
Total: ptr.To(float64(s.nproc)),
@@ -115,19 +195,21 @@ func (s *Statter) HostCPU() (*Result, error) {
115195
used := total - idle
116196
scaleFactor := float64(s.nproc) / total.Seconds()
117197
r.Used = used.Seconds() * scaleFactor
198+
r.Prefix = m
118199
return r, nil
119200
}
120201

121202
// HostMemory returns the memory usage of the host, in gigabytes.
122-
func (s *Statter) HostMemory() (*Result, error) {
203+
func (s *Statter) HostMemory(m Prefix) (*Result, error) {
123204
r := &Result{
124-
Unit: "GB",
205+
Unit: "B",
206+
Prefix: m,
125207
}
126208
hm, err := s.hi.Memory()
127209
if err != nil {
128210
return nil, xerrors.Errorf("get memory info: %w", err)
129211
}
130-
r.Total = ptr.To(float64(hm.Total) / 1024 / 1024 / 1024)
131-
r.Used = float64(hm.Used) / 1024 / 1024 / 1024
212+
r.Total = ptr.To(float64(hm.Total))
213+
r.Used = float64(hm.Used)
132214
return r, nil
133215
}

0 commit comments

Comments
 (0)