procmap

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package procmap resolves addresses into per-binary mapping identity (path, start/limit, file offset, build-id) by parsing /proc/<pid>/maps and ELF .note.gnu.build-id sections. Results feed pprof.Mapping so downstream tools can round-trip samples back to ELF file offsets.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReadBuildID

func ReadBuildID(path string) (string, error)

ReadBuildID returns the GNU build-id of the ELF at path as a lowercase hex string. Returns an empty string (with nil error) when the ELF is valid but has no .note.gnu.build-id note, and an error when the file can't be opened or isn't ELF.

Types

type AddressMapper added in v1.2.0

type AddressMapper struct {
	// contains filtered or unexported fields
}

AddressMapper is a port of pfelf.AddressMapper from github.com/open-telemetry/opentelemetry-ebpf-profiler (libpf/pfelf/addressmapper.go) — Apache-2.0, used per §4 of the license. Original copyright: Elasticsearch B.V. / OpenTelemetry Authors.

Maps a file offset within an ELF to the virtual address that offset would have in the running image, following the kernel's mmap alignment of PT_LOAD p_offset to the page boundary. Used to convert file-relative addresses (from /proc/<pid>/maps) into ELF-relative virtual addresses for symbolization.

func NewAddressMapper added in v1.2.0

func NewAddressMapper(path string) (*AddressMapper, error)

NewAddressMapper reads PHDRs from the ELF at path and returns a mapper for its executable PT_LOAD segments. Callers should choose a path that's readable from the agent's namespace — typically mapping.OpenablePath() which prefers /proc/<pid>/map_files/<va>-<va>.

func (*AddressMapper) FileOffsetToVirtualAddress added in v1.2.0

func (m *AddressMapper) FileOffsetToVirtualAddress(off uint64) (uint64, bool)

FileOffsetToVirtualAddress maps a file offset to its ELF virtual address. Returns (0, false) if the offset is outside every executable PT_LOAD.

The accepted range is [alignedOffset, p.offset+p.filesz) where alignedOffset = p.offset &^ (pageSize-1). The asymmetric bounds mirror what the kernel does at mmap time: it rounds p_offset DOWN to a page boundary when creating the mapping, so file offsets in the pre-segment padding [alignedOffset, p.offset) are part of the live mapping even though they precede the declared start. The upper bound stays at the declared end so we don't claim bytes past p_filesz.

The math vaddr + (off - p.offset) (equivalent to vaddr - (p.offset - off)) reproduces the correct virtual address for every offset in that range:

  • off == p.offset → vaddr (un-aligned start maps to un-aligned VA)
  • off == alignedOffset → vaddr - (p.offset - alignedOffset)
  • off in (p.offset, end) → vaddr + positive delta

Note: for off < p.Off the subexpression (off - p.Off) wraps under uint64 arithmetic; the wrap cancels when added to p.Vaddr, leaving the correct VA modulo 2^64 (which is what an address is).

type Mapping

type Mapping struct {
	Path string
	// MapFiles is /proc/<pid>/map_files/<start>-<limit>. Present even when
	// the symbolic Path is unreachable from the agent's mount namespace
	// (sidecar / deleted-binary cases). Empty when /proc/<pid>/map_files
	// is restricted by the kernel.
	MapFiles string
	Start    uint64
	Limit    uint64 // exclusive
	Offset   uint64 // p_offset of the backing PT_LOAD segment
	BuildID  string // hex; empty if no .note.gnu.build-id
	IsExec   bool
}

Mapping describes one executable range in a process's address space. Non-executable and anonymous ranges are dropped during parsing.

func (Mapping) OpenablePath added in v1.2.0

func (m Mapping) OpenablePath() string

OpenablePath returns the first openable path: MapFiles (preferred — works across mount namespaces and survives unlinked-but-mapped binaries) then the symbolic Path. Returns "" when neither is readable.

Note: the probe (open + close) and the caller's subsequent use of the returned path are not atomic. The file may be unlinked or become unreadable between the two. Callers must still handle errors from os.Open (or equivalent) on the returned path themselves.

type Option

type Option func(*resolverConfig)

Option configures a Resolver.

func WithProcRoot

func WithProcRoot(path string) Option

WithProcRoot overrides the filesystem root used to resolve /proc paths. Defaults to "/proc". Intended for unit tests with fake per-PID fixtures.

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver caches per-PID /proc/<pid>/maps snapshots and per-path build-ids. Safe for concurrent use. Populates lazily on first Lookup for a PID.

func NewResolver

func NewResolver(opts ...Option) *Resolver

NewResolver returns a Resolver ready for concurrent use.

func (*Resolver) BuildID

func (r *Resolver) BuildID(path string) string

BuildID returns a cached hex build-id for path, reading the ELF on first call. Read failures produce an empty string (cached) — a missing build-id is not a Lookup failure. Safe for concurrent use.

func (*Resolver) Close

func (r *Resolver) Close()

Close releases cached state. After Close, the Resolver remains usable but behaves as freshly constructed; in-flight Lookups that captured a *pidEntry before the call complete normally against their captured snapshot.

func (*Resolver) Invalidate

func (r *Resolver) Invalidate(pid uint32)

Invalidate drops any cached state for pid. The next Lookup re-parses /proc/<pid>/maps. Call on process exit or when the agent learns of whole-process churn (e.g., exec).

func (*Resolver) InvalidateAddr

func (r *Resolver) InvalidateAddr(pid uint32, addr uint64)

InvalidateAddr invalidates pid's cache only if addr falls outside every currently cached mapping — i.e., a new mmap extended the process's address space. Cheap no-op otherwise.

func (*Resolver) Lookup

func (r *Resolver) Lookup(pid uint32, addr uint64) (Mapping, bool)

Lookup returns the Mapping containing addr in pid's address space. Returns ok=false when pid has no cached (or resolvable) mappings, or when addr falls outside every known executable range.

func (*Resolver) Mappings added in v1.2.0

func (r *Resolver) Mappings(pid uint32) ([]Mapping, error)

Mappings returns a snapshot of pid's executable mappings, populating the per-PID cache on first call. The returned slice aliases the cached state — callers MUST NOT mutate it.

Return contract:

  • (nil, nil) — PID has no mappings: process gone, access restricted, or the maps file contained no executable regions.
  • (nil, err) — /proc parse failed (I/O error, unexpected format, etc.)
  • (mappings, nil) — success; slice may be empty if no executable regions were found (same observable result as the nil-nil case above).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL