1# 2# gdb helper commands and functions for Linux kernel debugging 3# 4# list tools 5# 6# Copyright (c) Thiebaud Weksteen, 2015 7# 8# Authors: 9# Thiebaud Weksteen <thiebaud@weksteen.fr> 10# 11# This work is licensed under the terms of the GNU GPL version 2. 12# 13 14import gdb 15 16from linux import utils 17 18list_head = utils.CachedType("struct list_head") 19hlist_head = utils.CachedType("struct hlist_head") 20hlist_node = utils.CachedType("struct hlist_node") 21 22 23def list_for_each(head): 24 if head.type == list_head.get_type().pointer(): 25 head = head.dereference() 26 elif head.type != list_head.get_type(): 27 raise TypeError("Must be struct list_head not {}" 28 .format(head.type)) 29 30 if head['next'] == 0: 31 gdb.write("list_for_each: Uninitialized list '{}' treated as empty\n" 32 .format(head.address)) 33 return 34 35 node = head['next'].dereference() 36 while node.address != head.address: 37 yield node.address 38 node = node['next'].dereference() 39 40 41def list_for_each_entry(head, gdbtype, member): 42 for node in list_for_each(head): 43 yield utils.container_of(node, gdbtype, member) 44 45 46def hlist_for_each(head): 47 if head.type == hlist_head.get_type().pointer(): 48 head = head.dereference() 49 elif head.type != hlist_head.get_type(): 50 raise TypeError("Must be struct hlist_head not {}" 51 .format(head.type)) 52 53 node = head['first'].dereference() 54 while node.address: 55 yield node.address 56 node = node['next'].dereference() 57 58 59def hlist_for_each_entry(head, gdbtype, member): 60 for node in hlist_for_each(head): 61 yield utils.container_of(node, gdbtype, member) 62 63 64def list_check(head): 65 nb = 0 66 if (head.type == list_head.get_type().pointer()): 67 head = head.dereference() 68 elif (head.type != list_head.get_type()): 69 raise gdb.GdbError('argument must be of type (struct list_head [*])') 70 c = head 71 try: 72 gdb.write("Starting with: {}\n".format(c)) 73 except gdb.MemoryError: 74 gdb.write('head is not accessible\n') 75 return 76 while True: 77 p = c['prev'].dereference() 78 n = c['next'].dereference() 79 try: 80 if p['next'] != c.address: 81 gdb.write('prev.next != current: ' 82 'current@{current_addr}={current} ' 83 'prev@{p_addr}={p}\n'.format( 84 current_addr=c.address, 85 current=c, 86 p_addr=p.address, 87 p=p, 88 )) 89 return 90 except gdb.MemoryError: 91 gdb.write('prev is not accessible: ' 92 'current@{current_addr}={current}\n'.format( 93 current_addr=c.address, 94 current=c 95 )) 96 return 97 try: 98 if n['prev'] != c.address: 99 gdb.write('next.prev != current: ' 100 'current@{current_addr}={current} ' 101 'next@{n_addr}={n}\n'.format( 102 current_addr=c.address, 103 current=c, 104 n_addr=n.address, 105 n=n, 106 )) 107 return 108 except gdb.MemoryError: 109 gdb.write('next is not accessible: ' 110 'current@{current_addr}={current}\n'.format( 111 current_addr=c.address, 112 current=c 113 )) 114 return 115 c = n 116 nb += 1 117 if c == head: 118 gdb.write("list is consistent: {} node(s)\n".format(nb)) 119 return 120 121 122class LxListChk(gdb.Command): 123 """Verify a list consistency""" 124 125 def __init__(self): 126 super(LxListChk, self).__init__("lx-list-check", gdb.COMMAND_DATA, 127 gdb.COMPLETE_EXPRESSION) 128 129 def invoke(self, arg, from_tty): 130 argv = gdb.string_to_argv(arg) 131 if len(argv) != 1: 132 raise gdb.GdbError("lx-list-check takes one argument") 133 list_check(gdb.parse_and_eval(argv[0])) 134 135 136LxListChk() 137