On 27 Jul 2022, at 16:48, Kevin Sprague wrote:
> During normal operations, it is useful to understand when a particular flow
> gets removed from the system. This can be useful when debugging performance
> issues tied to ofproto flow changes, trying to determine deployed traffic
> patterns, or while debugging dynamic systems where ports come and go.
>
> Prior to this change, there was a lack of visibility around flow expiration.
> The existing debugging infrastructure could tell us when a flow was added to
> the datapath, but not when it was removed or why.
>
> This change introduces a USDT probe at the point where the revalidator
> determines that the flow should be removed. Additionally, we track the
> reason for the flow eviction and provide that information as well. With
> this change, we can track the complete flow lifecycle for the netlink datapath
> by hooking the upcall tracepoint in kernel, the flow put USDT, and the
> revaldiator USDT, letting us watch as flows are added and removed from the
> kernel datapath.
>
> This change only enables this information via USDT probe, so it won't be
> possible to access this information any other way (see:
> Documentation/topics/usdt-probes.rst).
>
> Also included is a script (utilities/usdt-scripts/filter_probe.py) that serves
> as a demonstration of how the new USDT probe might be used going forward.
>
> Signed-off-by: Kevin Sprague <[email protected]>
See comments below, but it's not a full review, as unfortunately, I was running
out of time...
//Eelco
> ---
> Documentation/topics/usdt-probes.rst | 21 +
> ofproto/ofproto-dpif-upcall.c | 40 +-
> utilities/automake.mk | 2 +
> utilities/usdt-scripts/filter_probe.py | 549 +++++++++++++++++++++++++
> 4 files changed, 606 insertions(+), 6 deletions(-)
> create mode 100755 utilities/usdt-scripts/filter_probe.py
>
> diff --git a/Documentation/topics/usdt-probes.rst
> b/Documentation/topics/usdt-probes.rst
> index 7ce19aaed..a977dc006 100644
> --- a/Documentation/topics/usdt-probes.rst
> +++ b/Documentation/topics/usdt-probes.rst
> @@ -214,6 +214,7 @@ Available probes in ``ovs_vswitchd``:
> - dpif_recv:recv_upcall
> - main:poll_block
> - main:run_start
> +- revalidate:flow_result
>
>
> dpif_netlink_operate\_\_:op_flow_del
> @@ -294,6 +295,7 @@ DPIF_OP_FLOW_PUT operation as part of the dpif
> ``operate()`` callback.
>
> **Script references**:
>
> +- ``utilities/usdt-scripts/filter_probe.py``
> - ``utilities/usdt-scripts/upcall_cost.py``
>
>
> @@ -357,6 +359,25 @@ See also the ``main:run_start`` probe above.
>
> - ``utilities/usdt-scripts/bridge_loop.bt``
Need extra CR/LF here.
>
> +probe revalidate:flow_result
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +**Description**:
> +This probe is triggered when the revalidator decides whether or not to
> +revalidate a flow. ``reason`` is an enum that denotes that either the flow
> +is being kept, or the reason why the flow is being deleted. The
> +``filter_probe.py`` script uses this probe to notify users when flows
> +matching user-provided criteria are deleted.
> +
> +**Arguments**:
> +
> +- *arg0*: ``(enum flow_del_reason) reason``
> +- *arg1*: ``(struct udpif *) udpif``
> +- *arg2*: ``(struct udpif_key *) ukey``
> +
> +**Script references**:
> +
> +- ``utilities/usdt-scripts/filter_probe.py``
>
> Adding your own probes
> ----------------------
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index 57f94df54..efee8c0a4 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -31,6 +31,7 @@
> #include "openvswitch/list.h"
> #include "netlink.h"
> #include "openvswitch/ofpbuf.h"
> +#include "openvswitch/usdt-probes.h"
> #include "ofproto-dpif-ipfix.h"
> #include "ofproto-dpif-sflow.h"
> #include "ofproto-dpif-xlate.h"
> @@ -260,6 +261,17 @@ enum ukey_state {
> };
> #define N_UKEY_STATES (UKEY_DELETED + 1)
>
> +enum flow_del_reason {
> + FDR_FLOW_LIVE = 0,
> + FDR_FLOW_TIME_OUT, /* the flow went unused and was deleted. */
Start with a capital T.
> + FDR_TOO_EXPENSIVE,
> + FDR_FLOW_WILDCARDED,
> + FDR_BAD_ODP_FIT,
> + FDR_ASSOCIATED_OFPROTO,
> + FDR_XLATION_ERROR,
> + FDR_AVOID_CACHING,
I think it would make sense to add a description to each delete reason so that
when people use the USDT script, they can easily understand why the flow got
deleted.
> +};
> +
> /* 'udpif_key's are responsible for tracking the little bit of state udpif
> * needs to do flow expiration which can't be pulled directly from the
> * datapath. They may be created by any handler or revalidator thread at any
> @@ -2202,7 +2214,8 @@ populate_xcache(struct udpif *udpif, struct udpif_key
> *ukey,
> static enum reval_result
> revalidate_ukey__(struct udpif *udpif, const struct udpif_key *ukey,
> uint16_t tcp_flags, struct ofpbuf *odp_actions,
> - struct recirc_refs *recircs, struct xlate_cache *xcache)
> + struct recirc_refs *recircs, struct xlate_cache *xcache,
> + enum flow_del_reason *reason)
> {
> struct xlate_out *xoutp;
> struct netflow *netflow;
> @@ -2220,11 +2233,13 @@ revalidate_ukey__(struct udpif *udpif, const struct
> udpif_key *ukey,
> netflow = NULL;
>
> if (xlate_ukey(udpif, ukey, tcp_flags, &ctx)) {
> + *reason = FDR_XLATION_ERROR;
> goto exit;
> }
> xoutp = &ctx.xout;
>
> if (xoutp->avoid_caching) {
> + *reason = FDR_AVOID_CACHING;
> goto exit;
> }
>
> @@ -2238,6 +2253,7 @@ revalidate_ukey__(struct udpif *udpif, const struct
> udpif_key *ukey,
> ofpbuf_clear(odp_actions);
>
> if (!ofproto) {
> + *reason = FDR_ASSOCIATED_OFPROTO;
> goto exit;
> }
>
> @@ -2249,6 +2265,7 @@ revalidate_ukey__(struct udpif *udpif, const struct
> udpif_key *ukey,
> if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, &dp_mask, &ctx.flow,
> NULL)
> == ODP_FIT_ERROR) {
> + *reason = FDR_BAD_ODP_FIT;
> goto exit;
> }
>
> @@ -2258,6 +2275,7 @@ revalidate_ukey__(struct udpif *udpif, const struct
> udpif_key *ukey,
> * down. Note that we do not know if the datapath has ignored any of the
> * wildcarded bits, so we may be overly conservative here. */
> if (flow_wildcards_has_extra(&dp_mask, ctx.wc)) {
> + *reason = FDR_FLOW_WILDCARDED;
> goto exit;
> }
>
> @@ -2303,7 +2321,8 @@ static enum reval_result
> revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
> const struct dpif_flow_stats *stats,
> struct ofpbuf *odp_actions, uint64_t reval_seq,
> - struct recirc_refs *recircs, bool offloaded)
> + struct recirc_refs *recircs, bool offloaded,
> + enum flow_del_reason *reason)
> OVS_REQUIRES(ukey->mutex)
> {
> bool need_revalidate = ukey->reval_seq != reval_seq;
> @@ -2329,8 +2348,12 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key
> *ukey,
> xlate_cache_clear(ukey->xcache);
> }
> result = revalidate_ukey__(udpif, ukey, push.tcp_flags,
> - odp_actions, recircs, ukey->xcache);
> - } /* else delete; too expensive to revalidate */
> + odp_actions, recircs, ukey->xcache,
> + reason);
> + } else {
> + /* else delete; too expensive to revalidate */
See the previous review, as I think the following comment is more clear:
/* Delete the flow as it's too expensive to revalidate. */
> + *reason = FDR_TOO_EXPENSIVE;
> + }
> } else if (!push.n_packets || ukey->xcache
> || !populate_xcache(udpif, ukey, push.tcp_flags)) {
> result = UKEY_KEEP;
> @@ -2720,6 +2743,7 @@ revalidate(struct revalidator *revalidator)
> struct recirc_refs recircs = RECIRC_REFS_EMPTY_INITIALIZER;
> struct dpif_flow_stats stats = f->stats;
> enum reval_result result;
> + enum flow_del_reason reason = FDR_FLOW_LIVE;
> struct udpif_key *ukey;
> bool already_dumped;
> int error;
> @@ -2767,10 +2791,11 @@ revalidate(struct revalidator *revalidator)
> }
> if (kill_them_all || (used && used < now - max_idle)) {
> result = UKEY_DELETE;
> + reason = FDR_FLOW_TIME_OUT;
> } else {
> result = revalidate_ukey(udpif, ukey, &stats, &odp_actions,
> reval_seq, &recircs,
> - f->attrs.offloaded);
> + f->attrs.offloaded, &reason);
> }
> ukey->dump_seq = dump_seq;
>
> @@ -2779,6 +2804,7 @@ revalidate(struct revalidator *revalidator)
> udpif_update_flow_pps(udpif, ukey, f);
> }
>
> + OVS_USDT_PROBE(revalidate, flow_result, reason, udpif, ukey);
> if (result != UKEY_KEEP) {
> /* Takes ownership of 'recircs'. */
> reval_op_init(&ops[n_ops++], result, udpif, ukey, &recircs,
> @@ -2829,6 +2855,7 @@ revalidator_sweep__(struct revalidator *revalidator,
> bool purge)
> struct udpif_key *ukey;
> struct umap *umap = &udpif->ukeys[i];
> size_t n_ops = 0;
> + enum flow_del_reason reason = FDR_FLOW_LIVE;
>
> CMAP_FOR_EACH(ukey, cmap_node, &umap->cmap) {
> enum ukey_state ukey_state;
> @@ -2855,7 +2882,8 @@ revalidator_sweep__(struct revalidator *revalidator,
> bool purge)
> COVERAGE_INC(revalidate_missed_dp_flow);
> memset(&stats, 0, sizeof stats);
> result = revalidate_ukey(udpif, ukey, &stats,
> &odp_actions,
> - reval_seq, &recircs, false);
> + reval_seq, &recircs, false,
> + &reason);
> }
> if (result != UKEY_KEEP) {
> /* Clears 'recircs' if filled by revalidate_ukey(). */
> diff --git a/utilities/automake.mk b/utilities/automake.mk
> index eb57653a1..0f7d3adc1 100644
> --- a/utilities/automake.mk
> +++ b/utilities/automake.mk
> @@ -63,6 +63,7 @@ EXTRA_DIST += \
> utilities/docker/debian/Dockerfile \
> utilities/docker/debian/build-kernel-modules.sh \
> utilities/usdt-scripts/bridge_loop.bt \
> + utilities/usdt-scripts/filter_probe.py \
> utilities/usdt-scripts/upcall_cost.py \
> utilities/usdt-scripts/upcall_monitor.py
> MAN_ROOTS += \
> @@ -133,6 +134,7 @@ FLAKE8_PYFILES += utilities/ovs-pcap.in \
> utilities/ovs-check-dead-ifs.in \
> utilities/ovs-tcpdump.in \
> utilities/ovs-pipegen.py \
> + utilities/usdt-scripts/filter_probe.py \
> utilities/usdt-scripts/upcall_monitor.py \
> utilities/usdt-scripts/upcall_cost.py
>
> diff --git a/utilities/usdt-scripts/filter_probe.py
> b/utilities/usdt-scripts/filter_probe.py
> new file mode 100755
> index 000000000..1aac5d44c
> --- /dev/null
> +++ b/utilities/usdt-scripts/filter_probe.py
I think the name is not that intuitive what about something like
revalidate_monitor.py, or flow_delete_monitor.py?
For the script itself, I had quite some comments, and as I'm on PTO starting
today for three weeks, I thought it might be easier (and faster for me) to just
send a diff of things I think will require changes.
However I was still running out of time, so I did not get a chance to change
the "ufid" field in struct event_t to the ovs_u128 type, and look at the filter
code.
We can discuss this more after my PTO as I have very limited email access, or
someone else can review the remaining part.
diff --git a/utilities/usdt-scripts/filter_probe.py
b/utilities/usdt-scripts/filter_probe.py
index 1aac5d44c..9c007d340 100755
--- a/utilities/usdt-scripts/filter_probe.py
+++ b/utilities/usdt-scripts/filter_probe.py
@@ -91,6 +91,7 @@ import psutil
import struct
import sys
import time
+
#
# eBPF source code
#
@@ -98,21 +99,16 @@ bpf_src = """
#include <linux/types.h>
#include <uapi/linux/ptrace.h>
-#define MAX_KEY 2048
+#define MAX_KEY 2048
#define FLOW_FILTER <FILTER_BOOL>
+enum probe { OP_FLOW_PUT, FLOW_RESULT };
-enum probe { PUT, REVAL };
typedef union ovs_u128 {
u32 ufid32[4];
u64 ufid64[2];
} ovs_u128;
-struct nlattr {
- u16 len;
- u16 type;
-};
-
struct dpif_flow_put {
int flags;
void *key_ptr;
@@ -136,7 +132,7 @@ struct udpif_key {
struct event_t {
u64 ts;
u32 reason;
- u32 ufid[4];
+ u32 ufid[4]; /* FIXME: Change this also to the ovs_u128 type. */
u64 key_size;
unsigned char key[MAX_KEY];
enum probe probe;
@@ -145,46 +141,58 @@ struct event_t {
BPF_HASH(watchlist, ovs_u128);
BPF_RINGBUF_OUTPUT(events, <BUFFER_PAGE_COUNT>);
-int watch_reval(struct pt_regs *ctx) {
+int usdt__flow_result(struct pt_regs *ctx) {
u64 *ufid_present = NULL;
- struct udpif_key u;
- bpf_usdt_readarg_p(3, ctx, &u, sizeof(struct udpif_key));
- ovs_u128 ufid = u.ufid;
+ struct udpif_key ukey;
+
+ bpf_usdt_readarg_p(3, ctx, &ukey, sizeof ukey);
+ ovs_u128 ufid = ukey.ufid;
ufid_present = watchlist.lookup(&ufid);
- if(FLOW_FILTER && !ufid_present)
+
+ if(FLOW_FILTER && !ufid_present) {
return 0;
- struct event_t *data = events.ringbuf_reserve(sizeof(struct event_t));
- /* If we can't reserve the space we need for the ring buffer, return 1 */
- if(!data)
+ }
+
+ struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t));
+ if(!event) {
+ /* If we can't reserve the space in the ring buffer, return 1. */
return 1;
- data->probe = REVAL;
- data->ts = bpf_ktime_get_ns();
- bpf_probe_read(&data->ufid, sizeof(ufid), &ufid);
- bpf_usdt_readarg(1, ctx, &data->reason);
- events.ringbuf_submit(data, 0);
+ }
+
+ event->probe = FLOW_RESULT;
+ event->ts = bpf_ktime_get_ns();
+ bpf_probe_read(&event->ufid, sizeof ufid, &ufid);
+ bpf_usdt_readarg(1, ctx, &event->reason);
+ events.ringbuf_submit(event, 0);
+
return 0;
};
-int watch_put(struct pt_regs *ctx) {
- struct event_t *data = events.ringbuf_reserve(sizeof(struct event_t));
- struct dpif_flow_put f;
- struct nlattr nla;
+int usdt__op_flow_put(struct pt_regs *ctx) {
+ struct dpif_flow_put put;
ovs_u128 ufid;
- if(!data)
+
+ struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t));
+ if(!event) {
+ /* If we can't reserve the space in the ring buffer, return 1. */
return 1;
- data->probe = PUT;
- data->ts = bpf_ktime_get_ns();
- bpf_usdt_readarg_p(2, ctx, &f, sizeof(struct dpif_flow_put));
- bpf_probe_read(&data->ufid, sizeof(data->ufid), (void *) f.ufid_ptr);
- bpf_probe_read(&ufid, sizeof(ufid), &data->ufid);
- if (f.key_len > MAX_KEY) // verifier fails without this check.
- f.key_len = MAX_KEY;
- data->key_size = f.key_len;
- bpf_probe_read(&data->key, f.key_len, (void*)f.key_ptr);
+ }
+
+ event->probe = OP_FLOW_PUT;
+ event->ts = bpf_ktime_get_ns();
+ bpf_usdt_readarg_p(2, ctx, &put, sizeof put);
+ bpf_probe_read(&event->ufid, sizeof event->ufid, put.ufid_ptr);
+ bpf_probe_read(&ufid, sizeof ufid , &event->ufid);
+ if (put.key_len > MAX_KEY) {
+ put.key_len = MAX_KEY;
+ }
+ event->key_size = put.key_len;
+ bpf_probe_read(&event->key, put.key_len, put.key_ptr);
+ event->reason = 0;
+ events.ringbuf_submit(event, 0);
+
watchlist.increment(ufid);
- data->reason = 0;
- events.ringbuf_submit(data, 0);
return 0;
};
"""
@@ -194,10 +202,9 @@ int watch_put(struct pt_regs *ctx) {
# format_ufid()
#
def format_ufid(ufid):
- result = "ufid:%08x-%04x-%04x-%04x-%04x%08x" \
- % (ufid[0], ufid[1] >> 16, ufid[1] & 0xffff,
- ufid[2] >> 16, ufid[2] & 0, ufid[3])
- return result
+ return "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(
+ ufid[0], ufid[1] >> 16, ufid[1] & 0xffff,
+ ufid[2] >> 16, ufid[2] & 0, ufid[3])
#
@@ -219,19 +226,21 @@ def handle_flow_put(event):
if args.flow_keys or args.filter_flows is not None:
key = decode_key(bytes(event.key)[:event.key_size])
flow_dict, flow_str = parse_flow_dict(key)
- # for each attribute that we're watching
+ # For each attribute that we're watching.
if args.filter_flows is not None:
if not compare_dicts(args.filter_flows, flow_dict):
- # print("ignoring a flow")
find_and_delete_from_watchlist(event)
return
+
print("{:<18.9f} {:<45} {:<13}".format(event.ts / 1000000000,
- format_ufid(event.ufid), "flow_put"))
+ format_ufid(event.ufid), "Insert (put) flow to kernel"))
+
if args.flow_keys:
if len(flow_str) > 80:
flow_str = " " + "),\n ".join(flow_str.split("), "))
- print("{} has the following flow information:".
- format(format_ufid(event.ufid)))
+ else:
+ flow_str = " " + flow_str
+ print(" - It holds the following flow information:")
print(flow_str)
@@ -239,6 +248,7 @@ def handle_flow_put(event):
# compare_dicts()
#
def compare_dicts(filter, target):
+ # FIXME: filter is a reserved key word, so use something else.
for key in filter:
if key not in target:
return False
@@ -282,12 +292,20 @@ def parse_flow_str(flow_str):
# print_expiration()
#
def print_expiration(event):
+ reasons = ["Unknown flow expire reason!", "Flow timed out",
+ "Flow revalidation too expensive",
+ "Flow needs narrower wildcard mask",
+ "Bad ODP flow fit", "Flow with associated ofproto",
+ "Flow translation error", "Flow cache avoidance"]
+
ufid_str = format_ufid(event.ufid)
- reasons = ["Flow timed out", "Revalidation too expensive",
- "Flow wildcards", "Bad ODP fit", "Associated ofproto",
- "Translation error", "Cache avoidance"]
+ reason = event.reason
+
+ if reason not in range(0, len(reasons) - 1):
+ reason = 0
+
print("{:<18.9f} {:<45} {:<17}".
- format(event.ts / 1000000000, ufid_str, reasons[event.reason - 1]))
+ format(event.ts / 1000000000, ufid_str, reasons[reason]))
#
@@ -350,8 +368,10 @@ def get_ovs_key_attr_str(attr):
"ct_tuple4",
"ct_tuple6",
"nsh"]
+
if attr < 0 or attr > len(ovs_key_attr):
return "<UNKNOWN>: {}".format(attr)
+
return ovs_key_attr[attr]
@@ -361,11 +381,12 @@ def get_ovs_key_attr_str(attr):
def is_nonzero(val):
if isinstance(val, int):
return (val != 0)
- else:
- if isinstance(val, str):
- val = bytes(val, 'utf-8')
- # if it's not a string or an int, it's bytes.
- return (val.count(0) < len(val))
+
+ if isinstance(val, str):
+ val = bytes(val, 'utf-8')
+
+ # If it's not a string or an int, it's bytes.
+ return (val.count(0) < len(val))
#
@@ -475,68 +496,104 @@ def handle_event(ctx, data, size):
# 1. It's a revalidator probe and the reason is nonzero: A flow is expiring
# 2. It's a revalidator probe and the reason is zero: flow revalidated
# 3. It's a flow_put probe.
+ #
+ # We will ignore item 2, all others we report.
+ #
event = b["events"].event(data)
- if event.probe and event.reason:
- print_expiration(event)
- if not event.probe:
+ if event.probe == 0: # OP_FLOW_PUT
handle_flow_put(event)
+ elif event.probe == 1 and event.reason > 0: # FLOW_RESULT
+ print_expiration(event)
def main():
+ #
+ # Don't like these globals, but ctx passing does not seem to work with the
+ # existing open_ring_buffer() API :(
+ #
global b
global args
+
+ #
# Argument parsing
+ #
parser = argparse.ArgumentParser()
parser.add_argument("--buffer-page-count",
help="Number of BPF ring buffer pages, default 1024",
type=int, default=1024, metavar="NUMBER")
parser.add_argument("-k", "--flow-keys",
help="Print flow keys as flow strings",
- type=bool, const=True, default=False, nargs="?")
+ action="store_true")
parser.add_argument("--pid", "-p", metavar="VSWITCHD_PID",
help="ovs-vswitchd's PID", type=int, default=None)
parser.add_argument("-D", "--debug", help="Enable eBPF debugging",
type=int, const=0x3f, default=0, nargs="?")
- parser.add_argument("-f", "--filter-flows", metavar="FLOW STRING",
+ parser.add_argument("-f", "--filter-flows", metavar="FLOW_STRING",
help="Filter flows that match the specified flow",
type=str, default=None, nargs="*")
args = parser.parse_args()
- vswitch_pid = args.pid
- # If we didn't specify the PID for ovs-vswitchd, find its PID.
- if vswitch_pid is None:
+
+ #
+ # Find the PID of the ovs-vswitchd daemon if not specified.
+ #
+ if args.pid is None:
for proc in psutil.process_iter():
- if "ovs-vswitchd" in proc.name():
- if vswitch_pid is not None:
- print("Error: Multiple ovs-vswitchd daemons running. "
- "Use the -p option to specify one to track.")
+ if 'ovs-vswitchd' in proc.name():
+ if args.pid is not None:
+ print("ERROR: Multiple ovs-vswitchd daemons running, "
+ "use the -p option!")
sys.exit(-1)
- vswitch_pid = proc.pid
- if vswitch_pid is None:
- print("Error: is ovs-vswitchd running?")
+
+ args.pid = proc.pid
+ #
+ # Error checking on input parameters
+ #
+ if args.pid is None:
+ print("ERROR: Failed to find ovs-vswitchd's PID!")
sys.exit(-1)
- u = USDT(pid=int(vswitch_pid))
+
+ #
+ # Attach the usdt probes
+ #
+ u = USDT(pid=int(args.pid))
try:
- u.enable_probe(probe="op_flow_put", fn_name="watch_put")
+ u.enable_probe(probe="op_flow_put", fn_name="usdt__op_flow_put")
except USDTException as e:
- print("Error attaching flow_put probe.")
+ print("Error attaching the dpif_netlink_operate__:op_flow_put probe.")
print(str(e))
sys.exit(-1)
+
try:
- u.enable_probe(probe="flow_result", fn_name="watch_reval")
+ u.enable_probe(probe="flow_result", fn_name="usdt__flow_result")
except USDTException as e:
- print("Error attaching revalidator_deletion probe.")
+ print("Error attaching the revalidate:flow_result probe.")
print(str(e))
sys.exit(-1)
- filter_bool = 0
- if args.filter_flows is not None:
+
+ #
+ # Attach probe to running process
+ #
+ if args.filter_flows is None:
+ filter_bool = 0
+ else:
filter_bool = 1
args.filter_flows = parse_flow_str(args.filter_flows[0])
+
source = bpf_src.replace("<BUFFER_PAGE_COUNT>",
str(args.buffer_page_count))
source = source.replace("<FILTER_BOOL>", str(filter_bool))
+
b = BPF(text=source, usdt_contexts=[u], debug=args.debug)
- b["events"].open_ring_buffer(handle_event)
+
+ #
+ # Print header
+ #
print("{:<18} {:<45} {:<17}".format("TIME", "UFID", "EVENT/REASON"))
+
+ #
+ # Dump out all events
+ #
+ b["events"].open_ring_buffer(handle_event)
while 1:
try:
b.ring_buffer_poll()
@@ -545,5 +602,8 @@ def main():
break
+#
+# Start main() as the default entry point...
+#
if __name__ == "__main__":
main()
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev