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 ¶
- func ReadBuildID(path string) (string, error)
- type AddressMapper
- type Mapping
- type Option
- type Resolver
- func (r *Resolver) BuildID(path string) string
- func (r *Resolver) Close()
- func (r *Resolver) Invalidate(pid uint32)
- func (r *Resolver) InvalidateAddr(pid uint32, addr uint64)
- func (r *Resolver) Lookup(pid uint32, addr uint64) (Mapping, bool)
- func (r *Resolver) Mappings(pid uint32) ([]Mapping, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ReadBuildID ¶
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
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 ¶
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 ¶
NewResolver returns a Resolver ready for concurrent use.
func (*Resolver) BuildID ¶
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 ¶
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 ¶
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 ¶
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
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).