Skip to content

Commit c6bfa5d

Browse files
authored
Revert "feat: remove metric/memory package (#251)"
This reverts commit eee5698.
1 parent 2eadb5f commit c6bfa5d

File tree

10 files changed

+1029
-0
lines changed

10 files changed

+1029
-0
lines changed

metric/memory/memory.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package memory
19+
20+
import (
21+
"fmt"
22+
23+
"github.com/elastic/elastic-agent-libs/opt"
24+
"github.com/elastic/elastic-agent-system-metrics/metric"
25+
"github.com/elastic/elastic-agent-system-metrics/metric/system/resolve"
26+
)
27+
28+
// Memory holds os-specifc memory usage data
29+
// The vast majority of these values are cross-platform
30+
// However, we're wrapping all them for the sake of safety, and for the more variable swap metrics
31+
type Memory struct {
32+
Total opt.Uint `struct:"total,omitempty"`
33+
Used UsedMemStats `struct:"used,omitempty"`
34+
35+
Free opt.Uint `struct:"free,omitempty"`
36+
Cached opt.Uint `struct:"cached,omitempty"`
37+
// "Actual" values are, technically, a linux-only concept
38+
// For better or worse we've expanded it to include "derived"
39+
// Memory values on other platforms, which we should
40+
// probably keep for the sake of backwards compatibility
41+
// However, because the derived value varies from platform to platform,
42+
// We may want to more precisely document what these mean.
43+
Actual ActualMemoryMetrics `struct:"actual,omitempty"`
44+
45+
// Swap metrics
46+
Swap SwapMetrics `struct:"swap,omitempty"`
47+
}
48+
49+
// UsedMemStats wraps used.* memory metrics
50+
type UsedMemStats struct {
51+
Pct opt.Float `struct:"pct,omitempty"`
52+
Bytes opt.Uint `struct:"bytes,omitempty"`
53+
}
54+
55+
// ActualMemoryMetrics wraps the actual.* memory metrics
56+
type ActualMemoryMetrics struct {
57+
Free opt.Uint `struct:"free,omitempty"`
58+
Used UsedMemStats `struct:"used,omitempty"`
59+
}
60+
61+
// SwapMetrics wraps swap.* memory metrics
62+
type SwapMetrics struct {
63+
Total opt.Uint `struct:"total,omitempty"`
64+
Used UsedMemStats `struct:"used,omitempty"`
65+
Free opt.Uint `struct:"free,omitempty"`
66+
}
67+
68+
// Get returns platform-independent memory metrics.
69+
func Get(procfs resolve.Resolver) (Memory, error) {
70+
base, err := get(procfs)
71+
if err != nil {
72+
return Memory{}, fmt.Errorf("error getting system memory info: %w", err)
73+
}
74+
base.fillPercentages()
75+
return base, nil
76+
}
77+
78+
// IsZero implements the zeroer interface for structform's folders
79+
func (used UsedMemStats) IsZero() bool {
80+
return used.Pct.IsZero() && used.Bytes.IsZero()
81+
}
82+
83+
// IsZero implements the zeroer interface for structform's folders
84+
func (swap SwapMetrics) IsZero() bool {
85+
return swap.Free.IsZero() && swap.Used.IsZero() && swap.Total.IsZero()
86+
}
87+
88+
func (base *Memory) fillPercentages() {
89+
// Add percentages
90+
// In theory, `Used` and `Total` are available everywhere, so assume values are good.
91+
if base.Total.Exists() && base.Total.ValueOr(0) != 0 {
92+
percUsed := float64(base.Used.Bytes.ValueOr(0)) / float64(base.Total.ValueOr(1))
93+
base.Used.Pct = opt.FloatWith(metric.Round(percUsed))
94+
95+
actualPercUsed := float64(base.Actual.Used.Bytes.ValueOr(0)) / float64(base.Total.ValueOr(0))
96+
base.Actual.Used.Pct = opt.FloatWith(metric.Round(actualPercUsed))
97+
}
98+
99+
if base.Swap.Total.ValueOr(0) != 0 && base.Swap.Used.Bytes.Exists() {
100+
perc := float64(base.Swap.Used.Bytes.ValueOr(0)) / float64(base.Swap.Total.ValueOr(0))
101+
base.Swap.Used.Pct = opt.FloatWith(metric.Round(perc))
102+
}
103+
}

