def execute(self, argv): super_blocks = gdb.lookup_symbol('super_blocks', None)[0].value() sbtype = gdb.lookup_type('struct super_block') try: btrfs_fs_info_type = gdb.lookup_type('struct btrfs_fs_info') except gdb.error: # Load the module if it's not loaded yet module_type = gdb.lookup_type('struct module') modules = gdb.lookup_symbol('modules', None)[0].value() for module in list_for_each_entry(modules, module_type, 'list'): if module['name'].string() == "btrfs": addr = module['module_core'] gdb.execute("add-symbol-file {} {}".format(path, addr)) btrfs_fs_info_type = gdb.lookup_type('struct btrfs_fs_info') for sb in list_for_each_entry(super_blocks, sbtype, 's_list'): if sb['s_type']['name'].string() == "btrfs": fs_info = gdb.Value(sb['s_fs_info']).cast(btrfs_fs_info_type.pointer()) u = long(0) for i in range(0, 16): u <<= 8 u += int(fs_info['fsid'][i]) u = uuid.UUID(int=u) print "{} -> {} {}".format(sb.address, sb['s_id'].string(), u)
def execute(self, argv): super_blocks = gdb.lookup_symbol('super_blocks', None)[0].value() sbtype = gdb.lookup_type('struct super_block') try: btrfs_fs_info_type = gdb.lookup_type('struct btrfs_fs_info') except gdb.error: # Load the module if it's not loaded yet module_type = gdb.lookup_type('struct module') modules = gdb.lookup_symbol('modules', None)[0].value() for module in list_for_each_entry(modules, module_type, 'list'): if module['name'].string() == "btrfs": addr = module['module_core'] gdb.execute("add-symbol-file {} {}".format(path, addr)) btrfs_fs_info_type = gdb.lookup_type('struct btrfs_fs_info') for sb in list_for_each_entry(super_blocks, sbtype, 's_list'): if sb['s_type']['name'].string() == "btrfs": fs_info = gdb.Value(sb['s_fs_info']).cast( btrfs_fs_info_type.pointer()) u = 0 for i in range(0, 16): u <<= 8 u += int(fs_info['fsid'][i]) u = uuid.UUID(int=u) print "{} -> {} {}".format(sb.address, sb['s_id'].string(), u)
def setup_tasks(self): gdb.execute('set print thread-events 0') init_task = gdb.lookup_global_symbol('init_task') task_list = init_task.value()['tasks'] runqueues = gdb.lookup_global_symbol('runqueues') rqs = get_percpu_var(runqueues) rqscurrs = {long(x["curr"]): k for (k, x) in rqs.items()} self.pid_to_task_struct = {} print("Loading tasks...", end='') sys.stdout.flush() task_count = 0 tasks = [] for taskg in list_for_each_entry(task_list, init_task.type, 'tasks', include_head=True): tasks.append(taskg) for task in list_for_each_entry(taskg['thread_group'], init_task.type, 'thread_group'): tasks.append(task) for task in tasks: cpu = None regs = None active = long(task.address) in rqscurrs if active: cpu = rqscurrs[long(task.address)] regs = self.vmcore.attr.cpu[cpu].reg ltask = LinuxTask(task, active, cpu, regs) ptid = (LINUX_KERNEL_PID, task['pid'], 0) try: thread = gdb.selected_inferior().new_thread(ptid, ltask) except gdb.error as e: print("Failed to setup task @{:#x}".format(long(task.address))) continue thread.name = task['comm'].string() self.target.arch.setup_thread_info(thread) ltask.attach_thread(thread) ltask.set_get_stack_pointer(self.target.arch.get_stack_pointer) crash.cache.tasks.cache_task(ltask) task_count += 1 if task_count % 100 == 0: print(".", end='') sys.stdout.flush() print(" done. ({} tasks total)".format(task_count)) gdb.selected_inferior().executing = False
def setup_tasks(self): init_task = gdb.lookup_global_symbol('init_task') task_list = init_task.value()['tasks'] runqueues = gdb.lookup_global_symbol('runqueues') rqs = get_percpu_var(runqueues) rqscurrs = {long(x["curr"]) : k for (k, x) in rqs.items()} self.pid_to_task_struct = {} print("Loading tasks...", end='') sys.stdout.flush() task_count = 0 tasks = [] for taskg in list_for_each_entry(task_list, init_task.type, 'tasks'): tasks.append(taskg) for task in list_for_each_entry(taskg['thread_group'], init_task.type, 'thread_group'): tasks.append(task) for task in tasks: cpu = None regs = None active = long(task.address) in rqscurrs if active: cpu = rqscurrs[long(task.address)] regs = self.kdump.attr.cpu[cpu].reg ltask = LinuxTask(task, active, cpu, regs) ptid = (LINUX_KERNEL_PID, task['pid'], 0) try: thread = gdb.selected_inferior().new_thread(ptid, ltask) except gdb.error as e: print("Failed to setup task @{:#x}".format(long(task.address))) continue thread.name = task['comm'].string() self.arch.setup_thread_info(thread) ltask.attach_thread(thread) ltask.set_get_stack_pointer(self.arch.get_stack_pointer) crash.cache.tasks.cache_task(ltask) task_count += 1 if task_count % 100 == 0: print(".", end='') sys.stdout.flush() print(" done. ({} tasks total)".format(task_count)) gdb.selected_inferior().executing = False
def get_block_device(self): all_bdevs = gdb.lookup_symbol('all_bdevs', None)[0].value() for bdev in list_for_each_entry(all_bdevs, self.block_device_type, 'bd_list'): if int(bdev['bd_disk']) != 0 and int(bdev['bd_part']) != 0: return bdev return None
def get_hd_struct(self): all_bdevs = gdb.lookup_symbol('all_bdevs', None)[0].value() for bdev in list_for_each_entry(all_bdevs, self.block_device_type, 'bd_list'): if int(bdev['bd_part']) != 0: return bdev['bd_part'].dereference() return None
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 _check_free_area(self, area: gdb.Value, is_pcp: bool) -> None: nr_free = 0 if is_pcp: list_array_name = "lists" error_desc = "pcplist" else: list_array_name = "free_list" error_desc = "free area" for free_list in array_for_each(area[list_array_name]): try: for page_obj in list_for_each_entry(free_list, self.types.page_type, "lru"): page = crash.types.page.Page.from_obj(page_obj) nr_free += 1 if page.get_nid() != self.nid or page.get_zid( ) != self.zid: print( f"page 0x{int(page_obj.address):x} misplaced on " f"{error_desc} of node {self.nid} zone {self.zid}, " f"has flags for node {page.get_nid()} zone {page.get_zid()}" ) except BufferError as e: print(f"Error traversing free area: {e}") nr_expected = area["count"] if is_pcp else area["nr_free"] if nr_free != nr_expected: print(f"nr_free mismatch in {error_desc} 0x{int(area.address):x}: " f"expected {nr_expected}, counted {nr_free}")
def _for_each_mount_nsproxy(self, task: gdb.Value) -> Iterator[gdb.Value]: """ An implementation of for_each_mount that uses the task's nsproxy to locate the mount namespace. See :ref:`for_each_mount` for more details. """ return list_for_each_entry(task['nsproxy']['mnt_ns']['list'], types.mount_type, 'mnt_list')
def inode_paths(inode): for dentry in list_for_each_entry(inode['i_dentry'], dentry_type, ''): names = [dentry['d_name']['name'].string()] parent = dentry['d_parent'] while parent.address != parent['d_parent'].address: names.insert(0, parent['d_name']['name'].string()) parent = parent['d_parent'] yield '/'.join(names)
def test_normal_container_list_with_string(self): normal_list = get_symbol("good_container_list") short_list = get_symbol("good_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof with self.assertRaises(ArgumentTypeError): for node in list_for_each_entry(normal_list, 'struct container', 'list'): count += 1
def test_normal_container_list_with_type(self): normal_list = get_symbol("good_container_list") short_list = get_symbol("good_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof struct_container = gdb.lookup_type('struct container') count = 0 for node in list_for_each_entry(normal_list, struct_container, 'list'): count += 1
def test_bad_container_list_with_string(self): bad_list = get_symbol("bad_container_list") short_list = get_symbol("bad_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof count = 0 with self.assertRaises(CorruptListError): for node in list_for_each_entry(bad_list, 'struct container', 'list'): count += 1
def setup_slab_caches(slab_caches: gdb.Symbol) -> None: list_caches = slab_caches.value() for cache in list_for_each_entry(list_caches, types.kmem_cache_type, KmemCache.head_name): name = cache["name"].string() kmem_cache = KmemCache(name, cache) kmem_caches[name] = kmem_cache kmem_caches_by_addr[int(cache.address)] = kmem_cache
def test_bad_container_list_with_string(self): bad_list = get_symbol("bad_container_list") short_list = get_symbol("bad_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof count = 0 with self.assertRaises(ArgumentTypeError): for node in list_for_each_entry(bad_list, 'struct container', 'list', print_broken_links=False): count += 1
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 test_bad_container_list_with_type(self): bad_list = get_symbol("bad_container_list") short_list = get_symbol("bad_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof struct_container = gdb.lookup_type('struct container') count = 0 with self.assertRaises(CorruptListError): for node in list_for_each_entry(bad_list, struct_container, 'list', print_broken_links=False): count += 1
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 test_cycle_container_list_with_type(self): cycle_list = get_symbol("cycle_container_list") short_list = get_symbol("cycle_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof struct_container = gdb.lookup_type('struct container') count = 0 with self.assertRaises(ListCycleError): for node in list_for_each_entry(cycle_list, struct_container, 'list'): count += 1
def test_normal_container_list_with_string(self): normal_list = get_symbol("good_container_list") short_list = get_symbol("good_containers") expected_count = short_list.type.sizeof // short_list[0].type.sizeof count = 0 for node in list_for_each_entry(normal_list, 'struct container', 'list'): count += 1 self.assertTrue(count == expected_count)
def klist_for_each(self, klist): if klist.type == self.klist_type.pointer(): klist = klist.dereference() elif klist.type != self.klist_type: raise TypeError("klist must be gdb.Value representing 'struct klist' or 'struct klist *' not {}" .format(klist.type)) for node in list_for_each_entry(klist['k_list'], self.klist_node_type, 'n_node'): if node['n_klist'] != klist.address: raise KlistCorruptedError("Corrupted") yield node
def for_each_thread_group_leader() -> Iterator[gdb.Value]: """ Iterate the task list and yield each thread group leader Yields: :obj:`gdb.Value`: The next task on the list. The value is of type ``struct task_struct``. """ task_list = symvals.init_task['tasks'] for task in list_for_each_entry(task_list, symvals.init_task.type, 'tasks', include_head=True): yield task
def for_each_module() -> Iterable[gdb.Value]: """ Iterate over each module in the modules list Yields: :obj:`gdb.Value`: The next module on the list. The value is of type ``struct module``. """ for module in list_for_each_entry(symvals.modules, types.module_type, 'list'): yield module
def klist_for_each(self, klist): if klist.type == self.klist_type.pointer(): klist = klist.dereference() elif klist.type != self.klist_type: raise TypeError( "klist must be gdb.Value representing 'struct klist' or 'struct klist *' not {}" .format(klist.type)) for node in list_for_each_entry(klist['k_list'], self.klist_node_type, 'n_node'): if node['n_klist'] != klist.address: raise KlistCorruptedError("Corrupted") yield node
def _check_free_area(self, area: gdb.Value, is_pcp: bool, order_cpu: int) -> None: nr_free = 0 if is_pcp: list_array_name = "lists" error_desc = "pcplist" order_cpu_desc = f"cpu {order_cpu}" else: list_array_name = "free_list" error_desc = "free area" order_cpu_desc = f"order {order_cpu}" for mt in range(array_size(area[list_array_name])): free_list = area[list_array_name][mt] for reverse in [False, True]: if reverse: print("Retrying list in reverse direction") try: for page_obj in list_for_each_entry(free_list, self.types.page_type, "lru", reverse=reverse): page = crash.types.page.Page.from_obj(page_obj) if not page: print(f"page 0x{int(page_obj.address):x} is not a valid page pointer on " f"{error_desc} of node {self.nid} zone {self.zid}, {order_cpu_desc} mt {mt}") continue nr_free += 1 if is_pcp: errors = page.check_pcplist_page() else: errors = page.check_freelist_page(order_cpu) if errors != "": print(f"page 0x{int(page_obj.address):x} pfn {page.pfn} on {error_desc} of node " f"{self.nid} zone {self.zid}, {order_cpu_desc} mt {mt} had unexpected state: {errors}") if page.get_nid() != self.nid or page.get_zid() != self.zid: print(f"page 0x{int(page_obj.address):x} pfn {page.pfn} misplaced on " f"{error_desc} of node {self.nid} zone {self.zid}, {order_cpu_desc} mt {mt} " f"has flags for node {page.get_nid()} zone {page.get_zid()}") except CorruptListError as e: print(f"Error traversing {error_desc} 0x{int(area.address):x} for {order_cpu_desc} mt {mt}: {e}") continue except BufferError as e: print(f"Error traversing {error_desc} 0x{int(area.address):x} for {order_cpu_desc} mt {mt}: {e}") continue break nr_expected = area["count"] if is_pcp else area["nr_free"] if nr_free != nr_expected: print(f"nr_free mismatch in {error_desc} 0x{int(area.address):x} for {order_cpu_desc}: " f"expected {nr_expected}, counted {nr_free}")
def for_each_super_block() -> Iterable[gdb.Value]: """ Iterate over the list of super blocks and yield each one. Yields: :obj:`gdb.Value`: One value for each super block. Each value will be of type ``struct super_block``. Raises: :obj:`gdb.NotAvailableError`: The target value was not available. """ for sb in list_for_each_entry(symvals.super_blocks, types.super_block_type, 's_list'): yield sb
def setup_slab_caches(cls, slab_caches): cls.kmem_caches = dict() cls.kmem_caches_by_addr = dict() list_caches = slab_caches.value() for cache in list_for_each_entry(list_caches, KmemCache.kmem_cache_type, KmemCache.head_name): name = cache["name"].string() kmem_cache = KmemCache(name, cache) cls.kmem_caches[name] = kmem_cache cls.kmem_caches_by_addr[long(cache.address)] = kmem_cache
def for_each_thread_in_group(task: gdb.Value) -> Iterator[gdb.Value]: """ Iterate a thread group leader's thread list and yield each struct task_struct Args: task: The task_struct that is the thread group leader. The value must be of type ``struct task_struct``. Yields: :obj:`gdb.Value`: The next task on the list. The value is of type ``struct task_struct``. """ thread_list = task['thread_group'] for thread in list_for_each_entry(thread_list, symvals.init_task.type, 'thread_group'): yield thread
def xfs_for_each_ail_entry(ail: gdb.Value) -> Iterable[gdb.Value]: """ Iterates over the XFS Active Item Log and returns each item Args: ail: The XFS AIL to iterate. The value must be of type ``struct xfs_ail``. Yields: :obj:`gdb.Value`: A log item from the AIL. Each value will be of type ``struct xfs_log_item``. Raises: :obj:`gdb.NotAvailableError`: The target value was not available. """ head = XFS.get_ail_head(ail) for item in list_for_each_entry(head, types.xfs_log_item_type, 'li_ail'): yield item
def for_each_request_in_queue(queue: gdb.Value) -> Iterable[gdb.Value]: """ Iterates over each ``struct request`` in request_queue This method iterates over the ``request_queue``'s queuelist and returns a request for each member. Args: queue: The ``struct request_queue`` used to iterate. The value must be of type ``struct request_queue``. Yields: :obj:`gdb.Value`: Each ``struct request`` contained within the ``request_queue``'s queuelist. The value is of type ``struct request``. """ if int(queue) == 0: raise NoQueueError("Queue is NULL") return list_for_each_entry(queue['queue_head'], types.request_type, 'queuelist')
def for_each_request_in_queue(self, queue): """ Iterates over each struct request in request_queue This method iterates over the request_queue's queuelist and returns a request for each member. Args: queue(gdb.Value<struct request_queue>): The struct request_queue used to iterate Yields: gdb.Value<struct request>: Each struct request contained within the request_queue's queuelist """ if long(queue) == 0: raise NoQueueError("Queue is NULL") return list_for_each_entry(queue['queue_head'], self.request_type, 'queuelist')
def _check_free_area(self, area: gdb.Value, is_pcp: bool) -> None: nr_free = 0 list_array_name = "lists" if is_pcp else "free_list" for free_list in array_for_each(area[list_array_name]): for page_obj in list_for_each_entry(free_list, self.types.page_type, "lru"): page = crash.types.page.Page.from_obj(page_obj) nr_free += 1 if page.get_nid() != self.nid or page.get_zid() != self.zid: print( "page {:#x} misplaced on {} of zone {}:{}, has flags for zone {}:{}" .format(int(page_obj.address), "pcplist" if is_pcp else "freelist", self.nid, self.zid, page.get_nid(), page.get_zid())) nr_expected = area["count"] if is_pcp else area["nr_free"] if nr_free != nr_expected: print("nr_free mismatch in {} {}: expected {}, counted {}".format( "pcplist" if is_pcp else "area", area.address, nr_expected, nr_free))
def for_each_module(self): for module in list_for_each_entry(self.modules, self.module_type, 'list'): yield module
def xfs_for_each_ail_entry(ail): xfs_log_item_type = gdb.lookup_type('struct xfs_log_item') for item in list_for_each_entry(ail['xa_ail'], xfs_log_item_type, 'li_ail'): yield item
def for_each_mount_nsproxy(self, task): return list_for_each_entry(task['nsproxy']['mnt_ns']['list'], self.mount_type, 'mnt_list')
def for_each_request_in_queue(self, queue): if long(queue) == 0: raise NoQueueError("Queue is NULL") return list_for_each_entry(queue['queue_head'], self.request_type, 'queuelist')
#!/usr/bin/env python # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: from crash.types.list import list_for_each_entry import gdb import uuid from crash.cache import sys import os def find(name, path): for root, dirs, files in os.walk(path): if name in files: return os.path.join(root, name) sys.cache.init_sys_caches() path = "/lib/modules/{}".format(sys.cache.utsname_cache['release']) modules = gdb.lookup_symbol('modules', None)[0].value() module_type = gdb.lookup_type('struct module') for module in list_for_each_entry(modules, module_type, 'list'): modname = "{}.ko".format(module['name'].string()) modpath = find(modname, path) if not modpath and modname.find('_') != -1: modname = modname.replace('_', '-') modpath = find(modname, path) if not modpath: print "Couldn't find {} under {}.".format(module['name'], path) continue gdb.execute("add-symbol-file {} {}".format(modpath, module['module_core']))