On 26 Feb 2025, at 10:43, Adrian Moreno wrote:
> 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. > This patch looks good to me, but I’m missing the ‘Add a retry mechanism to dp cache’ change. Or is this in one of the two missing patches? Cheers, Eelco > 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 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev