summaryrefslogtreecommitdiff
path: root/utilities/gdb/ovs_gdb.py
diff options
context:
space:
mode:
authorEelco Chaudron <echaudro@redhat.com>2018-05-09 13:47:26 +0200
committerBen Pfaff <blp@ovn.org>2018-05-09 13:14:34 -0700
commit0a3348d1a9e3f8b1bc943ed21b5d3c47258cf669 (patch)
treed2e1447de25a8f5f0bbeec15a6af1f99e6345e3b /utilities/gdb/ovs_gdb.py
parent2c00b26f10090f972510eb17ee0f0be1e8dcc91b (diff)
downloadopenvswitch-0a3348d1a9e3f8b1bc943ed21b5d3c47258cf669.tar.gz
utilities: Add some GDB macros for ovs-vswitchd
This commit adds basic GDB macro's for ovs-vswitchd: - ovs_dump_bridge [ports|wanted] - ovs_dump_bridge_ports <struct bridge *> - ovs_dump_dp_netdev [ports] - ovs_dump_dp_netdev_ports <struct dp_netdev *> - ovs_dump_netdev These dump functions show limited info, but you can simply cut/paste the address and get the full structure info. For example: (gdb) ovs_dump_netdev (struct netdev *) 0x555771ed89e0: name = ovs-netdev , auto_classified = false, netdev_class = 0x5557714413c0 <netdev_tap_class> (struct netdev *) 0x555771fc62a0: name = ovs_pvp_br0 , auto_classified = false, netdev_class = 0x5557714413c0 <netdev_tap_class> (struct netdev *) 0x555771fc9660: name = vnet0 , auto_classified = true , netdev_class = 0x555771445e00 <netdev_linux_class> (struct netdev *) 0x555771fc78d0: name = virbr0 , auto_classified = true , netdev_class = 0x555771445e00 <netdev_linux_class> (struct netdev *) 0x7fbefffb5540: name = dpdk0 , auto_classified = false, netdev_class = 0x5557714419e0 <dpdk_class> (struct netdev *) 0x555771fc98b0: name = em3 , auto_classified = true , netdev_class = 0x555771445e00 <netdev_linux_class> (struct netdev *) 0x7fbea0a31c40: name = vhost0 , auto_classified = false, netdev_class = 0x555771442040 <dpdk_vhost_client_class> (gdb) p *((struct netdev *) 0x7fbefffb5540) $1 = {name = 0x555771ecef70 "dpdk0", netdev_class = 0x5557714419e0 <dpdk_class>, auto_classified = false, mtu_user_config = true, ref_cnt = 2, change_seq = 12, reconfigure_seq = 0x555771ecf2e0, last_reconfigure_seq = 110, n_txq = 2, n_rxq = 1, node = 0x555771efafe0, saved_flags_list = {prev = 0x7fbefffb5580, next = 0x7fbefffb5580}} Signed-off-by: Eelco Chaudron <echaudro@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'utilities/gdb/ovs_gdb.py')
-rw-r--r--utilities/gdb/ovs_gdb.py405
1 files changed, 405 insertions, 0 deletions
diff --git a/utilities/gdb/ovs_gdb.py b/utilities/gdb/ovs_gdb.py
new file mode 100644
index 000000000..a604ccb54
--- /dev/null
+++ b/utilities/gdb/ovs_gdb.py
@@ -0,0 +1,405 @@
+#
+# Copyright (c) 2018 Eelco Chaudron
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version
+# 2 of the License, or (at your option) any later version.
+#
+# Files name:
+# ovs_gdb.py
+#
+# Description:
+# GDB commands and functions for Open vSwitch debugging
+#
+# Author:
+# Eelco Chaudron
+#
+# Initial Created:
+# 23 April 2018
+#
+# Notes:
+# It implements the following GDB commands:
+# - ovs_dump_bridge [ports|wanted]
+# - ovs_dump_bridge_ports <struct bridge *>
+# - ovs_dump_dp_netdev [ports]
+# - ovs_dump_dp_netdev_ports <struct dp_netdev *>
+# - ovs_dump_netdev
+#
+# Example:
+# $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd)
+# (gdb) source ./utilities/gdb/ovs_gdb.py
+#
+# (gdb) ovs_dump_<TAB>
+# ovs_dump_bridge ovs_dump_bridge_ports ovs_dump_dp_netdev
+# ovs_dump_dp_netdev_ports ovs_dump_netdev
+#
+# (gdb) ovs_dump_bridge
+# (struct bridge *) 0x5615471ed2e0: name = br2, type = system
+# (struct bridge *) 0x561547166350: name = br0, type = system
+# (struct bridge *) 0x561547216de0: name = ovs_pvp_br0, type = netdev
+# (struct bridge *) 0x5615471d0420: name = br1, type = system
+#
+# (gdb) p *(struct bridge *) 0x5615471d0420
+# $1 = {node = {hash = 24776443, next = 0x0}, name = 0x5615471cca90 "br1",
+# type = 0x561547163bb0 "system",
+# ...
+# ...
+#
+
+import gdb
+
+
+#
+# The container_of code below is a copied from the Linux kernel project file,
+# scripts/gdb/linux/utils.py. It has the following copyright header:
+#
+# # gdb helper commands and functions for Linux kernel debugging
+# #
+# # common utilities
+# #
+# # Copyright (c) Siemens AG, 2011-2013
+# #
+# # Authors:
+# # Jan Kiszka <jan.kiszka@siemens.com>
+# #
+# # This work is licensed under the terms of the GNU GPL version 2.
+#
+class CachedType:
+ def __init__(self, name):
+ self._type = None
+ self._name = name
+
+ def _new_objfile_handler(self, event):
+ self._type = None
+ gdb.events.new_objfile.disconnect(self._new_objfile_handler)
+
+ def get_type(self):
+ if self._type is None:
+ self._type = gdb.lookup_type(self._name)
+ if self._type is None:
+ raise gdb.GdbError(
+ "cannot resolve type '{0}'".format(self._name))
+ if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'):
+ gdb.events.new_objfile.connect(self._new_objfile_handler)
+ return self._type
+
+
+long_type = CachedType("long")
+
+
+def get_long_type():
+ global long_type
+ return long_type.get_type()
+
+
+def offset_of(typeobj, field):
+ element = gdb.Value(0).cast(typeobj)
+ return int(str(element[field].address).split()[0], 16)
+
+
+def container_of(ptr, typeobj, member):
+ return (ptr.cast(get_long_type()) -
+ offset_of(typeobj, member)).cast(typeobj)
+
+
+#
+# Class that will provide an iterator over an OVS hmap.
+#
+class ForEachHMAP(object):
+ def __init__(self, hmap, typeobj=None, member='node'):
+ self.hmap = hmap
+ self.node = None
+ self.first = True
+ self.typeobj = typeobj
+ self.member = member
+
+ def __iter__(self):
+ return self
+
+ def __next(self, start):
+ for i in range(start, (self.hmap['mask'] + 1)):
+ self.node = self.hmap['buckets'][i]
+ if self.node != 0:
+ return
+
+ raise StopIteration
+
+ def next(self):
+ #
+ # In the real implementation the n values is never checked,
+ # however when debugging we do, as we might try to access
+ # a hmap that has been cleared/hmap_destroy().
+ #
+ if self.hmap['n'] <= 0:
+ raise StopIteration
+
+ if self.first:
+ self.first = False
+ self.__next(0)
+ elif self.node['next'] != 0:
+ self.node = self.node['next']
+ else:
+ self.__next((self.node['hash'] & self.hmap['mask']) + 1)
+
+ 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 shash.
+#
+class ForEachSHASH(ForEachHMAP):
+ def __init__(self, shash, typeobj=None):
+
+ self.data_typeobj = typeobj
+
+ super(ForEachSHASH, self).__init__(shash['map'],
+ "struct shash_node", "node")
+
+ def next(self):
+ node = super(ForEachSHASH, self).next()
+
+ if self.data_typeobj is None:
+ return node
+
+ return node['data'].cast(gdb.lookup_type(self.data_typeobj).pointer())
+
+
+#
+# Class that will provide an iterator over an OVS list.
+#
+class ForEachLIST():
+ def __init__(self, list, typeobj=None, member='node'):
+ self.list = list
+ self.node = list
+ self.typeobj = typeobj
+ self.member = member
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if self.list.address == self.node['next']:
+ raise StopIteration
+
+ self.node = self.node['next']
+
+ if self.typeobj is None:
+ return self.node
+
+ return container_of(self.node,
+ gdb.lookup_type(self.typeobj).pointer(),
+ self.member)
+
+
+#
+# Implements the GDB "ovs_dump_bridges" command
+#
+class CmdDumpBridge(gdb.Command):
+ """Dump all configured bridges.
+ Usage: ovs_dump_bridge [ports|wanted]
+ """
+ def __init__(self):
+ super(CmdDumpBridge, self).__init__("ovs_dump_bridge",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ ports = False
+ wanted = False
+ arg_list = gdb.string_to_argv(arg)
+ if len(arg_list) > 1 or \
+ (len(arg_list) == 1 and arg_list[0] != "ports" and
+ arg_list[0] != "wanted"):
+ print("usage: ovs_dump_bridge [ports|wanted]")
+ return
+ elif len(arg_list) == 1:
+ if arg_list[0] == "ports":
+ ports = True
+ else:
+ wanted = True
+
+ dp_netdevs = gdb.lookup_symbol('all_bridges')[0]
+ if dp_netdevs is None or not dp_netdevs.is_variable:
+ print("Can't find all_bridges global variable, are you sure "
+ "your debugging OVS?")
+ return
+ all_bridges = gdb.parse_and_eval('all_bridges')
+ for node in ForEachHMAP(all_bridges,
+ "struct bridge", "node"):
+ print("(struct bridge *) {}: name = {}, type = {}".
+ format(node, node['name'].string(),
+ node['type'].string()))
+
+ if ports:
+ for port in ForEachHMAP(node['ports'],
+ "struct port", "hmap_node"):
+ CmdDumpBridgePorts.display_single_port(port, 4)
+
+ if wanted:
+ for port in ForEachSHASH(node['wanted_ports'],
+ typeobj="struct ovsrec_port"):
+ print(" (struct ovsrec_port *) {}: name = {}".
+ format(port, port['name'].string()))
+ # print port.dereference()
+
+
+#
+# Implements the GDB "ovs_dump_bridge_ports" command
+#
+class CmdDumpBridgePorts(gdb.Command):
+ """Dump all ports added to a specific struct bridge*.
+ Usage: ovs_dump_bridge_ports <struct bridge *>
+ """
+ def __init__(self):
+ super(CmdDumpBridgePorts, self).__init__("ovs_dump_bridge_ports",
+ gdb.COMMAND_DATA)
+
+ @staticmethod
+ def display_single_port(port, indent=0):
+ indent = " " * indent
+ port = port.cast(gdb.lookup_type('struct port').pointer())
+ print("{}(struct port *) {}: name = {}, brige = (struct bridge *) {}".
+ format(indent, port, port['name'].string(),
+ port['bridge']))
+
+ indent += " " * 4
+ for iface in ForEachLIST(port['ifaces'], "struct iface", "port_elem"):
+ print("{}(struct iface *) {}: name = {}, ofp_port = {}, "
+ "netdev = (struct netdev *) {}".
+ format(indent, iface, iface['name'],
+ iface['ofp_port'], iface['netdev']))
+
+ def invoke(self, arg, from_tty):
+ arg_list = gdb.string_to_argv(arg)
+ if len(arg_list) != 1:
+ print("usage: ovs_dump_bridge_ports <struct bridge *>")
+ return
+ bridge = gdb.parse_and_eval(arg_list[0]).cast(
+ gdb.lookup_type('struct bridge').pointer())
+ for node in ForEachHMAP(bridge['ports'],
+ "struct port", "hmap_node"):
+ self.display_single_port(node)
+
+
+#
+# Implements the GDB "ovs_dump_dp_netdev" command
+#
+class CmdDumpDpNetdev(gdb.Command):
+ """Dump all registered dp_netdev structures.
+ Usage: ovs_dump_dp_netdev [ports]
+ """
+ def __init__(self):
+ super(CmdDumpDpNetdev, self).__init__("ovs_dump_dp_netdev",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ ports = False
+ arg_list = gdb.string_to_argv(arg)
+ if len(arg_list) > 1 or \
+ (len(arg_list) == 1 and arg_list[0] != "ports"):
+ print("usage: ovs_dump_dp_netdev [ports]")
+ return
+ elif len(arg_list) == 1:
+ ports = True
+
+ dp_netdevs = gdb.lookup_symbol('dp_netdevs')[0]
+ if dp_netdevs is None or not dp_netdevs.is_variable:
+ print("Can't find dp_netdevs global variable, are you sure "
+ "your debugging OVS?")
+ return
+ dp_netdevs = gdb.parse_and_eval('dp_netdevs')
+ for node in ForEachSHASH(dp_netdevs):
+ dp = node['data'].cast(
+ gdb.lookup_type('struct dp_netdev').pointer())
+
+ print("(struct dp_netdev *) {}: name = {}, class = "
+ "(struct dpif_class *) {}".
+ format(dp, dp['name'], dp['class']))
+
+ if ports:
+ for node in ForEachHMAP(dp['ports'],
+ "struct dp_netdev_port", "node"):
+ CmdDumpDpNetdevPorts.display_single_port(node, 4)
+
+
+#
+# Implements the GDB "ovs_dump_dp_netdev_ports" command
+#
+class CmdDumpDpNetdevPorts(gdb.Command):
+ """Dump all ports added to a specific struct dp_netdev*.
+ Usage: ovs_dump_dp_netdev_ports <struct dp_netdev *>
+ """
+ def __init__(self):
+ super(CmdDumpDpNetdevPorts, self).__init__("ovs_dump_dp_netdev_ports",
+ gdb.COMMAND_DATA)
+
+ @staticmethod
+ def display_single_port(port, indent=0):
+ indent = " " * indent
+ print("{}(struct dp_netdev_port *) {}:".format(indent, port))
+ print("{} port_no = {}, n_rxq = {}, type = {}".
+ format(indent, port['port_no'], port['n_rxq'],
+ port['type'].string()))
+ print("{} netdev = (struct netdev *) {}: name = {}, "
+ "n_txq/rxq = {}/{}".
+ format(indent, port['netdev'],
+ port['netdev']['name'].string(),
+ port['netdev']['n_txq'],
+ port['netdev']['n_rxq']))
+
+ def invoke(self, arg, from_tty):
+ arg_list = gdb.string_to_argv(arg)
+ if len(arg_list) != 1:
+ print("usage: ovs_dump_dp_netdev_ports <struct dp_netdev *>")
+ return
+ dp_netdev = gdb.parse_and_eval(arg_list[0]).cast(
+ gdb.lookup_type('struct dp_netdev').pointer())
+ for node in ForEachHMAP(dp_netdev['ports'],
+ "struct dp_netdev_port", "node"):
+ # print node.dereference()
+ self.display_single_port(node)
+
+
+#
+# Implements the GDB "ovs_dump_netdev" command
+#
+class CmdDumpNetdev(gdb.Command):
+ """Dump all registered netdev structures.
+ Usage: ovs_dump_netdev
+ """
+ def __init__(self):
+ super(CmdDumpNetdev, self).__init__("ovs_dump_netdev",
+ gdb.COMMAND_DATA)
+
+ @staticmethod
+ def display_single_netdev(netdev, indent=0):
+ indent = " " * indent
+ print("{}(struct netdev *) {}: name = {:15}, auto_classified = {:5}, "
+ "netdev_class = {}".
+ format(indent, netdev, netdev['name'].string(),
+ netdev['auto_classified'], netdev['netdev_class']))
+
+ def invoke(self, arg, from_tty):
+ netdev_shash = gdb.lookup_symbol('netdev_shash')[0]
+ if netdev_shash is None or not netdev_shash.is_variable:
+ print("Can't find netdev_shash global variable, are you sure "
+ "your debugging OVS?")
+ return
+ netdev_shash = gdb.parse_and_eval('netdev_shash')
+ for netdev in ForEachSHASH(netdev_shash, "struct netdev"):
+ self.display_single_netdev(netdev)
+
+
+#
+# Initialize all GDB commands
+#
+CmdDumpBridge()
+CmdDumpBridgePorts()
+CmdDumpDpNetdev()
+CmdDumpDpNetdevPorts()
+CmdDumpNetdev()