def setup_page_type(cls, gdbtype: gdb.Type) -> None: # TODO: should check config, but that failed to work on ppc64, hardcode # 64k for now if crash.current_target().arch.name() == "powerpc:common64": cls.PAGE_SHIFT = 16 # also a config cls.directmap_base = 0xc000000000000000 cls.sparsemem = True cls.sparsemem_vmemmap = False cls.sparsemem_extreme = False cls.SECTION_SIZE_BITS = 24 cls.PAGE_SIZE = 1 << cls.PAGE_SHIFT cls.PAGE_MASK = ~(cls.PAGE_SIZE - 1) cls.slab_cache_name = find_member_variant(gdbtype, ['slab_cache', 'lru']) cls.slab_page_name = find_member_variant(gdbtype, ['slab_page', 'lru']) cls.compound_head_name = find_member_variant( gdbtype, ['compound_head', 'first_page']) if not hasattr(cls, 'vmemmap'): cls.vmemmap = gdb.Value(cls.vmemmap_base).cast(gdbtype.pointer()) if struct_has_member(gdbtype, 'page_type'): cls._is_buddy = cls.__is_buddy_page_type else: cls._is_buddy = cls.__is_buddy_mapcount cls.setup_page_type_done = True if cls.setup_pageflags_done and not cls.setup_pageflags_finish_done: cls.setup_pageflags_finish()
def _check_struct_request(request_s: gdb.Type) -> None: global _rq_in_flight if struct_has_member(request_s, 'rq_state'): def _rq_in_flight(request: gdb.Value) -> bool: return (request['rq_state'] != types.enum_mq_rq_state_type['MQ_RQ_IDLE']) elif struct_has_member(request_s, 'atomic_flags'): def _rq_in_flight(request: gdb.Value) -> bool: return (request['atomic_flags'] & (1 << int( types.enum_rq_atomic_flags_type['REQ_ATOM_STARTED'].enumval)) != 0) else: def _rq_in_flight(request: gdb.Value) -> bool: return request['cmd_flags'] & REQ_STARTED != 0 # type: ignore
def _pick_get_rss(cls) -> None: if struct_has_member(types.mm_struct_type, 'rss'): cls._get_rss = cls._get_rss_field elif struct_has_member(types.mm_struct_type, '_rss'): cls._get_rss = cls._get__rss_field elif struct_has_member(types.mm_struct_type, 'rss_stat'): cls._get_rss = cls._get_rss_stat_field else: if struct_has_member(types.mm_struct_type, '_file_rss'): cls._anon_file_rss_fields.append('_file_rss') if struct_has_member(types.mm_struct_type, '_anon_rss'): cls._anon_file_rss_fields.append('_anon_rss') cls._get_rss = cls._get_anon_file_rss_fields if not cls._anon_file_rss_fields: raise RuntimeError("No method to retrieve RSS from task found.")
def _setup_dynamic_offset_cache(self) -> None: # TODO: interval tree would be more efficient, but this adds no 3rd # party module dependency... use_area_map = struct_has_member(types.pcpu_chunk_type, 'map') for slot in range(symvals.pcpu_nr_slots): for chunk in list_for_each_entry(symvals.pcpu_slot[slot], types.pcpu_chunk_type, 'list'): if use_area_map: self._setup_dynamic_offset_cache_area_map(chunk) else: self._setup_dynamic_offset_cache_bitmap(chunk)
def execute(self, args: argparse.Namespace) -> None: regex = None print_header = True if args.args: regex = re.compile(fnmatch.translate(args.args[0])) core_layout = None for mod in for_each_module(): if core_layout is None: core_layout = struct_has_member(mod.type, 'core_layout') modname = mod['name'].string() if regex: m = regex.match(modname) if m is None: continue if args.p is not None: if print_header: print_header = False if args.p == -1: print("Module\t\t\tPercpu Base\t\tSize") else: print("Module\t\t\tPercpu Base@CPU{:d}\t\tSize".format( args.p)) self.print_module_percpu(mod, args.p) continue if print_header: print_header = False print("Module\t\t\tAddress\t\t\tSize\tUsed by") if core_layout: addr = int(mod['core_layout']['base']) size = int(mod['core_layout']['size']) else: addr = int(mod['module_core']) size = int(mod['core_size']) module_use = "" count = 0 for use in list_for_each_entry(mod['source_list'], types.module_use_type, 'source_list'): if module_use == "": module_use += " " else: module_use += "," module_use += use['source']['name'].string() count += 1 print("{:16s}\t{:#x}\t{:d}\t{:d}{}".format(modname, addr, size, count, module_use))
def get_last_cpu(self) -> int: """ Returns the last cpu this task was scheduled to execute on Returns: :obj:`int`: The last cpu this task was scheduled to execute on """ if struct_has_member(self.task_struct, 'cpu'): cpu = self.task_struct['cpu'] else: cpu = self.thread_info['cpu'] return int(cpu)
def check_task_interface(cls, init_task: gdb.Symbol) -> None: """ Check which interface to iterating over mount structures is in use Meant to be used as a SymbolCallback. Args: init_task: The ``init_task`` symbol. """ cls._init_fs_root = init_task.value()['fs']['root'] if struct_has_member(init_task, 'nsproxy'): cls._for_each_mount = cls._for_each_mount_nsproxy else: raise NotImplementedError("Mount.for_each_mount is unhandled on this kernel version")
def queue_is_mq(queue: gdb.Value) -> bool: """ Tests whether the queue is blk-mq queue. Args: queue: The request queue to test. The value must be of type ``struct request_queue``. Returns: :obj:`bool`: whether the ``struct request_queue`` is a multiqueue queue """ if not struct_has_member(queue, 'mq_ops'): return False return int(queue['mq_ops']) != 0
def btrfs_metadata_uuid(sb: gdb.Value, force: bool = False) -> uuid.UUID: """ Returns the btrfs metadata uuid for the specified superblock. Args: super_block: The ``struct super_block`` for which to return the btrfs metadata uuid. The value must be of type ``struct super_block``. force: Ignore type checking. Returns: :obj:`uuid.UUID`: The Python UUID Object for the btrfs fsid Raises: :obj:`.InvalidArgumentError`: the super_block does not belong to btrfs :obj:`gdb.NotAvailableError`: The target value was not available. """ fs_info = btrfs_fs_info(sb, force) if struct_has_member(types.btrfs_fs_info_type, 'metadata_uuid'): return decode_uuid(fs_info['metadata_uuid']) if struct_has_member(fs_info['fs_devices'].type, 'metadata_uuid'): return decode_uuid(fs_info['fs_devices']['metadata_uuid']) return btrfs_fsid(sb, force)
def setup_iterator_type(cls, gdbtype: gdb.Type) -> None: """ Detect whether to iterate the class list using ``struct device`` or ``struct device_private``. Linux v5.1-rc1 moved ``knode_class`` from ``struct device`` to ``struct device_private``. We need to detect it here to ensure list iteration works properly. Meant to be used as a TypeCallback. Args: gdbtype: The ``struct device`` type. """ if struct_has_member(gdbtype, 'knode_class'): cls._class_is_private = False
def detect_ail_version(cls, gdbtype: gdb.Type) -> None: """ Detect what version of the ail structure is in use Linux v4.17 renamed the xfs_ail members to use ail_* instead of xa_* except for xa_ail which was renamed to ail_head. Meant to be used as a TypeCallback. Args: gdbtype: The ``struct xfs_ail`` type. """ if struct_has_member(gdbtype, 'ail_head'): cls._ail_head_name = 'ail_head' else: cls._ail_head_name = 'xa_ail'
def mount_flags(mnt: gdb.Value, show_hidden: bool = False) -> str: """ Returns the human-readable flags of the ``struct mount`` :ref:`structure <mount_structure>`. Args: mnt: The :ref:`mount structure <mount_structure>` for which to return flags show_hidden: Whether to return hidden flags Returns: :obj:`str`: The mount flags in human-readable form """ if struct_has_member(mnt, 'mnt'): mnt = mnt['mnt'] if show_hidden: return decode_flags(mnt['mnt_flags'], MNT_FLAGS_HIDDEN, ",") return decode_flags(mnt['mnt_flags'], MNT_FLAGS, ",")
def sq_requests_queued(queue: gdb.Value) -> Tuple[int, int]: """ Report how many requests are queued for this queue Args: queue: The request queue to inspect for queued requests. The value must be of type ``struct request_queue``. Returns: (:obj:`int`, :obj:`int`): The queued requests. The first member of the 2-tuple is the number of async requests, the second is the number of sync requests. """ _check_queue_type(queue) if struct_has_member(queue, 'rq'): rqlist = queue['rq'] else: rqlist = queue['root_rl'] return (int(rqlist['count'][0]), int(rqlist['count'][1]))
def sbitmap_for_each_set(sbitmap: gdb.Value) -> Iterable[int]: """ Yield each set bit in a scalable bitmap Args: sbitmap: The bitmap to iterate. Yields: :obj:`int`: The position of a bit that is set """ length = int(sbitmap['depth']) for i in range(0, int(sbitmap['map_nr'])): word = sbitmap['map'][i]['word'] if struct_has_member(sbitmap['map'][i], 'cleared'): word &= ~sbitmap['map'][i]['cleared'] offset = i << int(sbitmap['shift']) bits = min(int(sbitmap['map'][i]['depth']), length - offset) for j in range(0, bits): if word & (1 << j): yield offset + j
def dump_ail(self, args: argparse.Namespace) -> None: try: sb = get_super_block(args.addr) except gdb.NotAvailableError as e: raise CommandError(str(e)) from e mp = xfs_mount(sb) ail = mp['m_ail'] itemno = 0 print("AIL @ {:x}".format(int(ail))) print("target={} last_pushed_lsn={} log_flush=".format( int(ail['xa_target']), int(ail['xa_last_pushed_lsn'])), end='') # This was added in Linux v3.2 (670ce93fef93b) if struct_has_member(ail, 'xa_log_flush'): print("{}".format(int(ail['xa_log_flush']))) else: print("[N/A]") for bitem in xfs_for_each_ail_log_item(mp): li_type = int(bitem['li_type']) lsn = int(bitem['li_lsn']) item = xfs_log_item_typed(bitem) print("{}: item={:x} lsn={} {} ".format(itemno, int(bitem.address), lsn, XFS_LI_TYPES[li_type][7:]), end='') if li_type == XFS_LI_BUF: buf = item['bli_buf'] flags = decode_flags(item['bli_flags'], XFS_BLI_FLAGS) print(" buf@{:x} bli_flags={}".format(int(buf), flags)) print(" {}".format(xfs_format_xfsbuf(buf))) elif li_type == XFS_LI_INODE: ili_flags = int(item['ili_lock_flags']) xfs_inode = item['ili_inode'] print("inode@{:x} i_ino={} ili_lock_flags={:x} ".format( int(xfs_inode['i_vnode'].address), int(xfs_inode['i_ino']), ili_flags)) elif li_type == XFS_LI_EFI: efi = item['efi_format'] print("efi@{:x} size={}, nextents={}, id={:x}".format( int(item.address), int(efi['efi_size']), int(efi['efi_nextents']), int(efi['efi_id']))) elif li_type == XFS_LI_EFI: efd = item['efd_format'] print("efd@{:x} size={}, nextents={}, id={:x}".format( int(item.address), int(efd['efd_size']), int(efd['efd_nextents']), int(efd['efd_id']))) elif li_type == XFS_LI_DQUOT: dquot = item['qli_dquot'] flags = decode_flags(dquot['dq_flags'], XFS_DQ_FLAGS) print("dquot@{:x} flags={}".format(int(dquot), flags)) elif li_type == XFS_LI_QUOTAOFF: qoff = item['qql_format'] print("qoff@{:x} type={} size={} flags={}".format( int(qoff), int(qoff['qf_type']), int(qoff['qf_size']), int(qoff['qf_flags']))) else: print("item@{:x}".format(int(item.address))) itemno += 1