#
#  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()
