def klist_for_each(klist: gdb.Value) -> Iterable[gdb.Value]: """ Iterate over a klist and yield each node Args: klist: The list to iterate. The value must be of type ``struct klist`` or ``struct klist *``. Yields: :obj:`gdb.Value`: The next node in the list. The value is of type ``struct klist_node``. """ if klist.type == types.klist_type.pointer(): klist = klist.dereference() elif klist.type != types.klist_type: raise InvalidArgumentError( "klist must be gdb.Value representing 'struct klist' or 'struct klist *' not {}" .format(klist.type)) if klist.type is not types.klist_type: types.override('struct klist', klist.type) for node in list_for_each_entry(klist['k_list'], types.klist_node_type, 'n_node'): if node['n_klist'] != klist.address: raise KlistCorruptedError("Corrupted") yield node
def btrfs_fs_info(super_block: gdb.Value, force: bool = False) -> gdb.Value: """ Resolves a btrfs_fs_info from a VFS superblock This method resolves a struct btrfs_fs_info from a struct super_block Args: super_block: The ``struct super_block`` to use to resolve a' ``struct btrfs_fs_info``. A pointer to a ``struct super_block`` is also acceptable. force: Ignore type checking. Returns: :obj:`gdb.Value: The resolved ``struct btrfs_fs_info``. The value will be of type ``struct btrfs_fs_info``. Raises: :obj:`.InvalidArgumentError`: the super_block does not belong to btrfs :obj:`gdb.NotAvailableError`: The target value was not available. """ if not force and not is_btrfs_super(super_block): raise InvalidArgumentError("super_block does not belong to btrfs") fs_info = super_block['s_fs_info'].cast(types.btrfs_fs_info_p_type) return fs_info.dereference()
def _resolve_percpu_var(self, symvar: SymbolOrValue) -> gdb.Value: orig_var = symvar if isinstance(symvar, gdb.Symbol): var = symvar.value() else: var = symvar if not isinstance(var, gdb.Value): raise InvalidArgumentError( "Argument must be gdb.Symbol or gdb.Value") if var.type.code == gdb.TYPE_CODE_PTR: # The percpu contains pointers if var.address is not None and self.is_percpu_var(var.address): var = var.address # Pointer to a percpu elif self.is_percpu_var(var): if var.type != types.void_p_type: var = var.dereference().address assert self.is_percpu_var(var) else: raise PerCPUError(orig_var) # object is a percpu elif self.is_percpu_var(var.address): var = var.address else: raise PerCPUError(orig_var) return var
def percpu_counter_sum(var: SymbolOrValue) -> int: """ Returns the sum of a percpu counter Args: var: The percpu counter to sum. The value must be of type ``struct percpu_counter``. Returns: :obj:`int`: the sum of all components of the percpu counter """ if isinstance(var, gdb.Symbol): var = var.value() if not (var.type == types.percpu_counter_type or (var.type.code == gdb.TYPE_CODE_PTR and var.type.target() == types.percpu_counter_type)): raise InvalidArgumentError( "var must be gdb.Symbol or gdb.Value describing `{}' not `{}'". format(types.percpu_counter_type, var.type)) total = int(var['count']) v = get_percpu_vars(var['counters']) for cpu in v: total += int(v[cpu]) return total
def gendisk_name(gendisk: gdb.Value) -> str: """ Returns the name of the provided block device. This method evaluates the block device and returns the name, including partition number, if applicable. Args: gendisk: A ``struct gendisk`` or ``struct hd_struct`` for which to return the name. The value must be of type ``struct gendisk`` or ``struct hd_struct``. Returns: :obj:`str`: The name of the block device Raises: :obj:`.InvalidArgumentError`: gendisk does not describe a ``struct gendisk`` or ``struct hd_struct`` """ if gendisk.type.code == gdb.TYPE_CODE_PTR: gendisk = gendisk.dereference() if get_basic_type(gendisk.type) == types.gendisk_type: return gendisk['disk_name'].string() if get_basic_type(gendisk.type) == types.hd_struct_type: parent = dev_to_gendisk(part_to_dev(gendisk)['parent']) return "{}{:d}".format(gendisk_name(parent), int(gendisk['partno'])) raise InvalidArgumentError("expected {} or {}, not {}".format( types.gendisk_type, types.hd_struct_type, gendisk.type.unqualified()))
def _setup_roots(self, roots: PathSpecifier = None, verbose: bool = False) -> None: if roots is None: self.roots = ["/"] elif isinstance(roots, list) and roots and isinstance(roots[0], str): x = None for root in roots: if os.path.exists(root): if x is None: x = [root] else: x.append(root) else: print("root {} does not exist".format(root)) if x is None: x = ["/"] self.roots = x elif isinstance(roots, str): x = None if os.path.exists(roots): if x is None: x = [roots] else: x.append(roots) if x is None: x = ["/"] self.roots = x else: raise InvalidArgumentError( "roots must be None, str, or list of str") if verbose: print("roots={}".format(self.roots))
def _check_bitmap_type(bitmap: gdb.Value) -> None: if ((bitmap.type.code != gdb.TYPE_CODE_ARRAY or bitmap[0].type.code != types.unsigned_long_type.code or bitmap[0].type.sizeof != types.unsigned_long_type.sizeof) and (bitmap.type.code != gdb.TYPE_CODE_PTR or bitmap.type.target().code != types.unsigned_long_type.code or bitmap.type.target().sizeof != types.unsigned_long_type.sizeof)): raise InvalidArgumentError( "bitmaps are expected to be arrays of unsigned long not `{}'". format(bitmap.type))
def set_active(self, cpu: int, regs: Dict[str, int]) -> None: """ Set this task as active in the debugging environment Args: cpu: Which CPU this task was using regs: The registers associated with this task Raises: :obj:`.InvalidArgumentError`: The cpu was not a valid integer. """ if not (isinstance(cpu, int) and cpu >= 0): raise InvalidArgumentError("cpu must be integer >= 0") self.active = True self.cpu = cpu self.regs = regs
def _get_percpu_var(self, symvar: SymbolOrValue, cpu: int) -> gdb.Value: if isinstance(symvar, (gdb.Symbol, gdb.MinSymbol)): var = symvar.value() else: var = symvar if not isinstance(var, gdb.Value): raise InvalidArgumentError("Argument must be gdb.Symbol or gdb.Value") if cpu < 0: raise ValueError("cpu must be >= 0") addr = symvals['__per_cpu_offset'][cpu] if addr > 0: addr += self._relocated_offset(var) val = gdb.Value(addr).cast(var.type) if var.type != types.void_p_type: val = val.dereference() return val
def for_each_child(kn: gdb.Value) -> Iterable[gdb.Value]: """ Iterates over all child nodes of given kernfs_node. Args: kn: ``struct kernfs_node`` of directory type Yields: gdb.Value: ``struct kernfs_node`` Raises: :obj:`.InvalidArgumentError`: kernfs_node is not a directory """ if int(kn['flags']) & KERNFS_DIR == 0: raise InvalidArgumentError( f"kernfs_node at {kn.address} is not a directory") return rbtree_postorder_for_each_entry(kn['dir']['children'], types.kernfs_node_type, 'rb')
def item_to_quotaoff_log_item(item: gdb.Value) -> gdb.Value: """ Converts an xfs_log_item to an xfs_quotaoff_log_item Args: item: The log item to convert. The value must be of type ``struct xfs_log_item``. Returns: :obj:`gdb.Value`: The converted log item. The value will be of type ``struct xfs_quotaoff_log_item`` Raises: InvalidArgumentError: The type of log item is not ``XFS_LI_QUOTAOFF`` :obj:`gdb.NotAvailableError`: The target value was not available. """ if item['li_type'] != XFS_LI_QUOTAOFF: raise InvalidArgumentError("item is not an QUOTAOFF log item") return container_of(item, types.xfs_qoff_logitem_type, 'qql_item')
def for_each_block_device(subtype: gdb.Value = None) -> Iterable[gdb.Value]: """ Iterates over each block device registered with the block class. This method iterates over the block_class klist and yields every member found. The members are either struct gendisk or struct hd_struct, depending on whether it describes an entire disk or a partition, respectively. The members can be filtered by providing a subtype, which corresponds to a the the type field of the struct device. Args: subtype (optional): The ``struct device_type`` that will be used to match and filter. Typically the values associated with the ``disk_type`` or ``part_type`` :obj:`gdb.Symbol`. Yields: :obj:`gdb.Value`: The next block device that matches the subtype. The value is of type ``struct gendisk`` or ``struct hd_struct``. Raises: :obj:`RuntimeError`: An unknown device type was encountered during iteration. :obj:`TypeError`: The provided subtype was not of ``struct device_type`` or ``struct device type *`` """ if subtype: if get_basic_type(subtype.type) == types.device_type_type: subtype = subtype.address elif get_basic_type(subtype.type) != types.device_type_type.pointer(): raise InvalidArgumentError("subtype must be {} not {}".format( types.device_type_type.pointer(), subtype.type.unqualified())) for dev in for_each_class_device(symvals.block_class, subtype): if dev['type'] == symvals.disk_type.address: yield dev_to_gendisk(dev) elif dev['type'] == symvals.part_type.address: yield dev_to_part(dev) else: raise RuntimeError("Encountered unexpected device type {}".format( dev['type']))
def inode_to_block_device(inode: gdb.Value) -> gdb.Value: """ Returns the block device associated with this inode. If the inode describes a block device, return that block device. Otherwise, raise InvalidArgumentError. Args: inode: The ``struct inode`` for which to return the associated block device. The value must be of type ``struct inode``. Returns: :obj:`gdb.Value`: The ``struct block_device`` associated with the provided ``struct inode``. The value is of type ``struct block_device``. Raises: :obj:`.InvalidArgumentError`: inode does not describe a block device """ if inode['i_sb'] != symvals.blockdev_superblock: raise InvalidArgumentError("inode does not correspond to block device") return container_of(inode, types.bdev_inode_type, 'vfs_inode')['bdev']
def xfs_mount(sb: gdb.Value, force: bool = False) -> gdb.Value: """ Converts a VFS superblock to a xfs mount This method converts a ``struct super_block`` to a ``struct xfs_mount *`` Args: super_block: The struct super_block to convert to a ``struct xfs_fs_info``. The value must be of type ``struct super_block``. Returns: :obj:`gdb.Value`: The converted ``struct xfs_mount``. The value will be of type ``struct xfs_mount *``. Raises: InvalidArgumentError: The ``struct super_block`` does not belong to xfs :obj:`gdb.NotAvailableError`: The target value was not available. """ if not force and not is_xfs_super(sb): raise InvalidArgumentError("superblock does not belong to xfs") return sb['s_fs_info'].cast(types.xfs_mount_p_type)
def _setup_module_debuginfo_path( self, module_debuginfo_path: PathSpecifier = None, verbose: bool = False) -> None: x: List[str] = [] if module_debuginfo_path is None: defaults = [ "modules.debug", "lib/modules/{}".format(self.version), ] self.module_debuginfo_path = self._find_debuginfo_paths(defaults) elif (isinstance(module_debuginfo_path, list) and isinstance(module_debuginfo_path[0], str)): for root in self.roots: for mpath in module_debuginfo_path: path = "{}/{}".format(root, mpath) if os.path.exists(path): x.append(path) self.module_debuginfo_path = x elif isinstance(module_debuginfo_path, str): for root in self.roots: path = "{}/{}".format(root, module_debuginfo_path) if os.path.exists(path): x.append(path) self.module_debuginfo_path = x else: raise InvalidArgumentError( "module_debuginfo_path must be None, str, or list of str") if verbose: print("module_debuginfo_path={}".format( self.module_debuginfo_path))
def xfs_inode(vfs_inode: gdb.Value, force: bool = False) -> gdb.Value: """ Converts a VFS inode to a xfs inode This method converts a ``struct inode`` to a ``struct xfs_inode``. Args: vfs_inode: The ``struct inode`` to convert to a ``struct xfs_inode`` The value must be of type ``struct inode``. force: ignore type checking Returns: :obj:`gdb.Value`: The converted ``struct xfs_inode``. The value will be of type ``struct xfs_inode``. Raises: TypeError: The inode does not belong to xfs :obj:`gdb.NotAvailableError`: The target value was not available. """ if not force and not is_xfs_inode(vfs_inode): raise InvalidArgumentError("inode does not belong to xfs") return container_of(vfs_inode, types.xfs_inode, 'i_vnode')
def _setup_vmlinux_debuginfo(self, vmlinux_debuginfo: PathSpecifier = None, verbose: bool = False) -> None: if vmlinux_debuginfo is None: defaults = [ "{}.debug".format(self.kernel), "vmlinux-{}.debug".format(self.version), "boot/{}.debug".format(os.path.basename(self.kernel)), "boot/vmlinux-{}.debug".format(self.version), ] self.vmlinux_debuginfo = self._find_debuginfo_paths(defaults) elif (isinstance(vmlinux_debuginfo, list) and vmlinux_debuginfo and isinstance(vmlinux_debuginfo[0], str)): self.vmlinux_debuginfo = vmlinux_debuginfo elif isinstance(vmlinux_debuginfo, str): self.vmlinux_debuginfo = [vmlinux_debuginfo] else: raise InvalidArgumentError( "vmlinux_debuginfo must be None, str, or list of str") if verbose: print("vmlinux_debuginfo={}".format(self.vmlinux_debuginfo))
def _check_queue_type(queue: gdb.Value) -> None: if not queue_is_mq(queue): raise InvalidArgumentError( "Passed request queue is not a multiqueue queue")