123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- #!/usr/bin/env python3
- #
- import gzip
- import sys
- import pickle
- import matplotlib.pylab as plt
- import numpy as np
- #
- import log_system_usage
- #
- def plot_cells(stream, ax, time_offset=None, clickable=False, label=None, color=None):
- num_bytes = np.cumsum(stream['length'])
- timestamps = np.asarray(stream['timestamp'])
- #
- if time_offset is not None:
- timestamps = timestamps-time_offset
- #
- return ax.step(timestamps, num_bytes/(1024**2), where='post', label=label, color=color, picker=(5 if clickable else None))[0]
- #
- def onresize(event):
- # matplotlib axes size only scales based on fractions (even with tight_layout), so we manually calculate a fixed padding size
- w = event.width/event.canvas.figure.dpi
- h = event.height/event.canvas.figure.dpi
- w_padding = 0.8 # in inches
- h_padding = 0.6 # in inches
- #
- for ax in event.canvas.figure.axes:
- for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() + ax.get_yticklabels()):
- item.set_fontsize(11+0.5*(w*h/(5**2)))
- #
- #
- event.canvas.figure.subplots_adjust(left=w_padding/w+0.01, right=1-((w_padding/2)/w), top=1-(h_padding/h), bottom=h_padding/h+0.01)
- #
- def zoom_axes_cb(event):
- scale = 2**(-event.step)
- ax = event.inaxes
- mouse_x = event.xdata
- mouse_y = event.ydata
- old_xlim = ax.get_xlim()
- old_ylim = ax.get_ylim()
- #
- ax.set_xlim([old_xlim[0]*scale+mouse_x*(1-scale), old_xlim[1]*scale+mouse_x*(1-scale)])
- ax.set_ylim([old_ylim[0]*scale+mouse_y*(1-scale), old_ylim[1]*scale+mouse_y*(1-scale)])
- #
- event.canvas.draw()
- #
- def pick_measureme_line_cb(event, lines, target_axes=None):
- if target_axes != None and event.mouseevent.inaxes not in target_axes:
- return
- #
- this_line = event.artist
- if event.mouseevent.button == 1 and not event.mouseevent.dblclick and event.mouseevent.key is None:
- # if the mouse is single-clicked and no keyboard key is held down
- if this_line.get_visible() and this_line in lines.keys():
- this_info = [x[1] for x in lines.items() if x[0] == this_line][0]
- #
- for_legend = []
- for (line, info) in lines.items():
- if info['measureme_id'] != this_info['measureme_id']:
- line.set_visible(False)
- else:
- line.set_visible(True)
- for_legend.append(line)
- #
- #
- event.mouseevent.inaxes.legend(for_legend, [x.get_label() for x in for_legend], loc='lower right')
- event.canvas.draw_idle()
- #
- #
- #
- def reset_measureme_lines_cb(event, lines, target_axes=None):
- if target_axes != None and event.inaxes not in target_axes:
- return
- #
- if event.button == 1 and event.dblclick and event.key is None:
- # if the mouse is double-clicked and no keyboard key is held down
- for (line, info) in lines.items():
- if info['is_main_plot']:
- line.set_visible(True)
- else:
- line.set_visible(False)
- #
- #
- legend = event.inaxes.get_legend()
- if legend != None:
- legend.remove()
- #
- event.canvas.draw_idle()
- #
- #
- def mouse_pan_cb(event, pan_settings):
- if event.inaxes is not None and event.key == 'control' and event.button == 1:
- ax = event.inaxes
- pixel_to_data = ax.transData.inverted()
- current_pos = pixel_to_data.transform_point((event.x, event.y))
- last_pos = pixel_to_data.transform_point((pan_settings['start_x'], pan_settings['start_y']))
- #
- old_xlim = ax.get_xlim()
- old_ylim = ax.get_ylim()
- #
- ax.set_xlim([old_xlim[0]+(last_pos[0]-current_pos[0]), old_xlim[1]+(last_pos[0]-current_pos[0])])
- ax.set_ylim([old_ylim[0]+(last_pos[1]-current_pos[1]), old_ylim[1]+(last_pos[1]-current_pos[1])])
- #
- event.canvas.draw_idle()
- #
- pan_settings['start_x'] = event.x
- pan_settings['start_y'] = event.y
- #
- def subsample_plot_cb(event, lines, target_axes=None):
- if target_axes != None and event.inaxes not in target_axes:
- return
- #
- if event.key == 'control':
- num_points = 0
- range_x = event.inaxes.xaxis.get_view_interval()
- range_y = event.inaxes.yaxis.get_view_interval()
- #
- for line in [l for l in lines if l.get_visible()]:
- data_x = line.get_xdata(orig=True)
- data_y = line.get_ydata(orig=True)
- #
- num_points += ((data_x>=range_x[0]) & (data_x<=range_x[1]) & (data_y>=range_y[0]) & (data_y<=range_y[1])).sum()
- # how many points are being rendered
- #
- for (line, info) in lines.items():
- data_x = line.get_xdata(orig=True)
- data_y = line.get_ydata(orig=True)
- line.orig_x = data_x
- line.orig_y = data_y
- line.orig_drawstyle = line.get_drawstyle()
- #
- subsample_spacing = max(1, int(num_points/10000))
- # the constant can be decreased to speed up plotting on slower computers
- #
- mask = np.ones(len(data_x))
- mask[::subsample_spacing] = 0
- line.set_xdata(data_x[mask==0])
- line.set_ydata(data_y[mask==0])
- line.set_drawstyle('default')
- #
- event.canvas.draw_idle()
- #
- #
- def undo_subsampling_cb(event, lines, target_axes=None):
- if target_axes != None and event.inaxes not in target_axes:
- return
- #
- if event.key == 'control':
- for (line, info) in lines.items():
- line.set_xdata(line.orig_x)
- line.set_ydata(line.orig_y)
- line.set_drawstyle(line.orig_drawstyle)
- #
- event.canvas.draw_idle()
- #
- #
- def get_complimentary_color(color_index):
- return (color_index+1 if color_index%2==0 else color_index-1)
- #
- if __name__ == '__main__':
- with gzip.GzipFile(sys.argv[1], 'rb') as f:
- streams = pickle.load(f)
- #
- with gzip.GzipFile(sys.argv[2], 'rb') as f:
- system_usage = pickle.load(f)
- system_usage['timestamps'] = np.array(system_usage['timestamps'])
- #
- with gzip.GzipFile(sys.argv[3], 'rb') as f:
- numa_data = pickle.load(f)
- #
- with gzip.GzipFile(sys.argv[4], 'rb') as f:
- fingerprints = pickle.load(f)
- #
- #fig, ax = plt.subplots()#constrained_layout=True
- fig, (ax, ax_cpu_usage) = plt.subplots(2, 1, sharex=True)
- #
- start_time = min([hop[t]['timestamp'][0] for m in streams for s in streams[m] for d in streams[m][s]
- for hop in streams[m][s][d] for t in ('received','sent')])
- #
- lines = {}
- assigned_colors = []
- colormap = plt.get_cmap('tab20') #'tab10'
- direction_shortforms = {'forward':'fwd', 'backward':'bwd'}
- transmission_shortforms = {'received':'recv', 'sent':'sent'}
- #
- for measureme_id in streams:
- # for each circuit
- for stream_id in streams[measureme_id]:
- # for each stream
- direction = 'forward'
- for hop_index in range(len(streams[measureme_id][stream_id][direction])):
- # for each hop in the circuit (including the OP)
- for transmission in ('received', 'sent'):
- data = streams[measureme_id][stream_id][direction][hop_index][transmission]
- #
- if hop_index == len(streams[measureme_id][stream_id][direction])-1:
- guard_fingerprint = streams[measureme_id][stream_id][direction][1]['fingerprint']
- if guard_fingerprint not in assigned_colors:
- assigned_colors.append(guard_fingerprint)
- #
- if transmission == 'sent':
- color_index = assigned_colors.index(guard_fingerprint)
- else:
- color_index = get_complimentary_color(assigned_colors.index(guard_fingerprint))
- #
- else:
- color_index = hop_index*2 + ('received', 'sent').index(transmission)
- #
- is_main_plot = (hop_index == len(streams[measureme_id][stream_id][direction])-1 and transmission == 'sent')
- direction_label = direction_shortforms[direction]
- transmission_label = transmission_shortforms[transmission]
- fingerprint = str(streams[measureme_id][stream_id][direction][hop_index]['fingerprint'])
- label = 'hop={}, {:.4}, {:.4}, mid={}, sid={}, fprnt={:.6s}'.format(hop_index, direction_label, transmission_label,
- measureme_id, stream_id, fingerprint)
- line = plot_cells(data, ax, time_offset=start_time, clickable=is_main_plot, label=label, color=colormap(color_index))
- if not is_main_plot:
- line.set_visible(False)
- #
- lines[line] = {'measureme_id':measureme_id, 'stream_id':stream_id, 'direction':direction,
- 'hop_index':hop_index, 'transmission':transmission, 'is_main_plot':is_main_plot}
- #
- #
- #
- #
- guard_counter = {}
- for measureme_id in streams:
- for stream_id in streams[measureme_id]:
- direction = 'forward'
- fingerprint = streams[measureme_id][stream_id][direction][1]['fingerprint']
- if fingerprint not in guard_counter:
- guard_counter[fingerprint] = 0
- #
- guard_counter[fingerprint] += 1
- #
- #
- system_usage_timestamps = (system_usage['timestamps'][1:]+system_usage['timestamps'][:-1])/2 - start_time
- cpu_usages = {int(cpu): np.array(log_system_usage.calculate_cpu_usage_continuous(system_usage['stats']['cpus'][cpu])) for cpu in system_usage['stats']['cpus']}
- tor_cpu_usages = {nick: np.sum([cpu_usages[cpu] for cpu in numa_data[nick][1]], axis=0) for nick in numa_data}
- #
- mask = system_usage_timestamps > 0
- system_usage_timestamps = system_usage_timestamps[mask]
- tor_cpu_usages = {nick: tor_cpu_usages[nick][mask] for nick in tor_cpu_usages}
- #
- for nick in sorted(fingerprints.keys()):
- #if fingerprints[nick] != None:
- # is not an OP
- guard_circuit_count = 0
- if fingerprints[nick] in guard_counter:
- guard_circuit_count = guard_counter[fingerprints[nick]]
- elif fingerprints[nick] is None:
- guard_circuit_count = '?'
- #
- ax_cpu_usage.plot(system_usage_timestamps, tor_cpu_usages[nick]*100, label='{} / {:.6s} (guard for {} circuits)'.format(nick, str(fingerprints[nick]),
- guard_circuit_count))
- #
- #
- ax_cpu_usage.set_xlabel('Time (s)')
- ax_cpu_usage.set_ylabel('CPU Usage (%)')
- ax_cpu_usage.legend()
- ax_cpu_usage.grid(linestyle=':')
- #
- ax.set_xlabel('Time (s)')
- ax.set_ylabel('Data (MiB)')
- ax.set_title(sys.argv[1])
- ax.grid(linestyle=':')
- fig.tight_layout(pad=0)
- #ax.set_ylim(0, None)
- #
- fig.canvas.mpl_connect('resize_event', onresize)
- fig.canvas.mpl_connect('scroll_event', zoom_axes_cb)
- fig.canvas.mpl_connect('pick_event', lambda event,lines=lines,axes=[ax]: pick_measureme_line_cb(event, lines, target_axes=axes))
- fig.canvas.mpl_connect('button_press_event', lambda event,lines=lines,axes=[ax]: reset_measureme_lines_cb(event, lines, target_axes=axes))
- fig.canvas.mpl_connect('motion_notify_event', lambda event,pan_settings={'start_x':0,'start_y':0}: mouse_pan_cb(event, pan_settings))
- fig.canvas.mpl_connect('key_press_event', lambda event,lines=lines,axes=[ax]: subsample_plot_cb(event, lines, target_axes=axes))
- fig.canvas.mpl_connect('key_release_event', lambda event,lines=lines,axes=[ax]: undo_subsampling_cb(event, lines, target_axes=axes))
- #
- plt.show(fig)
- #
|