diff --git a/remotememory/remotememory.go b/remotememory/remotememory.go index 00ca1ed71..ed371155f 100644 --- a/remotememory/remotememory.go +++ b/remotememory/remotememory.go @@ -9,11 +9,8 @@ package remotememory // import "go.opentelemetry.io/ebpf-profiler/remotememory" import ( "bytes" "encoding/binary" - "fmt" "io" - "golang.org/x/sys/unix" - "go.opentelemetry.io/ebpf-profiler/libpf" ) @@ -122,30 +119,13 @@ func (rm RemoteMemory) StringPtr(addr libpf.Address) string { return rm.String(addr) } -// ProcessVirtualMemory implements RemoteMemory by using process_vm_readv syscalls +// ProcessVirtualMemory implements ReaderAt by using process_vm_readv syscalls // to read the remote memory. type ProcessVirtualMemory struct { pid libpf.PID } -func (vm ProcessVirtualMemory) ReadAt(p []byte, off int64) (int, error) { - numBytesWanted := len(p) - if numBytesWanted == 0 { - return 0, nil - } - localIov := []unix.Iovec{{Base: &p[0], Len: uint64(numBytesWanted)}} - remoteIov := []unix.RemoteIovec{{Base: uintptr(off), Len: numBytesWanted}} - numBytesRead, err := unix.ProcessVMReadv(int(vm.pid), localIov, remoteIov, 0) - if err != nil { - err = fmt.Errorf("failed to read PID %v at 0x%x: %w", vm.pid, off, err) - } else if numBytesRead != numBytesWanted { - err = fmt.Errorf("failed to read PID %v at 0x%x: got only %d of %d", - vm.pid, off, numBytesRead, numBytesWanted) - } - return numBytesRead, err -} - -// NewRemoteMemory returns ProcessVirtualMemory implementation of RemoteMemory. +// NewProcessVirtualMemory returns RemoteMemory with ProcessVirtualMemory as the underlying reader func NewProcessVirtualMemory(pid libpf.PID) RemoteMemory { return RemoteMemory{ReaderAt: ProcessVirtualMemory{pid}} } diff --git a/remotememory/remotememory_linux.go b/remotememory/remotememory_linux.go new file mode 100644 index 000000000..9fa21d425 --- /dev/null +++ b/remotememory/remotememory_linux.go @@ -0,0 +1,29 @@ +//go:build linux + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package remotememory // import "go.opentelemetry.io/ebpf-profiler/remotememory" + +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +func (vm ProcessVirtualMemory) ReadAt(p []byte, off int64) (int, error) { + numBytesWanted := len(p) + if numBytesWanted == 0 { + return 0, nil + } + localIov := []unix.Iovec{{Base: &p[0], Len: uint64(numBytesWanted)}} + remoteIov := []unix.RemoteIovec{{Base: uintptr(off), Len: numBytesWanted}} + numBytesRead, err := unix.ProcessVMReadv(int(vm.pid), localIov, remoteIov, 0) + if err != nil { + err = fmt.Errorf("failed to read PID %v at 0x%x: %w", vm.pid, off, err) + } else if numBytesRead != numBytesWanted { + err = fmt.Errorf("failed to read PID %v at 0x%x: got only %d of %d", + vm.pid, off, numBytesRead, numBytesWanted) + } + return numBytesRead, err +} diff --git a/remotememory/remotememory_other.go b/remotememory/remotememory_other.go new file mode 100644 index 000000000..5107a86b1 --- /dev/null +++ b/remotememory/remotememory_other.go @@ -0,0 +1,17 @@ +//go:build !linux + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package remotememory // import "go.opentelemetry.io/ebpf-profiler/remotememory" + +import ( + "fmt" + "runtime" +) + +// ReadAt is the stub implementation, allowing to compile the remotememory +// package on non linux systems, always failing at runtime with an error if used. +func (vm ProcessVirtualMemory) ReadAt(_ []byte, _ int64) (int, error) { + return 0, fmt.Errorf("unsupported os %s", runtime.GOOS) +} diff --git a/remotememory/remotememory_test.go b/remotememory/remotememory_test.go index fcbfa0ab6..f3e5d5403 100644 --- a/remotememory/remotememory_test.go +++ b/remotememory/remotememory_test.go @@ -7,6 +7,7 @@ import ( "bytes" "errors" "os" + "runtime" "syscall" "testing" "unsafe" @@ -19,14 +20,14 @@ import ( func RemoteMemTests(t *testing.T, rm RemoteMemory) { data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} - dataPtr := libpf.Address(uintptr(unsafe.Pointer(&data[0]))) + dataPtr := libpf.Address(unsafe.Pointer(&data[0])) str := []byte("this is a string\x00") - strPtr := libpf.Address(uintptr(unsafe.Pointer(&str[0]))) + strPtr := libpf.Address(unsafe.Pointer(&str[0])) longStr := append(bytes.Repeat([]byte("long test string"), 4095/16), 0x00) - longStrPtr := libpf.Address(uintptr(unsafe.Pointer(&longStr[0]))) + longStrPtr := libpf.Address(unsafe.Pointer(&longStr[0])) foo := make([]byte, len(data)) - err := rm.Read(libpf.Address(uintptr(unsafe.Pointer(&data))), foo) + err := rm.Read(libpf.Address(unsafe.Pointer(&data)), foo) if errors.Is(err, syscall.ENOSYS) { t.Skipf("skipping due to error: %v", err) } @@ -38,5 +39,8 @@ func RemoteMemTests(t *testing.T, rm RemoteMemory) { } func TestProcessVirtualMemory(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skipf("unsupported os %s", runtime.GOOS) + } RemoteMemTests(t, NewProcessVirtualMemory(libpf.PID(os.Getpid()))) }