diff options
author | Eelco Chaudron <echaudro@redhat.com> | 2018-05-09 13:47:26 +0200 |
---|---|---|
committer | Ben Pfaff <blp@ovn.org> | 2018-05-09 13:14:34 -0700 |
commit | 0a3348d1a9e3f8b1bc943ed21b5d3c47258cf669 (patch) | |
tree | d2e1447de25a8f5f0bbeec15a6af1f99e6345e3b /utilities | |
parent | 2c00b26f10090f972510eb17ee0f0be1e8dcc91b (diff) | |
download | openvswitch-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')
-rw-r--r-- | utilities/automake.mk | 1 | ||||
-rw-r--r-- | utilities/gdb/ovs_gdb.py | 405 |
2 files changed, 406 insertions, 0 deletions
diff --git a/utilities/automake.mk b/utilities/automake.mk index 60cf1c5ed..afdbdb9f4 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -38,6 +38,7 @@ noinst_SCRIPTS += utilities/ovs-sim utilities/ovs-lib: $(top_builddir)/config.status EXTRA_DIST += \ + utilities/gdb/ovs_gdb.py \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-check-dead-ifs.in \ utilities/ovs-ctl.in \ 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() |