2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 18:07:40 +00:00
ovs/utilities/gdb/ovs_gdb.py

406 lines
13 KiB
Python
Raw Normal View History

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>
2018-05-09 13:47:26 +02:00
#
# 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()