Printing just the datapath on each upcall gives little information (most often, there will only be one well-known datapath). Instead, print both the input port name (plus the datapath).
In order to do this, refactor decode_nla to always generate the dump that only gets printed if needed. That way it can be called earlier on. Signed-off-by: Adrian Moreno <amore...@redhat.com> --- utilities/usdt-scripts/upcall_cost.py | 4 +- utilities/usdt-scripts/upcall_monitor.py | 110 +++++++++++++---------- utilities/usdt-scripts/usdt_lib.py | 20 ++++- 3 files changed, 84 insertions(+), 50 deletions(-) diff --git a/utilities/usdt-scripts/upcall_cost.py b/utilities/usdt-scripts/upcall_cost.py index 53028f059..d1948dae7 100755 --- a/utilities/usdt-scripts/upcall_cost.py +++ b/utilities/usdt-scripts/upcall_cost.py @@ -354,7 +354,7 @@ class DpUpcall(Event): pkt_frag_len): super(DpUpcall, self).__init__(ts, pid, comm, cpu, EventType.DP_UPCALL) self.dpif_name = dpif_name - self.dp_port = dp_map.get(dpif_name, port) + self.dp_port = dp_map.get_port_number(dpif_name, port) if self.dp_port is None: # # As we only identify interfaces at startup, new interfaces could @@ -447,7 +447,7 @@ class RecvUpcall(Event): self.pkt_len) def get_system_dp_port(dpif_name): - return dp_map.get_map().get(dpif_name, {}).get("ovs-system", None) + return dp_map.get_port_number(dpif_name, "ovs-system") def decode_nlm(msg, indent=4, dump=True): bytes_left = len(msg) diff --git a/utilities/usdt-scripts/upcall_monitor.py b/utilities/usdt-scripts/upcall_monitor.py index 917892b0e..b09fe1820 100755 --- a/utilities/usdt-scripts/upcall_monitor.py +++ b/utilities/usdt-scripts/upcall_monitor.py @@ -20,10 +20,10 @@ # packets sent by the kernel to ovs-vswitchd. By default, it will show all # upcall events, which looks something like this: # -# TIME CPU COMM PID DPIF_NAME TYPE PKT_LEN... -# 5952147.003848809 2 handler4 1381158 system@ovs-system 0 98 132 -# 5952147.003879643 2 handler4 1381158 system@ovs-system 0 70 160 -# 5952147.003914924 2 handler4 1381158 system@ovs-system 0 98 152 +# TIME CPU COMM PID PORT_NAME TYPE .. +# 5952147.003848809 2 handler4 1381158 eth0 (system@ovs-system) 0 +# 5952147.003879643 2 handler4 1381158 eth0 (system@ovs-system) 0 +# 5952147.003914924 2 handler4 1381158 eth0 (system@ovs-system) 0 # # Also, upcalls dropped by the kernel (e.g: because the netlink buffer is full) # are reported. This requires the kernel version to be greater or equal to 5.14. @@ -70,7 +70,7 @@ # # $ ./upcall_monitor.py --packet-decode decode --flow-key-decode nlraw \ # --packet-size 128 --flow-key-size 256 -# TIME CPU COMM PID DPIF_NAME ... +# TIME CPU COMM PID PORT_NAME ... # 5953013.333214231 2 handler4 1381158 system@ovs-system ... # Flow key size 132 bytes, size captured 132 bytes. # nla_len 8, nla_type OVS_KEY_ATTR_RECIRC_ID[20], data: 00 00 00 00 @@ -120,6 +120,8 @@ from os.path import exists from scapy.all import hexdump, wrpcap from scapy.layers.l2 import Ether +from usdt_lib import DpPortMapping + import argparse import psutil import re @@ -276,7 +278,7 @@ int kretprobe__ovs_dp_upcall(struct pt_regs *ctx) # # print_key() # -def print_key(event): +def print_key(event, decode_dump): if event.key_size < options.flow_key_size: key_len = event.key_size else: @@ -297,39 +299,48 @@ def print_key(event): dump=True), flags=re.MULTILINE)) - if options.flow_key_decode == 'nlraw': - nla = decode_nlm(bytes(event.key)[:key_len]) - else: - nla = decode_nlm(bytes(event.key)[:key_len], dump=False) - - if "OVS_KEY_ATTR_IN_PORT" in nla: - port = struct.unpack("=I", nla["OVS_KEY_ATTR_IN_PORT"])[0] - else: - port = "Unknown" - - return port + if options.flow_key_decode == "nlraw": + for line in decode_dump: + print(line) # # print_event() # def print_event(ctx, data, size): - event = b['events'].event(data) - print("{:<18.9f} {:<4} {:<16} {:<10} {:<32} {:<4} {:<10} {:<12} {:<8}". - format(event.ts / 1000000000, - event.cpu, - event.comm.decode("utf-8"), - event.pid, - event.dpif_name.decode("utf-8"), - event.upcall_type, - event.pkt_size, - event.key_size, - event.result)) + event = b["events"].event(data) + dp = event.dpif_name.decode("utf-8") + + nla, key_dump = decode_nlm( + bytes(event.key)[: min(event.key_size, options.flow_key_size)] + ) + if "OVS_KEY_ATTR_IN_PORT" in nla: + port_no = struct.unpack("=I", nla["OVS_KEY_ATTR_IN_PORT"])[0] + port = dp_map.get_port_name(dp.partition("@")[-1], port_no) + if not port: + port = str(port_no) + else: + port = "Unknown" + + print( + "{:<18.9f} {:<4} {:<16} {:<10} {:<40} {:<4} {:<10} {:<12} {:<8}". + format( + event.ts / 1000000000, + event.cpu, + event.comm.decode("utf-8"), + event.pid, + "{} ({})".format(port, dp), + event.upcall_type, + event.pkt_size, + event.key_size, + event.result, + ) + ) # # Dump flow key information # - port = print_key(event) + print_key(event, key_dump) # # Decode packet only if there is data @@ -368,23 +379,26 @@ def print_event(ctx, data, size): # # decode_nlm() # -def decode_nlm(msg, indent=4, dump=True): +def decode_nlm(msg, indent=4): bytes_left = len(msg) result = {} + dump = [] while bytes_left: if bytes_left < 4: - if dump: - print("{}WARN: decode truncated; can't read header".format( - ' ' * indent)) + dump.append( + "{}WARN: decode truncated; can't read header".format( + " " * indent + ) + ) break nla_len, nla_type = struct.unpack("=HH", msg[:4]) if nla_len < 4: - if dump: - print("{}WARN: decode truncated; nla_len < 4".format( - ' ' * indent)) + dump.append( + "{}WARN: decode truncated; nla_len < 4".format(" " * indent) + ) break nla_data = msg[4:nla_len] @@ -396,16 +410,19 @@ def decode_nlm(msg, indent=4, dump=True): else: result[get_ovs_key_attr_str(nla_type)] = nla_data - if dump: - print("{}nla_len {}, nla_type {}[{}], data: {}{}".format( + dump.append( + "{}nla_len {}, nla_type {}[{}], data: {}{}".format( ' ' * indent, nla_len, get_ovs_key_attr_str(nla_type), nla_type, - "".join("{:02x} ".format(b) for b in nla_data), trunc)) + "".join("{:02x} ".format(b) for b in nla_data), trunc) + ) if trunc != "": - if dump: - print("{}WARN: decode truncated; nla_len > msg_len[{}] ". - format(' ' * indent, bytes_left)) + dump.append( + "{}WARN: decode truncated; nla_len > msg_len[{}] ".format( + " " * indent, bytes_left + ) + ) break # update next offset, but make sure it's aligned correctly @@ -413,7 +430,7 @@ def decode_nlm(msg, indent=4, dump=True): msg = msg[next_offset:] bytes_left -= next_offset - return result + return result, dump # @@ -498,6 +515,9 @@ def main(): # global b global options + global dp_map + + dp_map = DpPortMapping() # # Argument parsing @@ -606,8 +626,8 @@ def main(): # # Print header # - print("{:<18} {:<4} {:<16} {:<10} {:<32} {:<4} {:<10} {:<12} {:<8}".format( - "TIME", "CPU", "COMM", "PID", "DPIF_NAME", "TYPE", "PKT_LEN", + print("{:<18} {:<4} {:<16} {:<10} {:<40} {:<4} {:<10} {:<12} {:<8}".format( + "TIME", "CPU", "COMM", "PID", "PORT_NAME", "TYPE", "PKT_LEN", "FLOW_KEY_LEN", "RESULT")) # diff --git a/utilities/usdt-scripts/usdt_lib.py b/utilities/usdt-scripts/usdt_lib.py index cf6fa576a..d8659102f 100644 --- a/utilities/usdt-scripts/usdt_lib.py +++ b/utilities/usdt-scripts/usdt_lib.py @@ -33,12 +33,26 @@ class DpPortMapping: """Override the internal cache map.""" self.cache_map = cache_map - def get(self, dp, port_no): - """Get the portname from a port number.""" + def get_port_name(self, dp, port_no): + """Get the port name from a port number.""" if self.cache_map is None: self._get_mapping() - return self.cache_map.get(dp, {}).get(port_no, None) + if not self.cache_map.get(dp): + return None + + for name, num in self.cache_map[dp].items(): + if num == port_no: + return name + + return None + + def get_port_number(self, dp, port): + """Get the port number from a port name.""" + if self.cache_map is None: + self._get_mapping() + + return self.cache_map.get(dp, {}).get(port, None) def _get_mapping(self): """Get the datapath port mapping from the running OVS.""" -- 2.48.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev