On Tue, Jan 17, 2023 at 10:19:23AM +0100, Eelco Chaudron wrote:
> This patch adds a Python script that can be used to analyze the
> revalidator runs by providing statistics (including some real time
> graphs).
> 
> The USDT events can also be captured to a file and used for
> later offline analysis.
> 
> The following blog explains the Open vSwitch revalidator
> implementation and how this tool can help you understand what is
> happening in your system.
> 
> https://developers.redhat.com/articles/2022/10/19/open-vswitch-revalidator-process-explained
> 
> Signed-off-by: Eelco Chaudron <echau...@redhat.com>

Thanks Eelco,

this looks very nice.

A few minor nits from my side, but that not withstanding.

Acked-by: Simon Horman <simon.hor...@corigine.com>

...

> +#
> +# event_to_dict()
> +#
> +def event_to_dict(event):
> +    event_dict = {}
> +
> +    for field, _ in event._fields_:
> +        if isinstance(getattr(event, field), (int, bytes)):
> +            event_dict[field] = getattr(event, field)
> +
> +    return event_dict

I thought that a list comprehension might be nicer here,
but now I'm not so sure.

    *completely untested*

    return dict([(field, getattr(event, field))
                 for (field, _) in fields
                 if isinstance(getattr(event, field), (int, bytes))])


...

> +def _process_event(event):
> +    global graph
> +
> +    if export_file is not None:
> +        export_file.write("event = {}\n".format(event_to_dict(event)))
> +
> +    if event.id == 0 and not state['running']:
> +        start = state["last_start"]
> +        done = state["last_done"]
> +        if done and start:
> +            actual_wait = (event.ts - done.ts) / 1000000
> +            csv = "{}, {}, {}, {}, {}, {}, {}, {}, {}, {:.2f}".format(
> +                start.ts, done.ts, done.n_flows, graph.ukey_count,
> +                done.avg_n_flows, done.max_n_flows, done.flow_limit,
> +                done.dump_duration, done.poll_wait, actual_wait)
> +
> +            if graph.base_time == 0:
> +                graph = graph._replace(base_time=done.ts)
> +
> +            graph.time.append((done.ts - graph.base_time) / 1000000000)
> +            graph.n_flows.append(done.n_flows)
> +            graph.n_reval_flows.append(graph.ukey_count)
> +            graph.avg_n_flows.append(done.avg_n_flows)
> +            graph.max_n_flows.append(done.max_n_flows)
> +            graph.flow_limit.append(done.flow_limit)
> +            graph.dump_duration.append(done.dump_duration)
> +            graph.poll_wait.append(done.poll_wait)
> +            graph.actual_wait.append(actual_wait)
> +
> +            if not options.no_gui and not options.no_realtime_plots:
> +                updated_graph = dynamic_plot_update(
> +                    graph, refresh=options.update_interval)
> +                if updated_graph is None:
> +                    raise KeyboardInterrupt
> +                graph = updated_graph
> +
> +            if options.compress_output:
> +                last_csv = state["last_csv"]
> +                if not last_csv or \
> +                   csv.split(",")[2:-1] != last_csv.split(",")[2:-1] or \
> +                   abs((event.ts - done.ts) / 1000000 - done.poll_wait) > 
> 100:
> +                    print(csv)
> +                else:
> +                    state["last_not_printed_csv"] = csv
> +
> +                state["last_csv"] = csv
> +            else:
> +                print(csv)

nit: maybe it is nicer to store csv as a tuple and provide
a helper to print it? (Maybe not :)

> +
> +        state["last_start"] = event
> +        state['running'] = True
> +        graph = graph._replace(ukey_count=0)
> +    elif event.id == 1 and state['running']:
> +        state["last_done"] = event
> +        state['running'] = False
> +    elif event.id == 2 and state['running']:
> +        graph = graph._replace(ukey_count=graph.ukey_count + 1)
> +
> +
> +#
> +# run_program()
> +#
> +def run_program(command, need_result=True):

nit: It appears that the need_result is passed by callers.
     So perhaps the parameter can be dropped and the function simplified?

> +    try:
> +        process = subprocess.run(command,
> +                                 stdout=subprocess.PIPE,
> +                                 stderr=subprocess.STDOUT,
> +                                 encoding='utf8',
> +                                 check=True)
> +
> +    except subprocess.CalledProcessError as perror:
> +        if need_result:
> +            return perror.returncode, perror.stdout
> +
> +        return perror.returncode
> +
> +    if need_result:
> +        return 0, process.stdout
> +
> +    return 0
> +
> +
> +#
> +# get_ovs_definitions()
> +#
> +def get_ovs_definitions(objects, pahole="pahole", pid=None):
> +    if pid is None:
> +        raise ValueError("A valid pid value should be supplied!")
> +
> +    if not isinstance(objects, list):
> +        objects = [objects]
> +
> +    if len(objects) == 0:
> +        raise ValueError("Must supply at least one object!")
> +
> +    vswitchd = Path("/proc/{}/exe".format(str(pid))).resolve()
> +
> +    object_str = ""
> +    for obj in objects:
> +        object_str += obj + ','
> +
> +    object_str = object_str.rstrip(',')

nit: Maybe something like this is a bit nicer?

     object_str = ','.join(objects)

     *completely untested*

...
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to