diff --git a/lisa/_assets/kmodules/lisa/introspect_header.py b/lisa/_assets/kmodules/lisa/introspect_header.py index eedd77ac064710cc9ca50f083d15a188a705d3d4..bce6ee5a60f4b827b3e9368ac490c40efe8330ad 100755 --- a/lisa/_assets/kmodules/lisa/introspect_header.py +++ b/lisa/_assets/kmodules/lisa/introspect_header.py @@ -22,7 +22,7 @@ import abc import sys import itertools import argparse -from collections import namedtuple, deque +from collections import namedtuple, deque, defaultdict import functools import json import re @@ -80,8 +80,20 @@ def process_btf(out, path, introspect, internal_type_prefix, define_typ_names): else: return rename(name) + tagged = { + cls: defaultdict(set) + for cls in (btf.BTFStruct, btf.BTFUnion, btf.BTFEnum) + } + fwd_decls = set() + for typ in reachable_typs: - if isinstance(typ, btf.BTFEnum): + for _cls, _typs in tagged.items(): + if isinstance(typ, _cls): + _typs[typ.name].add(typ) + + if isinstance(typ, btf.BTFForwardDecl): + fwd_decls.add(typ) + elif isinstance(typ, btf.BTFEnum): typ.enumerators = { ( name @@ -90,11 +102,36 @@ def process_btf(out, path, introspect, internal_type_prefix, define_typ_names): ): value for name, value in typ.enumerators.items() } + + resolved = {} + for typ in fwd_decls: + for _cls, _typs in tagged.items(): + if issubclass(typ.typ_cls, _cls): + try: + (target,) = _typs[typ.name] + except (KeyError, ValueError): + pass + else: + resolved[typ] = target + break + + # Resolve all the forward declarations we can to the type they point to. + # This prevents arbitrary breakage of code in pointer chains spanning + # multiple structs that were not explicitly asked by the user. + # + # The main risk is e.g. "struct foo;" forward decl being matched with a + # "struct foo {...};" that was done in another file and has nothing to do + # with it. But as long as we only print a subset of kernel types, we are + # unlikely to run into that, or at least less likely to run into that than + # running into broken pointer chains because of forward decls. + btf.BTFType.map_typs(lambda typ: resolved.get(typ, typ), reachable_typs) + + for typ in reachable_typs: if isinstance(typ, named_classes) and typ.name: typ.name = rename_internal_typ(typ) # Dump declaration for all the types we are interested in - btf.dump_c(define_typs, fileobj=out, introspection=False, decls=True) + btf.dump_c(reachable_typs, fileobj=out, introspection=False, decls=True) def is_exported_symbol(code): diff --git a/lisa/_btf.py b/lisa/_btf.py index a10e0d31cfa6f60a5904888496d0a3b01670528d..44805a31e5fddafd818887011a00a7752dfd9aac 100644 --- a/lisa/_btf.py +++ b/lisa/_btf.py @@ -133,6 +133,12 @@ class BTFType(metaclass=_BTFTypeMeta): typ._map_typs(lambda x: x, visited=reachable_typs) return reachable_typs + @classmethod + def map_typs(cls, f, typs): + visited = set() + for typ in typs: + typ._map_typs(f, visited=visited) + def _map_typs(self, f, visited): if self in visited: return @@ -176,8 +182,8 @@ class _CDecl: def _do_dump_c_decls(self, ctx): pass - def _dump_c_decls(self, ctx, memoize=True, **kwargs): - if memoize: + def _dump_c_decls(self, ctx, **kwargs): + if kwargs.get('memoize', True): try: x = ctx._memo[self] except KeyError: @@ -477,7 +483,7 @@ class _BTFStructUnion(_CDecl, BTFType): for member in self.members: member._dump_c_introspection(ctx) - def _do_dump_c_decls(self, ctx, anonymous=False): + def _do_dump_c_decls(self, ctx, anonymous=False, memoize=True): members = self._all_members size = self.size kind = self._KIND @@ -732,21 +738,27 @@ class BTFStruct(_BTFStructUnion): @property def alignment(self): - alignment = self._alignment - if alignment is None: - max_alignment = self._max_alignment - # We have something weird going on, like a struct manually padded using - # anonymous bitfields at the end. - self._alignment = self._min_alignment if self.size % max_alignment else max_alignment - return self._alignment + size = self.size + max_alignment = self._max_alignment + + # We have something weird going on, like a struct manually padded using + # anonymous bitfields at the end. + if size % max_alignment: + return 1 else: - return alignment + alignment = self._alignment + if alignment is None: + # We have something weird going on, like a struct manually padded using + # anonymous bitfields at the end. + self._alignment = self._min_alignment if self.size % max_alignment else max_alignment + return self._alignment + else: + return alignment @property def _align_attribute(self): members = self.members size = self.size - alignment = self.alignment min_alignment = self._min_alignment max_alignment = self._max_alignment @@ -776,9 +788,10 @@ class BTFStruct(_BTFStructUnion): ) align = None else: + alignment = self.alignment padding = None align = f'aligned({alignment})' if alignment > min_alignment else '' - packed = '' + packed = None attrs = ','.join(attr for attr in (packed, align) if attr) return ( @@ -1735,11 +1748,11 @@ def _dedup_names(typs): for typ in typs: if isinstance(typ, BTFTypedef): cat = typedef_names - # BTFForwardDecl are not renamed since we don't know what they - # logically point to. It could be any of the types that share that - # name, or yet another unknown. Fortunately, they are kind of useless - # since we will create any actually needed forward decl when dumping C - # code. + # BTFForwardDecl are renamed independently from the type they declare + # since we don't know what they logically point to. It could be any of + # the types that share that name, or yet another unknown. Fortunately, + # they are kind of useless since we will create any actually needed + # forward decl when dumping C code. elif isinstance(typ, (BTFStruct, BTFUnion, BTFEnum)): cat = tagged_names # We still dedup names there, in case they end up being printed and