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