Updated the dpif_nl_exec_monitor.py utility to also optionally monitor
the other DPIF netlink operations, i.e., DPIF_OP_FLOW_PUT,
DPIF_OP_FLOW_DEL and DPIF_OP_FLOW_GET

Signed-off-by: Eelco Chaudron <[email protected]>
---
 Documentation/topics/usdt-probes.rst          |   7 +-
 utilities/automake.mk                         |   6 +-
 ..._exec_monitor.py => dpif_op_nl_monitor.py} | 279 +++++++++++++-----
 3 files changed, 207 insertions(+), 85 deletions(-)
 rename utilities/usdt-scripts/{dpif_nl_exec_monitor.py => 
dpif_op_nl_monitor.py} (72%)

diff --git a/Documentation/topics/usdt-probes.rst 
b/Documentation/topics/usdt-probes.rst
index b9a6c54b2..93bfaec12 100644
--- a/Documentation/topics/usdt-probes.rst
+++ b/Documentation/topics/usdt-probes.rst
@@ -239,7 +239,7 @@ DPIF_OP_FLOW_DEL operation as part of the dpif 
``operate()`` callback.
 
 **Script references**:
 
-- *None*
+- ``utilities/usdt-scripts/dpif_op_nl_monitor.py``
 
 
 dpif_netlink_operate\_\_:op_flow_execute
@@ -260,7 +260,7 @@ DPIF_OP_FLOW_EXECUTE operation as part of the dpif 
``operate()`` callback.
 
 **Script references**:
 
-- ``utilities/usdt-scripts/dpif_nl_exec_monitor.py``
+- ``utilities/usdt-scripts/dpif_op_nl_monitor.py``
 - ``utilities/usdt-scripts/upcall_cost.py``
 
 
@@ -281,7 +281,7 @@ DPIF_OP_FLOW_GET operation as part of the dpif 
``operate()`` callback.
 
 **Script references**:
 
-- *None*
+- ``utilities/usdt-scripts/dpif_op_nl_monitor.py``
 
 
 dpif_netlink_operate\_\_:op_flow_put
@@ -301,6 +301,7 @@ DPIF_OP_FLOW_PUT operation as part of the dpif 
``operate()`` callback.
 
 **Script references**:
 
+- ``utilities/usdt-scripts/dpif_op_nl_monitor.py``
 - ``utilities/usdt-scripts/upcall_cost.py``
 
 
diff --git a/utilities/automake.mk b/utilities/automake.mk
index 146b8c37f..acc1af4e0 100644
--- a/utilities/automake.mk
+++ b/utilities/automake.mk
@@ -22,7 +22,7 @@ scripts_SCRIPTS += \
 scripts_DATA += utilities/ovs-lib
 usdt_SCRIPTS += \
        utilities/usdt-scripts/bridge_loop.bt \
-       utilities/usdt-scripts/dpif_nl_exec_monitor.py \
+       utilities/usdt-scripts/dpif_op_nl_monitor.py \
        utilities/usdt-scripts/flow_reval_monitor.py \
        utilities/usdt-scripts/kernel_delay.py \
        utilities/usdt-scripts/kernel_delay.rst \
@@ -72,7 +72,7 @@ EXTRA_DIST += \
        utilities/docker/debian/Dockerfile \
        utilities/docker/debian/build-kernel-modules.sh \
        utilities/usdt-scripts/bridge_loop.bt \
-       utilities/usdt-scripts/dpif_nl_exec_monitor.py \
+       utilities/usdt-scripts/dpif_op_nl_monitor.py \
        utilities/usdt-scripts/flow_reval_monitor.py \
        utilities/usdt-scripts/kernel_delay.py \
        utilities/usdt-scripts/kernel_delay.rst \
@@ -147,7 +147,7 @@ FLAKE8_PYFILES += utilities/ovs-pcap.in \
        utilities/ovs-check-dead-ifs.in \
        utilities/ovs-tcpdump.in \
        utilities/ovs-pipegen.py \
-       utilities/usdt-scripts/dpif_nl_exec_monitor.py \
+       utilities/usdt-scripts/dpif_op_nl_monitor.py \
        utilities/usdt-scripts/flow_reval_monitor.py \
        utilities/usdt-scripts/upcall_monitor.py \
        utilities/usdt-scripts/upcall_cost.py