metric/memory/memory_aix.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package memory
19+
20+
/*
21+
#cgo LDFLAGS: -L/usr/lib -lperfstat
22+
23+
#include <libperfstat.h>
24+
#include <procinfo.h>
25+
#include <unistd.h>
26+
#include <utmp.h>
27+
#include <sys/mntctl.h>
28+
#include <sys/proc.h>
29+
#include <sys/types.h>
30+
#include <sys/vmount.h>
31+
32+
*/
33+
import "C"
34+
35+
import (
36+
"fmt"
37+
"os"
38+
39+
"github.com/elastic/elastic-agent-libs/opt"
40+
"github.com/elastic/elastic-agent-system-metrics/metric/system/resolve"
41+
)
42+
43+
var system struct {
44+
ticks uint64
45+
btime uint64
46+
pagesize uint64
47+
}
48+
49+
func init() {
50+
// sysconf(_SC_CLK_TCK) returns the number of ticks by second.
51+
system.ticks = uint64(C.sysconf(C._SC_CLK_TCK))
52+
system.pagesize = uint64(os.Getpagesize())
53+
}
54+
55+
func get(_ resolve.Resolver) (Memory, error) {
56+
memData := Memory{}
57+
meminfo := C.perfstat_memory_total_t{}
58+
_, err := C.perfstat_memory_total(nil, &meminfo, C.sizeof_perfstat_memory_total_t, 1)
59+
if err != nil {
60+
return memData, fmt.Errorf("perfstat_memory_total: %s", err)
61+
}
62+
63+
totalMem := uint64(meminfo.real_total) * system.pagesize
64+
freeMem := uint64(meminfo.real_free) * system.pagesize
65+
66+
memData.Total = opt.UintWith(totalMem)
67+
memData.Free = opt.UintWith(freeMem)
68+
69+
kern := uint64(meminfo.numperm) * system.pagesize // number of pages in file cache
70+
71+
memData.Used.Bytes = opt.UintWith(totalMem - freeMem)
72+
memData.Actual.Free = opt.UintWith(freeMem + kern)
73+
memData.Actual.Used.Bytes = opt.UintWith(memData.Used.Bytes.ValueOr(0) - kern)
74+
75+
return memData, nil
76+
}

metric/memory/memory_darwin.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package memory
19+
20+
/*
21+
#include <stdlib.h>
22+
#include <sys/sysctl.h>
23+
#include <sys/mount.h>
24+
#include <mach/mach_init.h>
25+
#include <mach/mach_host.h>
26+
#include <mach/host_info.h>
27+
#include <libproc.h>
28+
#include <mach/processor_info.h>
29+
#include <mach/vm_map.h>
30+
*/
31+
import "C"
32+
33+
import (
34+
"bytes"
35+
"encoding/binary"
36+
"fmt"
37+
"syscall"
38+
"unsafe"
39+
40+
"github.com/elastic/elastic-agent-libs/opt"
41+
"github.com/elastic/elastic-agent-system-metrics/metric/system/resolve"
42+
)
43+
44+
type xswUsage struct {
45+
Total, Avail, Used uint64
46+
}
47+
48+
// get is the darwin implementation for fetching Memory data
49+
func get(_ resolve.Resolver) (Memory, error) {
50+
var vmstat C.vm_statistics_data_t
51+
52+
mem := Memory{}
53+
54+
var total uint64
55+
56+
if err := sysctlbyname("hw.memsize", &total); err != nil {
57+
return Memory{}, fmt.Errorf("error getting memsize: %w", err)
58+
}
59+
mem.Total = opt.UintWith(total)
60+
61+
if err := vmInfo(&vmstat); err != nil {
62+
return Memory{}, fmt.Errorf("error getting VM info")
63+
}
64+
65+
kern := uint64(vmstat.inactive_count) << 12
66+
free := uint64(vmstat.free_count) << 12
67+
68+
mem.Free = opt.UintWith(free)
69+
mem.Used.Bytes = opt.UintWith(total - free)
70+
71+
mem.Actual.Free = opt.UintWith(free + kern)
72+
mem.Actual.Used.Bytes = opt.UintWith((total - free) - kern)
73+
74+
var err error
75+
mem.Swap, err = getSwap()
76+
if err != nil {
77+
return mem, fmt.Errorf("error getting swap memory: %w", err)
78+
}
79+
80+
return mem, nil
81+
}
82+
83+
// Get fetches swap data
84+
func getSwap() (SwapMetrics, error) {
85+
swUsage := xswUsage{}
86+
87+
swap := SwapMetrics{}
88+
if err := sysctlbyname("vm.swapusage", &swUsage); err != nil {
89+
return swap, fmt.Errorf("error getting swap usage: %w", err)
90+
}
91+
92+
swap.Total = opt.UintWith(swUsage.Total)
93+
swap.Used.Bytes = opt.UintWith(swUsage.Used)
94+
swap.Free = opt.UintWith(swUsage.Avail)
95+
96+
return swap, nil
97+
}
98+
99+
// generic Sysctl buffer unmarshalling
100+
func sysctlbyname(name string, data interface{}) (err error) {
101+
val, err := syscall.Sysctl(name)
102+
if err != nil {
103+
return err
104+
}
105+
106+
buf := []byte(val)
107+
108+
switch v := data.(type) {
109+
case *uint64:
110+
*v = *(*uint64)(unsafe.Pointer(&buf[0]))
111+
return
112+
}
113+
114+
bbuf := bytes.NewBuffer([]byte(val))
115+
return binary.Read(bbuf, binary.LittleEndian, data)
116+
}
117+
118+
func vmInfo(vmstat *C.vm_statistics_data_t) error {
119+
var count C.mach_msg_type_number_t = C.HOST_VM_INFO_COUNT
120+
121+
status := C.host_statistics(
122+
C.host_t(C.mach_host_self()),
123+
C.HOST_VM_INFO,
124+
C.host_info_t(unsafe.Pointer(vmstat)),
125+
&count)
126+
127+
if status != C.KERN_SUCCESS {
128+
return fmt.Errorf("host_statistics=%d", status)
129+
}
130+
131+
return nil
132+
}

0 commit comments

Comments
 (0)