Skip to content
Draft
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
4 changes: 2 additions & 2 deletions interpreter/instancestubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ package interpreter // import "go.opentelemetry.io/ebpf-profiler/interpreter"

import (
"go.opentelemetry.io/ebpf-profiler/host"
"go.opentelemetry.io/ebpf-profiler/libc"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/metrics"
"go.opentelemetry.io/ebpf-profiler/process"
"go.opentelemetry.io/ebpf-profiler/reporter"
"go.opentelemetry.io/ebpf-profiler/tpbase"
)

// InstanceStubs provides empty implementations of Instance hooks that are
Expand All @@ -22,7 +22,7 @@ func (is *InstanceStubs) SynchronizeMappings(EbpfHandler, reporter.ExecutableRep
return nil
}

func (is *InstanceStubs) UpdateTSDInfo(EbpfHandler, libpf.PID, tpbase.TSDInfo) error {
func (is *InstanceStubs) UpdateLibcInfo(EbpfHandler, libpf.PID, libc.LibcInfo) error {
return nil
}

Expand Down
8 changes: 4 additions & 4 deletions interpreter/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (

"go.opentelemetry.io/ebpf-profiler/host"
"go.opentelemetry.io/ebpf-profiler/internal/log"
"go.opentelemetry.io/ebpf-profiler/libc"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/metrics"
"go.opentelemetry.io/ebpf-profiler/process"
"go.opentelemetry.io/ebpf-profiler/remotememory"
"go.opentelemetry.io/ebpf-profiler/reporter"
"go.opentelemetry.io/ebpf-profiler/tpbase"
)

// MultiData implements the Data interface for multiple interpreters.
Expand Down Expand Up @@ -104,11 +104,11 @@ func (m *MultiInstance) SynchronizeMappings(ebpf EbpfHandler,
return errors.Join(errs...)
}

// UpdateTSDInfo updates TSD info for all interpreter instances.
func (m *MultiInstance) UpdateTSDInfo(ebpf EbpfHandler, pid libpf.PID, info tpbase.TSDInfo) error {
// UpdateLibcInfo updates libc info for all interpreter instances.
func (m *MultiInstance) UpdateLibcInfo(ebpf EbpfHandler, pid libpf.PID, info libc.LibcInfo) error {
var errs []error
for _, instance := range m.instances {
if err := instance.UpdateTSDInfo(ebpf, pid, info); err != nil {
if err := instance.UpdateLibcInfo(ebpf, pid, info); err != nil {
errs = append(errs, err)
}
}
Expand Down
13 changes: 6 additions & 7 deletions interpreter/perl/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import (

"go.opentelemetry.io/ebpf-profiler/host"
"go.opentelemetry.io/ebpf-profiler/interpreter"
"go.opentelemetry.io/ebpf-profiler/libc"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/libpf/pfunsafe"
"go.opentelemetry.io/ebpf-profiler/metrics"
npsr "go.opentelemetry.io/ebpf-profiler/nopanicslicereader"
"go.opentelemetry.io/ebpf-profiler/remotememory"
"go.opentelemetry.io/ebpf-profiler/successfailurecounter"
"go.opentelemetry.io/ebpf-profiler/support"
"go.opentelemetry.io/ebpf-profiler/tpbase"
"go.opentelemetry.io/ebpf-profiler/util"
)

Expand Down Expand Up @@ -73,9 +73,8 @@ func hashCOPKey(k copKey) uint32 {
return uint32(h ^ xxh3.HashString128(k.funcName.String()).Lo)
}

func (i *perlInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
tsdInfo tpbase.TSDInfo,
) error {
func (i *perlInstance) UpdateLibcInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
libcInfo libc.LibcInfo) error {
d := i.d
stateInTSD := uint8(0)
if d.stateInTSD {
Expand All @@ -88,9 +87,9 @@ func (i *perlInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID
StateInTSD: stateInTSD,

TsdInfo: support.TSDInfo{
Offset: tsdInfo.Offset,
Multiplier: tsdInfo.Multiplier,
Indirect: tsdInfo.Indirect,
Offset: libcInfo.TSDInfo.Offset,
Multiplier: libcInfo.TSDInfo.Multiplier,
Indirect: libcInfo.TSDInfo.Indirect,
},

Interpreter_curcop: uint16(vms.interpreter.curcop),
Expand Down
13 changes: 6 additions & 7 deletions interpreter/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import (

"go.opentelemetry.io/ebpf-profiler/host"
"go.opentelemetry.io/ebpf-profiler/interpreter"
"go.opentelemetry.io/ebpf-profiler/libc"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
"go.opentelemetry.io/ebpf-profiler/metrics"
npsr "go.opentelemetry.io/ebpf-profiler/nopanicslicereader"
"go.opentelemetry.io/ebpf-profiler/remotememory"
"go.opentelemetry.io/ebpf-profiler/successfailurecounter"
"go.opentelemetry.io/ebpf-profiler/support"
"go.opentelemetry.io/ebpf-profiler/tpbase"
"go.opentelemetry.io/ebpf-profiler/util"
)

Expand Down Expand Up @@ -371,19 +371,18 @@ func (p *pythonInstance) GetAndResetMetrics() ([]metrics.Metric, error) {
}, nil
}

func (p *pythonInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
tsdInfo tpbase.TSDInfo,
) error {
func (p *pythonInstance) UpdateLibcInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
libcInfo libc.LibcInfo) error {
d := p.d
vm := &d.vmStructs
cdata := support.PyProcInfo{
AutoTLSKeyAddr: uint64(d.autoTLSKey) + uint64(p.bias),
Version: d.version,

TsdInfo: support.TSDInfo{
Offset: tsdInfo.Offset,
Multiplier: tsdInfo.Multiplier,
Indirect: tsdInfo.Indirect,
Offset: libcInfo.TSDInfo.Offset,
Multiplier: libcInfo.TSDInfo.Multiplier,
Indirect: libcInfo.TSDInfo.Indirect,
},

PyThreadState_frame: uint8(vm.PyThreadState.Frame),
Expand Down
32 changes: 32 additions & 0 deletions interpreter/ruby/ruby.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package ruby // import "go.opentelemetry.io/ebpf-profiler/interpreter/ruby"

import (
"debug/elf"
"encoding/binary"
"errors"
"fmt"
Expand Down Expand Up @@ -92,6 +93,12 @@ type rubyData struct {
// Address to the ruby_current_ec variable in TLS, as an offset from tpbase
currentEcTpBaseTlsOffset libpf.Address

// Offset of the current EC within TLS for this module
currentEcTlsOffset libpf.Address

// TLS Module ID offset in ELF, to read mod ID after loaded and written by linker
tlsModuleIdOffset libpf.Address

// version of the currently used Ruby interpreter.
// major*0x10000 + minor*0x100 + release (e.g. 3.0.1 -> 0x30001)
version uint32
Expand Down Expand Up @@ -192,11 +199,19 @@ func (r *rubyData) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, bias libp
tlsOffset = rm.Uint64(bias + r.currentEcTpBaseTlsOffset + 8)
}

var modId uint64
if r.tlsModuleIdOffset != 0 {
modId = rm.Uint64(bias + r.tlsModuleIdOffset)
log.Debugf("Read TLS module as %d", modId)
}

cdata := support.RubyProcInfo{
Version: r.version,

Current_ctx_ptr: uint64(r.currentCtxPtr + bias),
Current_ec_tpbase_tls_offset: tlsOffset,
Current_ec_tls_offset: uint64(r.currentEcTlsOffset),
Tls_module_id: modId,

Vm_stack: r.vmStructs.execution_context_struct.vm_stack,
Vm_stack_size: r.vmStructs.execution_context_struct.vm_stack_size,
Expand Down Expand Up @@ -853,11 +868,28 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
log.Warnf("failed to locate TLS descriptor: %v", err)
}

var tlsModuleIdOffset libpf.Address
if err = ef.VisitRelocations(func(r pfelf.ElfReloc, symName string) bool {
// We've already verified that the relocation is DTPMOD type
// so if we get here, we should be able to read the module ID from this offset
log.Debugf("Found module id at %x", r.Off)
tlsModuleIdOffset = libpf.Address(r.Off)
return false
}, func(rela pfelf.ElfReloc) bool {
ty := rela.Info & 0xffff
return (ef.Machine == elf.EM_AARCH64 && elf.R_AARCH64(ty) == elf.R_AARCH64_TLS_DTPMOD64) ||
(ef.Machine == elf.EM_X86_64 && elf.R_X86_64(ty) == elf.R_X86_64_DTPMOD64)
}); err != nil {
log.Warnf("failed to mod offset: %v", err)
}

log.Debugf("Discovered EC tls tpbase offset %x, fallback ctx %x, interp ranges: %v", currentEcTpBaseTlsOffset, currentCtxPtr, interpRanges)

rid := &rubyData{
version: version,
currentEcTpBaseTlsOffset: libpf.Address(currentEcTpBaseTlsOffset),
tlsModuleIdOffset: tlsModuleIdOffset,
currentEcTlsOffset: libpf.Address(currentEcSymbolAddress),
currentCtxPtr: libpf.Address(currentCtxPtr),
}

Expand Down
6 changes: 3 additions & 3 deletions interpreter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
"unsafe"

"go.opentelemetry.io/ebpf-profiler/host"
"go.opentelemetry.io/ebpf-profiler/libc"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/lpm"
"go.opentelemetry.io/ebpf-profiler/metrics"
"go.opentelemetry.io/ebpf-profiler/process"
"go.opentelemetry.io/ebpf-profiler/remotememory"
"go.opentelemetry.io/ebpf-profiler/reporter"
"go.opentelemetry.io/ebpf-profiler/tpbase"
"go.opentelemetry.io/ebpf-profiler/util"
)

Expand Down Expand Up @@ -143,9 +143,9 @@ type Instance interface {
SynchronizeMappings(ebpf EbpfHandler, exeReporter reporter.ExecutableReporter,
pr process.Process, mappings []process.Mapping) error

// UpdateTSDInfo is called when the process C-library Thread Specific Data related
// UpdateLibcInfo is called when the process C-library related
// introspection data has been updated.
UpdateTSDInfo(ebpf EbpfHandler, pid libpf.PID, info tpbase.TSDInfo) error
UpdateLibcInfo(ebpf EbpfHandler, pid libpf.PID, info libc.LibcInfo) error

// Symbolize converts one ebpf frame to one or more (if inlining was expanded) libpf.Frame.
// The resulting libpf.Frame values are appended to frames.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"

import (
"errors"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tpbase
package libc

import (
"debug/elf"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"

import (
"bytes"
Expand Down
55 changes: 54 additions & 1 deletion tpbase/libc.go → libc/libc.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"

import (
"debug/elf"
Expand All @@ -11,6 +11,17 @@ import (
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
)

type LibcInfo struct {
DTVInfo *DTVInfo
TSDInfo *TSDInfo
}

type DTVInfo struct {
Offset int32 // Offset of DTV from FS base (or from thread pointer)
EntryWidth uint32 // Width of each DTV entry in bytes
Indirect uint8 // 0 if DTV is at FS+offset, 1 if at [FS+0]+offset
}

// TSDInfo contains information to access C-library's Thread Specific Data from eBPF
type TSDInfo struct {
// Offset is the pointer difference from "tpbase" pointer to the C-library
Expand Down Expand Up @@ -84,6 +95,23 @@ func IsPotentialTSDDSO(filename string) bool {
return libcRegex.MatchString(filename)
}

func ExtractLibcInfo(ef *pfelf.File) (*LibcInfo, error) {
tsdinfo, err := ExtractTSDInfo(ef)
if err != nil {
return nil, err
}

dtvinfo, err := extractDTVInfo(ef)
if err != nil {
return nil, err
}

return &LibcInfo{
TSDInfo: tsdinfo,
DTVInfo: dtvinfo,
}, nil
}

// ExtractTSDInfo extracts the introspection data for pthread thread specific data.
func ExtractTSDInfo(ef *pfelf.File) (*TSDInfo, error) {
_, code, err := ef.SymbolData("__pthread_getspecific", 2048)
Expand Down Expand Up @@ -111,3 +139,28 @@ func ExtractTSDInfo(ef *pfelf.File) (*TSDInfo, error) {
}
return &info, nil
}

// extractDTVInfo extracts the introspection data for the DTV to access TLS vars
func extractDTVInfo(ef *pfelf.File) (*DTVInfo, error) {
_, code, err := ef.SymbolData("__tls_get_addr", 2048)
if err != nil {
return nil, fmt.Errorf("unable to read '__tls_get_addr': %s", err)
}
if len(code) < 8 {
return nil, fmt.Errorf("__tls_get_addr function size is %d", len(code))
}

var info DTVInfo
switch ef.Machine {
case elf.EM_AARCH64:
info, err = extractDTVInfoARM(code)
case elf.EM_X86_64:
info, err = extractDTVInfoX86(code)
default:
return nil, fmt.Errorf("unsupported arch %s", ef.Machine.String())
}
if err != nil {
return nil, fmt.Errorf("failed to extract DTV data: %s", err)
}
return &info, nil
}
Loading
Loading