| # | |
| # gdb helper commands and functions for Linux kernel debugging | |
| # | |
| # list tools | |
| # | |
| # Copyright (c) Thiebaud Weksteen, 2015 | |
| # | |
| # Authors: | |
| # Thiebaud Weksteen <thiebaud@weksteen.fr> | |
| # | |
| # This work is licensed under the terms of the GNU GPL version 2. | |
| # | |
| import gdb | |
| from linux import utils | |
| list_head = utils.CachedType("struct list_head") | |
| hlist_head = utils.CachedType("struct hlist_head") | |
| hlist_node = utils.CachedType("struct hlist_node") | |
| def list_for_each(head): | |
| if head.type == list_head.get_type().pointer(): | |
| head = head.dereference() | |
| elif head.type != list_head.get_type(): | |
| raise TypeError("Must be struct list_head not {}" | |
| .format(head.type)) | |
| if head['next'] == 0: | |
| gdb.write("list_for_each: Uninitialized list '{}' treated as empty\n" | |
| .format(head.address)) | |
| return | |
| node = head['next'].dereference() | |
| while node.address != head.address: | |
| yield node.address | |
| node = node['next'].dereference() | |
| def list_for_each_entry(head, gdbtype, member): | |
| for node in list_for_each(head): | |
| yield utils.container_of(node, gdbtype, member) | |
| def hlist_for_each(head): | |
| if head.type == hlist_head.get_type().pointer(): | |
| head = head.dereference() | |
| elif head.type != hlist_head.get_type(): | |
| raise TypeError("Must be struct hlist_head not {}" | |
| .format(head.type)) | |
| node = head['first'].dereference() | |
| while node.address: | |
| yield node.address | |
| node = node['next'].dereference() | |
| def hlist_for_each_entry(head, gdbtype, member): | |
| for node in hlist_for_each(head): | |
| yield utils.container_of(node, gdbtype, member) | |
| def list_check(head): | |
| nb = 0 | |
| if (head.type == list_head.get_type().pointer()): | |
| head = head.dereference() | |
| elif (head.type != list_head.get_type()): | |
| raise gdb.GdbError('argument must be of type (struct list_head [*])') | |
| c = head | |
| try: | |
| gdb.write("Starting with: {}\n".format(c)) | |
| except gdb.MemoryError: | |
| gdb.write('head is not accessible\n') | |
| return | |
| while True: | |
| p = c['prev'].dereference() | |
| n = c['next'].dereference() | |
| try: | |
| if p['next'] != c.address: | |
| gdb.write('prev.next != current: ' | |
| 'current@{current_addr}={current} ' | |
| 'prev@{p_addr}={p}\n'.format( | |
| current_addr=c.address, | |
| current=c, | |
| p_addr=p.address, | |
| p=p, | |
| )) | |
| return | |
| except gdb.MemoryError: | |
| gdb.write('prev is not accessible: ' | |
| 'current@{current_addr}={current}\n'.format( | |
| current_addr=c.address, | |
| current=c | |
| )) | |
| return | |
| try: | |
| if n['prev'] != c.address: | |
| gdb.write('next.prev != current: ' | |
| 'current@{current_addr}={current} ' | |
| 'next@{n_addr}={n}\n'.format( | |
| current_addr=c.address, | |
| current=c, | |
| n_addr=n.address, | |
| n=n, | |
| )) | |
| return | |
| except gdb.MemoryError: | |
| gdb.write('next is not accessible: ' | |
| 'current@{current_addr}={current}\n'.format( | |
| current_addr=c.address, | |
| current=c | |
| )) | |
| return | |
| c = n | |
| nb += 1 | |
| if c == head: | |
| gdb.write("list is consistent: {} node(s)\n".format(nb)) | |
| return | |
| class LxListChk(gdb.Command): | |
| """Verify a list consistency""" | |
| def __init__(self): | |
| super(LxListChk, self).__init__("lx-list-check", gdb.COMMAND_DATA, | |
| gdb.COMPLETE_EXPRESSION) | |
| def invoke(self, arg, from_tty): | |
| argv = gdb.string_to_argv(arg) | |
| if len(argv) != 1: | |
| raise gdb.GdbError("lx-list-check takes one argument") | |
| list_check(gdb.parse_and_eval(argv[0])) | |
| LxListChk() |