forked from mirrors/linux
		
	If the list is uninitialized (next pointer is NULL), list_for_each gets stuck in an infinite loop. Print a message and treat list as empty. Link: https://lkml.kernel.org/r/4ae23bb1-c333-f669-da2d-fa35c4f49018@amazon.com Signed-off-by: George Prekas <prekageo@amazon.com> Reviewed-by: Jan Kiszka <jan.kiszka@siemens.com> Cc: Kieran Bingham <kbingham@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			136 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#
 | 
						|
# 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()
 |