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

Reply via email to