mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 09:49:21 +08:00
b885cb3633
So we can focus on single element of struct, same as array. Fix typo in foreach array args.element Signed-off-by: xuxingliang <xuxingliang@xiaomi.com>
421 lines
13 KiB
Python
421 lines
13 KiB
Python
############################################################################
|
|
# tools/gdb/nuttxgdb/lists.py
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership. The
|
|
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance with the
|
|
# License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
############################################################################
|
|
|
|
import argparse
|
|
|
|
import gdb
|
|
|
|
from . import utils
|
|
|
|
list_node_type = utils.lookup_type("struct list_node")
|
|
sq_queue_type = utils.lookup_type("sq_queue_t")
|
|
sq_entry_type = utils.lookup_type("sq_entry_t")
|
|
dq_queue_type = utils.lookup_type("dq_queue_t")
|
|
|
|
|
|
class NxList:
|
|
def __init__(self, list, container_type=None, member=None, reverse=False):
|
|
"""Initialize the list iterator. Optionally specify the container type and member name."""
|
|
|
|
if not list:
|
|
raise ValueError("The head cannot be None.\n")
|
|
|
|
if list.type.code != gdb.TYPE_CODE_PTR:
|
|
list = list.address # Make sure list is a pointer.
|
|
|
|
if container_type and not member:
|
|
raise ValueError("Must specify the member name in container.\n")
|
|
|
|
self.list = list
|
|
self.reverse = reverse
|
|
self.container_type = container_type
|
|
self.member = member
|
|
self.current = self._get_first()
|
|
|
|
def _get_first(self):
|
|
"""Get the initial node based on the direction of traversal."""
|
|
|
|
prev = self.list["prev"]
|
|
next = self.list["next"]
|
|
|
|
first = prev if self.reverse else next
|
|
return first if first and first != self.list else None
|
|
|
|
def _get_next(self, node):
|
|
# for(node = (list)->next; node != (list); node = node->next)
|
|
return node["next"] if node["next"] != self.list else None
|
|
|
|
def _get_prev(self, node):
|
|
# for(node = (list)->next; node != (list); node = node->prev)
|
|
return node["prev"] if node["prev"] != self.list else None
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
if self.current is None:
|
|
raise StopIteration
|
|
|
|
node = self.current
|
|
self.current = self._get_prev(node) if self.reverse else self._get_next(node)
|
|
return (
|
|
utils.container_of(node, self.container_type, self.member)
|
|
if self.container_type
|
|
else node
|
|
)
|
|
|
|
|
|
class NxSQueue(NxList):
|
|
def __init__(self, list, container_type=None, member=None, reverse=False):
|
|
"""Initialize the singly linked list iterator. Optionally specify the container type and member name."""
|
|
if reverse:
|
|
raise ValueError(
|
|
"Reverse iteration is not supported for singly linked lists.\n"
|
|
)
|
|
super().__init__(list, container_type, member, reverse)
|
|
|
|
def _get_first(self):
|
|
# for ((p) = (q)->head; (p) != NULL; (p) = (p)->flink)
|
|
return self.list["head"] or None
|
|
|
|
def _get_next(self, node):
|
|
# if not node["flink"], then return None, to indicate end of list
|
|
return node["flink"] or None
|
|
|
|
|
|
class NxDQueue(NxList):
|
|
def __init__(self, list, container_type=None, member=None, reverse=False):
|
|
"""Initialize the doubly linked list iterator. Optionally specify the container type and member name."""
|
|
super().__init__(list, container_type, member, reverse)
|
|
|
|
def _get_first(self):
|
|
head = self.list["head"]
|
|
tail = self.list["tail"]
|
|
|
|
first = head if not self.reverse else tail
|
|
return first or None
|
|
|
|
def _get_next(self, node):
|
|
# for ((p) = (q)->head; (p) != NULL; (p) = (p)->flink)
|
|
return node["flink"] or None
|
|
|
|
def _get_prev(self, node):
|
|
# for ((p) = (q)->tail; (p) != NULL; (p) = (p)->blink)
|
|
return node["blink"] or None
|
|
|
|
|
|
def list_check(head):
|
|
"""Check the consistency of a list"""
|
|
nb = 0
|
|
|
|
if head.type == list_node_type.pointer():
|
|
head = head.dereference()
|
|
elif head.type != list_node_type:
|
|
raise gdb.GdbError("argument must be of type (struct list_node [*])")
|
|
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
|
|
|
|
|
|
def sq_is_empty(sq):
|
|
"""Check if a singly linked list is empty"""
|
|
if sq.type == sq_queue_type.pointer():
|
|
sq = sq.dereference()
|
|
elif sq.type != sq_queue_type:
|
|
return False
|
|
|
|
if sq["head"] == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def sq_check(sq, verbose=True) -> int:
|
|
"""Check the consistency of a singly linked list"""
|
|
nb = 0
|
|
if sq.type == sq_queue_type.pointer():
|
|
sq = sq.dereference()
|
|
elif sq.type != sq_queue_type:
|
|
gdb.write("Must be struct sq_queue not {}".format(sq.type))
|
|
return nb
|
|
|
|
if sq["head"] == 0:
|
|
if verbose:
|
|
gdb.write("sq_queue head is empty {}\n".format(sq.address))
|
|
return nb
|
|
|
|
nodes = set()
|
|
entry = sq["head"].dereference()
|
|
try:
|
|
while entry.address:
|
|
nb += 1
|
|
if int(entry.address) in nodes:
|
|
gdb.write("sq_queue is circular: {}\n".format(entry.address))
|
|
return nb
|
|
nodes.add(int(entry.address))
|
|
entry = entry["flink"].dereference()
|
|
except gdb.MemoryError:
|
|
gdb.write("entry address is unaccessible {}\n".format(entry.address))
|
|
return nb
|
|
|
|
if int(sq["tail"]) not in nodes:
|
|
gdb.write("sq_queue tail is not in the list {}\n".format(sq["tail"]))
|
|
return nb
|
|
if sq["tail"]["flink"] != 0:
|
|
gdb.write("sq_queue tail->flink is not null {}\n".format(sq["tail"]))
|
|
return nb
|
|
|
|
if verbose:
|
|
gdb.write("sq_queue is consistent: {} node(s)\n".format(nb))
|
|
return nb
|
|
|
|
|
|
def sq_count(sq) -> int:
|
|
"""Count sq elements, abort if check failed"""
|
|
return sq_check(sq, verbose=False)
|
|
|
|
|
|
def dq_for_every(dq, entry=None):
|
|
"""Iterate over a doubly linked list"""
|
|
if dq.type == dq_queue_type.pointer():
|
|
dq = dq.dereference()
|
|
elif dq.type != dq_queue_type:
|
|
gdb.write("Must be struct dq_queue not {}".format(dq.type))
|
|
return
|
|
|
|
if dq["head"] == 0:
|
|
return
|
|
|
|
if not entry:
|
|
entry = dq["head"].dereference()
|
|
|
|
while entry.address:
|
|
yield entry.address
|
|
entry = entry["flink"].dereference()
|
|
|
|
|
|
def dq_check(dq):
|
|
"""Check the consistency of a doubly linked list"""
|
|
nb = 0
|
|
if dq.type == dq_queue_type.pointer():
|
|
dq = dq.dereference()
|
|
elif dq.type != dq_queue_type:
|
|
gdb.write("Must be struct dq_queue not {}".format(dq.type))
|
|
return
|
|
|
|
if dq["head"] == 0:
|
|
gdb.write("dq_queue head is empty {}\n".format(dq.address))
|
|
return
|
|
|
|
nodes = set()
|
|
entry = dq["head"].dereference()
|
|
try:
|
|
while entry.address:
|
|
nb += 1
|
|
if int(entry.address) in nodes:
|
|
gdb.write("dq_queue is circular: {}\n".format(entry.address))
|
|
return
|
|
nodes.add(int(entry.address))
|
|
entry = entry["flink"].dereference()
|
|
except gdb.MemoryError:
|
|
gdb.write("entry address is unaccessible {}\n".format(entry.address))
|
|
return
|
|
|
|
if int(dq["tail"]) not in nodes:
|
|
gdb.write("dq_queue tail is not in the list {}\n".format(dq["tail"]))
|
|
return
|
|
if dq["tail"]["flink"] != 0:
|
|
gdb.write("dq_queue tail->flink is not null {}\n".format(dq["tail"]))
|
|
return
|
|
|
|
gdb.write("dq_queue is consistent: {} node(s)\n".format(nb))
|
|
|
|
|
|
class ListCheck(gdb.Command):
|
|
"""Verify a list consistency"""
|
|
|
|
def __init__(self):
|
|
super().__init__("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("nx-list-check takes one argument")
|
|
|
|
obj = gdb.parse_and_eval(argv[0])
|
|
if obj.type == list_node_type.pointer():
|
|
list_check(obj)
|
|
elif obj.type == sq_queue_type.pointer():
|
|
sq_check(obj)
|
|
elif obj.type == dq_queue_type.pointer():
|
|
dq_check(obj)
|
|
else:
|
|
raise gdb.GdbError("Invalid argument type: {}".format(obj.type))
|
|
|
|
|
|
class ForeachListEntry(gdb.Command):
|
|
"""Dump list members for a given list"""
|
|
|
|
def __init__(self):
|
|
super().__init__("foreach list", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
argv = gdb.string_to_argv(arg)
|
|
|
|
parser = argparse.ArgumentParser(description="Iterate the items in list")
|
|
parser.add_argument("head", type=str, help="List head")
|
|
parser.add_argument(
|
|
"-n",
|
|
"--next",
|
|
type=str,
|
|
help="The name of the next pointer in the list node",
|
|
default="next",
|
|
)
|
|
parser.add_argument(
|
|
"-c", "--container", type=str, default=None, help="Optional container type"
|
|
)
|
|
parser.add_argument(
|
|
"-m", "--member", type=str, default=None, help="Member name in container"
|
|
)
|
|
parser.add_argument(
|
|
"-e",
|
|
"--element",
|
|
type=str,
|
|
help="Only dump this element in array member struct.",
|
|
default=None,
|
|
)
|
|
try:
|
|
args = parser.parse_args(argv)
|
|
except SystemExit:
|
|
gdb.write("Invalid arguments\n")
|
|
return
|
|
|
|
pointer = gdb.parse_and_eval(args.head)
|
|
node = pointer
|
|
i = 0
|
|
while node:
|
|
entry = (
|
|
utils.container_of(node, args.container, args.member)
|
|
if args.container
|
|
else node
|
|
)
|
|
entry = entry.dereference()
|
|
entry = entry[args.element] if args.element else entry
|
|
gdb.write(
|
|
f"{i} *({entry.type} *){hex(entry.address)} {entry.format_string(styling=True)}\n"
|
|
)
|
|
i += 1
|
|
node = node[args.next]
|
|
if node == pointer:
|
|
break
|
|
|
|
|
|
class ForeachArray(gdb.Command):
|
|
"""Dump array members."""
|
|
|
|
def __init__(self):
|
|
super().__init__("foreach array", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
argv = gdb.string_to_argv(arg)
|
|
|
|
parser = argparse.ArgumentParser(description="Iterate the items in array")
|
|
parser.add_argument("head", type=str, help="List head")
|
|
parser.add_argument(
|
|
"-l",
|
|
"--length",
|
|
type=int,
|
|
help="The array length",
|
|
default=None,
|
|
)
|
|
parser.add_argument(
|
|
"-e",
|
|
"--element",
|
|
type=str,
|
|
help="Only dump this element in array member struct.",
|
|
default=None,
|
|
)
|
|
try:
|
|
args = parser.parse_args(argv)
|
|
except SystemExit:
|
|
gdb.write("Invalid arguments\n")
|
|
return
|
|
|
|
pointer = gdb.parse_and_eval(args.head)
|
|
node = pointer
|
|
len = args.length if args.length else utils.nitems(pointer)
|
|
for i in range(len):
|
|
entry = node[i][args.element] if args.element else node[i]
|
|
gdb.write(f"{i}: {entry.format_string(styling=True)}\n")
|