Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions remotememory/remotememory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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}}
}
29 changes: 29 additions & 0 deletions remotememory/remotememory_linux.go
Original file line number Diff line number Diff line change
@@ -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
}
17 changes: 17 additions & 0 deletions remotememory/remotememory_other.go
Original file line number Diff line number Diff line change
@@ -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)
}
12 changes: 8 additions & 4 deletions remotememory/remotememory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"errors"
"os"
"runtime"
"syscall"
"testing"
"unsafe"
Expand All @@ -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)
}
Expand All @@ -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())))
}