2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00
ovs/utilities/gdb/ovs_gdb.py
Rosemarie O'Riorden bd90524550 Remove Python 2 leftovers.
Fixes: 1ca0323e7c29 ("Require Python 3 and remove support for Python 2.")
Reported at: https://bugzilla.redhat.com/show_bug.cgi?id=1949875
Signed-off-by: Rosemarie O'Riorden <roriorde@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-06-22 21:29:57 +02:00

1327 lines
44 KiB
Python

#
# 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_poll_threads <struct dp_netdev *>
# - ovs_dump_dp_netdev_ports <struct dp_netdev *>
# - ovs_dump_dp_provider
# - ovs_dump_netdev
# - ovs_dump_netdev_provider
# - ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]}
# - ovs_dump_simap <struct simap *>
# - ovs_dump_smap <struct smap *>
# - ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short}
# - ovs_show_fdb {[<bridge_name>] {dbg} {hash}}
# - ovs_show_upcall {dbg}
#
# 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
import sys
import uuid
#
# Global #define's from OVS which might need updating based on a version.
#
N_UMAPS = 512
#
# 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)
def get_global_variable(name):
var = gdb.lookup_symbol(name)[0]
if var is None or not var.is_variable:
print("Can't find {} global variable, are you sure "
"you are debugging OVS?".format(name))
return None
return gdb.parse_and_eval(name)
def get_time_msec():
# There is no variable that stores the current time each iteration,
# to get a decent time time_now() value. For now we take the global
# "coverage_run_time" value, which is the current time + max 5 seconds
# (COVERAGE_RUN_INTERVAL)
return int(get_global_variable("coverage_run_time")), -5000
def get_time_now():
# See get_time_msec() above
return int(get_global_variable("coverage_run_time"))/1000, -5
def eth_addr_to_string(eth_addr):
return "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(
int(eth_addr['ea'][0]),
int(eth_addr['ea'][1]),
int(eth_addr['ea'][2]),
int(eth_addr['ea'][3]),
int(eth_addr['ea'][4]),
int(eth_addr['ea'][5]))
#
# Simple class to print a spinner on the console
#
class ProgressIndicator(object):
def __init__(self, message=None):
self.spinner = "/-\|"
self.spinner_index = 0
self.message = message
if self.message is not None:
print(self.message, end='')
def update(self):
print("{}\b".format(self.spinner[self.spinner_index]), end='')
sys.stdout.flush()
self.spinner_index += 1
if self.spinner_index >= len(self.spinner):
self.spinner_index = 0
def pause(self):
print("\r\033[K", end='')
def resume(self):
if self.message is not None:
print(self.message, end='')
self.update()
def done(self):
self.pause()
#
# 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)
def next(self):
return self.__next__()
#
# 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)
def next(self):
return self.__next__()
#
# Class that will provide an iterator over an Netlink attributes
#
class ForEachNL():
def __init__(self, nlattrs, nlattrs_len):
self.attr = nlattrs.cast(gdb.lookup_type('struct nlattr').pointer())
self.attr_len = int(nlattrs_len)
def __iter__(self):
return self
def round_up(self, val, round_to):
return int(val) + (round_to - int(val)) % round_to
def __next__(self):
if self.attr is None or \
self.attr_len < 4 or self.attr['nla_len'] < 4 or \
self.attr['nla_len'] > self.attr_len:
#
# Invalid attr set, maybe we should raise an exception?
#
raise StopIteration
attr = self.attr
self.attr_len -= self.round_up(attr['nla_len'], 4)
self.attr = self.attr.cast(gdb.lookup_type('void').pointer()) \
+ self.round_up(attr['nla_len'], 4)
self.attr = self.attr.cast(gdb.lookup_type('struct nlattr').pointer())
return attr
def next(self):
return self.__next__()
#
# 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())
def next(self):
return self.__next__()
#
# Class that will provide an iterator over an OVS simap.
#
class ForEachSIMAP(ForEachHMAP):
def __init__(self, shash):
super(ForEachSIMAP, self).__init__(shash['map'],
"struct simap_node", "node")
def __next__(self):
node = super(ForEachSIMAP, self).__next__()
return node['name'], node['data']
def next(self):
return self.__next__()
#
# Class that will provide an iterator over an OVS smap.
#
class ForEachSMAP(ForEachHMAP):
def __init__(self, shash):
super(ForEachSMAP, self).__init__(shash['map'],
"struct smap_node", "node")
def __next__(self):
node = super(ForEachSMAP, self).__next__()
return node['key'], node['value']
def next(self):
return self.__next__()
#
# 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)
def next(self):
return self.__next__()
#
# Class that will provide an iterator over an OFPACTS.
#
class ForEachOFPACTS():
def __init__(self, ofpacts, ofpacts_len):
self.ofpact = ofpacts.cast(gdb.lookup_type('struct ofpact').pointer())
self.length = int(ofpacts_len)
def __round_up(self, val, round_to):
return int(val) + (round_to - int(val)) % round_to
def __iter__(self):
return self
def __next__(self):
if self.ofpact is None or self.length <= 0:
raise StopIteration
ofpact = self.ofpact
length = self.__round_up(ofpact['len'], 8)
self.length -= length
self.ofpact = self.ofpact.cast(
gdb.lookup_type('void').pointer()) + length
self.ofpact = self.ofpact.cast(
gdb.lookup_type('struct ofpact').pointer())
return ofpact
def next(self):
return self.__next__()
#
# 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
all_bridges = get_global_variable('all_bridges')
if all_bridges is None:
return
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 = {}, bridge = (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 = get_global_variable('dp_netdevs')
if dp_netdevs is None:
return
for dp in ForEachSHASH(dp_netdevs, typeobj=('struct dp_netdev')):
print("(struct dp_netdev *) {}: name = {}, class = "
"(struct dpif_class *) {}".
format(dp, dp['name'].string(), 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_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 = {}, "
"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
#
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_dp_provider" command
#
class CmdDumpDpProvider(gdb.Command):
"""Dump all registered registered_dpif_class structures.
Usage: ovs_dump_dp_provider
"""
def __init__(self):
super(CmdDumpDpProvider, self).__init__("ovs_dump_dp_provider",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
dp_providers = get_global_variable('dpif_classes')
if dp_providers is None:
return
for dp_class in ForEachSHASH(dp_providers,
typeobj="struct registered_dpif_class"):
print("(struct registered_dpif_class *) {}: "
"(struct dpif_class *) 0x{:x} = {{type = {}, ...}}, "
"refcount = {}".
format(dp_class,
int(dp_class['dpif_class']),
dp_class['dpif_class']['type'].string(),
dp_class['refcount']))
#
# 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 = {}, "
"netdev_class = {}".
format(indent, netdev, netdev['name'].string(),
netdev['auto_classified'], netdev['netdev_class']))
def invoke(self, arg, from_tty):
netdev_shash = get_global_variable('netdev_shash')
if netdev_shash is None:
return
for netdev in ForEachSHASH(netdev_shash, "struct netdev"):
self.display_single_netdev(netdev)
#
# Implements the GDB "ovs_dump_netdev_provider" command
#
class CmdDumpNetdevProvider(gdb.Command):
"""Dump all registered netdev providers.
Usage: ovs_dump_netdev_provider
"""
def __init__(self):
super(CmdDumpNetdevProvider, self).__init__("ovs_dump_netdev_provider",
gdb.COMMAND_DATA)
@staticmethod
def is_class_vport_class(netdev_class):
netdev_class = netdev_class.cast(
gdb.lookup_type('struct netdev_class').pointer())
vport_construct = gdb.lookup_symbol('netdev_vport_construct')[0]
if netdev_class['construct'] == vport_construct.value():
return True
return False
@staticmethod
def display_single_netdev_provider(reg_class, indent=0):
indent = " " * indent
print("{}(struct netdev_registered_class *) {}: refcnt = {},".
format(indent, reg_class, reg_class['refcnt']))
print("{} (struct netdev_class *) 0x{:x} = {{type = {}, "
"is_pmd = {}, ...}}, ".
format(indent, int(reg_class['class']),
reg_class['class']['type'].string(),
reg_class['class']['is_pmd']))
if CmdDumpNetdevProvider.is_class_vport_class(reg_class['class']):
vport = container_of(
reg_class['class'],
gdb.lookup_type('struct vport_class').pointer(),
'netdev_class')
if vport['dpif_port'] != 0:
dpif_port = vport['dpif_port'].string()
else:
dpif_port = "\"\""
print("{} (struct vport_class *) 0x{:x} = "
"{{ dpif_port = {}, ... }}".
format(indent, int(vport), dpif_port))
def invoke(self, arg, from_tty):
netdev_classes = get_global_variable('netdev_classes')
if netdev_classes is None:
return
for reg_class in ForEachCMAP(netdev_classes,
"struct netdev_registered_class",
"cmap_node"):
self.display_single_netdev_provider(reg_class)
#
# 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 else ""))
if dump:
print(" {}\n".format(container_of(
node,
gdb.lookup_type(typeobj).pointer(),
member).dereference()))
#
# Implements the GDB "ovs_dump_simap" command
#
class CmdDumpSimap(gdb.Command):
"""Dump all key, value entries of a simap
Usage: ovs_dump_simap <struct simap *>
"""
def __init__(self):
super(CmdDumpSimap, self).__init__("ovs_dump_simap",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
arg_list = gdb.string_to_argv(arg)
if len(arg_list) != 1:
print("ERROR: Missing argument!\n")
print(self.__doc__)
return
simap = gdb.parse_and_eval(arg_list[0]).cast(
gdb.lookup_type('struct simap').pointer())
values = dict()
max_name_len = 0
for name, value in ForEachSIMAP(simap.dereference()):
values[name.string()] = int(value)
if len(name.string()) > max_name_len:
max_name_len = len(name.string())
for name in sorted(values.keys()):
print("{}: {} / 0x{:x}".format(name.ljust(max_name_len),
values[name], values[name]))
#
# Implements the GDB "ovs_dump_smap" command
#
class CmdDumpSmap(gdb.Command):
"""Dump all key, value pairs of a smap
Usage: ovs_dump_smap <struct smap *>
"""
def __init__(self):
super(CmdDumpSmap, self).__init__("ovs_dump_smap",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
arg_list = gdb.string_to_argv(arg)
if len(arg_list) != 1:
print("ERROR: Missing argument!\n")
print(self.__doc__)
return
smap = gdb.parse_and_eval(arg_list[0]).cast(
gdb.lookup_type('struct smap').pointer())
values = dict()
max_key_len = 0
for key, value in ForEachSMAP(smap.dereference()):
values[key.string()] = value.string()
if len(key.string()) > max_key_len:
max_key_len = len(key.string())
for key in sorted(values.keys()):
print("{}: {}".format(key.ljust(max_key_len),
values[key]))
#
# Implements the GDB "ovs_dump_udpif_keys" command
#
class CmdDumpUdpifKeys(gdb.Command):
"""Dump all nodes of an ovs_list give
Usage: ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short}
<udpif_name> : Full name of the udpif's dpif to dump
<udpif_address> : Address of the udpif structure to dump. If both the
<udpif_name> and <udpif_address> are omitted the
available udpif structures are displayed.
short : Only dump ukey structure addresses, no content details
no_count : Do not count the number of ukeys, as it might be slow
"""
def __init__(self):
super(CmdDumpUdpifKeys, self).__init__("ovs_dump_udpif_keys",
gdb.COMMAND_DATA)
def count_all_ukeys(self, udpif):
count = 0
spinner = ProgressIndicator("Counting all ukeys: ")
for j in range(0, N_UMAPS):
spinner.update()
count += udpif['ukeys'][j]['cmap']['impl']['p']['n']
spinner.done()
return count
def dump_all_ukeys(self, udpif, indent=0, short=False):
indent = " " * indent
spinner = ProgressIndicator("Walking ukeys: ")
for j in range(0, N_UMAPS):
spinner.update()
if udpif['ukeys'][j]['cmap']['impl']['p']['n'] != 0:
spinner.pause()
print("{}(struct umap *) {}:".
format(indent, udpif['ukeys'][j].address))
for ukey in ForEachCMAP(udpif['ukeys'][j]['cmap'],
"struct udpif_key", "cmap_node"):
base_str = "{} (struct udpif_key *) {}: ". \
format(indent, ukey)
if short:
print(base_str)
continue
print("{}key_len = {}, mask_len = {}".
format(base_str, ukey['key_len'], ukey['mask_len']))
indent_b = " " * len(base_str)
if ukey['ufid_present']:
print("{}ufid = {}".
format(
indent_b, str(uuid.UUID(
"{:08x}{:08x}{:08x}{:08x}".
format(int(ukey['ufid']['u32'][3]),
int(ukey['ufid']['u32'][2]),
int(ukey['ufid']['u32'][1]),
int(ukey['ufid']['u32'][0]))))))
print("{}hash = 0x{:8x}, pmd_id = {}".
format(indent_b, int(ukey['hash']), ukey['pmd_id']))
print("{}state = {}".format(indent_b, ukey['state']))
print("{}n_packets = {}, n_bytes = {}".
format(indent_b,
ukey['stats']['n_packets'],
ukey['stats']['n_bytes']))
print("{}used = {}, tcp_flags = 0x{:04x}".
format(indent_b,
ukey['stats']['used'],
int(ukey['stats']['tcp_flags'])))
#
# TODO: Would like to add support for dumping key, mask
# actions, and xlate_cache
#
# key = ""
# for nlattr in ForEachNL(ukey['key'], ukey['key_len']):
# key += "{}{}".format(
# "" if len(key) == 0 else ", ",
# nlattr['nla_type'].cast(
# gdb.lookup_type('enum ovs_key_attr')))
# print("{}key attributes = {}".format(indent_b, key))
spinner.resume()
spinner.done()
def invoke(self, arg, from_tty):
arg_list = gdb.string_to_argv(arg)
all_udpifs = get_global_variable('all_udpifs')
no_count = "no_count" in arg_list
if all_udpifs is None:
return
udpifs = dict()
for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"):
udpifs[udpif['dpif']['full_name'].string()] = udpif
if len(arg_list) == 0 or (
len(arg_list) == 1 and arg_list[0] == "no_count"):
print("(struct udpif *) {}: name = {}, total keys = {}".
format(udpif, udpif['dpif']['full_name'].string(),
self.count_all_ukeys(udpif) if not no_count
else "<not counted!>"))
if len(arg_list) == 0 or (
len(arg_list) == 1 and arg_list[0] == "no_count"):
return
if arg_list[0] in udpifs:
udpif = udpifs[arg_list[0]]
else:
try:
udpif = gdb.parse_and_eval(arg_list[0]).cast(
gdb.lookup_type('struct udpif').pointer())
except Exception:
udpif = None
if udpif is None:
print("Can't find provided udpif address!")
return
self.dump_all_ukeys(udpif, 0, "short" in arg_list[1:])
#
# Implements the GDB "ovs_show_fdb" command
#
class CmdShowFDB(gdb.Command):
"""Show FDB information
Usage: ovs_show_fdb {<bridge_name> {dbg} {hash}}
<bridge_name> : Optional bridge name, if not supplied FDB summary
information is displayed for all bridges.
dbg : Will show structure address information
hash : Will display the forwarding table using the hash
table, rather than the rlu list.
"""
def __init__(self):
super(CmdShowFDB, self).__init__("ovs_show_fdb",
gdb.COMMAND_DATA)
@staticmethod
def __get_port_name_num(mac_entry):
if mac_entry['mlport'] is not None:
port = mac_entry['mlport']['port'].cast(
gdb.lookup_type('struct ofbundle').pointer())
port_name = port['name'].string()
port_no = int(container_of(
port['ports']['next'],
gdb.lookup_type('struct ofport_dpif').pointer(),
'bundle_node')['up']['ofp_port'])
if port_no == 0xfff7:
port_no = "UNSET"
elif port_no == 0xfff8:
port_no = "IN_PORT"
elif port_no == 0xfff9:
port_no = "TABLE"
elif port_no == 0xfffa:
port_no = "NORMAL"
elif port_no == 0xfffb:
port_no = "FLOOD"
elif port_no == 0xfffc:
port_no = "ALL"
elif port_no == 0xfffd:
port_no = "CONTROLLER"
elif port_no == 0xfffe:
port_no = "LOCAL"
elif port_no == 0xffff:
port_no = "NONE"
else:
port_no = str(port_no)
else:
port_name = "-"
port_no = "?"
return port_name, port_no
@staticmethod
def display_ml_summary(ml, indent=0, dbg=False):
indent = " " * indent
if ml is None:
return
if dbg:
print("[(struct mac_learning *) {}]".format(ml))
print("{}table.n : {}".format(indent, ml['table']['n']))
print("{}secret : 0x{:x}".format(indent, int(ml['secret'])))
print("{}idle_time : {}".format(indent, ml['idle_time']))
print("{}max_entries : {}".format(indent, ml['max_entries']))
print("{}ref_count : {}".format(indent, ml['ref_cnt']['count']))
print("{}need_revalidate : {}".format(indent, ml['need_revalidate']))
print("{}ports_by_ptr.n : {}".format(indent, ml['ports_by_ptr']['n']))
print("{}ports_by_usage.n: {}".format(indent,
ml['ports_by_usage']['n']))
print("{}total_learned : {}".format(indent, ml['total_learned']))
print("{}total_expired : {}".format(indent, ml['total_expired']))
print("{}total_evicted : {}".format(indent, ml['total_evicted']))
print("{}total_moved : {}".format(indent, ml['total_moved']))
@staticmethod
def display_mac_entry(mac_entry, indent=0, dbg=False):
port_name, port_no = CmdShowFDB.__get_port_name_num(mac_entry)
line = "{}{:16.16} {:-4} {} {:-9}".format(
indent,
"{}[{}]".format(port_no, port_name),
int(mac_entry['vlan']),
eth_addr_to_string(mac_entry['mac']),
int(mac_entry['expires']))
if dbg:
line += " [(struct mac_entry *) {}]".format(mac_entry)
print(line)
@staticmethod
def display_ml_entries(ml, indent=0, hash=False, dbg=False):
indent = " " * indent
if ml is None:
return
print("\n{}FDB \"{}\" table:".format(indent,
"lrus" if not hash else "hash"))
print("{}port VLAN MAC Age out @".
format(indent))
print("{}----------------- ---- ----------------- ---------".
format(indent))
mac_entries = 0
if hash:
for mac_entry in ForEachHMAP(ml['table'],
"struct mac_entry",
"hmap_node"):
CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg)
mac_entries += 1
else:
for mac_entry in ForEachLIST(ml['lrus'],
"struct mac_entry",
"lru_node"):
CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg)
mac_entries += 1
print("\nTotal MAC entries: {}".format(mac_entries))
time_now = list(get_time_now())
time_now[1] = time_now[0] + time_now[1]
print("\n{}Current time is between {} and {} seconds.\n".
format(indent, min(time_now[0], time_now[1]),
max(time_now[0], time_now[1])))
def invoke(self, arg, from_tty):
arg_list = gdb.string_to_argv(arg)
all_ofproto_dpifs_by_name = get_global_variable(
'all_ofproto_dpifs_by_name')
if all_ofproto_dpifs_by_name is None:
return
all_name = dict()
max_name_len = 0
for node in ForEachHMAP(all_ofproto_dpifs_by_name,
"struct ofproto_dpif",
"all_ofproto_dpifs_by_name_node"):
all_name[node['up']['name'].string()] = node
if len(node['up']['name'].string()) > max_name_len:
max_name_len = len(node['up']['name'].string())
if len(arg_list) == 0:
for name in sorted(all_name.keys()):
print("{}: (struct mac_learning *) {}".
format(name.ljust(max_name_len),
all_name[name]['ml']))
self.display_ml_summary(all_name[name]['ml'], 4)
else:
if not arg_list[0] in all_name:
print("ERROR: Given bridge name is not known!")
return
ml = all_name[arg_list[0]]['ml']
self.display_ml_summary(ml, 0, "dbg" in arg_list[1:])
self.display_ml_entries(ml, 0, "hash" in arg_list[1:],
"dbg" in arg_list[1:])
#
# Implements the GDB "ovs_show_fdb" command
#
class CmdShowUpcall(gdb.Command):
"""Show upcall information
Usage: ovs_show_upcall {dbg}
dbg : Will show structure address information
"""
def __init__(self):
super(CmdShowUpcall, self).__init__("ovs_show_upcall",
gdb.COMMAND_DATA)
@staticmethod
def display_udpif_upcall(udpif, indent=0, dbg=False):
indent = " " * indent
enable_ufid = get_global_variable('enable_ufid')
if enable_ufid is None:
return
dbg_str = ""
if dbg:
dbg_str = ", ((struct udpif *) {})".format(udpif)
print("{}{}{}:".format(
indent, udpif['dpif']['full_name'].string(),
dbg_str))
print("{} flows : (current {}) (avg {}) (max {}) (limit {})".
format(indent, udpif['n_flows'], udpif['avg_n_flows'],
udpif['max_n_flows'], udpif['flow_limit']))
print("{} dump duration : {}ms".
format(indent, udpif['dump_duration']))
print("{} ufid enabled : {}\n".
format(indent, enable_ufid &
udpif['backer']['rt_support']['ufid']))
for i in range(0, int(udpif['n_revalidators'])):
revalidator = udpif['revalidators'][i]
dbg_str = ""
if dbg:
dbg_str = ", ((struct revalidator *) {})".\
format(revalidator.address)
count = 0
j = i
spinner = ProgressIndicator("Counting all ukeys: ")
while j < N_UMAPS:
spinner.update()
count += udpif['ukeys'][j]['cmap']['impl']['p']['n']
j += int(udpif['n_revalidators'])
spinner.done()
print("{} {}: (keys {}){}".
format(indent, revalidator['id'], count, dbg_str))
print("")
def invoke(self, arg, from_tty):
arg_list = gdb.string_to_argv(arg)
all_udpifs = get_global_variable('all_udpifs')
if all_udpifs is None:
return
for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"):
self.display_udpif_upcall(udpif, 0, "dbg" in arg_list)
#
# Implements the GDB "ovs_dump_ofpacts" command
#
class CmdDumpOfpacts(gdb.Command):
"""Dump all actions in an ofpacts set
Usage: ovs_dump_ofpacts <struct ofpact *> <ofpacts_len>
<struct ofpact *> : Pointer to set of ofpact structures.
<ofpacts_len> : Total length of the set.
Example dumping all actions when in the clone_xlate_actions() function:
(gdb) ovs_dump_ofpacts actions actions_len
(struct ofpact *) 0x561c7be487c8: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
(struct ofpact *) 0x561c7be487e0: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
(struct ofpact *) 0x561c7be487f8: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
(struct ofpact *) 0x561c7be48810: {type = OFPACT_SET_FIELD, raw = 255 '', len = 32}
(struct ofpact *) 0x561c7be48830: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
(struct ofpact *) 0x561c7be48848: {type = OFPACT_RESUBMIT, raw = 38 '&', len = 16}
"""
def __init__(self):
super(CmdDumpOfpacts, self).__init__("ovs_dump_ofpacts",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
arg_list = gdb.string_to_argv(arg)
if len(arg_list) != 2:
print("usage: ovs_dump_ofpacts <struct ofpact *> <ofpacts_len>")
return
ofpacts = gdb.parse_and_eval(arg_list[0]).cast(
gdb.lookup_type('struct ofpact').pointer())
length = gdb.parse_and_eval(arg_list[1])
for node in ForEachOFPACTS(ofpacts, length):
print("(struct ofpact *) {}: {}".format(node, node.dereference()))
#
# Initialize all GDB commands
#
CmdDumpBridge()
CmdDumpBridgePorts()
CmdDumpDpNetdev()
CmdDumpDpNetdevPollThreads()
CmdDumpDpNetdevPorts()
CmdDumpDpProvider()
CmdDumpNetdev()
CmdDumpNetdevProvider()
CmdDumpOfpacts()
CmdDumpOvsList()
CmdDumpSimap()
CmdDumpSmap()
CmdDumpUdpifKeys()
CmdShowFDB()
CmdShowUpcall()