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