diff --git a/utilities/usdt-scripts/dpif_nl_exec_monitor.py 
b/utilities/usdt-scripts/dpif_op_nl_monitor.py
similarity index 72%
rename from utilities/usdt-scripts/dpif_nl_exec_monitor.py
rename to utilities/usdt-scripts/dpif_op_nl_monitor.py
index a5cf1a35a..c0e0a9fae 100755
--- a/utilities/usdt-scripts/dpif_nl_exec_monitor.py
+++ b/utilities/usdt-scripts/dpif_op_nl_monitor.py
@@ -19,7 +19,8 @@
 # dpif_nl_exec_monitor.py uses the dpif_netlink_operate__:op_flow_execute USDT
 # probe to receive all DPIF_OP_EXECUTE operations that are queued for
 # transmission over the netlink socket. It will do some basic decoding, and if
-# requested a packet dump.
+# requested a packet dump. Note that there are also options to obtain
+# additional information for the DPIF_OP_FLOW_* operations.
 #
 # Here is an example:
 #
@@ -75,31 +76,9 @@
 #                seq       = 0xc
 #
 # The example above dumps the full netlink and packet decode. However options
-# exist to disable this. Here is the full list of supported options:
-#
-# usage: dpif_nl_exec_monitor.py [-h] [--buffer-page-count NUMBER] [-D [DEBUG]]
-#                                [-d {none,hex,decode}] [-n {none,hex,nlraw}]
-#                                [-p VSWITCHD_PID] [-s [64-2048]]
-#                                [-w PCAP_FILE]
-#
-# optional arguments:
-#   -h, --help            show this help message and exit
-#   --buffer-page-count NUMBER
-#                         Number of BPF ring buffer pages, default 1024
-#   -D [DEBUG], --debug [DEBUG]
-#                         Enable eBPF debugging
-#   -d {none,hex,decode}, --packet-decode {none,hex,decode}
-#                         Display packet content in selected mode, default none
-#   -n {none,hex,nlraw}, --nlmsg-decode {none,hex,nlraw}
-#                         Display netlink message content in selected mode,
-#                         default nlraw
-#   -p VSWITCHD_PID, --pid VSWITCHD_PID
-#                         ovs-vswitch's PID
-#   -s [64-2048], --nlmsg-size [64-2048]
-#                         Set maximum netlink message size to capture, default
-#                         512
-#   -w PCAP_FILE, --pcap PCAP_FILE
-#                         Write upcall packets to specified pcap file
+# exist to disable this. For a complete list of options, please use the
+# '--help' or '-h' argument.
+#
 
 from bcc import BPF, USDT, USDTException
 from os.path import exists
@@ -142,12 +121,10 @@ struct ofpbuf {
 BPF_RINGBUF_OUTPUT(events, <BUFFER_PAGE_CNT>);
 BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1);
 
-int trace__op_flow_execute(struct pt_regs *ctx) {
-    struct ofpbuf nlbuf;
+static int trace_event(struct ofpbuf *nlbuf)
+{
     uint32_t size;
 
-    bpf_usdt_readarg_p(5, ctx, &nlbuf, sizeof(nlbuf));
-
     struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t));
     if (!event) {
         uint32_t type = 0;
@@ -163,17 +140,51 @@ int trace__op_flow_execute(struct pt_regs *ctx) {
     event->pid = bpf_get_current_pid_tgid();
     bpf_get_current_comm(&event->comm, sizeof(event->comm));
 
-    event->nl_size = nlbuf.size;
+    event->nl_size = nlbuf->size;
     if (event->nl_size > MAX_NLMSG)
         size = MAX_NLMSG;
     else
         size = event->nl_size;
 
-    bpf_probe_read(&event->nl_msg, size, nlbuf.data);
+    bpf_probe_read(&event->nl_msg, size, nlbuf->data);
 
     events.ringbuf_submit(event, 0);
     return 0;
+}
+
+int trace__op_execute(struct pt_regs *ctx) {
+    struct ofpbuf nlbuf;
+
+    bpf_usdt_readarg_p(5, ctx, &nlbuf, sizeof(nlbuf));
+    return trace_event(&nlbuf);
+};
+
+#if <ENABLE_OP_FLOW_PUT>
+int trace__op_flow_put(struct pt_regs *ctx) {
+    struct ofpbuf nlbuf;
+
+    bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf));
+    return trace_event(&nlbuf);
+};
+#endif
+
+#if <ENABLE_OP_FLOW_DEL>
+int trace__op_flow_del(struct pt_regs *ctx) {
+    struct ofpbuf nlbuf;
+
+    bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf));
+    return trace_event(&nlbuf);
 };
