ReadExternalTargetMemory reads bytes out of the target's address space:
process_vm_readv for live bytes (gated like ptrace access), falling back
to the target's on-disk image located by scanning /proc/<pid>/maps
directly - the caller runs while the symbol thread may be rebuilding the
shared image cache on refresh.
The resolver previously captured only the first pcinfo result, so inline
frames were lost, and reported ELF virtual addresses the server could
never re-map to the target. Mirror the in-process pcinfo+syminfo flow,
and report symAddr as the target VMA (loadBias + ELF vaddr) so the
server's re-queries can resolve it.
backtrace_create_state_for_file opens the file lazily on first use and,
on a failed open, silently falls back to /proc/self/exe - so a state
created from a path the monitor cannot open would symbolize the target's
addresses against the monitor's own binary. Probe the target's root path
(and, for unlinked images, the mapping's map_files entry), and only
create a state for a path that actually opens.
The list only ever added entries, so a mapping the target re-mapped or
unloaded stayed in the list and could shadow the new mapping or keep
serving an already-unloaded library. Rebuild it from the target's
current maps on each refresh, carrying over the cached backtrace state
for unchanged mappings and dropping entries the current maps no longer
confirm. The dropped entries' paths are abandoned rather than freed:
queued callstack items still carry the path pointer, which the worker
thread reads at its own pace. Strip the kernel's " (deleted)" suffix so
unlinked images converge to their canonical path.
The minVaddr heuristic assumes the mapping's file offset is measured
against the lowest PT_LOAD vaddr of the image. The kernel maps each
PT_LOAD independently, at load_bias + ELF_PAGESTART(p_vaddr) from
p_offset - ELF_PAGEOFFSET(p_vaddr) (fs/binfmt_elf.c, elf_map), so the
assumption only holds for the segment carrying the minimum vaddr. Match
the mapping to its PT_LOAD through the exact offset relation the kernel
used, and take the base from that segment's own vaddr; the minVaddr
heuristic remains as the fallback. The header must be ELF64
(e_ident[4]): the fixed-size elf_ehdr/elf_phdr structs misparse other
classes, and a false segment match on misparsed headers would return a
silently wrong base.
The image paths from /proc/<pid>/maps are only valid in the target's
mount namespace, so resolve them through /proc/<pid>/root (proc(5)). For
unlinked images the kernel keeps the bytes alive in the mapping's
map_files entry, which is opened directly instead.
SysTraceStart previously reported success even when no perf events
could be opened, leaving a worker thread running over no buffers
and an empty CPU section without explanation; it now fails in that
case. Also log when /proc/kallsyms cannot be read, which otherwise
silently results in ??? kernel stack frames.
The function is about to dispatch between rpmalloc and a pluggable allocator hook, so the rpmalloc-specific name no longer fits. Pure rename plus a small consequence: the SymbolWorker call site no longer needs the TRACY_USE_RPMALLOC guard, since the no-op static-inline fallback in TracyAlloc.hpp makes InitAllocator() safe to call unconditionally.
The end address is now readily available in lower_bound search, instead
of needing to be calculated constantly in the lambda.
The size can be equivalently calculated from the end address, but this
only happens once, after the symbol is found.
FindExternalImageRefresh already re-parsed /proc/<pid>/maps on miss,
but only one of three external decode paths used it. Switch the
DecodeCallstackPtrFastExternal and DecodeSymbolAddressExternal paths
over so symbol-name and file/line lookups stay fresh after the target
dlopens a library.
Rate-limit the re-parse to once per wall-clock second so samples
landing on permanently unresolvable regions (JIT, vDSO, stacks) do
not trigger a full parse each time.
On 32-bit arm, phdr.p_vaddr is 32-bit, which causes a compilation
error because std::min expects both arguments to be of the same
type. Adding the static cast handles this case explicitly.
This is often a source of missing symbols or incomprehension as to why they are not getting resolved. Having a debug log will help debugging such cases.
- Introduce both s_imageCache and s_krnlCache on all platforms, even if unused (will be reused later to unify platforms handling)
- This means that what userland images that used to be unsorted are now sorted
The name was a bit misleading as it could be mistaken to mean "The cache contains the address" and not as "has an image with this start address". ie: that it could be mistaken to do GetImageForAddress( startAddress ) != nullptr.
[`/DELAYLOAD`](https://learn.microsoft.com/en-us/cpp/build/reference/delayload-delay-load-import?view=msvc-170) only works with exported functions, not variables (as it patches thunks after the first load).
So instead of using exporting a pointer for `___tracy_RtlWalkFrameChain`, make it a function that will call said pointer. This only adds a `mov`+`jmp` instructions in optimized builds to the call, which is negligible compared to the actual cost of RtlWalkFrameChain. This also does not affect the reported callstack since it will use a `jmp` without touching the stack, so `___tracy_RtlWalkFrameChain` won't appear in the capture stacktrace.