]>
Commit | Line | Data |
---|---|---|
0a3348d1 EC |
1 | # |
2 | # Copyright (c) 2018 Eelco Chaudron | |
3 | # | |
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version | |
7 | # 2 of the License, or (at your option) any later version. | |
8 | # | |
9 | # Files name: | |
10 | # ovs_gdb.py | |
11 | # | |
12 | # Description: | |
13 | # GDB commands and functions for Open vSwitch debugging | |
14 | # | |
15 | # Author: | |
16 | # Eelco Chaudron | |
17 | # | |
18 | # Initial Created: | |
19 | # 23 April 2018 | |
20 | # | |
21 | # Notes: | |
22 | # It implements the following GDB commands: | |
ef557e2c | 23 | # - ovs_dump_bridge {ports|wanted} |
0a3348d1 EC |
24 | # - ovs_dump_bridge_ports <struct bridge *> |
25 | # - ovs_dump_dp_netdev [ports] | |
5d0a6267 | 26 | # - ovs_dump_dp_netdev_poll_threads <struct dp_netdev *> |
0a3348d1 | 27 | # - ovs_dump_dp_netdev_ports <struct dp_netdev *> |
b9683ff0 | 28 | # - ovs_dump_dp_provider |
0a3348d1 | 29 | # - ovs_dump_netdev |
42562061 | 30 | # - ovs_dump_netdev_provider |
5d0a6267 | 31 | # - ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]} |
42562061 | 32 | # - ovs_dump_simap <struct simap *> |
cd5b89a5 | 33 | # - ovs_dump_smap <struct smap *> |
b23d42a6 | 34 | # - ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short} |
ef557e2c | 35 | # - ovs_show_fdb {[<bridge_name>] {dbg} {hash}} |
b23d42a6 | 36 | # - ovs_show_upcall {dbg} |
0a3348d1 EC |
37 | # |
38 | # Example: | |
39 | # $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd) | |
40 | # (gdb) source ./utilities/gdb/ovs_gdb.py | |
41 | # | |
42 | # (gdb) ovs_dump_<TAB> | |
43 | # ovs_dump_bridge ovs_dump_bridge_ports ovs_dump_dp_netdev | |
44 | # ovs_dump_dp_netdev_ports ovs_dump_netdev | |
45 | # | |
46 | # (gdb) ovs_dump_bridge | |
47 | # (struct bridge *) 0x5615471ed2e0: name = br2, type = system | |
48 | # (struct bridge *) 0x561547166350: name = br0, type = system | |
49 | # (struct bridge *) 0x561547216de0: name = ovs_pvp_br0, type = netdev | |
50 | # (struct bridge *) 0x5615471d0420: name = br1, type = system | |
51 | # | |
52 | # (gdb) p *(struct bridge *) 0x5615471d0420 | |
53 | # $1 = {node = {hash = 24776443, next = 0x0}, name = 0x5615471cca90 "br1", | |
54 | # type = 0x561547163bb0 "system", | |
55 | # ... | |
56 | # ... | |
57 | # | |
0a3348d1 | 58 | import gdb |
b23d42a6 EC |
59 | import uuid |
60 | ||
61 | ||
62 | # | |
63 | # Global #define's from OVS which might need updating based on a version. | |
64 | # | |
65 | N_UMAPS = 512 | |
0a3348d1 EC |
66 | |
67 | ||
68 | # | |
69 | # The container_of code below is a copied from the Linux kernel project file, | |
70 | # scripts/gdb/linux/utils.py. It has the following copyright header: | |
71 | # | |
72 | # # gdb helper commands and functions for Linux kernel debugging | |
73 | # # | |
74 | # # common utilities | |
75 | # # | |
76 | # # Copyright (c) Siemens AG, 2011-2013 | |
77 | # # | |
78 | # # Authors: | |
79 | # # Jan Kiszka <jan.kiszka@siemens.com> | |
80 | # # | |
81 | # # This work is licensed under the terms of the GNU GPL version 2. | |
82 | # | |
83 | class CachedType: | |
84 | def __init__(self, name): | |
85 | self._type = None | |
86 | self._name = name | |
87 | ||
88 | def _new_objfile_handler(self, event): | |
89 | self._type = None | |
90 | gdb.events.new_objfile.disconnect(self._new_objfile_handler) | |
91 | ||
92 | def get_type(self): | |
93 | if self._type is None: | |
94 | self._type = gdb.lookup_type(self._name) | |
95 | if self._type is None: | |
96 | raise gdb.GdbError( | |
97 | "cannot resolve type '{0}'".format(self._name)) | |
98 | if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'): | |
99 | gdb.events.new_objfile.connect(self._new_objfile_handler) | |
100 | return self._type | |
101 | ||
102 | ||
103 | long_type = CachedType("long") | |
104 | ||
105 | ||
106 | def get_long_type(): | |
107 | global long_type | |
108 | return long_type.get_type() | |
109 | ||
110 | ||
111 | def offset_of(typeobj, field): | |
112 | element = gdb.Value(0).cast(typeobj) | |
113 | return int(str(element[field].address).split()[0], 16) | |
114 | ||
115 | ||
116 | def container_of(ptr, typeobj, member): | |
117 | return (ptr.cast(get_long_type()) - | |
118 | offset_of(typeobj, member)).cast(typeobj) | |
119 | ||
120 | ||
b9683ff0 EC |
121 | def get_global_variable(name): |
122 | var = gdb.lookup_symbol(name)[0] | |
123 | if var is None or not var.is_variable: | |
124 | print("Can't find {} global variable, are you sure " | |
125 | "your debugging OVS?".format(name)) | |
126 | return None | |
127 | return gdb.parse_and_eval(name) | |
128 | ||
129 | ||
ef557e2c EC |
130 | def get_time_msec(): |
131 | # There is no variable that stores the current time each iteration, | |
132 | # to get a decent time time_now() value. For now we take the global | |
133 | # "coverage_run_time" value, which is the current time + max 5 seconds | |
134 | # (COVERAGE_RUN_INTERVAL) | |
135 | return long(get_global_variable("coverage_run_time")), -5000 | |
136 | ||
137 | ||
138 | def get_time_now(): | |
139 | # See get_time_msec() above | |
140 | return long(get_global_variable("coverage_run_time"))/1000, -5 | |
141 | ||
142 | ||
143 | def eth_addr_to_string(eth_addr): | |
144 | return "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format( | |
145 | long(eth_addr['ea'][0]), | |
146 | long(eth_addr['ea'][1]), | |
147 | long(eth_addr['ea'][2]), | |
148 | long(eth_addr['ea'][3]), | |
149 | long(eth_addr['ea'][4]), | |
150 | long(eth_addr['ea'][5])) | |
151 | ||
b23d42a6 | 152 | |
5d0a6267 EC |
153 | # |
154 | # Class that will provide an iterator over an OVS cmap. | |
155 | # | |
156 | class ForEachCMAP(object): | |
157 | def __init__(self, cmap, typeobj=None, member='node'): | |
158 | self.cmap = cmap | |
159 | self.first = True | |
160 | self.typeobj = typeobj | |
161 | self.member = member | |
162 | # Cursor values | |
163 | self.node = 0 | |
164 | self.bucket_idx = 0 | |
165 | self.entry_idx = 0 | |
166 | ||
167 | def __iter__(self): | |
168 | return self | |
169 | ||
170 | def __get_CMAP_K(self): | |
171 | ptr_type = gdb.lookup_type("void").pointer() | |
172 | return (64 - 4) / (4 + ptr_type.sizeof) | |
173 | ||
174 | def __next(self): | |
175 | ipml = self.cmap['impl']['p'] | |
176 | ||
177 | if self.node != 0: | |
178 | self.node = self.node['next']['p'] | |
179 | if self.node != 0: | |
180 | return | |
181 | ||
182 | while self.bucket_idx <= ipml['mask']: | |
183 | buckets = ipml['buckets'][self.bucket_idx] | |
184 | while self.entry_idx < self.__get_CMAP_K(): | |
185 | self.node = buckets['nodes'][self.entry_idx]['next']['p'] | |
186 | self.entry_idx += 1 | |
187 | if self.node != 0: | |
188 | return | |
189 | ||
190 | self.bucket_idx += 1 | |
191 | self.entry_idx = 0 | |
192 | ||
193 | raise StopIteration | |
194 | ||
195 | def next(self): | |
196 | ipml = self.cmap['impl']['p'] | |
197 | if ipml['n'] == 0: | |
198 | raise StopIteration | |
199 | ||
200 | self.__next() | |
201 | ||
202 | if self.typeobj is None: | |
203 | return self.node | |
204 | ||
205 | return container_of(self.node, | |
206 | gdb.lookup_type(self.typeobj).pointer(), | |
207 | self.member) | |
208 | ||
209 | ||
0a3348d1 EC |
210 | # |
211 | # Class that will provide an iterator over an OVS hmap. | |
212 | # | |
213 | class ForEachHMAP(object): | |
214 | def __init__(self, hmap, typeobj=None, member='node'): | |
215 | self.hmap = hmap | |
216 | self.node = None | |
217 | self.first = True | |
218 | self.typeobj = typeobj | |
219 | self.member = member | |
220 | ||
221 | def __iter__(self): | |
222 | return self | |
223 | ||
224 | def __next(self, start): | |
225 | for i in range(start, (self.hmap['mask'] + 1)): | |
226 | self.node = self.hmap['buckets'][i] | |
227 | if self.node != 0: | |
228 | return | |
229 | ||
230 | raise StopIteration | |
231 | ||
232 | def next(self): | |
233 | # | |
234 | # In the real implementation the n values is never checked, | |
235 | # however when debugging we do, as we might try to access | |
236 | # a hmap that has been cleared/hmap_destroy(). | |
237 | # | |
238 | if self.hmap['n'] <= 0: | |
239 | raise StopIteration | |
240 | ||
241 | if self.first: | |
242 | self.first = False | |
243 | self.__next(0) | |
244 | elif self.node['next'] != 0: | |
245 | self.node = self.node['next'] | |
246 | else: | |
247 | self.__next((self.node['hash'] & self.hmap['mask']) + 1) | |
248 | ||
249 | if self.typeobj is None: | |
250 | return self.node | |
251 | ||
252 | return container_of(self.node, | |
253 | gdb.lookup_type(self.typeobj).pointer(), | |
254 | self.member) | |
255 | ||
256 | ||
b23d42a6 EC |
257 | # |
258 | # Class that will provide an iterator over an Netlink attributes | |
259 | # | |
260 | class ForEachNL(): | |
261 | def __init__(self, nlattrs, nlattrs_len): | |
262 | self.attr = nlattrs.cast(gdb.lookup_type('struct nlattr').pointer()) | |
263 | self.attr_len = long(nlattrs_len) | |
264 | ||
265 | def __iter__(self): | |
266 | return self | |
267 | ||
268 | def round_up(self, val, round_to): | |
269 | return int(val) + (round_to - int(val)) % round_to | |
270 | ||
271 | def next(self): | |
272 | if self.attr is None or \ | |
273 | self.attr_len < 4 or self.attr['nla_len'] < 4 or \ | |
274 | self.attr['nla_len'] > self.attr_len: | |
275 | # | |
276 | # Invalid attr set, maybe we should raise an exception? | |
277 | # | |
278 | raise StopIteration | |
279 | ||
280 | attr = self.attr | |
281 | self.attr_len -= self.round_up(attr['nla_len'], 4) | |
282 | ||
283 | self.attr = self.attr.cast(gdb.lookup_type('void').pointer()) \ | |
284 | + self.round_up(attr['nla_len'], 4) | |
285 | self.attr = self.attr.cast(gdb.lookup_type('struct nlattr').pointer()) | |
286 | ||
287 | return attr | |
288 | ||
289 | ||
0a3348d1 EC |
290 | # |
291 | # Class that will provide an iterator over an OVS shash. | |
292 | # | |
293 | class ForEachSHASH(ForEachHMAP): | |
294 | def __init__(self, shash, typeobj=None): | |
295 | ||
296 | self.data_typeobj = typeobj | |
297 | ||
298 | super(ForEachSHASH, self).__init__(shash['map'], | |
299 | "struct shash_node", "node") | |
300 | ||
301 | def next(self): | |
302 | node = super(ForEachSHASH, self).next() | |
303 | ||
304 | if self.data_typeobj is None: | |
305 | return node | |
306 | ||
307 | return node['data'].cast(gdb.lookup_type(self.data_typeobj).pointer()) | |
308 | ||
309 | ||
42562061 EC |
310 | # |
311 | # Class that will provide an iterator over an OVS simap. | |
312 | # | |
313 | class ForEachSIMAP(ForEachHMAP): | |
314 | def __init__(self, shash): | |
315 | super(ForEachSIMAP, self).__init__(shash['map'], | |
316 | "struct simap_node", "node") | |
317 | ||
318 | def next(self): | |
319 | node = super(ForEachSIMAP, self).next() | |
320 | return node['name'], node['data'] | |
321 | ||
322 | ||
cd5b89a5 EC |
323 | # |
324 | # Class that will provide an iterator over an OVS smap. | |
325 | # | |
326 | class ForEachSMAP(ForEachHMAP): | |
327 | def __init__(self, shash): | |
328 | super(ForEachSMAP, self).__init__(shash['map'], | |
329 | "struct smap_node", "node") | |
330 | ||
331 | def next(self): | |
332 | node = super(ForEachSMAP, self).next() | |
333 | return node['key'], node['value'] | |
334 | ||
335 | ||
0a3348d1 EC |
336 | # |
337 | # Class that will provide an iterator over an OVS list. | |
338 | # | |
339 | class ForEachLIST(): | |
340 | def __init__(self, list, typeobj=None, member='node'): | |
341 | self.list = list | |
342 | self.node = list | |
343 | self.typeobj = typeobj | |
344 | self.member = member | |
345 | ||
346 | def __iter__(self): | |
347 | return self | |
348 | ||
349 | def next(self): | |
350 | if self.list.address == self.node['next']: | |
351 | raise StopIteration | |
352 | ||
353 | self.node = self.node['next'] | |
354 | ||
355 | if self.typeobj is None: | |
356 | return self.node | |
357 | ||
358 | return container_of(self.node, | |
359 | gdb.lookup_type(self.typeobj).pointer(), | |
360 | self.member) | |
361 | ||
362 | ||
363 | # | |
364 | # Implements the GDB "ovs_dump_bridges" command | |
365 | # | |
366 | class CmdDumpBridge(gdb.Command): | |
367 | """Dump all configured bridges. | |
ef557e2c | 368 | Usage: ovs_dump_bridge {ports|wanted} |
0a3348d1 EC |
369 | """ |
370 | def __init__(self): | |
371 | super(CmdDumpBridge, self).__init__("ovs_dump_bridge", | |
372 | gdb.COMMAND_DATA) | |
373 | ||
374 | def invoke(self, arg, from_tty): | |
375 | ports = False | |
376 | wanted = False | |
377 | arg_list = gdb.string_to_argv(arg) | |
378 | if len(arg_list) > 1 or \ | |
379 | (len(arg_list) == 1 and arg_list[0] != "ports" and | |
380 | arg_list[0] != "wanted"): | |
ef557e2c | 381 | print("usage: ovs_dump_bridge {ports|wanted}") |
0a3348d1 EC |
382 | return |
383 | elif len(arg_list) == 1: | |
384 | if arg_list[0] == "ports": | |
385 | ports = True | |
386 | else: | |
387 | wanted = True | |
388 | ||
b9683ff0 EC |
389 | all_bridges = get_global_variable('all_bridges') |
390 | if all_bridges is None: | |
0a3348d1 | 391 | return |
b9683ff0 | 392 | |
0a3348d1 EC |
393 | for node in ForEachHMAP(all_bridges, |
394 | "struct bridge", "node"): | |
395 | print("(struct bridge *) {}: name = {}, type = {}". | |
396 | format(node, node['name'].string(), | |
397 | node['type'].string())) | |
398 | ||
399 | if ports: | |
400 | for port in ForEachHMAP(node['ports'], | |
401 | "struct port", "hmap_node"): | |
402 | CmdDumpBridgePorts.display_single_port(port, 4) | |
403 | ||
404 | if wanted: | |
405 | for port in ForEachSHASH(node['wanted_ports'], | |
406 | typeobj="struct ovsrec_port"): | |
407 | print(" (struct ovsrec_port *) {}: name = {}". | |
408 | format(port, port['name'].string())) | |
409 | # print port.dereference() | |
410 | ||
411 | ||
412 | # | |
413 | # Implements the GDB "ovs_dump_bridge_ports" command | |
414 | # | |
415 | class CmdDumpBridgePorts(gdb.Command): | |
416 | """Dump all ports added to a specific struct bridge*. | |
417 | Usage: ovs_dump_bridge_ports <struct bridge *> | |
418 | """ | |
419 | def __init__(self): | |
420 | super(CmdDumpBridgePorts, self).__init__("ovs_dump_bridge_ports", | |
421 | gdb.COMMAND_DATA) | |
422 | ||
423 | @staticmethod | |
424 | def display_single_port(port, indent=0): | |
425 | indent = " " * indent | |
426 | port = port.cast(gdb.lookup_type('struct port').pointer()) | |
427 | print("{}(struct port *) {}: name = {}, brige = (struct bridge *) {}". | |
428 | format(indent, port, port['name'].string(), | |
429 | port['bridge'])) | |
430 | ||
431 | indent += " " * 4 | |
432 | for iface in ForEachLIST(port['ifaces'], "struct iface", "port_elem"): | |
433 | print("{}(struct iface *) {}: name = {}, ofp_port = {}, " | |
434 | "netdev = (struct netdev *) {}". | |
435 | format(indent, iface, iface['name'], | |
436 | iface['ofp_port'], iface['netdev'])) | |
437 | ||
438 | def invoke(self, arg, from_tty): | |
439 | arg_list = gdb.string_to_argv(arg) | |
440 | if len(arg_list) != 1: | |
441 | print("usage: ovs_dump_bridge_ports <struct bridge *>") | |
442 | return | |
443 | bridge = gdb.parse_and_eval(arg_list[0]).cast( | |
444 | gdb.lookup_type('struct bridge').pointer()) | |
445 | for node in ForEachHMAP(bridge['ports'], | |
446 | "struct port", "hmap_node"): | |
447 | self.display_single_port(node) | |
448 | ||
449 | ||
450 | # | |
451 | # Implements the GDB "ovs_dump_dp_netdev" command | |
452 | # | |
453 | class CmdDumpDpNetdev(gdb.Command): | |
454 | """Dump all registered dp_netdev structures. | |
455 | Usage: ovs_dump_dp_netdev [ports] | |
456 | """ | |
457 | def __init__(self): | |
458 | super(CmdDumpDpNetdev, self).__init__("ovs_dump_dp_netdev", | |
459 | gdb.COMMAND_DATA) | |
460 | ||
461 | def invoke(self, arg, from_tty): | |
462 | ports = False | |
463 | arg_list = gdb.string_to_argv(arg) | |
464 | if len(arg_list) > 1 or \ | |
465 | (len(arg_list) == 1 and arg_list[0] != "ports"): | |
466 | print("usage: ovs_dump_dp_netdev [ports]") | |
467 | return | |
468 | elif len(arg_list) == 1: | |
469 | ports = True | |
470 | ||
b9683ff0 EC |
471 | dp_netdevs = get_global_variable('dp_netdevs') |
472 | if dp_netdevs is None: | |
0a3348d1 | 473 | return |
b9683ff0 EC |
474 | |
475 | for dp in ForEachSHASH(dp_netdevs, typeobj=('struct dp_netdev')): | |
0a3348d1 EC |
476 | |
477 | print("(struct dp_netdev *) {}: name = {}, class = " | |
478 | "(struct dpif_class *) {}". | |
b9683ff0 | 479 | format(dp, dp['name'].string(), dp['class'])) |
0a3348d1 EC |
480 | |
481 | if ports: | |
482 | for node in ForEachHMAP(dp['ports'], | |
483 | "struct dp_netdev_port", "node"): | |
484 | CmdDumpDpNetdevPorts.display_single_port(node, 4) | |
485 | ||
486 | ||
5d0a6267 EC |
487 | # |
488 | # Implements the GDB "ovs_dump_dp_netdev_poll_threads" command | |
489 | # | |
490 | class CmdDumpDpNetdevPollThreads(gdb.Command): | |
491 | """Dump all poll_thread info added to a specific struct dp_netdev*. | |
492 | Usage: ovs_dump_dp_netdev_poll_threads <struct dp_netdev *> | |
493 | """ | |
494 | def __init__(self): | |
495 | super(CmdDumpDpNetdevPollThreads, self).__init__( | |
496 | "ovs_dump_dp_netdev_poll_threads", | |
497 | gdb.COMMAND_DATA) | |
498 | ||
499 | @staticmethod | |
500 | def display_single_poll_thread(pmd_thread, indent=0): | |
501 | indent = " " * indent | |
502 | print("{}(struct dp_netdev_pmd_thread *) {}: core_id = {:s}, " | |
503 | "numa_id {}".format(indent, | |
504 | pmd_thread, pmd_thread['core_id'], | |
505 | pmd_thread['numa_id'])) | |
506 | ||
507 | def invoke(self, arg, from_tty): | |
508 | arg_list = gdb.string_to_argv(arg) | |
509 | if len(arg_list) != 1: | |
510 | print("usage: ovs_dump_dp_netdev_poll_threads " | |
511 | "<struct dp_netdev *>") | |
512 | return | |
513 | dp_netdev = gdb.parse_and_eval(arg_list[0]).cast( | |
514 | gdb.lookup_type('struct dp_netdev').pointer()) | |
515 | for node in ForEachCMAP(dp_netdev['poll_threads'], | |
516 | "struct dp_netdev_pmd_thread", "node"): | |
517 | self.display_single_poll_thread(node) | |
518 | ||
519 | ||
0a3348d1 EC |
520 | # |
521 | # Implements the GDB "ovs_dump_dp_netdev_ports" command | |
522 | # | |
523 | class CmdDumpDpNetdevPorts(gdb.Command): | |
524 | """Dump all ports added to a specific struct dp_netdev*. | |
525 | Usage: ovs_dump_dp_netdev_ports <struct dp_netdev *> | |
526 | """ | |
527 | def __init__(self): | |
528 | super(CmdDumpDpNetdevPorts, self).__init__("ovs_dump_dp_netdev_ports", | |
529 | gdb.COMMAND_DATA) | |
530 | ||
531 | @staticmethod | |
532 | def display_single_port(port, indent=0): | |
533 | indent = " " * indent | |
534 | print("{}(struct dp_netdev_port *) {}:".format(indent, port)) | |
535 | print("{} port_no = {}, n_rxq = {}, type = {}". | |
536 | format(indent, port['port_no'], port['n_rxq'], | |
537 | port['type'].string())) | |
538 | print("{} netdev = (struct netdev *) {}: name = {}, " | |
539 | "n_txq/rxq = {}/{}". | |
540 | format(indent, port['netdev'], | |
541 | port['netdev']['name'].string(), | |
542 | port['netdev']['n_txq'], | |
543 | port['netdev']['n_rxq'])) | |
544 | ||
545 | def invoke(self, arg, from_tty): | |
546 | arg_list = gdb.string_to_argv(arg) | |
547 | if len(arg_list) != 1: | |
548 | print("usage: ovs_dump_dp_netdev_ports <struct dp_netdev *>") | |
549 | return | |
550 | dp_netdev = gdb.parse_and_eval(arg_list[0]).cast( | |
551 | gdb.lookup_type('struct dp_netdev').pointer()) | |
552 | for node in ForEachHMAP(dp_netdev['ports'], | |
553 | "struct dp_netdev_port", "node"): | |
554 | # print node.dereference() | |
555 | self.display_single_port(node) | |
556 | ||
557 | ||
b9683ff0 EC |
558 | # |
559 | # Implements the GDB "ovs_dump_dp_provider" command | |
560 | # | |
561 | class CmdDumpDpProvider(gdb.Command): | |
562 | """Dump all registered registered_dpif_class structures. | |
563 | Usage: ovs_dump_dp_provider | |
564 | """ | |
565 | def __init__(self): | |
566 | super(CmdDumpDpProvider, self).__init__("ovs_dump_dp_provider", | |
567 | gdb.COMMAND_DATA) | |
568 | ||
569 | def invoke(self, arg, from_tty): | |
b9683ff0 EC |
570 | dp_providers = get_global_variable('dpif_classes') |
571 | if dp_providers is None: | |
572 | return | |
573 | ||
574 | for dp_class in ForEachSHASH(dp_providers, | |
575 | typeobj="struct registered_dpif_class"): | |
576 | ||
577 | print("(struct registered_dpif_class *) {}: " | |
578 | "(struct dpif_class *) 0x{:x} = {{type = {}, ...}}, " | |
579 | "refcount = {}". | |
580 | format(dp_class, | |
581 | long(dp_class['dpif_class']), | |
582 | dp_class['dpif_class']['type'].string(), | |
583 | dp_class['refcount'])) | |
584 | ||
585 | ||
0a3348d1 EC |
586 | # |
587 | # Implements the GDB "ovs_dump_netdev" command | |
588 | # | |
589 | class CmdDumpNetdev(gdb.Command): | |
590 | """Dump all registered netdev structures. | |
591 | Usage: ovs_dump_netdev | |
592 | """ | |
593 | def __init__(self): | |
594 | super(CmdDumpNetdev, self).__init__("ovs_dump_netdev", | |
595 | gdb.COMMAND_DATA) | |
596 | ||
597 | @staticmethod | |
598 | def display_single_netdev(netdev, indent=0): | |
599 | indent = " " * indent | |
600 | print("{}(struct netdev *) {}: name = {:15}, auto_classified = {:5}, " | |
601 | "netdev_class = {}". | |
602 | format(indent, netdev, netdev['name'].string(), | |
603 | netdev['auto_classified'], netdev['netdev_class'])) | |
604 | ||
605 | def invoke(self, arg, from_tty): | |
b9683ff0 EC |
606 | netdev_shash = get_global_variable('netdev_shash') |
607 | if netdev_shash is None: | |
0a3348d1 | 608 | return |
b9683ff0 | 609 | |
0a3348d1 EC |
610 | for netdev in ForEachSHASH(netdev_shash, "struct netdev"): |
611 | self.display_single_netdev(netdev) | |
612 | ||
613 | ||
42562061 EC |
614 | # |
615 | # Implements the GDB "ovs_dump_netdev_provider" command | |
616 | # | |
617 | class CmdDumpNetdevProvider(gdb.Command): | |
618 | """Dump all registered netdev providers. | |
619 | Usage: ovs_dump_netdev_provider | |
620 | """ | |
621 | def __init__(self): | |
622 | super(CmdDumpNetdevProvider, self).__init__("ovs_dump_netdev_provider", | |
623 | gdb.COMMAND_DATA) | |
624 | ||
625 | @staticmethod | |
626 | def is_class_vport_class(netdev_class): | |
627 | netdev_class = netdev_class.cast( | |
628 | gdb.lookup_type('struct netdev_class').pointer()) | |
629 | ||
630 | vport_construct = gdb.lookup_symbol('netdev_vport_construct')[0] | |
631 | ||
632 | if netdev_class['construct'] == vport_construct.value(): | |
633 | return True | |
634 | return False | |
635 | ||
636 | @staticmethod | |
637 | def display_single_netdev_provider(reg_class, indent=0): | |
638 | indent = " " * indent | |
639 | print("{}(struct netdev_registered_class *) {}: refcnt = {},". | |
640 | format(indent, reg_class, reg_class['refcnt'])) | |
641 | ||
642 | print("{} (struct netdev_class *) 0x{:x} = {{type = {}, " | |
643 | "is_pmd = {}, ...}}, ". | |
644 | format(indent, long(reg_class['class']), | |
645 | reg_class['class']['type'].string(), | |
646 | reg_class['class']['is_pmd'])) | |
647 | ||
648 | if CmdDumpNetdevProvider.is_class_vport_class(reg_class['class']): | |
649 | vport = container_of( | |
650 | reg_class['class'], | |
651 | gdb.lookup_type('struct vport_class').pointer(), | |
652 | 'netdev_class') | |
653 | ||
654 | if vport['dpif_port'] != 0: | |
655 | dpif_port = vport['dpif_port'].string() | |
656 | else: | |
657 | dpif_port = "\"\"" | |
658 | ||
659 | print("{} (struct vport_class *) 0x{:x} = " | |
660 | "{{ dpif_port = {}, ... }}". | |
661 | format(indent, long(vport), dpif_port)) | |
662 | ||
663 | def invoke(self, arg, from_tty): | |
664 | netdev_classes = get_global_variable('netdev_classes') | |
665 | if netdev_classes is None: | |
666 | return | |
667 | ||
668 | for reg_class in ForEachCMAP(netdev_classes, | |
669 | "struct netdev_registered_class", | |
670 | "cmap_node"): | |
671 | self.display_single_netdev_provider(reg_class) | |
672 | ||
673 | ||
5d0a6267 EC |
674 | # |
675 | # Implements the GDB "ovs_dump_ovs_list" command | |
676 | # | |
677 | class CmdDumpOvsList(gdb.Command): | |
678 | """Dump all nodes of an ovs_list give | |
679 | Usage: ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]} | |
680 | ||
681 | For example dump all the none quiescent OvS RCU threads: | |
682 | ||
683 | (gdb) ovs_dump_ovs_list &ovsrcu_threads | |
684 | (struct ovs_list *) 0x7f2a14000900 | |
685 | (struct ovs_list *) 0x7f2acc000900 | |
686 | (struct ovs_list *) 0x7f2a680668d0 | |
687 | ||
688 | This is not very useful, so please use this with the container_of mode: | |
689 | ||
690 | (gdb) ovs_dump_ovs_list &ovsrcu_threads 'struct ovsrcu_perthread' list_node | |
691 | (struct ovsrcu_perthread *) 0x7f2a14000900 | |
692 | (struct ovsrcu_perthread *) 0x7f2acc000900 | |
693 | (struct ovsrcu_perthread *) 0x7f2a680668d0 | |
694 | ||
695 | Now you can manually use the print command to show the content, or use the | |
696 | dump option to dump the structure for all nodes: | |
697 | ||
698 | (gdb) ovs_dump_ovs_list &ovsrcu_threads 'struct ovsrcu_perthread' list_node dump | |
699 | (struct ovsrcu_perthread *) 0x7f2a14000900 = | |
700 | {list_node = {prev = 0xf48e80 <ovsrcu_threads>, next = 0x7f2acc000900}, mutex... | |
701 | ||
702 | (struct ovsrcu_perthread *) 0x7f2acc000900 = | |
703 | {list_node = {prev = 0x7f2a14000900, next = 0x7f2a680668d0}, mutex ... | |
704 | ||
705 | (struct ovsrcu_perthread *) 0x7f2a680668d0 = | |
706 | {list_node = {prev = 0x7f2acc000900, next = 0xf48e80 <ovsrcu_threads>}, ... | |
707 | """ | |
708 | def __init__(self): | |
709 | super(CmdDumpOvsList, self).__init__("ovs_dump_ovs_list", | |
710 | gdb.COMMAND_DATA) | |
711 | ||
712 | def invoke(self, arg, from_tty): | |
713 | arg_list = gdb.string_to_argv(arg) | |
714 | typeobj = None | |
715 | member = None | |
716 | dump = False | |
717 | ||
718 | if len(arg_list) != 1 and len(arg_list) != 3 and len(arg_list) != 4: | |
719 | print("usage: ovs_dump_ovs_list <struct ovs_list *> " | |
720 | "{[<structure>] [<member>] {dump}]}") | |
721 | return | |
722 | ||
723 | header = gdb.parse_and_eval(arg_list[0]).cast( | |
724 | gdb.lookup_type('struct ovs_list').pointer()) | |
725 | ||
726 | if len(arg_list) >= 3: | |
727 | typeobj = arg_list[1] | |
728 | member = arg_list[2] | |
729 | if len(arg_list) == 4 and arg_list[3] == "dump": | |
730 | dump = True | |
731 | ||
732 | for node in ForEachLIST(header.dereference()): | |
733 | if typeobj is None or member is None: | |
734 | print("(struct ovs_list *) {}".format(node)) | |
735 | else: | |
736 | print("({} *) {} =".format( | |
737 | typeobj, | |
738 | container_of(node, | |
739 | gdb.lookup_type(typeobj).pointer(), member))) | |
740 | if dump: | |
741 | print(" {}\n".format(container_of( | |
742 | node, | |
743 | gdb.lookup_type(typeobj).pointer(), | |
744 | member).dereference())) | |
745 | ||
746 | ||
42562061 EC |
747 | # |
748 | # Implements the GDB "ovs_dump_simap" command | |
749 | # | |
750 | class CmdDumpSimap(gdb.Command): | |
cd5b89a5 EC |
751 | """Dump all key, value entries of a simap |
752 | Usage: ovs_dump_simap <struct simap *> | |
42562061 EC |
753 | """ |
754 | ||
755 | def __init__(self): | |
756 | super(CmdDumpSimap, self).__init__("ovs_dump_simap", | |
757 | gdb.COMMAND_DATA) | |
758 | ||
759 | def invoke(self, arg, from_tty): | |
760 | arg_list = gdb.string_to_argv(arg) | |
761 | ||
762 | if len(arg_list) != 1: | |
763 | print("ERROR: Missing argument!\n") | |
764 | print(self.__doc__) | |
765 | return | |
766 | ||
767 | simap = gdb.parse_and_eval(arg_list[0]).cast( | |
768 | gdb.lookup_type('struct simap').pointer()) | |
769 | ||
770 | values = dict() | |
771 | max_name_len = 0 | |
772 | for name, value in ForEachSIMAP(simap.dereference()): | |
773 | values[name.string()] = long(value) | |
774 | if len(name.string()) > max_name_len: | |
775 | max_name_len = len(name.string()) | |
776 | ||
777 | for name in sorted(values.iterkeys()): | |
778 | print("{}: {} / 0x{:x}".format(name.ljust(max_name_len), | |
779 | values[name], values[name])) | |
780 | ||
781 | ||
b23d42a6 | 782 | # |
cd5b89a5 EC |
783 | # Implements the GDB "ovs_dump_smap" command |
784 | # | |
785 | class CmdDumpSmap(gdb.Command): | |
786 | """Dump all key, value pairs of a smap | |
787 | Usage: ovs_dump_smap <struct smap *> | |
788 | """ | |
789 | ||
790 | def __init__(self): | |
791 | super(CmdDumpSmap, self).__init__("ovs_dump_smap", | |
792 | gdb.COMMAND_DATA) | |
793 | ||
794 | def invoke(self, arg, from_tty): | |
795 | arg_list = gdb.string_to_argv(arg) | |
796 | ||
797 | if len(arg_list) != 1: | |
798 | print("ERROR: Missing argument!\n") | |
799 | print(self.__doc__) | |
800 | return | |
801 | ||
802 | smap = gdb.parse_and_eval(arg_list[0]).cast( | |
803 | gdb.lookup_type('struct smap').pointer()) | |
804 | ||
805 | values = dict() | |
806 | max_key_len = 0 | |
807 | for key, value in ForEachSMAP(smap.dereference()): | |
808 | values[key.string()] = value.string() | |
809 | if len(key.string()) > max_key_len: | |
810 | max_key_len = len(key.string()) | |
811 | ||
812 | for key in sorted(values.iterkeys()): | |
813 | print("{}: {}".format(key.ljust(max_key_len), | |
814 | values[key])) | |
815 | ||
816 | ||
817 | # | |
818 | # Implements the GDB "ovs_dump_udpif_keys" command | |
b23d42a6 EC |
819 | # |
820 | class CmdDumpUdpifKeys(gdb.Command): | |
821 | """Dump all nodes of an ovs_list give | |
822 | Usage: ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short} | |
823 | ||
824 | <udpif_name> : Full name of the udpif's dpif to dump | |
825 | <udpif_address> : Address of the udpif structure to dump. If both the | |
826 | <udpif_name> and <udpif_address> are omitted the | |
827 | available udpif structures are displayed. | |
828 | short : Only dump ukey structure addresses, no content details | |
829 | """ | |
830 | ||
831 | def __init__(self): | |
832 | super(CmdDumpUdpifKeys, self).__init__("ovs_dump_udpif_keys", | |
833 | gdb.COMMAND_DATA) | |
834 | ||
835 | def count_all_ukeys(self, udpif): | |
836 | count = 0 | |
837 | for j in range(0, N_UMAPS): | |
838 | count += udpif['ukeys'][j]['cmap']['impl']['p']['n'] | |
839 | return count | |
840 | ||
841 | def dump_all_ukeys(self, udpif, indent=0, short=False): | |
842 | indent = " " * indent | |
843 | for j in range(0, N_UMAPS): | |
844 | if udpif['ukeys'][j]['cmap']['impl']['p']['n'] != 0: | |
845 | print("{}(struct umap *) {}:". | |
846 | format(indent, udpif['ukeys'][j].address)) | |
847 | for ukey in ForEachCMAP(udpif['ukeys'][j]['cmap'], | |
848 | "struct udpif_key", "cmap_node"): | |
849 | ||
850 | base_str = "{} (struct udpif_key *) {}: ". \ | |
851 | format(indent, ukey) | |
852 | if short: | |
853 | print(base_str) | |
854 | continue | |
855 | ||
856 | print("{}key_len = {}, mask_len = {}". | |
857 | format(base_str, ukey['key_len'], ukey['mask_len'])) | |
858 | ||
859 | indent_b = " " * len(base_str) | |
860 | if ukey['ufid_present']: | |
861 | print("{}ufid = {}". | |
862 | format( | |
863 | indent_b, str(uuid.UUID( | |
864 | "{:08x}{:08x}{:08x}{:08x}". | |
865 | format(long(ukey['ufid']['u32'][3]), | |
866 | long(ukey['ufid']['u32'][2]), | |
867 | long(ukey['ufid']['u32'][1]), | |
868 | long(ukey['ufid']['u32'][0])))))) | |
869 | ||
870 | print("{}hash = 0x{:8x}, pmd_id = {}". | |
871 | format(indent_b, long(ukey['hash']), ukey['pmd_id'])) | |
872 | print("{}state = {}".format(indent_b, ukey['state'])) | |
873 | print("{}n_packets = {}, n_bytes = {}". | |
874 | format(indent_b, | |
875 | ukey['stats']['n_packets'], | |
876 | ukey['stats']['n_bytes'])) | |
877 | print("{}used = {}, tcp_flags = 0x{:04x}". | |
878 | format(indent_b, | |
879 | ukey['stats']['used'], | |
880 | long(ukey['stats']['tcp_flags']))) | |
881 | ||
882 | # | |
883 | # TODO: Would like to add support for dumping key, mask | |
884 | # actions, and xlate_cache | |
885 | # | |
886 | # key = "" | |
887 | # for nlattr in ForEachNL(ukey['key'], ukey['key_len']): | |
888 | # key += "{}{}".format( | |
889 | # "" if len(key) == 0 else ", ", | |
890 | # nlattr['nla_type'].cast( | |
891 | # gdb.lookup_type('enum ovs_key_attr'))) | |
892 | # print("{}key attributes = {}".format(indent_b, key)) | |
893 | ||
894 | def invoke(self, arg, from_tty): | |
895 | arg_list = gdb.string_to_argv(arg) | |
896 | all_udpifs = get_global_variable('all_udpifs') | |
897 | if all_udpifs is None: | |
898 | return | |
899 | ||
900 | udpifs = dict() | |
901 | for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"): | |
902 | udpifs[udpif['dpif']['full_name'].string()] = udpif | |
903 | ||
904 | if len(arg_list) == 0: | |
905 | print("(struct udpif *) {}: name = {}, total keys = {}". | |
906 | format(udpif, udpif['dpif']['full_name'].string(), | |
907 | self.count_all_ukeys(udpif))) | |
908 | ||
909 | if len(arg_list) == 0: | |
910 | return | |
911 | ||
912 | if arg_list[0] in udpifs: | |
913 | udpif = udpifs[arg_list[0]] | |
914 | else: | |
915 | try: | |
916 | udpif = gdb.parse_and_eval(arg_list[0]).cast( | |
917 | gdb.lookup_type('struct udpif').pointer()) | |
918 | except Exception: | |
919 | udpif = None | |
920 | ||
921 | if udpif is None: | |
922 | print("Can't find provided udpif address!") | |
923 | return | |
924 | ||
925 | self.dump_all_ukeys(udpif, 0, "short" in arg_list[1:]) | |
926 | ||
927 | ||
ef557e2c EC |
928 | # |
929 | # Implements the GDB "ovs_show_fdb" command | |
930 | # | |
931 | class CmdShowFDB(gdb.Command): | |
932 | """Show FDB information | |
933 | Usage: ovs_show_fdb {<bridge_name> {dbg} {hash}} | |
934 | ||
935 | <bridge_name> : Optional bridge name, if not supplied FDB summary | |
936 | information is displayed for all bridges. | |
937 | dbg : Will show structure address information | |
938 | hash : Will display the forwarding table using the hash | |
939 | table, rather than the rlu list. | |
940 | """ | |
941 | ||
942 | def __init__(self): | |
943 | super(CmdShowFDB, self).__init__("ovs_show_fdb", | |
944 | gdb.COMMAND_DATA) | |
945 | ||
946 | @staticmethod | |
947 | def __get_port_name_num(mac_entry): | |
948 | if mac_entry['mlport'] is not None: | |
949 | port = mac_entry['mlport']['port'].cast( | |
950 | gdb.lookup_type('struct ofbundle').pointer()) | |
951 | ||
952 | port_name = port['name'].string() | |
953 | port_no = long(container_of( | |
954 | port['ports']['next'], | |
955 | gdb.lookup_type('struct ofport_dpif').pointer(), | |
956 | 'bundle_node')['up']['ofp_port']) | |
957 | ||
958 | if port_no == 0xfff7: | |
959 | port_no = "UNSET" | |
960 | elif port_no == 0xfff8: | |
961 | port_no = "IN_PORT" | |
962 | elif port_no == 0xfff9: | |
963 | port_no = "TABLE" | |
964 | elif port_no == 0xfffa: | |
965 | port_no = "NORMAL" | |
966 | elif port_no == 0xfffb: | |
967 | port_no = "FLOOD" | |
968 | elif port_no == 0xfffc: | |
969 | port_no = "ALL" | |
970 | elif port_no == 0xfffd: | |
971 | port_no = "CONTROLLER" | |
972 | elif port_no == 0xfffe: | |
973 | port_no = "LOCAL" | |
974 | elif port_no == 0xffff: | |
975 | port_no = "NONE" | |
976 | else: | |
977 | port_no = str(port_no) | |
978 | else: | |
979 | port_name = "-" | |
980 | port_no = "?" | |
981 | ||
982 | return port_name, port_no | |
983 | ||
984 | @staticmethod | |
985 | def display_ml_summary(ml, indent=0, dbg=False): | |
986 | indent = " " * indent | |
987 | if ml is None: | |
988 | return | |
989 | ||
990 | if dbg: | |
991 | print("[(struct mac_learning *) {}]".format(ml)) | |
992 | ||
993 | print("{}table.n : {}".format(indent, ml['table']['n'])) | |
994 | print("{}secret : 0x{:x}".format(indent, long(ml['secret']))) | |
995 | print("{}idle_time : {}".format(indent, ml['idle_time'])) | |
996 | print("{}max_entries : {}".format(indent, ml['max_entries'])) | |
997 | print("{}ref_count : {}".format(indent, ml['ref_cnt']['count'])) | |
998 | print("{}need_revalidate : {}".format(indent, ml['need_revalidate'])) | |
999 | print("{}ports_by_ptr.n : {}".format(indent, ml['ports_by_ptr']['n'])) | |
1000 | print("{}ports_by_usage.n: {}".format(indent, | |
1001 | ml['ports_by_usage']['n'])) | |
4ef1edf0 EC |
1002 | print("{}total_learned : {}".format(indent, ml['total_learned'])) |
1003 | print("{}total_expired : {}".format(indent, ml['total_expired'])) | |
1004 | print("{}total_evicted : {}".format(indent, ml['total_evicted'])) | |
1005 | print("{}total_moved : {}".format(indent, ml['total_moved'])) | |
ef557e2c EC |
1006 | |
1007 | @staticmethod | |
1008 | def display_mac_entry(mac_entry, indent=0, dbg=False): | |
1009 | port_name, port_no = CmdShowFDB.__get_port_name_num(mac_entry) | |
1010 | ||
1011 | line = "{}{:16.16} {:-4} {} {:-9}".format( | |
1012 | indent, | |
1013 | "{}[{}]".format(port_no, port_name), | |
1014 | long(mac_entry['vlan']), | |
1015 | eth_addr_to_string(mac_entry['mac']), | |
1016 | long(mac_entry['expires'])) | |
1017 | ||
1018 | if dbg: | |
1019 | line += " [(struct mac_entry *) {}]".format(mac_entry) | |
1020 | ||
1021 | print(line) | |
1022 | ||
1023 | @staticmethod | |
1024 | def display_ml_entries(ml, indent=0, hash=False, dbg=False): | |
1025 | indent = " " * indent | |
1026 | if ml is None: | |
1027 | return | |
1028 | ||
1029 | print("\n{}FDB \"{}\" table:".format(indent, | |
1030 | "lrus" if not hash else "hash")) | |
1031 | print("{}port VLAN MAC Age out @". | |
1032 | format(indent)) | |
1033 | print("{}----------------- ---- ----------------- ---------". | |
1034 | format(indent)) | |
1035 | ||
1036 | mac_entries = 0 | |
1037 | ||
1038 | if hash: | |
1039 | for mac_entry in ForEachHMAP(ml['table'], | |
1040 | "struct mac_entry", | |
1041 | "hmap_node"): | |
1042 | CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg) | |
1043 | mac_entries += 1 | |
1044 | else: | |
1045 | for mac_entry in ForEachLIST(ml['lrus'], | |
1046 | "struct mac_entry", | |
1047 | "lru_node"): | |
1048 | CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg) | |
1049 | mac_entries += 1 | |
1050 | ||
1051 | print("\nTotal MAC entries: {}".format(mac_entries)) | |
1052 | time_now = list(get_time_now()) | |
1053 | time_now[1] = time_now[0] + time_now[1] | |
1054 | print("\n{}Current time is between {} and {} seconds.\n". | |
1055 | format(indent, min(time_now[0], time_now[1]), | |
1056 | max(time_now[0], time_now[1]))) | |
1057 | ||
1058 | def invoke(self, arg, from_tty): | |
1059 | arg_list = gdb.string_to_argv(arg) | |
1060 | ||
1061 | all_ofproto_dpifs_by_name = get_global_variable( | |
1062 | 'all_ofproto_dpifs_by_name') | |
1063 | if all_ofproto_dpifs_by_name is None: | |
1064 | return | |
1065 | ||
1066 | all_name = dict() | |
1067 | max_name_len = 0 | |
1068 | for node in ForEachHMAP(all_ofproto_dpifs_by_name, | |
1069 | "struct ofproto_dpif", | |
1070 | "all_ofproto_dpifs_by_name_node"): | |
1071 | ||
1072 | all_name[node['up']['name'].string()] = node | |
1073 | if len(node['up']['name'].string()) > max_name_len: | |
1074 | max_name_len = len(node['up']['name'].string()) | |
1075 | ||
1076 | if len(arg_list) == 0: | |
1077 | for name in sorted(all_name.iterkeys()): | |
1078 | print("{}: (struct mac_learning *) {}". | |
1079 | format(name.ljust(max_name_len), | |
1080 | all_name[name]['ml'])) | |
1081 | ||
1082 | self.display_ml_summary(all_name[name]['ml'], 4) | |
1083 | else: | |
1084 | if not arg_list[0] in all_name: | |
1085 | print("ERROR: Given bridge name is not known!") | |
1086 | return | |
1087 | ||
1088 | ml = all_name[arg_list[0]]['ml'] | |
1089 | self.display_ml_summary(ml, 0, "dbg" in arg_list[1:]) | |
1090 | self.display_ml_entries(ml, 0, "hash" in arg_list[1:], | |
1091 | "dbg" in arg_list[1:]) | |
1092 | ||
b23d42a6 EC |
1093 | |
1094 | # | |
1095 | # Implements the GDB "ovs_show_fdb" command | |
1096 | # | |
1097 | class CmdShowUpcall(gdb.Command): | |
1098 | """Show upcall information | |
1099 | Usage: ovs_show_upcall {dbg} | |
1100 | ||
1101 | dbg : Will show structure address information | |
1102 | """ | |
1103 | ||
1104 | def __init__(self): | |
1105 | super(CmdShowUpcall, self).__init__("ovs_show_upcall", | |
1106 | gdb.COMMAND_DATA) | |
1107 | ||
1108 | @staticmethod | |
1109 | def display_udpif_upcall(udpif, indent=0, dbg=False): | |
1110 | indent = " " * indent | |
1111 | ||
1112 | enable_ufid = get_global_variable('enable_ufid') | |
1113 | if enable_ufid is None: | |
1114 | return | |
1115 | ||
1116 | dbg_str = "" | |
1117 | if dbg: | |
1118 | dbg_str = ", ((struct udpif *) {})".format(udpif) | |
1119 | ||
1120 | print("{}{}{}:".format( | |
1121 | indent, udpif['dpif']['full_name'].string(), | |
1122 | dbg_str)) | |
1123 | ||
1124 | print("{} flows : (current {}) (avg {}) (max {}) (limit {})". | |
1125 | format(indent, udpif['n_flows'], udpif['avg_n_flows'], | |
1126 | udpif['max_n_flows'], udpif['flow_limit'])) | |
1127 | print("{} dump duration : {}ms". | |
1128 | format(indent, udpif['dump_duration'])) | |
1129 | print("{} ufid enabled : {}\n". | |
1130 | format(indent, enable_ufid & | |
1131 | udpif['backer']['rt_support']['ufid'])) | |
1132 | ||
1133 | for i in range(0, int(udpif['n_revalidators'])): | |
1134 | revalidator = udpif['revalidators'][i] | |
1135 | ||
1136 | dbg_str = "" | |
1137 | if dbg: | |
1138 | dbg_str = ", ((struct revalidator *) {})".\ | |
1139 | format(revalidator.address) | |
1140 | ||
1141 | count = 0 | |
1142 | j = i | |
1143 | while j < N_UMAPS: | |
1144 | count += udpif['ukeys'][j]['cmap']['impl']['p']['n'] | |
1145 | j += int(udpif['n_revalidators']) | |
1146 | ||
1147 | print("{} {}: (keys {}){}". | |
1148 | format(indent, revalidator['id'], count, dbg_str)) | |
1149 | ||
1150 | print("") | |
1151 | ||
1152 | def invoke(self, arg, from_tty): | |
1153 | arg_list = gdb.string_to_argv(arg) | |
1154 | ||
1155 | all_udpifs = get_global_variable('all_udpifs') | |
1156 | if all_udpifs is None: | |
1157 | return | |
1158 | ||
1159 | for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"): | |
1160 | self.display_udpif_upcall(udpif, 0, "dbg" in arg_list) | |
1161 | ||
1162 | ||
0a3348d1 EC |
1163 | # |
1164 | # Initialize all GDB commands | |
1165 | # | |
1166 | CmdDumpBridge() | |
1167 | CmdDumpBridgePorts() | |
1168 | CmdDumpDpNetdev() | |
5d0a6267 | 1169 | CmdDumpDpNetdevPollThreads() |
0a3348d1 | 1170 | CmdDumpDpNetdevPorts() |
b9683ff0 | 1171 | CmdDumpDpProvider() |
0a3348d1 | 1172 | CmdDumpNetdev() |
42562061 | 1173 | CmdDumpNetdevProvider() |
5d0a6267 | 1174 | CmdDumpOvsList() |
42562061 | 1175 | CmdDumpSimap() |
cd5b89a5 | 1176 | CmdDumpSmap() |
b23d42a6 | 1177 | CmdDumpUdpifKeys() |
ef557e2c | 1178 | CmdShowFDB() |
b23d42a6 | 1179 | CmdShowUpcall() |