diff mbox series

[ovs-dev,1/1] utilities: Add gdb debug commands to dump lists and pmd info

Message ID 8e6ce0378d64bcb21fa24f6fbd7b41dbccc1d6e7.1526484978.git.echaudro@redhat.com
State Accepted
Headers show
Series [ovs-dev,1/1] utilities: Add gdb debug commands to dump lists and pmd info | expand

Commit Message

Eelco Chaudron May 16, 2018, 3:37 p.m. UTC
Adds back-end support for walking ovs cmaps, and the following
commands to the gdb script:

- Dump all poll_thread info added to a specific struct dp_netdev*.
  Usage: ovs_dump_dp_netdev_poll_threads <struct dp_netdev *>

- Dump all nodes of an ovs_list:
    Usage: ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]}

    For example dump all the none quiescent OvS RCU threads:

      (gdb) ovs_dump_ovs_list &ovsrcu_threads
      (struct ovs_list *) 0x7f2a14000900
      (struct ovs_list *) 0x7f2acc000900
      (struct ovs_list *) 0x7f2a680668d0

    This is not very useful, so please use this with the container_of mode:

      (gdb) ovs_dump_ovs_list &ovsrcu_threads 'struct ovsrcu_perthread' list_node
      (struct ovsrcu_perthread *) 0x7f2a14000900
      (struct ovsrcu_perthread *) 0x7f2acc000900
      (struct ovsrcu_perthread *) 0x7f2a680668d0

    Now you can manually use the print command to show the content, or use the
    dump option to dump the structure for all nodes:

      (gdb) ovs_dump_ovs_list &ovsrcu_threads 'struct ovsrcu_perthread' list_node dump
      (struct ovsrcu_perthread *) 0x7f2a14000900 =
        {list_node = {prev = 0xf48e80 <ovsrcu_threads>, next = 0x7f2acc000900}, mutex...

      (struct ovsrcu_perthread *) 0x7f2acc000900 =
        {list_node = {prev = 0x7f2a14000900, next = 0x7f2a680668d0}, mutex ...

      (struct ovsrcu_perthread *) 0x7f2a680668d0 =
        {list_node = {prev = 0x7f2acc000900, next = 0xf48e80 <ovsrcu_threads>}, ...

Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
---
 utilities/gdb/ovs_gdb.py | 167 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 167 insertions(+)

Comments

Ben Pfaff May 16, 2018, 10:43 p.m. UTC | #1
On Wed, May 16, 2018 at 05:37:11PM +0200, Eelco Chaudron wrote:
> Adds back-end support for walking ovs cmaps, and the following
> commands to the gdb script:

Thanks.  I applied this to master.
diff mbox series

Patch

diff --git a/utilities/gdb/ovs_gdb.py b/utilities/gdb/ovs_gdb.py
index a604ccb54..69361281d 100644
--- a/utilities/gdb/ovs_gdb.py
+++ b/utilities/gdb/ovs_gdb.py
@@ -23,8 +23,10 @@ 
 #    - ovs_dump_bridge [ports|wanted]
 #    - ovs_dump_bridge_ports <struct bridge *>
 #    - ovs_dump_dp_netdev [ports]
+#    - ovs_dump_dp_netdev_poll_threads <struct dp_netdev *>
 #    - ovs_dump_dp_netdev_ports <struct dp_netdev *>
 #    - ovs_dump_netdev
+#    - ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]}
 #
 #  Example:
 #    $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd)
@@ -103,6 +105,63 @@  def container_of(ptr, typeobj, member):
             offset_of(typeobj, member)).cast(typeobj)
 
 
+#
+# Class that will provide an iterator over an OVS cmap.
+#
+class ForEachCMAP(object):
+    def __init__(self, cmap, typeobj=None, member='node'):
+        self.cmap = cmap
+        self.first = True
+        self.typeobj = typeobj
+        self.member = member
+        # Cursor values
+        self.node = 0
+        self.bucket_idx = 0
+        self.entry_idx = 0
+
+    def __iter__(self):
+        return self
+
+    def __get_CMAP_K(self):
+        ptr_type = gdb.lookup_type("void").pointer()
+        return (64 - 4) / (4 + ptr_type.sizeof)
+
+    def __next(self):
+        ipml = self.cmap['impl']['p']
+
+        if self.node != 0:
+            self.node = self.node['next']['p']
+            if self.node != 0:
+                return
+
+        while self.bucket_idx <= ipml['mask']:
+            buckets = ipml['buckets'][self.bucket_idx]
+            while self.entry_idx < self.__get_CMAP_K():
+                self.node = buckets['nodes'][self.entry_idx]['next']['p']
+                self.entry_idx += 1
+                if self.node != 0:
+                    return
+
+            self.bucket_idx += 1
+            self.entry_idx = 0
+
+        raise StopIteration
+
+    def next(self):
+        ipml = self.cmap['impl']['p']
+        if ipml['n'] == 0:
+            raise StopIteration
+
+        self.__next()
+
+        if self.typeobj is None:
+            return self.node
+
+        return container_of(self.node,
+                            gdb.lookup_type(self.typeobj).pointer(),
+                            self.member)
+
+
 #
 # Class that will provide an iterator over an OVS hmap.
 #
@@ -327,6 +386,39 @@  class CmdDumpDpNetdev(gdb.Command):
                     CmdDumpDpNetdevPorts.display_single_port(node, 4)
 
 
+#
+# Implements the GDB "ovs_dump_dp_netdev_poll_threads" command
+#
+class CmdDumpDpNetdevPollThreads(gdb.Command):
+    """Dump all poll_thread info added to a specific struct dp_netdev*.
+    Usage: ovs_dump_dp_netdev_poll_threads <struct dp_netdev *>
+    """
+    def __init__(self):
+        super(CmdDumpDpNetdevPollThreads, self).__init__(
+            "ovs_dump_dp_netdev_poll_threads",
+            gdb.COMMAND_DATA)
+
+    @staticmethod
+    def display_single_poll_thread(pmd_thread, indent=0):
+        indent = " " * indent
+        print("{}(struct dp_netdev_pmd_thread *) {}: core_id = {:s}, "
+              "numa_id {}".format(indent,
+                                  pmd_thread, pmd_thread['core_id'],
+                                  pmd_thread['numa_id']))
+
+    def invoke(self, arg, from_tty):
+        arg_list = gdb.string_to_argv(arg)
+        if len(arg_list) != 1:
+            print("usage: ovs_dump_dp_netdev_poll_threads "
+                  "<struct dp_netdev *>")
+            return
+        dp_netdev = gdb.parse_and_eval(arg_list[0]).cast(
+            gdb.lookup_type('struct dp_netdev').pointer())
+        for node in ForEachCMAP(dp_netdev['poll_threads'],
+                                "struct dp_netdev_pmd_thread", "node"):
+            self.display_single_poll_thread(node)
+
+
 #
 # Implements the GDB "ovs_dump_dp_netdev_ports" command
 #
@@ -395,11 +487,86 @@  class CmdDumpNetdev(gdb.Command):
             self.display_single_netdev(netdev)
 
 
+#
+# Implements the GDB "ovs_dump_ovs_list" command
+#
+class CmdDumpOvsList(gdb.Command):
+    """Dump all nodes of an ovs_list give
+    Usage: ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]}
+
+    For example dump all the none quiescent OvS RCU threads:
+
+      (gdb) ovs_dump_ovs_list &ovsrcu_threads
+      (struct ovs_list *) 0x7f2a14000900
+      (struct ovs_list *) 0x7f2acc000900
+      (struct ovs_list *) 0x7f2a680668d0
+
+    This is not very useful, so please use this with the container_of mode:
+
+      (gdb) ovs_dump_ovs_list &ovsrcu_threads 'struct ovsrcu_perthread' list_node
+      (struct ovsrcu_perthread *) 0x7f2a14000900
+      (struct ovsrcu_perthread *) 0x7f2acc000900
+      (struct ovsrcu_perthread *) 0x7f2a680668d0
+
+    Now you can manually use the print command to show the content, or use the
+    dump option to dump the structure for all nodes:
+
+      (gdb) ovs_dump_ovs_list &ovsrcu_threads 'struct ovsrcu_perthread' list_node dump
+      (struct ovsrcu_perthread *) 0x7f2a14000900 =
+        {list_node = {prev = 0xf48e80 <ovsrcu_threads>, next = 0x7f2acc000900}, mutex...
+
+      (struct ovsrcu_perthread *) 0x7f2acc000900 =
+        {list_node = {prev = 0x7f2a14000900, next = 0x7f2a680668d0}, mutex ...
+
+      (struct ovsrcu_perthread *) 0x7f2a680668d0 =
+        {list_node = {prev = 0x7f2acc000900, next = 0xf48e80 <ovsrcu_threads>}, ...
+    """
+    def __init__(self):
+        super(CmdDumpOvsList, self).__init__("ovs_dump_ovs_list",
+                                             gdb.COMMAND_DATA)
+
+    def invoke(self, arg, from_tty):
+        arg_list = gdb.string_to_argv(arg)
+        typeobj = None
+        member = None
+        dump = False
+
+        if len(arg_list) != 1 and len(arg_list) != 3 and len(arg_list) != 4:
+            print("usage: ovs_dump_ovs_list <struct ovs_list *> "
+                  "{[<structure>] [<member>] {dump}]}")
+            return
+
+        header = gdb.parse_and_eval(arg_list[0]).cast(
+            gdb.lookup_type('struct ovs_list').pointer())
+
+        if len(arg_list) >= 3:
+            typeobj = arg_list[1]
+            member = arg_list[2]
+            if len(arg_list) == 4 and arg_list[3] == "dump":
+                dump = True
+
+        for node in ForEachLIST(header.dereference()):
+            if typeobj is None or member is None:
+                print("(struct ovs_list *) {}".format(node))
+            else:
+                print("({} *) {} =".format(
+                    typeobj,
+                    container_of(node,
+                                 gdb.lookup_type(typeobj).pointer(), member)))
+                if dump:
+                    print("  {}\n".format(container_of(
+                        node,
+                        gdb.lookup_type(typeobj).pointer(),
+                        member).dereference()))
+
+
 #
 # Initialize all GDB commands
 #
 CmdDumpBridge()
 CmdDumpBridgePorts()
 CmdDumpDpNetdev()
+CmdDumpDpNetdevPollThreads()
 CmdDumpDpNetdevPorts()
 CmdDumpNetdev()
+CmdDumpOvsList()