commit fe6ff0874dfd9b18bf5eb71c2394431ca44874bc Author: Karsten Loesing karsten.loesing@gmx.net Date: Tue Nov 24 12:17:21 2020 +0100
Avoid tracebacks when visualizing measurements.
Attempting to visualize analysis files containing only unsuccessful measurements results in various tracebacks.
With this patch we check more carefully whether a data frame is empty before adding a plot.
Another related change is that we always include "time_to_{first,last}_byte" and "mbps" columns in the CSV output, regardless of whether there are there are any non-null values in the data. See also #40004 for a previous related change.
And we check whether a Tor stream identifier exists before retrieving a Tor stream.
Finally, we only include TTFB/TTLB if the usecs value is non-zero.
Fixes #44012. --- CHANGELOG.md | 5 +++++ onionperf/visualization.py | 37 +++++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c1f5ca..2655e01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# Changes in version 0.9 - 2020-1?-?? + + - Avoid tracebacks when visualizing analysis files containing only + unsuccessful measurements. Fixes #44012. + # Changes in version 0.8 - 2020-09-16
- Add a new `onionperf filter` mode that takes an OnionPerf analysis diff --git a/onionperf/visualization.py b/onionperf/visualization.py index 2cd3161..75ec2b5 100644 --- a/onionperf/visualization.py +++ b/onionperf/visualization.py @@ -66,6 +66,7 @@ class TGenVisualization(Visualization): tgen_streams = analysis.get_tgen_streams(client) tgen_transfers = analysis.get_tgen_transfers(client) while tgen_streams or tgen_transfers: + stream = {"time_to_first_byte": None, "time_to_last_byte": None, "error_code": None, "mbps": None} error_code = None source_port = None unix_ts_end = None @@ -76,15 +77,15 @@ class TGenVisualization(Visualization): # that value with unit megabits per second. if tgen_streams: stream_id, stream_data = tgen_streams.popitem() - stream = {"id": stream_id, "label": label, - "filesize_bytes": int(stream_data["stream_info"]["recvsize"]), - "error_code": None} + stream["id"] = stream_id + stream["label"] = label + stream["filesize_bytes"] = int(stream_data["stream_info"]["recvsize"]) stream["server"] = "onion" if ".onion:" in stream_data["transport_info"]["remote"] else "public" if "time_info" in stream_data: s = stream_data["time_info"] - if "usecs-to-first-byte-recv" in s: + if "usecs-to-first-byte-recv" in s and float(s["usecs-to-first-byte-recv"]) >= 0: stream["time_to_first_byte"] = float(s["usecs-to-first-byte-recv"])/1000000 - if "usecs-to-last-byte-recv" in s: + if "usecs-to-last-byte-recv" in s and float(s["usecs-to-last-byte-recv"]) >= 0: stream["time_to_last_byte"] = float(s["usecs-to-last-byte-recv"])/1000000 if "elapsed_seconds" in stream_data: s = stream_data["elapsed_seconds"] @@ -100,9 +101,9 @@ class TGenVisualization(Visualization): stream["start"] = datetime.datetime.utcfromtimestamp(stream_data["unix_ts_start"]) elif tgen_transfers: transfer_id, transfer_data = tgen_transfers.popitem() - stream = {"id": transfer_id, "label": label, - "filesize_bytes": transfer_data["filesize_bytes"], - "error_code": None} + stream["id"] = transfer_id + stream["label"] = label + stream["filesize_bytes"] = transfer_data["filesize_bytes"] stream["server"] = "onion" if ".onion:" in transfer_data["endpoint_remote"] else "public" if "elapsed_seconds" in transfer_data: s = transfer_data["elapsed_seconds"] @@ -125,7 +126,7 @@ class TGenVisualization(Visualization): stream["start"] = datetime.datetime.utcfromtimestamp(transfer_data["unix_ts_start"]) tor_stream = None tor_circuit = None - if source_port and unix_ts_end: + if source_port and source_port in tor_streams_by_source_port and unix_ts_end: for s in tor_streams_by_source_port[source_port]: if abs(unix_ts_end - s["unix_ts_end"]) < 150.0: tor_stream = s @@ -233,6 +234,8 @@ class TGenVisualization(Visualization):
def __draw_ecdf(self, x, hue, hue_name, data, title, xlabel, ylabel): data = data.dropna(subset=[x]) + if data.empty: + return p0 = data[x].quantile(q=0.0, interpolation="lower") p99 = data[x].quantile(q=0.99, interpolation="higher") ranks = data.groupby(hue)[x].rank(pct=True) @@ -255,8 +258,10 @@ class TGenVisualization(Visualization): plt.close()
def __draw_timeplot(self, x, y, hue, hue_name, data, title, xlabel, ylabel): - plt.figure() data = data.dropna(subset=[y]) + if data.empty: + return + plt.figure() data = data.rename(columns={hue: hue_name}) xmin = data[x].min() xmax = data[x].max() @@ -271,8 +276,10 @@ class TGenVisualization(Visualization): plt.close()
def __draw_boxplot(self, x, y, data, title, xlabel, ylabel): - plt.figure() data = data.dropna(subset=[y]) + if data.empty: + return + plt.figure() g = sns.boxplot(data=data, x=x, y=y, sym="") g.set(title=title, xlabel=xlabel, ylabel=ylabel, ylim=(0, None)) sns.despine() @@ -280,8 +287,10 @@ class TGenVisualization(Visualization): plt.close()
def __draw_barplot(self, x, y, data, title, xlabel, ylabel): - plt.figure() data = data.dropna(subset=[y]) + if data.empty: + return + plt.figure() g = sns.barplot(data=data, x=x, y=y, ci=None) g.set(title=title, xlabel=xlabel, ylabel=ylabel) sns.despine() @@ -289,6 +298,8 @@ class TGenVisualization(Visualization): plt.close()
def __draw_countplot(self, x, data, title, xlabel, ylabel, hue=None, hue_name=None): + if data.empty: + return plt.figure() if hue is not None: data = data.rename(columns={hue: hue_name}) @@ -299,6 +310,8 @@ class TGenVisualization(Visualization): plt.close()
def __draw_stripplot(self, x, y, hue, hue_name, data, title, xlabel, ylabel): + if data.empty: + return plt.figure() data = data.rename(columns={hue: hue_name}) xmin = data[x].min()
tor-commits@lists.torproject.org