Re: [PATCH v1] perf trace: BTF-based enum pretty printing

From: Howard Chu
Date: Tue Jun 11 2024 - 21:52:11 EST


[Resend because of the HTML error]

Hello Arnaldo,
Thanks a lot for the review, I guess you call it v1 for a reason. :)
> > +
> > + id = btf__find_by_name(btf, type);
>
> int id = ...

Do you want me to do the initialization in the middle of the function
body sir? A little reminder, char* pointer 'type' has to be shifted to
the first non-enum-prefix location to do the btf__find_by_name().

>
> > + if (id < 0)
>
> Shouldn't we have some warning here? ok, I see you do it later, in
> trace__read_syscall_info().

I'm sorry, could you be more specific please? To my understanding, it
is syscall__scnprintf_args() who called btf_enum_scnprintf(), and I
did the error handling(or fallback) by calling
syscall_arg_fmt__scnprintf_val(). It's like:

if btf_enum_scnprintf() returns non-0 // success
continue;
else // error
syscall_arg_fmt__scnprintf_val()

So we fall back to just printing the long value.

>
> Also I looked at the btf_enum_scnprintf() caller and if this isn't found
> nothing is printed, it is better to fallback to printing the integer
> value, as done in other parts, see:

Do you think the code below could be seen as a sort of fallback
mechanism? If nothing is printed, btf_enum_scnprintf() returns a 0, we
continue to do a syscall_arg_fmt__scnprintf_val() as the fallback. I
tested it by putting return 0 at the top of btf_enum_scnprintf(), and
it works, although not so straightforward... Maybe I should put the
fallback straight in btf_enum_scnprintf().

> > + if (sc->arg_fmt[arg.idx].is_enum == true && trace->btf) {
> > + p = btf_enum_scnprintf(bf + printed, size - printed, val,
> > + trace->btf, field->type);
> > + if (p) {
> > + printed += p;
> > + continue;
> > + }
> > + }
> > +
> > printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx],
> > bf + printed, size - printed, &arg, val);

>
> size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_prefix, int val)
>
> That intfmt is configurable to show hex or decimal and is used when the
> 'val' isn't found in the strarray, so we should use the same approach
> with BTF.
>
> > + return 0;
> > +
> > + bt = btf__type_by_id(btf, id);
> > + e = btf_enum(bt);
>
> Declare 'bt' and 'e' here
>
> > +
> > + for (int i = 0; i < btf_vlen(bt); i++, e++) {
> > + if (e->val == val)
> > + return scnprintf(bf, size, "%s",
> > + btf__name_by_offset(btf, e->name_off));

you mean doing it like this?
```
for (bt = btf__type_by_id(btf, id), e = btf_enum(bt), i = 0;
i < btf_vlen(bt); i++, e++) {
if (e->val == val) {
return scnprintf(bf, size, "%s",
btf__name_by_offset(btf, e->name_off));
}
}
```

> This is shaping up super nicely, great!

:) Thank you so much sir.

>
> I'm pushing the simplified first patch to my tmp.perf-tools-next branch
> in my tree at:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tmp.perf-tools-next
>
> https://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git/log/?h=tmp.perf-tools-next

Sure, I'll pull from it and build the enum support on top of that.

>
> Some more comments below.
>
> > if (last_field)
> > sc->args_size = last_field->offset + last_field->size;
> > @@ -1811,6 +1854,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)
> > char tp_name[128];
> > struct syscall *sc;
> > const char *name = syscalltbl__name(trace->sctbl, id);
> > + int err;
> >
> > #ifdef HAVE_SYSCALL_TABLE_SUPPORT
> > if (trace->syscalls.table == NULL) {
> > @@ -1883,7 +1927,17 @@ static int trace__read_syscall_info(struct trace *trace, int id)
> > sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit");
> > sc->is_open = !strcmp(name, "open") || !strcmp(name, "openat");
> >
> > - return syscall__set_arg_fmts(sc);
> > + err = syscall__set_arg_fmts(sc);
>
> > + /* after calling syscall__set_arg_fmts() we'll know whether use_btf is true */
> > + if (sc->use_btf && trace->btf == NULL) {
> > + trace->btf = btf__load_vmlinux_btf();
> > + if (verbose > 0)
> > + fprintf(trace->output, trace->btf ? "vmlinux BTF loaded\n" :
> > + "Failed to load vmlinux BTF\n");
> > + }
>
> One suggestion here is to get the body of the above if and have it in a
> trace__load_vmlinux_btf(), as that call and the test under verbose will
> be used in other places, for instance, when supporting using BTF to
> pretty print non-syscall tracepoints.
>
> This function probably will grow to support detached BTF, possibly that
> idea about generating BTF from the scrape scripts, etc.

Sure.

Thank you very much for reviewing this patch, v2 is coming up.

Thanks,
Howard

>
> Thanks,
>
> - Arnaldo
>
> > + return err;
> > }
> >
> > static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
> > @@ -2050,7 +2104,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
> > unsigned char *args, void *augmented_args, int augmented_args_size,
> > struct trace *trace, struct thread *thread)
> > {
> > - size_t printed = 0;
> > + size_t printed = 0, p;
> > unsigned long val;
> > u8 bit = 1;
> > struct syscall_arg arg = {
> > @@ -2103,6 +2157,15 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
> > if (trace->show_arg_names)
> > printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
> >
> > }
> > --
> > 2.45.2