On Thu, Feb 11, 2021 at 11:59:02AM -0800, Andrii Nakryiko wrote: SNIP > > return strcmp(a->name, b->name); > > } > > > > +static int functions_cmp_addr(const void *_a, const void *_b) > > +{ > > + const struct elf_function *a = _a; > > + const struct elf_function *b = _b; > > + > > + if (a->addr == b->addr) > > + return 0; > > + return a->addr < b->addr ? -1 : 1; > > +} > > + > > static void delete_functions(void) > > { > > free(functions); > > @@ -98,6 +109,7 @@ static int collect_function(struct btf_elf *btfe, GElf_Sym *sym, > > > > functions[functions_cnt].name = name; > > functions[functions_cnt].addr = elf_sym__value(sym); > > + functions[functions_cnt].end = (__u64) -1; > > functions[functions_cnt].sh_addr = sh.sh_addr; > > functions[functions_cnt].generated = false; > > functions_cnt++; > > @@ -236,6 +248,40 @@ get_kmod_addrs(struct btf_elf *btfe, __u64 **paddrs, __u64 *pcount) > > return 0; > > } > > > > +static int is_ftrace_func(struct elf_function *func, __u64 *addrs, > > return bool, not int? yep > > > + __u64 count, bool kmod) > > +{ > > + /* > > + * For vmlinux image both addrs[x] and functions[x]::addr > > + * values are final address and are comparable. > > + * > > + * For kernel module addrs[x] is final address, but > > + * functions[x]::addr is relative address within section > > + * and needs to be relocated by adding sh_addr. > > + */ > > + __u64 start = kmod ? func->addr + func->sh_addr : func->addr; > > + __u64 end = kmod ? func->end + func->sh_addr : func->end; > > + > > + size_t l = 0, r = count - 1, m; > > + __u64 addr = 0; > > + > > + while (l < r) { > > + m = l + (r - l + 1) / 2; > > + addr = addrs[m]; > > + > > + if (start <= addr && addr < end) > > + return true; > > this extra check on each step shouldn't be necessary > > > + > > + if (start <= addr) > > I don't think this is correct, start == addr is actually a good case, > but you'll do r = m - 1, skipping it. See below about invariants. the == case is covered in the check above, but yes it should be < > > > + r = m - 1; > > + else > > + l = m; > > So in my previous example I assumed we have address ranges for ftrace > section, which is exactly the opposite from what we have. So this > binary search should be a bit different. start <= addr seems wrong > here as well. > > The invariant here should be that addr[r] is the smallest address that > is >= than function start addr, right? Except the corner case where > there is no such r, but for that we have a final check in the return > below. If you wanted to use index l, you'd need to change the > invariant to find the largest addr, such that it is < end, but that > seems a bit convoluted. > > So, with that, I think it should be like this: > > size_t l = 0, r = count - 1, m; > > /* make sure we don't use invalid r */ > if (count == 0) return false; > > while (l < r) { > /* note no +1 in this case, it's so that at the end, when you > * have, say, l = 0, and r = 1, you try l first, not r. > * Otherwise you might end in in the infinite loop when r never == l. > */ nice catch, did not see that > m = l + (r - l) / 2; > addr = addrs[m]; > > if (addr >= start) > /* we satisfy invariant, so tighten r */ > r = m; > else > /* m is not good enough as l, maybe m + 1 will be */ > l = m + 1; > } > > return start <= addrs[r] && addrs[r] < end; > > > So, basically, r is maintained as a valid index always, while we > constantly try to tighten the l. > > Does this make sense? I did not see the possibility to let the search go through all the way to l == r and that 'extra' check allowed me to split the interval, without caring about invariant but I think your solution is cleaner, I'll send new version > > > > + } > > + > > + addr = addrs[l]; > > + return start <= addr && addr < end; > > +} > > + > > static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl) > > { > > __u64 *addrs, count, i; > > @@ -267,7 +313,7 @@ static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl) > > } > > > > qsort(addrs, count, sizeof(addrs[0]), addrs_cmp); > > - qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp); > > + qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp_addr); > > See below assumptions about function end. If we get it from ELF, you > don't need to do this extra sort, right? I had the same assumption and was surprised why my code is not working ;-) > > > > > /* > > * Let's got through all collected functions and filter > > @@ -275,18 +321,12 @@ static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl) > > */ > > for (i = 0; i < functions_cnt; i++) { > > struct elf_function *func = &functions[i]; > > - /* > > - * For vmlinux image both addrs[x] and functions[x]::addr > > - * values are final address and are comparable. > > - * > > - * For kernel module addrs[x] is final address, but > > - * functions[x]::addr is relative address within section > > - * and needs to be relocated by adding sh_addr. > > - */ > > - __u64 addr = kmod ? func->addr + func->sh_addr : func->addr; > > + > > + if (i + 1 < functions_cnt) > > + func->end = functions[i + 1].addr; > > This makes a bunch of unnecessary assumptions about functions layout. > But why, if we have STT_FUNC symbol with function size, so that we > know the function end right when we collect function info. ugh forgot about the st_size for STT_FUNC thanks, jirka