From patchwork Wed May 9 11:47:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eelco Chaudron X-Patchwork-Id: 910797 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40gvjp5z1Pz9s4v for ; Wed, 9 May 2018 21:47:41 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id C0D0297A; Wed, 9 May 2018 11:47:37 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 968C4950 for ; Wed, 9 May 2018 11:47:36 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 38ED7671 for ; Wed, 9 May 2018 11:47:35 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 37F78A1BCB for ; Wed, 9 May 2018 11:47:34 +0000 (UTC) Received: from rhvm.com (ovpn-117-15.ams2.redhat.com [10.36.117.15]) by smtp.corp.redhat.com (Postfix) with ESMTP id B7BD810F1C18 for ; Wed, 9 May 2018 11:47:33 +0000 (UTC) From: Eelco Chaudron To: dev@openvswitch.org Date: Wed, 9 May 2018 13:47:26 +0200 Message-Id: X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 09 May 2018 11:47:34 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 09 May 2018 11:47:34 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'echaudro@redhat.com' RCPT:'' X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH 1/1] utilities: Add some GDB macros for ovs-vswitchd X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This commit adds basic GDB macro's for ovs-vswitchd: - ovs_dump_bridge [ports|wanted] - ovs_dump_bridge_ports - ovs_dump_dp_netdev [ports] - ovs_dump_dp_netdev_ports - 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 (struct netdev *) 0x555771fc62a0: name = ovs_pvp_br0 , auto_classified = false, netdev_class = 0x5557714413c0 (struct netdev *) 0x555771fc9660: name = vnet0 , auto_classified = true , netdev_class = 0x555771445e00 (struct netdev *) 0x555771fc78d0: name = virbr0 , auto_classified = true , netdev_class = 0x555771445e00 (struct netdev *) 0x7fbefffb5540: name = dpdk0 , auto_classified = false, netdev_class = 0x5557714419e0 (struct netdev *) 0x555771fc98b0: name = em3 , auto_classified = true , netdev_class = 0x555771445e00 (struct netdev *) 0x7fbea0a31c40: name = vhost0 , auto_classified = false, netdev_class = 0x555771442040 (gdb) p *((struct netdev *) 0x7fbefffb5540) $1 = {name = 0x555771ecef70 "dpdk0", netdev_class = 0x5557714419e0 , 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 --- Documentation/tutorials/ovs-advanced.rst | 4 + utilities/gdb/ovs_gdb.py | 405 +++++++++++++++++++++++++++++++ 2 files changed, 409 insertions(+) create mode 100644 utilities/gdb/ovs_gdb.py diff --git a/Documentation/tutorials/ovs-advanced.rst b/Documentation/tutorials/ovs-advanced.rst index 676137f3c..992182d0e 100644 --- a/Documentation/tutorials/ovs-advanced.rst +++ b/Documentation/tutorials/ovs-advanced.rst @@ -145,6 +145,10 @@ can be passed via the ``SANDBOXFLAGS`` environment variable. ``make sandbox SANDBOXFLAGS=-g`` will start the sandbox with ovs-vswitchd running under GDB in its own xterm if X is available. +In addition, a set of GDB macros are available in ``utilities/gdb/ovs_gdb.py``. +Which are able to dump various internal data structures. See the header of the +file itself for some more details and an example. + Motivation ---------- 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 +# - ovs_dump_dp_netdev [ports] +# - ovs_dump_dp_netdev_ports +# - ovs_dump_netdev +# +# Example: +# $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd) +# (gdb) source ./utilities/gdb/ovs_gdb.py +# +# (gdb) ovs_dump_ +# 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 +# # +# # 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 + """ + 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 ") + 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 + """ + 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 ") + 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()