Adds back-end support for walking ovs cmaps, and the following commands to the gdb script:
- Dump all poll_thread info added to a specific struct dp_netdev*. Usage: ovs_dump_dp_netdev_poll_threads <struct dp_netdev *> - Dump all nodes of an ovs_list: 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>}, ... Signed-off-by: Eelco Chaudron <echau...@redhat.com> --- utilities/gdb/ovs_gdb.py | 167 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/utilities/gdb/ovs_gdb.py b/utilities/gdb/ovs_gdb.py index a604ccb54..69361281d 100644 --- a/utilities/gdb/ovs_gdb.py +++ b/utilities/gdb/ovs_gdb.py @@ -23,8 +23,10 @@ # - 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_netdev +# - ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]} # # Example: # $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd) @@ -103,6 +105,63 @@ def container_of(ptr, typeobj, member): offset_of(typeobj, member)).cast(typeobj) +# +# 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) + + # # Class that will provide an iterator over an OVS hmap. # @@ -327,6 +386,39 @@ class CmdDumpDpNetdev(gdb.Command): 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 = {:s}, " + "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 # @@ -395,11 +487,86 @@ class CmdDumpNetdev(gdb.Command): self.display_single_netdev(netdev) +# +# 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: + print(" {}\n".format(container_of( + node, + gdb.lookup_type(typeobj).pointer(), + member).dereference())) + + # # Initialize all GDB commands # CmdDumpBridge() CmdDumpBridgePorts() CmdDumpDpNetdev() +CmdDumpDpNetdevPollThreads() CmdDumpDpNetdevPorts() CmdDumpNetdev() +CmdDumpOvsList() -- 2.14.3 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev