On 28 Jul 2022, at 13:57, Eelco Chaudron wrote:
> 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.
Forgot to add I also did not review the help text you added to the script :(
>
> 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