diff --git a/go.mod b/go.mod index 2340c7778..b5b974e0a 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( ) require ( + github.com/ebitengine/purego v0.7.1 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect diff --git a/go.sum b/go.sum index 59d98938c..d6f78b436 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= +github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/prometheus/process_collector_amd64_darwin.go b/prometheus/process_collector_amd64_darwin.go new file mode 100644 index 000000000..8ae71d3f1 --- /dev/null +++ b/prometheus/process_collector_amd64_darwin.go @@ -0,0 +1,54 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build amd64 + +package prometheus + +// These are macros in xnu/osfmk/mach/shared_memory_server.h +const ( + globalSharedTextSegment uint64 = 0x90000000 + + sharedTextRegionSize uint64 = 0x10000000 + sharedDataRegionSize uint64 = 0x10000000 +) + +const ( + machTaskBasicInfoSizeOf = 48 // sizeof(struct mach_task_basic_info) + vmRegionBasicInfo64SizeOf = 36 // sizeof(struct vm_region_basic_info_64) +) + +// Fundamental mach types defined in xnu/osfmk/mach/i386/vm_types.h. The integer_t and +// __darwin_natural_t types are explicitly 32-bit here to help decoding into a structure, +// where 'int' types are not supported. The __darwin_natural_t type is defined in +// xnu/bsd/i386/_types.h. +type ( + __darwin_natural_t = uint32 // typedef unsigned int __darwin_natural_t; + integer_t = int32 // typedef int integer_t; + natural_t = __darwin_natural_t // typedef __darwin_natural_t natural_t; + + mach_vm_offset_t = uint64 // typedef uint64_t mach_vm_offset_t __kernel_ptr_semantics; + mach_vm_size_t = uint64 // typedef uint64_t mach_vm_size_t; +) + +// Defined in xnu/osfmk/mach/i386/boolean.h, and explicitly 32-bit here to help decoding +// into a structure, where 'int' types are not supported. +type ( + boolean_t = uint32 // typedef unsigned int boolean_t; +) + +// Defined in xnu/osfmk/mach/i386/kern_return.h; see xnu/osfmk/mach/kern_return.h for +// possible values. +type ( + kern_return_t = int // typedef int kern_return_t; +) diff --git a/prometheus/process_collector_arm64_darwin.go b/prometheus/process_collector_arm64_darwin.go new file mode 100644 index 000000000..613e16d52 --- /dev/null +++ b/prometheus/process_collector_arm64_darwin.go @@ -0,0 +1,55 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build arm64 + +package prometheus + +// These are macros in xnu/osfmk/mach/shared_memory_server.h. Note that __arm__ +// is not defined for 64-bit arm. +const ( + globalSharedTextSegment uint64 = 0x90000000 + + sharedTextRegionSize uint64 = 0x10000000 + sharedDataRegionSize uint64 = 0x10000000 +) + +const ( + machTaskBasicInfoSizeOf = 48 // sizeof(struct mach_task_basic_info) + vmRegionBasicInfo64SizeOf = 36 // sizeof(struct vm_region_basic_info_64) +) + +// Fundamental mach types defined in xnu/osfmk/mach/arm/vm_types.h. The integer_t and +// __darwin_natural_t types are explicitly 32-bit here to help decoding into a structure, +// where 'int' types are not supported. The __darwin_natural_t type is defined in +// xnu/bsd/arm/_types.h. +type ( + __darwin_natural_t = uint32 // typedef unsigned int __darwin_natural_t; + integer_t = int32 // typedef int integer_t; + natural_t = __darwin_natural_t // typedef __darwin_natural_t natural_t; + + mach_vm_offset_t = uint64 // typedef uint64_t mach_vm_offset_t __kernel_ptr_semantics; + mach_vm_size_t = uint64 // typedef uint64_t mach_vm_size_t; +) + +// Defined in xnu/osfmk/mach/arm/boolean.h, and explicitly 32-bit here to help decoding +// into a structure, where 'int' types are not supported. +type ( + boolean_t = int32 // typedef int boolean_t; +) + +// Defined in xnu/osfmk/mach/arm/kern_return.h; see xnu/osfmk/mach/kern_return.h for +// possible values. +type ( + kern_return_t = int // typedef int kern_return_t; +) diff --git a/prometheus/process_collector_darwin.go b/prometheus/process_collector_darwin.go index 4d9314c3d..022a8835d 100644 --- a/prometheus/process_collector_darwin.go +++ b/prometheus/process_collector_darwin.go @@ -85,7 +85,13 @@ func (c *processCollector) processCollect(ch chan<- Metric) { c.reportError(ch, c.cpuTotal, err) } - // TODO: publish c.vsize and c.rss values + if info, err := getMemoryUsage(); err == nil { + ch <- MustNewConstMetric(c.rss, GaugeValue, float64(info.ResidentSize)) + ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(info.VirtualSize)) + } else { + c.reportError(ch, c.rss, err) + c.reportError(ch, c.vsize, err) + } if fds, err := getOpenFileCount(); err == nil { ch <- MustNewConstMetric(c.openFDs, GaugeValue, fds) diff --git a/prometheus/process_collector_purego_darwin.go b/prometheus/process_collector_purego_darwin.go new file mode 100644 index 000000000..fd35a437c --- /dev/null +++ b/prometheus/process_collector_purego_darwin.go @@ -0,0 +1,219 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus + +import ( + "bytes" + "encoding/binary" + "fmt" + "github.com/ebitengine/purego" +) + +func init() { + if lib, err := purego.Dlopen("/usr/lib/system/libsystem_kernel.dylib", + purego.RTLD_NOW|purego.RTLD_GLOBAL); err == nil { + + // purego.RegisterLibFunc() panics if the symbol is missing. Ignore any error, + // and the metric will simply be unavailable when it is queried, instead of + // bringing down the whole process. + defer func() { + if err := recover(); err != nil { + // TODO: Log this somehow + } + }() + + purego.RegisterLibFunc(&machTaskSelf, lib, "mach_task_self") + purego.RegisterLibFunc(&taskInfo, lib, "task_info") + purego.RegisterLibFunc(&machVmRegion, lib, "mach_vm_region") + } +} + +const ( + // The task_info() flavor MACH_TASK_BASIC_INFO for retrieving machTaskBasicInfo. + mach_task_basic_info task_flavor_t = 20 /* always 64-bit basic info */ + + // The MACH_TASK_BASIC_INFO_COUNT value, which is passed to the Mach API as the size + // of the payload for MACH_TASK_BASIC_INFO commands. + machTaskBasicInfoCount mach_msg_type_number_t = machTaskBasicInfoSizeOf / 4 +) + +// Defined in xnu/osfmk/mach/policy.h, xnu/iokit/IOKit/IORPC.h, and xnu/osfmk/mach/message.h +// respectively. policy_t is explicitly 32-bit here to help decoding into a structure, +// where 'int' types are not supported. +type ( + policy_t = int32 // typedef int policy_t; + mach_port_t = natural_t // typedef natural_t mach_port_t; + mach_msg_type_number_t = natural_t // typedef natural_t mach_msg_type_number_t; +) + +// Defined in xnu/osfmk/mach/task_info.h. Note that task_info_t is actually defined as +// integer_t* and cast from the address of the structure in C. Define it as []byte to +// keep the type system happy. +type ( + task_flavor_t = natural_t // typedef natural_t task_flavor_t + task_info_t = []byte // typedef integer_t *task_info_t /* varying array of int */ +) + +// time_value_t is the type for kernel time values, defined in xnu/osfmk/mach/time_value.h +type time_value_t struct { + Seconds integer_t + MicroSeconds integer_t +} + +var machTaskSelf func() mach_port_t + +var taskInfo func( + mach_port_t, + task_flavor_t, + task_info_t, + *mach_msg_type_number_t, +) kern_return_t + +// machTaskBasicInfo is the representation of `struct mach_task_basic_info` defined in +// xnu/osfmk/mach/task_info.h, which is the architecture independent payload for fetching +// certain task values. +type machTaskBasicInfo struct { + VirtualSize mach_vm_size_t // virtual memory size (bytes) + ResidentSize mach_vm_size_t // resident memory size (bytes) + ResidentSizeMax mach_vm_size_t // maximum resident memory size (bytes) + UserTime time_value_t // total user run time for terminated threads + SystemTime time_value_t // total system run time for terminated threads + Policy policy_t // default policy for new threads + SuspendCount integer_t // suspend count for task +} + +func getBasicTaskInfo() (*machTaskBasicInfo, error) { + var info machTaskBasicInfo + + if taskInfo == nil { + return nil, fmt.Errorf("task_info() is not supported") + } + + var count = machTaskBasicInfoCount + buf := make([]byte, machTaskBasicInfoSizeOf) + + if ret := taskInfo(machTaskSelf(), mach_task_basic_info, buf, &count); ret != 0 { + return nil, fmt.Errorf("task_info() returned %d", ret) + } + + if err := loadStruct(buf, &info); err != nil { + return nil, err + } + + return &info, nil +} + +// Defined in xnu/osfmk/mach/memory_object_types.h, xnu/osfmk/mach/vm_behavior.h, +// defined in xnu/osfmk/mach/vm_inherit.h, and defined in xnu/osfmk/mach/vm_prot.h +// respectively. These types are not identical to the native definitions, because the +// struct decoding requires primitives with a specific width. The widths here are the +// same as the native types. +type ( + memory_object_offset_t = uint64 // typedef unsigned long long memory_object_offset_t; + vm_behavior_t = int32 // typedef int vm_behavior_t + vm_inherit_t = uint32 // typedef unsigned int vm_inherit_t; + vm_prot_t = int32 // typedef int vm_prot_t; +) + +// These are defined in xnu/osfmk/mach/vm_types.h. +type ( + vm_map_t = mach_port_t // typedef mach_port_t vm_map_t; +) + +// Defined in xnu/osfmk/mach/vm_region.h. vm_region_flavor_t is explicitly 32-bit here to +// help decoding into a structure, where 'int' types are not supported, and vm_region_info_t +// is []byte to keep the type system happy. +type ( + vm_region_flavor_t = int32 // typedef int vm_region_flavor_t; + vm_region_info_t = []byte // typedef int *vm_region_info_t; +) + +const ( + // The mach_vm_region() flavor VM_REGION_BASIC_INFO_64 for retrieving + // vmRegionBasicInfo64. + vm_region_basic_info_64 vm_region_flavor_t = 9 + + // The VM_REGION_BASIC_INFO_COUNT_64 value, which is passed to the Mach API as the + // size of the payload for VM_REGION_BASIC_INFO_64 commands. + vmRegionBasicInfoCount64 mach_msg_type_number_t = vmRegionBasicInfo64SizeOf / 4 +) + +// vmRegionBasicInfo64 is the representation of `struct vm_region_basic_info_64` defined +// in xnu/osfmk/mach/vm_region.h. This is enclosed in `#pragma pack(push, 4)` in C, so +// unsafe.SizeOf() won't match the actual sizeof(vm_region_basic_info_64). +type vmRegionBasicInfo64 struct { + Protection vm_prot_t + MaxProtection vm_prot_t + Inheritance vm_inherit_t + Shared boolean_t + Reserved boolean_t + Offset memory_object_offset_t + Behavior vm_behavior_t + UserWiredCount uint16 +} + +var machVmRegion func( + vm_map_t, + *mach_vm_offset_t, /* IN/OUT */ + *mach_vm_size_t, /* OUT */ + vm_region_flavor_t, /* IN */ + vm_region_info_t, /* OUT */ + *mach_msg_type_number_t, /* IN/OUT */ + *mach_port_t, /* OUT */ +) kern_return_t + +func getMemoryUsage() (*machTaskBasicInfo, error) { + // The logic in here follows how the ps(1) utility determines the memory values. The + // basic_task_info command used here is a more modern, cross-architecture one that is + // suggested in the kernel header files. + // + // https://github.com/apple-oss-distributions/adv_cmds/blob/8744084ea0ff41ca4bb96b0f9c22407d0e48e9b7/ps/tasks.c#L132 + + info, err := getBasicTaskInfo() + + if err != nil { + return nil, err + } else if machVmRegion != nil { + + var textInfo vmRegionBasicInfo64 + + buf := make([]byte, vmRegionBasicInfo64SizeOf) + address := globalSharedTextSegment + var size mach_vm_size_t + var objectName mach_port_t + + cmd := vm_region_basic_info_64 + count := vmRegionBasicInfoCount64 + + ret := machVmRegion(machTaskSelf(), &address, &size, cmd, buf, &count, &objectName) + + if ret == 0 { + if err := loadStruct(buf, &textInfo); err == nil { + adjustment := sharedTextRegionSize + sharedDataRegionSize + if textInfo.Reserved != 0 && size == sharedTextRegionSize && info.VirtualSize > adjustment { + info.VirtualSize -= adjustment + } + } + } + } + + return info, nil +} + +func loadStruct(buffer []byte, data any) error { + r := bytes.NewReader(buffer) + + // TODO: NativeEndian was added in go 1.21 + return binary.Read(r, binary.LittleEndian, data) +} diff --git a/prometheus/process_collector_purego_darwin_test.go b/prometheus/process_collector_purego_darwin_test.go new file mode 100644 index 000000000..36fa49e2d --- /dev/null +++ b/prometheus/process_collector_purego_darwin_test.go @@ -0,0 +1,214 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build cgo && darwin + +package prometheus + +import ( + "bytes" + "encoding/binary" + "github.com/prometheus/client_golang/prometheus/testutil/purego" + "math" + "reflect" + "testing" +) + +// TestNativeTypeMatches ensures that const values for preprocessor macros, native struct +// sizes, and field offsets are in sync with the native code. +func TestNativeTypeMatches(t *testing.T) { + tests := []struct { + name string + want uint64 + got func() uint64 + }{ + { + "sizeof(machTaskBasicInfo)", + machTaskBasicInfoSizeOf, + purego.GetSizeofMachTaskBasicInfo, + }, + { + "sizeof(machTaskBasicInfo.VirtualSize)", + 8, + purego.GetSizeofMachTaskBasicInfo_virtual_size, + }, + { + "sizeof(vmRegionBasicInfo64.ResidentSize)", + 8, + purego.GetSizeofMachTaskBasicInfo_resident_size, + }, + { + "sizeof(vmRegionBasicInfo64)", + vmRegionBasicInfo64SizeOf, + purego.GetSizeofVmRegionBasicInfo64, + }, + { + "sizeof(vmRegionBasicInfo64.Reserved)", + 4, + purego.GetSizeofVmRegionBasicInfo64_reserved, + }, + { + "value of globalSharedTextSegment", + globalSharedTextSegment, + purego.GetGlobalSharedTextSegment, + }, + { + "value of sharedTextRegionSize", + sharedTextRegionSize, + purego.GetSharedTextRegionSize, + }, + { + "value of sharedDataRegionSize", + sharedDataRegionSize, + purego.GetSharedDataRegionSize, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.got() + if test.want != got { + t.Errorf("Expected %d, got %d\n", test.want, got) + } + }) + } +} + +// TestNativeStructMapping ensures that fields of a proper size and at the proper offset +// in a byte array can properly initialize a corresponding structure. Since the field +// offset in a Go struct may not match the offset in the C struct, the most robust test +// is to ensure that a field value written to a specific byte buffer offset is mapped to +// the corresponding Go struct field. The value should be the maximum, to ensure the +// entire byte range is mapped to the structure field. +func TestNativeStructMapping(t *testing.T) { + tests := []struct { + name string // The name of the test + field string // The structure field to test + value reflect.Value // The value of the field to set and validate + offset func() uint64 // The offset of the field in the native struct + fieldSize func() uint64 // The native width of the field in the struct + structSize func() uint64 // The native size of the C structure + dataType reflect.Type + }{ + { + "machTaskBasicInfo_VirtualSize", + "VirtualSize", + reflect.ValueOf(mach_vm_size_t(math.MaxUint64)), + purego.GetOffsetOfMachTaskBasicInfo_virtual_size, + purego.GetSizeofMachTaskBasicInfo_virtual_size, + purego.GetSizeofMachTaskBasicInfo, + reflect.TypeOf(machTaskBasicInfo{}), + }, + { + "machTaskBasicInfo_ResidentSize", + "ResidentSize", + reflect.ValueOf(mach_vm_size_t(math.MaxUint64)), + purego.GetOffsetOfMachTaskBasicInfo_resident_size, + purego.GetSizeofMachTaskBasicInfo_resident_size, + purego.GetSizeofMachTaskBasicInfo, + reflect.TypeOf(machTaskBasicInfo{}), + }, + { + "vmRegionBasicInfo64_Reserved", + "Reserved", + reflect.ValueOf(boolean_t(math.MaxInt32)), + purego.GetOffsetOfVmRegionBasicInfo64_reserved, + purego.GetSizeofVmRegionBasicInfo64_reserved, + purego.GetSizeofVmRegionBasicInfo64, + reflect.TypeOf(vmRegionBasicInfo64{}), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + offset := test.offset() + fieldSize := test.fieldSize() + structSize := test.structSize() + + buf := bytes.NewBuffer(make([]byte, 0, structSize)) + order := binary.LittleEndian + + // Move the position to the offset of the field + for i := uint64(0); i < offset; i++ { + var pad byte = 0 + if err := binary.Write(buf, order, pad); err != nil { + t.Error(err) + return + } + } + + // Write the test value at the desired offset. + if err := binary.Write(buf, order, test.value.Interface()); err != nil { + t.Error(err) + return + } + + // Zero fill the rest of the buffer to avoid a premature EOF. + for i := offset + fieldSize; i < structSize; i++ { + var pad byte = 0 + if err := binary.Write(buf, order, pad); err != nil { + t.Error(err) + return + } + } + + // Instantiate a new structure, decode the buffer into it, and then compare + // the structure field to the expected value. + ptr := reflect.New(test.dataType) + + if err := binary.Read(buf, order, ptr.Interface()); err != nil { + t.Error(err) + return + } + + field := ptr.Elem().FieldByName(test.field) + + if field.IsZero() { + t.Errorf("Missing field %s\n", test.field) + return + } + + if field.CanInt() { + got := field.Int() + + if got != test.value.Int() { + t.Errorf("Got %d, wanted %s\n", got, test.value) + return + } + } else if field.CanUint() { + got := field.Uint() + + if got != test.value.Uint() { + t.Errorf("Got %d, wanted %s\n", got, test.value) + return + } + } else { + t.Errorf("Unhandled field type: %s\n", field.Type()) + } + }) + } +} + +func TestSyscall(t *testing.T) { + // There's not a good way to validate that the value returned from the syscall is + // accurate, but we can ensure that the function pointer is non-nil, and that no + // error is returned. + + if taskInfo == nil { + t.Errorf("No task_info() method found\n") + } + + if _, err := getMemoryUsage(); err != nil { + t.Errorf("getMemoryUsage() failed with %v\n", err) + } +} diff --git a/prometheus/testutil/purego/process_collector_cgo_darwin.c b/prometheus/testutil/purego/process_collector_cgo_darwin.c new file mode 100644 index 000000000..312c1afa8 --- /dev/null +++ b/prometheus/testutil/purego/process_collector_cgo_darwin.c @@ -0,0 +1,80 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build cgo && darwin + +#include +#include +// Compiler warns shared_memory_server.h is deprecated, use this instead. +// But this doesn't define SHARED_DATA_REGION_SIZE and SHARED_TEXT_REGION_SIZE. +//#include +#include +#include +#include + + +size_t getGlobalSharedTextSegment() +{ + return GLOBAL_SHARED_TEXT_SEGMENT; +} +size_t getSharedTextRegionSize() +{ + return SHARED_TEXT_REGION_SIZE; +} +size_t getSharedDataRegionSize() +{ + return SHARED_DATA_REGION_SIZE; +} + +size_t getSizeofMachTaskBasicInfo() +{ + return sizeof(struct mach_task_basic_info); +} + +size_t getOffsetOfMachTaskBasicInfo_virtual_size() +{ + return offsetof(struct mach_task_basic_info, virtual_size); +} + +size_t getSizeofMachTaskBasicInfo_virtual_size() +{ + struct mach_task_basic_info info; + return sizeof(info.virtual_size); +} + +size_t getOffsetOfMachTaskBasicInfo_resident_size() +{ + return offsetof(struct mach_task_basic_info, resident_size); +} + +size_t getSizeofMachTaskBasicInfo_resident_size() +{ + struct mach_task_basic_info info; + return sizeof(info.resident_size); +} + +size_t getSizeofVmRegionBasicInfo64() +{ + return sizeof(struct vm_region_basic_info_64); +} + +size_t getOffsetOfVmRegionBasicInfo64_reserved() +{ + return offsetof(struct vm_region_basic_info_64, reserved); +} + +size_t getSizeofVmRegionBasicInfo64_reserved() +{ + struct vm_region_basic_info_64 data; + return sizeof(data.reserved); +} diff --git a/prometheus/testutil/purego/process_collector_cgo_darwin.go b/prometheus/testutil/purego/process_collector_cgo_darwin.go new file mode 100644 index 000000000..cf200f251 --- /dev/null +++ b/prometheus/testutil/purego/process_collector_cgo_darwin.go @@ -0,0 +1,77 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build cgo && darwin + +package purego + +/* +size_t getGlobalSharedTextSegment(); +size_t getSharedTextRegionSize(); +size_t getSharedDataRegionSize(); + +size_t getSizeofMachTaskBasicInfo(); +size_t getOffsetOfMachTaskBasicInfo_virtual_size(); +size_t getSizeofMachTaskBasicInfo_virtual_size(); +size_t getOffsetOfMachTaskBasicInfo_resident_size(); +size_t getSizeofMachTaskBasicInfo_resident_size(); + +size_t getSizeofVmRegionBasicInfo64(); +size_t getOffsetOfVmRegionBasicInfo64_reserved(); +size_t getSizeofVmRegionBasicInfo64_reserved(); +*/ +import "C" + +func GetGlobalSharedTextSegment() uint64 { + return uint64(C.getGlobalSharedTextSegment()) +} + +func GetSharedTextRegionSize() uint64 { + return uint64(C.getSharedTextRegionSize()) +} + +func GetSharedDataRegionSize() uint64 { + return uint64(C.getSharedDataRegionSize()) +} + +func GetSizeofMachTaskBasicInfo() uint64 { + return uint64(C.getSizeofMachTaskBasicInfo()) +} + +func GetOffsetOfMachTaskBasicInfo_virtual_size() uint64 { + return uint64(C.getOffsetOfMachTaskBasicInfo_virtual_size()) +} + +func GetSizeofMachTaskBasicInfo_virtual_size() uint64 { + return uint64(C.getSizeofMachTaskBasicInfo_virtual_size()) +} + +func GetOffsetOfMachTaskBasicInfo_resident_size() uint64 { + return uint64(C.getOffsetOfMachTaskBasicInfo_resident_size()) +} + +func GetSizeofMachTaskBasicInfo_resident_size() uint64 { + return uint64(C.getSizeofMachTaskBasicInfo_resident_size()) +} + +func GetSizeofVmRegionBasicInfo64() uint64 { + return uint64(C.getSizeofVmRegionBasicInfo64()) +} + +func GetOffsetOfVmRegionBasicInfo64_reserved() uint64 { + return uint64(C.getOffsetOfVmRegionBasicInfo64_reserved()) +} + +func GetSizeofVmRegionBasicInfo64_reserved() uint64 { + return uint64(C.getSizeofVmRegionBasicInfo64_reserved()) +}