+#endif
+
+#if <ENABLE_OP_FLOW_GET>
+int trace__op_flow_get(struct pt_regs *ctx) {
+    struct ofpbuf nlbuf;
+
+    bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf));
+    return trace_event(&nlbuf);
+};
+#endif
 """
 
 
@@ -182,21 +193,24 @@ int trace__op_flow_execute(struct pt_regs *ctx) {
 #
 def print_event(ctx, data, size):
     event = b["events"].event(data)
-    print("{:<18.9f} {:<4} {:<16} {:<10} {:<10}".
+
+    if event.nl_size < options.nlmsg_size:
+        nl_size = event.nl_size
+    else:
+        nl_size = options.nlmsg_size
+
+    print("{:<18.9f} {:<4} {:<16} {:<10} {:<10} {}".
           format(event.ts / 1000000000,
                  event.cpu,
                  event.comm.decode("utf-8"),
                  event.pid,
-                 event.nl_size))
+                 event.nl_size,
+                 get_ovs_dpif_op_str(get_cmd_type_from_nlm(
+                     bytes(event.nl_msg)[:nl_size]))))
 
     #
     # Dumping the netlink message data if requested.
     #
-    if event.nl_size < options.nlmsg_size:
-        nl_size = event.nl_size
-    else:
-        nl_size = options.nlmsg_size
-
     if options.nlmsg_decode == "hex":
         #
         # Abuse scapy's hex dump to dump flow key
@@ -306,6 +320,19 @@ def decode_nlm_tlvs(tlvs, header=None, indent=4, dump=True,
     return result
 
 
+#
+# get_cmd_type_from_nlm()
+#
+def get_cmd_type_from_nlm(nlm):
+    # The netlink message consists of at least a 'struct nlmsghdr' (16-bytes)
+    # followed by a 'struct genlmsghdr'. The first byte of the genlmsghdr
+    # structure contains the command. Which is what we will extract here.
+    if len(nlm) < 17:
+        return -1
+
+    return nlm[16]
+
+
 #
 # decode_nlm()
 #
@@ -325,9 +352,11 @@ def decode_nlm(msg, indent=4, dump=True):
     #
     # Decode 'struct genlmsghdr'
     #
+    cmd, version, reserved = struct.unpack("=BBH", msg[:4])
+
     if dump:
-        print("{}genlmsghdr: cmd = {}, version = {}, reserver = {}".format(
-            " " * indent, *struct.unpack("=BBH", msg[:4])))
+        print("{}genlmsghdr: cmd = {}, version = {}, reserved = {}".format(
+            " " * indent, get_ovs_dpif_op_str(cmd), version, reserved))
 
     msg = msg[4:]
 
@@ -343,42 +372,94 @@ def decode_nlm(msg, indent=4, dump=True):
     #
     # Decode TLVs
     #
-    nl_attr_tree = {
-        "OVS_PACKET_ATTR_KEY": {
-            "header": "> Decode OVS_KEY_ATTR_* TLVs:",
-            "indent": 4,
-            "attr_str_func": get_ovs_key_attr_str,
-            "decode_tree": None,
-        },
-        "OVS_PACKET_ATTR_ACTIONS": {
-            "header": "> Decode OVS_ACTION_ATTR_* TLVs:",
-            "indent": 4,
-            "attr_str_func": get_ovs_action_attr_str,
-            "decode_tree": {
-                "OVS_ACTION_ATTR_SET": {
-                    "header": "> Decode OVS_KEY_ATTR_* TLVs:",
-                    "indent": 4,
-                    "attr_str_func": get_ovs_key_attr_str,
-                    "decode_tree": {
-                        "OVS_KEY_ATTR_TUNNEL": {
-                            "header": "> Decode OVS_TUNNEL_KEY_ATTR_* TLVs:",
-                            "indent": 4,
-                            "attr_str_func": get_ovs_tunnel_key_attr_str,
-                            "decode_tree": None,
-                        },
+    nl_key_attr = {
+        "header": "> Decode OVS_KEY_ATTR_* TLVs:",
+        "indent": 4,
+        "attr_str_func": get_ovs_key_attr_str,
+        "decode_tree": {
+            "OVS_KEY_ATTR_ENCAP": {
+                "header": "> Decode OVS_KEY_ATTR_* TLVs:",
+                "indent": 4,
+                "attr_str_func": get_ovs_key_attr_str,
+                "decode_tree": {
+                    "OVS_KEY_ATTR_ENCAP": {
+                        "header": "> Decode OVS_KEY_ATTR_* TLVs:",
+                        "indent": 4,
+                        "attr_str_func": get_ovs_key_attr_str,
+                        "decode_tree": None,
+                    },
+                },
+            },
+        }
+    }
+
+    nl_action_attr = {
+        "header": "> Decode OVS_ACTION_ATTR_* TLVs:",
+        "indent": 4,
+        "attr_str_func": get_ovs_action_attr_str,
+        "decode_tree": {
+            "OVS_ACTION_ATTR_SET": {
+                "header": "> Decode OVS_KEY_ATTR_* TLVs:",
+                "indent": 4,
+                "attr_str_func": get_ovs_key_attr_str,
+                "decode_tree": {
+                    "OVS_KEY_ATTR_TUNNEL": {
+                        "header": "> Decode OVS_TUNNEL_KEY_ATTR_* TLVs:",
+                        "indent": 4,
+                        "attr_str_func": get_ovs_tunnel_key_attr_str,
+                        "decode_tree": None,
                     },
                 },
             },
         },
     }
 
-    result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump,
-                             header="> Decode OVS_PACKET_ATTR_* TLVs:",
-                             attr_to_str_func=get_ovs_pkt_attr_str,
-                             decode_tree=nl_attr_tree)
+    nl_attr_tree_exec = {
+        "OVS_PACKET_ATTR_KEY": nl_key_attr,
+        "OVS_PACKET_ATTR_ACTIONS": nl_action_attr,
+    }
+
+    nl_attr_tree_put = {
+        "OVS_FLOW_ATTR_KEY": nl_key_attr,
+        "OVS_FLOW_ATTR_MASK": nl_key_attr,
+        "OVS_FLOW_ATTR_ACTIONS": nl_action_attr,
+    }
+
+    if get_ovs_dpif_op_str(cmd) == "DPIF_OP_EXECUTE":
+        result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump,
+                                 header="> Decode OVS_PACKET_ATTR_* TLVs:",
+                                 attr_to_str_func=get_ovs_pkt_attr_str,
+                                 decode_tree=nl_attr_tree_exec)
+    else:
+        result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump,
+                                 header="> Decode OVS_FLOW_ATTR_* TLVs:",
+                                 attr_to_str_func=get_ovs_flow_attr_str,
+                                 decode_tree=nl_attr_tree_put)
     return result
 
 
+#
+# get_ovs_flow_attr_str()
+#
+def get_ovs_flow_attr_str(attr):
+    ovs_flow_attr = ["OVS_FLOW_ATTR_UNSPEC",
+                     "OVS_FLOW_ATTR_KEY",
+                     "OVS_FLOW_ATTR_ACTIONS",
+                     "OVS_FLOW_ATTR_STATS",
+                     "OVS_FLOW_ATTR_TCP_FLAGS",
+                     "OVS_FLOW_ATTR_USED",
+                     "OVS_FLOW_ATTR_CLEAR",
+                     "OVS_FLOW_ATTR_MASK",
+                     "OVS_FLOW_ATTR_PROBE",
+                     "OVS_FLOW_ATTR_UFID",
+                     "OVS_FLOW_ATTR_UFID_FLAGS",
+                     "OVS_FLOW_ATTR_PAD"]
+    if attr < 0 or attr >= len(ovs_flow_attr):
+        return "<UNKNOWN:{}>".format(attr)
+
+    return ovs_flow_attr[attr]
+
+
 #
 # get_ovs_pkt_attr_str()
 #
@@ -507,6 +588,22 @@ def get_ovs_tunnel_key_attr_str(attr):
     return ovs_tunnel_key_attr[attr]
 
 
+#
+# get_ovs_dpif_op_str()
+#
+def get_ovs_dpif_op_str(op):
+    ovs_dpif_ops = ["DPIF_OP_UNSPEC",
+                    "DPIF_OP_FLOW_PUT",
+                    "DPIF_OP_FLOW_DEL",
+                    "DPIF_OP_EXECUTE",
+                    "DPIF_OP_FLOW_GET"]
+
+    if op < 0 or op >= len(ovs_dpif_ops):
+        return "<UNKNOWN:{}>".format(op)
+
+    return ovs_dpif_ops[op]
+
+
 #
 # buffer_size_type()
 #
@@ -566,8 +663,17 @@ def main():
                         help="Set maximum netlink message size to capture, "
                         "default 512", type=buffer_size_type, default=512,
                         metavar="[64-2048]")
+    parser.add_argument("--trace-del-op",
+                        help="Monitor DPIF_OP_FLOW_DEL messages",
+                        action="store_true")
+    parser.add_argument("--trace-get-op",
+                        help="Monitor DPIF_OP_FLOW_GET messages",
+                        action="store_true")
+    parser.add_argument("--trace-put-op",
+                        help="Monitor DPIF_OP_FLOW_PUT messages",
+                        action="store_true")
     parser.add_argument("-w", "--pcap", metavar="PCAP_FILE",
-                        help="Write upcall packets to specified pcap file",
+                        help="Write execute packets to specified pcap file",
                         type=str, default=None)
 
     options = parser.parse_args()
@@ -606,13 +712,21 @@ def main():
     u = USDT(pid=int(options.pid))
     try:
         u.enable_probe(probe="dpif_netlink_operate__:op_flow_execute",
-                       fn_name="trace__op_flow_execute")
+                       fn_name="trace__op_execute")
+        if options.trace_put_op:
+            u.enable_probe(probe="dpif_netlink_operate__:op_flow_put",
+                           fn_name="trace__op_flow_put")
+        if options.trace_del_op:
+            u.enable_probe(probe="dpif_netlink_operate__:op_flow_del",
+                           fn_name="trace__op_flow_del")
+        if options.trace_get_op:
+            u.enable_probe(probe="dpif_netlink_operate__:op_flow_get",
+                           fn_name="trace__op_flow_get")
     except USDTException as e:
-        print("ERROR: {}"
-              "ovs-vswitchd!".format(
-                  (re.sub("^", " " * 7, str(e), flags=re.MULTILINE)).strip().
-                  replace("--with-dtrace or --enable-dtrace",
-                          "--enable-usdt-probes")))
+        print("ERROR: {}".format(
+            (re.sub("^", " " * 7, str(e), flags=re.MULTILINE)).strip().
+            replace("--with-dtrace or --enable-dtrace",
+                    "--enable-usdt-probes")))
         sys.exit(-1)
 
     #
@@ -627,15 +741,22 @@ def main():
     source = source.replace("<BUFFER_PAGE_CNT>",
                             str(options.buffer_page_count))
 
+    source = source.replace("<ENABLE_OP_FLOW_DEL>",
+                            "1" if options.trace_del_op else "0")
+    source = source.replace("<ENABLE_OP_FLOW_GET>",
+                            "1" if options.trace_get_op else "0")
+    source = source.replace("<ENABLE_OP_FLOW_PUT>",
+                            "1" if options.trace_put_op else "0")
+
     b = BPF(text=source, usdt_contexts=[u], debug=options.debug)
 
     #
     # Print header
     #
-    print("Display DPIF_OP_EXECUTE operations being queued for transmission "
-          "onto the netlink socket.")
-    print("{:<18} {:<4} {:<16} {:<10} {:<10}".format(
-        "TIME", "CPU", "COMM", "PID", "NL_SIZE"))
+    print("Display DPIF operations being queued for transmission onto the "
+          "netlink socket.")
+    print("{:<18} {:<4} {:<16} {:<10} {:<10} {}".format(
+        "TIME", "CPU", "COMM", "PID", "NL_SIZE", "DPIF_OPERATION"))
 
     #
     # Dump out all events
-- 
2.45.2

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to