Re: [PATCH v4 4/5] perf script: Add --lazy-load-symbols for lazy symbol loading

From: Alireza Haghdoost

Date: Fri Oct 02 2026 - 19:53:17 EST


On Fri, Oct 2, 2026 at 3:39 PM Ian Rogers <irogers@xxxxxxxxxx> wrote:
>
> > +
> > +struct dso_ondemand {
> > + struct dso *data_dso;
>
> Could you add kernel doc for dso_ondemand? I believe it is keeping
> state while resolving symbols, but the name makes it sound more
> persistent.
>

Yes, will do. It is persistent per-DSO state that holds the compact index
and symbol source used for lazy address resolution. It remains attached
until name sorting materializes all symbols, or until the DSO is destroyed
during teardown.

I'll add kernel-doc documenting its purpose, lifetime and locking. I can also
rename it to dso_lazy_symbols if that makes the lifetime clearer.


> > struct symbol *map__find_symbol(struct map *map, u64 addr)
> > {
> > + struct dso *dso;
> > +
> > if (map__load(map) < 0)
> > return NULL;
> >
> > - return dso__find_symbol(map__dso(map), addr);
> > + dso = map__dso(map);
> > + if (symbol_conf.lazy_load_symbols)
> > + return dso__find_symbol_ondemand(dso, addr);
> > + return dso__find_symbol(dso, addr);
>
> Why not do this logic in dso__find_symbol?
>

Good point. Will do.

> > +/* Return the number of entries starting at or before @addr. */
> > +static u32 sym_idx__upper_bound(const struct dso_ondemand *od, u64 addr)
> > +{
> > + u32 lo = 0, hi = od->nr_sorted;
> > +
> > + while (lo < hi) {
> > + u32 mid = lo + (hi - lo) / 2;
> > +
> > + if (od->sorted[mid].start <= addr)
> > + lo = mid + 1;
> > + else
> > + hi = mid;
> > + }
> > + return lo;
> > +}
>
> Would the upper and lower bound only matter if symbols were
> duplicated? I'm wondering if we can use bsearch?
>

Not only for duplicates. Lower bound finds the first start >= addr, while
upper bound finds the first start > addr.

For example:

outer: [100, 200)
inner: [120, 150)

At address 130, a range-based bsearch() may return either symbol because both
contain it. Upper bound selects inner, and at address 160 its outer link leads
back to outer. At the exact start 120, upper bound is also needed so that
decrementing the result selects inner rather than outer.

Duplicate-start aliases are normally collapsed by sym_idx__dedup_aliases();
they remain only with allow_aliases. The lower-bound callers also need an
insertion point, which bsearch() cannot provide.

I can combine the two loops into one helper parameterized by lower versus
upper bound and document this behavior.


> > +static bool sym_idx__from_sym(struct symsrc *syms_ss,
> > + struct symsrc *runtime_ss, Elf_Data *secstrs,
> > + bool dynsym, const struct sym_idx *symtab,
> > + u32 nr, const size_t *strndx,
> > + const GElf_Sym *sym, struct sym_idx *idx)
> > +{
> > + Elf *elf = syms_ss->elf;
> > + GElf_Shdr shdr = dynsym ? syms_ss->dynshdr : syms_ss->symshdr;
> > + u16 e_machine = syms_ss->ehdr.e_machine;
> > + u64 adjusted = sym->st_value;
> > + GElf_Phdr phdr;
> > +
> > + if (!ondemand_sym_ok(elf, secstrs, sym, shdr.sh_link, e_machine))
> > + return false;
> > +
> > + if (e_machine == EM_ARM && GELF_ST_TYPE(sym->st_info) == STT_FUNC &&
> > + (adjusted & 1))
> > + --adjusted;
>
> Worth a comment that this is stripping of the thumb/not-thumb indicator bit.
>

Ack. I'll add the same explanation used by the eager loader: ARM Thumb
function symbols have bit 0 set as an indicator, so the index strips that bit
from the address.

Thanks,
Alireza