On Fri, May 17, 2024 at 12:06 PM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote: > > As a preparatory step for introducing public API for BTF topological > ordering / single type printing, this commit removes ordering logic > from btf_dump_emit_type() and moves parts necessary to compute forward > declarations to btf_dump_order_type(). > > Before this commit the topological ordering of types was effectively > done twice: > - first time in btf_dump_order_type(), which ordered types using only > "strong" links (one structure embedded in another); > - second time in btf_dump_emit_type() to emit forward declarations. > > After this commit: > - btf_dump_emit_type() is responsible only for printing > declaration / forward declaration of a single type; > - btf_dump->emit_queue now contains pairs of form > {id, forward declaration flag}; > - btf_dump->emit_state is no longer necessary, > as EMITTED state is effectively replaced by ORDERED state > in btf_dump->order_state; > > Notable changes to btf_dump_order_type() logic: > - no need to return strong / weak result, emit forward declaration to > the queue for weak links instead; > - track containing type id ('cont_id'), as btf_dump_emit_type() did, > to avoid unnecessary forward declarations in recursive structs; > - PTRs can no longer be marked ORDERED (see comment in the code); > - incorporate btf_dump_emit_missing_aliases() and > btf_dump_is_blacklisted() checks from btf_dump_emit_type(). > > When called for e.g. PTR type pointing to a struct > btf_dump__dump_type() would previously result in an empty emit queue: > btf_dump_order_type() would have reached struct with > 'through_ptr' == true, thus not adding it to the queue. > To mimic such behavior this patch adds a type filter to > btf_dump__dump_type(): only STRUCT, UNION, TYPEDEF, ENUM, ENUM64, FWD > types are ordered. > > The downside of a single pass algorithm is that for the following > situation old logic would have avoided extra forward declaration: > > struct bar {}; > > struct foo { /* Suppose btf_dump__dump_type(foo) */ > struct bar *a; /* is called first. */ > struct bar b; > }; > > The btf_dump_order_type() would have ordered 'bar' before 'foo', > btf_dump_emit_type() would have been first called for 'bar' thus > avoiding forward declaration for 'sturct bar' when processing 'foo'. > > In contrast, new logic would act as follows: > - when processing foo->a forward declaration for 'bar' would be enqueued; > - when processing foo->b full declaration for 'bar' would be enqueued; > > In practice this does not seem to be a big issue, number of forward > declarations in vmlinux.h (for BPF selftests config) compared: > llvm gcc > - before this patch: 1772 1235 > - after this patch: 1786 1249 > > Signed-off-by: Eduard Zingerman <eddyz87@xxxxxxxxx> > --- > tools/lib/bpf/btf_dump.c | 351 ++++++++++++++++++--------------------- > 1 file changed, 161 insertions(+), 190 deletions(-) > > diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c > index 5dbca76b953f..10532ae9ff14 100644 > --- a/tools/lib/bpf/btf_dump.c > +++ b/tools/lib/bpf/btf_dump.c > @@ -36,24 +36,16 @@ enum btf_dump_type_order_state { > ORDERED, > }; > > -enum btf_dump_type_emit_state { > - NOT_EMITTED, > - EMITTING, > - EMITTED, > -}; > - > /* per-type auxiliary state */ > struct btf_dump_type_aux_state { > /* topological sorting state */ > enum btf_dump_type_order_state order_state: 2; > - /* emitting state used to determine the need for forward declaration */ > - enum btf_dump_type_emit_state emit_state: 2; > - /* whether forward declaration was already emitted */ > - __u8 fwd_emitted: 1; > /* whether unique non-duplicate name was already assigned */ > __u8 name_resolved: 1; > /* whether type is referenced from any other type */ > __u8 referenced: 1; > + /* whether forward declaration was already ordered */ > + __u8 fwd_ordered: 1; > }; > > /* indent string length; one indent string is added for each indent level */ > @@ -93,7 +85,10 @@ struct btf_dump { > size_t cached_names_cap; > > /* topo-sorted list of dependent type definitions */ > - __u32 *emit_queue; > + struct { > + __u32 id:31; > + __u32 fwd:1; > + } *emit_queue; let's define the named type right in this patch, no need to use typeof() hack just to remove it later. Also, let's maybe have struct <whatever> { __u32 id; __u32 flags; }; and define enum btf_dump_emit_flags { BTF_DUMP_FWD_DECL = 0x1, }; Or something along those lines? Having a few more flags available will make it less like that we'll need to add a new set of APIs just to accommodate one extra flag. (Though, if we add another field, we'll end up adding another API anyways, but I really hope we will never have to do this). > int emit_queue_cap; > int emit_queue_cnt; > > @@ -208,7 +203,6 @@ static int btf_dump_resize(struct btf_dump *d) > if (d->last_id == 0) { > /* VOID is special */ > d->type_states[0].order_state = ORDERED; > - d->type_states[0].emit_state = EMITTED; > } > > /* eagerly determine referenced types for anon enums */ > @@ -255,8 +249,8 @@ void btf_dump__free(struct btf_dump *d) > free(d); > } > > -static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr); > -static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id); > +static int btf_dump_order_type(struct btf_dump *d, __u32 id, __u32 cont_id, bool through_ptr); > +static void btf_dump_emit_type(struct btf_dump *d, __u32 id, bool fwd); > > /* > * Dump BTF type in a compilable C syntax, including all the necessary > @@ -276,6 +270,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id); > */ > int btf_dump__dump_type(struct btf_dump *d, __u32 id) > { > + const struct btf_type *t; > int err, i; > > if (id >= btf__type_cnt(d->btf)) > @@ -286,12 +281,23 @@ int btf_dump__dump_type(struct btf_dump *d, __u32 id) > return libbpf_err(err); > > d->emit_queue_cnt = 0; > - err = btf_dump_order_type(d, id, false); > - if (err < 0) > - return libbpf_err(err); > + t = btf_type_by_id(d->btf, id); > + switch (btf_kind(t)) { > + case BTF_KIND_STRUCT: > + case BTF_KIND_UNION: > + case BTF_KIND_TYPEDEF: > + case BTF_KIND_ENUM: > + case BTF_KIND_ENUM64: > + case BTF_KIND_FWD: > + err = btf_dump_order_type(d, id, id, false); > + if (err < 0) > + return libbpf_err(err); > + default: > + break; > + }; > > for (i = 0; i < d->emit_queue_cnt; i++) > - btf_dump_emit_type(d, d->emit_queue[i], 0 /*top-level*/); > + btf_dump_emit_type(d, d->emit_queue[i].id, d->emit_queue[i].fwd); > > return 0; > } > @@ -374,9 +380,9 @@ static int btf_dump_mark_referenced(struct btf_dump *d) > return 0; > } > > -static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id) > +static int __btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id, bool fwd) I don't like those underscored functions in libbpf code base, please don't add them. But I'm also not sure we need to have it, there are only a few calles of the original btf_dump_add_emit_queue_id(), so we can just update them to pass true/false as appropriate. > { > - __u32 *new_queue; > + typeof(d->emit_queue[0]) *new_queue = NULL; > size_t new_cap; > > if (d->emit_queue_cnt >= d->emit_queue_cap) { > @@ -388,10 +394,45 @@ static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id) > d->emit_queue_cap = new_cap; > } > > - d->emit_queue[d->emit_queue_cnt++] = id; > + d->emit_queue[d->emit_queue_cnt].id = id; > + d->emit_queue[d->emit_queue_cnt].fwd = fwd; > + d->emit_queue_cnt++; > return 0; > } > > +static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id) > +{ > + return __btf_dump_add_emit_queue_id(d, id, false); > +} > + > +static int btf_dump_add_emit_queue_fwd(struct btf_dump *d, __u32 id) > +{ > + struct btf_dump_type_aux_state *tstate = &d->type_states[id]; > + > + if (tstate->fwd_ordered) > + return 0; > + > + tstate->fwd_ordered = 1; > + return __btf_dump_add_emit_queue_id(d, id, true); > +} > + see above, do we really need these wrappers, passing true/false in a few places doesn't seem to be a big deal > +static bool btf_dump_emit_missing_aliases(struct btf_dump *d, __u32 id, bool dry_run); > + > +static bool btf_dump_is_blacklisted(struct btf_dump *d, __u32 id) > +{ > + const struct btf_type *t = btf__type_by_id(d->btf, id); > + > + /* __builtin_va_list is a compiler built-in, which causes compilation > + * errors, when compiling w/ different compiler, then used to compile > + * original code (e.g., GCC to compile kernel, Clang to use generated > + * C header from BTF). As it is built-in, it should be already defined > + * properly internally in compiler. > + */ > + if (t->name_off == 0) > + return false; > + return strcmp(btf_name_of(d, t->name_off), "__builtin_va_list") == 0; > +} > + why moving btf_dump_is_blacklisted() but forward declaring btf_dump_emit_missing_aliases()? Let's do the same to both, whichever it is (forward declaring probably is least distracting here) > /* > * Determine order of emitting dependent types and specified type to satisfy > * C compilation rules. This is done through topological sorting with an > @@ -441,32 +482,33 @@ static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id) > * The rule is as follows. Given a chain of BTF types from X to Y, if there is > * BTF_KIND_PTR type in the chain and at least one non-anonymous type > * Z (excluding X, including Y), then link is weak. Otherwise, it's strong. > - * Weak/strong relationship is determined recursively during DFS traversal and > - * is returned as a result from btf_dump_order_type(). > + * Weak/strong relationship is determined recursively during DFS traversal. > + * > + * When type id is reached via a weak link a forward declaration for > + * that type is added to the emit queue, otherwise "full" declaration > + * is added to the emit queue. > + * > + * We also keep track of "containing struct/union type ID" to determine when > + * we reference it from inside and thus can avoid emitting unnecessary forward > + * declaration. > * > * btf_dump_order_type() is trying to avoid unnecessary forward declarations, > * but it is not guaranteeing that no extraneous forward declarations will be > * emitted. > * > * To avoid extra work, algorithm marks some of BTF types as ORDERED, when > - * it's done with them, but not for all (e.g., VOLATILE, CONST, RESTRICT, > - * ARRAY, FUNC_PROTO), as weak/strong semantics for those depends on the > + * it's done with them, but not for all (e.g., PTR, VOLATILE, CONST, RESTRICT, > + * ARRAY, FUNC_PROTO, TYPEDEF), as weak/strong semantics for those depends on the > * entire graph path, so depending where from one came to that BTF type, it > * might cause weak or strong ordering. For types like STRUCT/UNION/INT/ENUM, > * once they are processed, there is no need to do it again, so they are > - * marked as ORDERED. We can mark PTR as ORDERED as well, as it semi-forces > - * weak link, unless subsequent referenced STRUCT/UNION/ENUM is anonymous. But > - * in any case, once those are processed, no need to do it again, as the > - * result won't change. > + * marked as ORDERED. > * > * Returns: > - * - 1, if type is part of strong link (so there is strong topological > - * ordering requirements); > - * - 0, if type is part of weak link (so can be satisfied through forward > - * declaration); > + * - 0, on success; > * - <0, on error (e.g., unsatisfiable type loop detected). > */ > -static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) > +static int btf_dump_order_type(struct btf_dump *d, __u32 id, __u32 cont_id, bool through_ptr) > { > /* > * Order state is used to detect strong link cycles, but only for BTF > @@ -486,48 +528,78 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) > > /* return true, letting typedefs know that it's ok to be emitted */ > if (tstate->order_state == ORDERED) > - return 1; > + return 0; > > t = btf__type_by_id(d->btf, id); > > if (tstate->order_state == ORDERING) { > /* type loop, but resolvable through fwd declaration */ > - if (btf_is_composite(t) && through_ptr && t->name_off != 0) > - return 0; > + if (btf_is_composite(t) && through_ptr && t->name_off != 0) { > + if (id != cont_id) > + return btf_dump_add_emit_queue_fwd(d, id); > + else > + return 0; > + } > pr_warn("unsatisfiable type cycle, id:[%u]\n", id); > return -ELOOP; > } > > switch (btf_kind(t)) { > case BTF_KIND_INT: > + tstate->order_state = ORDERED; > + if (btf_dump_emit_missing_aliases(d, id, true)) > + return btf_dump_add_emit_queue_id(d, id); > + else > + return 0; > case BTF_KIND_FLOAT: > tstate->order_state = ORDERED; > return 0; > > case BTF_KIND_PTR: > - err = btf_dump_order_type(d, t->type, true); > - tstate->order_state = ORDERED; > + /* Depending on whether pointer is a part of a recursive struct > + * declaration, it might not necessitate generation of a forward > + * declaration for the target type, e.g.: > + * > + * struct foo { > + * struct foo *p; // no need for forward declaration > + * } > + * > + * struct bar { > + * struct foo *p; // forward declaration is needed > + * } > + * > + * Hence, don't mark pointer as ORDERED, to allow traversal > + * to the target type and comparison with 'cont_id'. > + */ > + err = btf_dump_order_type(d, t->type, cont_id, true); > return err; > > case BTF_KIND_ARRAY: > - return btf_dump_order_type(d, btf_array(t)->type, false); > + return btf_dump_order_type(d, btf_array(t)->type, cont_id, false); > > case BTF_KIND_STRUCT: > case BTF_KIND_UNION: { > const struct btf_member *m = btf_members(t); > + __u32 new_cont_id; > + > /* > * struct/union is part of strong link, only if it's embedded > * (so no ptr in a path) or it's anonymous (so has to be > * defined inline, even if declared through ptr) > */ > - if (through_ptr && t->name_off != 0) > - return 0; > + if (through_ptr && t->name_off != 0) { > + if (id != cont_id) > + return btf_dump_add_emit_queue_fwd(d, id); > + else > + return 0; very subjective nit, but this "else return 0;" just doesn't feel right here. Let's do: if (id == cont_id) return 0; return btf_dump_add_emit_queue_fwd(); It feels a bit more natural as "if it's a special nice case, we are done (return 0); otherwise we need to emit extra fwd decl." > + } > > tstate->order_state = ORDERING; > > + new_cont_id = t->name_off == 0 ? cont_id : id; > vlen = btf_vlen(t); > for (i = 0; i < vlen; i++, m++) { > - err = btf_dump_order_type(d, m->type, false); > + err = btf_dump_order_type(d, m->type, new_cont_id, false); just inline `t->name_off ? id : cont_id` here? It's short and straightforward enough, I suppose (named type defines new containing "scope", anonymous type continues existing scope) > if (err < 0) > return err; > } > @@ -539,7 +611,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) > } > > tstate->order_state = ORDERED; > - return 1; > + return 0; > } > case BTF_KIND_ENUM: > case BTF_KIND_ENUM64: > @@ -555,51 +627,53 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) > return err; > } > tstate->order_state = ORDERED; > - return 1; > - > - case BTF_KIND_TYPEDEF: { > - int is_strong; > - > - is_strong = btf_dump_order_type(d, t->type, through_ptr); > - if (is_strong < 0) > - return is_strong; > + return 0; > > - /* typedef is similar to struct/union w.r.t. fwd-decls */ > - if (through_ptr && !is_strong) > + case BTF_KIND_TYPEDEF: > + /* Do not mark typedef as ORDERED, always emit a forward declaration for > + * it instead. Otherwise the following situation would be troublesome: > + * > + * typedef struct foo foo_alias; > + * > + * struct foo {}; > + * > + * struct root { > + * foo_alias *a; > + * foo_alias b; > + * }; > + * > + */ > + if (btf_dump_is_blacklisted(d, id)) > return 0; > > - /* typedef is always a named definition */ > - err = btf_dump_add_emit_queue_id(d, id); > - if (err) > + err = btf_dump_order_type(d, t->type, t->type, through_ptr); > + if (err < 0) > return err; > > - d->type_states[id].order_state = ORDERED; > - return 1; > - } > + err = btf_dump_add_emit_queue_fwd(d, id); > + if (err) > + return err; > + return 0; return btf_dump_add_emit_queue_fwd(...); ? this is the last step, so seems appropriate > case BTF_KIND_VOLATILE: > case BTF_KIND_CONST: > case BTF_KIND_RESTRICT: > case BTF_KIND_TYPE_TAG: > - return btf_dump_order_type(d, t->type, through_ptr); > + return btf_dump_order_type(d, t->type, cont_id, through_ptr); > > case BTF_KIND_FUNC_PROTO: { > const struct btf_param *p = btf_params(t); > - bool is_strong; > > - err = btf_dump_order_type(d, t->type, through_ptr); > + err = btf_dump_order_type(d, t->type, cont_id, through_ptr); > if (err < 0) > return err; > - is_strong = err > 0; > > vlen = btf_vlen(t); > for (i = 0; i < vlen; i++, p++) { > - err = btf_dump_order_type(d, p->type, through_ptr); > + err = btf_dump_order_type(d, p->type, cont_id, through_ptr); > if (err < 0) > return err; > - if (err > 0) > - is_strong = true; > } > - return is_strong; > + return err; this should always be zero, right? Just return zero explicit, don't make reader to guess > } > case BTF_KIND_FUNC: > case BTF_KIND_VAR: > @@ -613,9 +687,6 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) > } > } > > -static void btf_dump_emit_missing_aliases(struct btf_dump *d, __u32 id, > - const struct btf_type *t); > - > static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id, > const struct btf_type *t); > static void btf_dump_emit_struct_def(struct btf_dump *d, __u32 id, > @@ -649,73 +720,33 @@ static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id); > static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, > const char *orig_name); > > -static bool btf_dump_is_blacklisted(struct btf_dump *d, __u32 id) > -{ > - const struct btf_type *t = btf__type_by_id(d->btf, id); > - > - /* __builtin_va_list is a compiler built-in, which causes compilation > - * errors, when compiling w/ different compiler, then used to compile > - * original code (e.g., GCC to compile kernel, Clang to use generated > - * C header from BTF). As it is built-in, it should be already defined > - * properly internally in compiler. > - */ > - if (t->name_off == 0) > - return false; > - return strcmp(btf_name_of(d, t->name_off), "__builtin_va_list") == 0; > -} > - > /* > * Emit C-syntax definitions of types from chains of BTF types. > * > - * High-level handling of determining necessary forward declarations are handled > - * by btf_dump_emit_type() itself, but all nitty-gritty details of emitting type > + * All nitty-gritty details of emitting type > * declarations/definitions in C syntax are handled by a combo of > * btf_dump_emit_type_decl()/btf_dump_emit_type_chain() w/ delegation to > * corresponding btf_dump_emit_*_{def,fwd}() functions. > * > - * We also keep track of "containing struct/union type ID" to determine when > - * we reference it from inside and thus can avoid emitting unnecessary forward > - * declaration. > - * > * This algorithm is designed in such a way, that even if some error occurs > * (either technical, e.g., out of memory, or logical, i.e., malformed BTF > * that doesn't comply to C rules completely), algorithm will try to proceed > * and produce as much meaningful output as possible. > */ > -static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) > +static void btf_dump_emit_type(struct btf_dump *d, __u32 id, bool fwd) > { > - struct btf_dump_type_aux_state *tstate = &d->type_states[id]; > - bool top_level_def = cont_id == 0; > const struct btf_type *t; > __u16 kind; > > - if (tstate->emit_state == EMITTED) > - return; > - > t = btf__type_by_id(d->btf, id); > kind = btf_kind(t); > > - if (tstate->emit_state == EMITTING) { > - if (tstate->fwd_emitted) > - return; > - > + if (fwd) { > switch (kind) { > case BTF_KIND_STRUCT: > case BTF_KIND_UNION: > - /* > - * if we are referencing a struct/union that we are > - * part of - then no need for fwd declaration > - */ > - if (id == cont_id) > - return; > - if (t->name_off == 0) { > - pr_warn("anonymous struct/union loop, id:[%u]\n", > - id); > - return; > - } > btf_dump_emit_struct_fwd(d, id, t); > btf_dump_printf(d, ";\n\n"); > - tstate->fwd_emitted = 1; > break; > case BTF_KIND_TYPEDEF: > /* > @@ -723,11 +754,8 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) > * was emitted, but it can be used only for "weak" > * references through pointer only, not for embedding > */ > - if (!btf_dump_is_blacklisted(d, id)) { > - btf_dump_emit_typedef_def(d, id, t, 0); > - btf_dump_printf(d, ";\n\n"); > - } > - tstate->fwd_emitted = 1; > + btf_dump_emit_typedef_def(d, id, t, 0); > + btf_dump_printf(d, ";\n\n"); > break; > default: > break; > @@ -739,36 +767,18 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) > switch (kind) { > case BTF_KIND_INT: > /* Emit type alias definitions if necessary */ > - btf_dump_emit_missing_aliases(d, id, t); > - > - tstate->emit_state = EMITTED; > + btf_dump_emit_missing_aliases(d, id, false); > break; > case BTF_KIND_ENUM: > case BTF_KIND_ENUM64: > - if (top_level_def) { > - btf_dump_emit_enum_def(d, id, t, 0); > - btf_dump_printf(d, ";\n\n"); > - } > - tstate->emit_state = EMITTED; > - break; > - case BTF_KIND_PTR: > - case BTF_KIND_VOLATILE: > - case BTF_KIND_CONST: > - case BTF_KIND_RESTRICT: > - case BTF_KIND_TYPE_TAG: > - btf_dump_emit_type(d, t->type, cont_id); > - break; > - case BTF_KIND_ARRAY: > - btf_dump_emit_type(d, btf_array(t)->type, cont_id); > + btf_dump_emit_enum_def(d, id, t, 0); > + btf_dump_printf(d, ";\n\n"); > break; > case BTF_KIND_FWD: > btf_dump_emit_fwd_def(d, id, t); > btf_dump_printf(d, ";\n\n"); > - tstate->emit_state = EMITTED; > break; > case BTF_KIND_TYPEDEF: > - tstate->emit_state = EMITTING; > - btf_dump_emit_type(d, t->type, id); > /* > * typedef can server as both definition and forward > * declaration; at this stage someone depends on > @@ -776,55 +786,14 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) > * through pointer), so unless we already did it, > * emit typedef as a forward declaration > */ > - if (!tstate->fwd_emitted && !btf_dump_is_blacklisted(d, id)) { > - btf_dump_emit_typedef_def(d, id, t, 0); > - btf_dump_printf(d, ";\n\n"); > - } > - tstate->emit_state = EMITTED; > + btf_dump_emit_typedef_def(d, id, t, 0); > + btf_dump_printf(d, ";\n\n"); > break; > case BTF_KIND_STRUCT: > case BTF_KIND_UNION: > - tstate->emit_state = EMITTING; > - /* if it's a top-level struct/union definition or struct/union > - * is anonymous, then in C we'll be emitting all fields and > - * their types (as opposed to just `struct X`), so we need to > - * make sure that all types, referenced from struct/union > - * members have necessary forward-declarations, where > - * applicable > - */ > - if (top_level_def || t->name_off == 0) { > - const struct btf_member *m = btf_members(t); > - __u16 vlen = btf_vlen(t); > - int i, new_cont_id; > - > - new_cont_id = t->name_off == 0 ? cont_id : id; > - for (i = 0; i < vlen; i++, m++) > - btf_dump_emit_type(d, m->type, new_cont_id); > - } else if (!tstate->fwd_emitted && id != cont_id) { > - btf_dump_emit_struct_fwd(d, id, t); > - btf_dump_printf(d, ";\n\n"); > - tstate->fwd_emitted = 1; > - } > - > - if (top_level_def) { > - btf_dump_emit_struct_def(d, id, t, 0); > - btf_dump_printf(d, ";\n\n"); > - tstate->emit_state = EMITTED; > - } else { > - tstate->emit_state = NOT_EMITTED; > - } > - break; > - case BTF_KIND_FUNC_PROTO: { > - const struct btf_param *p = btf_params(t); > - __u16 n = btf_vlen(t); > - int i; > - > - btf_dump_emit_type(d, t->type, cont_id); > - for (i = 0; i < n; i++, p++) > - btf_dump_emit_type(d, p->type, cont_id); > - > + btf_dump_emit_struct_def(d, id, t, 0); > + btf_dump_printf(d, ";\n\n"); > break; > - } > default: > break; > } > @@ -1037,19 +1006,21 @@ static const char *missing_base_types[][2] = { > { "__Poly128_t", "unsigned __int128" }, > }; > > -static void btf_dump_emit_missing_aliases(struct btf_dump *d, __u32 id, > - const struct btf_type *t) > +static bool btf_dump_emit_missing_aliases(struct btf_dump *d, __u32 id, bool dry_run) this dry_run approach look like a sloppy hack, tbh. Maybe just return `const char *` of aliased name, and let caller handle whatever is necessary (either making decision about the need for alias or actually emitting `typedef %s %s`? > { > const char *name = btf_dump_type_name(d, id); > int i; > > for (i = 0; i < ARRAY_SIZE(missing_base_types); i++) { > - if (strcmp(name, missing_base_types[i][0]) == 0) { > + if (strcmp(name, missing_base_types[i][0]) != 0) > + continue; > + if (!dry_run) > btf_dump_printf(d, "typedef %s %s;\n\n", > missing_base_types[i][1], name); > - break; > - } > + return true; > } > + > + return false; > } > > static void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id, > -- > 2.34.1 >