plot_streams.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import gzip
  2. import pickle
  3. import matplotlib.pylab as plt
  4. import numpy as np
  5. #
  6. def plot_cells(stream, plot_widget, time_offset=None, clickable=False, label=None, color=None):
  7. num_bytes = np.cumsum(stream['length'])
  8. timestamps = np.asarray(stream['timestamp'])
  9. #
  10. if time_offset is not None:
  11. timestamps = timestamps-time_offset
  12. #
  13. return ax.step(timestamps, num_bytes/(1024**2), where='post', label=label, color=color, picker=(5 if clickable else None))[0]
  14. #
  15. def onresize(event):
  16. # matplotlib axes size only scales based on fractions (even with tight_layout), so we manually calculate a fixed padding size
  17. w = event.width/event.canvas.figure.dpi
  18. h = event.height/event.canvas.figure.dpi
  19. w_padding = 0.8 # in inches
  20. h_padding = 0.6 # in inches
  21. #
  22. for ax in event.canvas.figure.axes:
  23. for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() + ax.get_yticklabels()):
  24. item.set_fontsize(11+0.5*(w*h/(5**2)))
  25. #
  26. #
  27. 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)
  28. #
  29. def onscroll(event):
  30. scale = 2**(-event.step)
  31. ax = event.inaxes
  32. mouse_x = event.xdata
  33. mouse_y = event.ydata
  34. old_xlim = ax.get_xlim()
  35. old_ylim = ax.get_ylim()
  36. #
  37. ax.set_xlim([old_xlim[0]*scale+mouse_x*(1-scale), old_xlim[1]*scale+mouse_x*(1-scale)])
  38. ax.set_ylim([old_ylim[0]*scale+mouse_y*(1-scale), old_ylim[1]*scale+mouse_y*(1-scale)])
  39. #
  40. event.canvas.draw()
  41. #
  42. def onpick(event, lines):
  43. this_line = event.artist
  44. if event.mouseevent.button == 1 and not event.mouseevent.dblclick and event.mouseevent.key is None:
  45. # if the mouse is single-clicked and no keyboard key is held down
  46. if this_line.get_visible() and this_line in lines.keys():
  47. this_info = [x[1] for x in lines.items() if x[0] == this_line][0]
  48. #
  49. for_legend = []
  50. for (line, info) in lines.items():
  51. if info['measureme_id'] != this_info['measureme_id']:
  52. line.set_visible(False)
  53. else:
  54. line.set_visible(True)
  55. for_legend.append(line)
  56. #
  57. #
  58. event.mouseevent.inaxes.legend(for_legend, [x.get_label() for x in for_legend], loc='upper right')
  59. event.canvas.draw_idle()
  60. #
  61. #
  62. #
  63. def onclick(event, lines):
  64. if event.button == 1 and event.dblclick and event.key is None:
  65. # if the mouse is double-clicked and no keyboard key is held down
  66. for (line, info) in lines.items():
  67. if info['is_main_plot']:
  68. line.set_visible(True)
  69. else:
  70. line.set_visible(False)
  71. #
  72. #
  73. event.inaxes.get_legend().remove()
  74. event.canvas.draw_idle()
  75. #
  76. #
  77. def onmotion(event, pan_settings):
  78. if event.inaxes is not None and event.key == 'control' and event.button == 1:
  79. ax = event.inaxes
  80. pixel_to_data = ax.transData.inverted()
  81. current_pos = pixel_to_data.transform_point((event.x, event.y))
  82. last_pos = pixel_to_data.transform_point((pan_settings['start_x'], pan_settings['start_y']))
  83. #
  84. old_xlim = ax.get_xlim()
  85. old_ylim = ax.get_ylim()
  86. #
  87. ax.set_xlim([old_xlim[0]+(last_pos[0]-current_pos[0]), old_xlim[1]+(last_pos[0]-current_pos[0])])
  88. ax.set_ylim([old_ylim[0]+(last_pos[1]-current_pos[1]), old_ylim[1]+(last_pos[1]-current_pos[1])])
  89. #
  90. event.canvas.draw_idle()
  91. #
  92. pan_settings['start_x'] = event.x
  93. pan_settings['start_y'] = event.y
  94. #
  95. def onkeypress(event, lines):
  96. if event.key == 'control':
  97. num_points = 0
  98. range_x = event.inaxes.xaxis.get_view_interval()
  99. range_y = event.inaxes.yaxis.get_view_interval()
  100. #
  101. for line in [l for l in lines if l.get_visible()]:
  102. data_x = line.get_xdata(orig=True)
  103. data_y = line.get_ydata(orig=True)
  104. #
  105. num_points += ((data_x>=range_x[0]) & (data_x<=range_x[1]) & (data_y>=range_y[0]) & (data_y<=range_y[1])).sum()
  106. # how many points are being rendered
  107. #
  108. for (line, info) in lines.items():
  109. data_x = line.get_xdata(orig=True)
  110. data_y = line.get_ydata(orig=True)
  111. line.orig_x = data_x
  112. line.orig_y = data_y
  113. line.orig_drawstyle = line.get_drawstyle()
  114. #
  115. subsample_spacing = max(1, int(num_points/10000))
  116. # the constant can be decreased to speed up plotting on slower computers
  117. #
  118. mask = np.ones(len(data_x))
  119. mask[::subsample_spacing] = 0
  120. line.set_xdata(data_x[mask==0])
  121. line.set_ydata(data_y[mask==0])
  122. line.set_drawstyle('default')
  123. #
  124. event.canvas.draw_idle()
  125. #
  126. #
  127. def onkeyrelease(event, lines):
  128. if event.key == 'control':
  129. for (line, info) in lines.items():
  130. line.set_xdata(line.orig_x)
  131. line.set_ydata(line.orig_y)
  132. line.set_drawstyle(line.orig_drawstyle)
  133. #
  134. event.canvas.draw_idle()
  135. #
  136. #
  137. def get_complimentary_color(color_index):
  138. return (color_index+1 if color_index%2==0 else color_index-1)
  139. #
  140. if __name__ == '__main__':
  141. with gzip.GzipFile('processed-data.pickle.gz', 'rb') as f:
  142. streams = pickle.load(f)
  143. #
  144. fig, ax = plt.subplots()#constrained_layout=True
  145. #
  146. start_time = min([hop[t]['timestamp'][0] for m in streams for s in streams[m] for d in streams[m][s]
  147. for hop in streams[m][s][d] for t in ('received','sent')])
  148. #
  149. lines = {}
  150. assigned_colors = []
  151. colormap = plt.get_cmap('tab20') #'tab10'
  152. direction_shortforms = {'forward':'fwd', 'backward':'bwd'}
  153. transmission_shortforms = {'received':'recv', 'sent':'sent'}
  154. #
  155. for measureme_id in streams:
  156. # for each circuit
  157. for stream_id in streams[measureme_id]:
  158. # for each stream
  159. direction = 'forward'
  160. for hop_index in range(len(streams[measureme_id][stream_id][direction])):
  161. # for each hop in the circuit (including the OP)
  162. for transmission in ('received', 'sent'):
  163. data = streams[measureme_id][stream_id][direction][hop_index][transmission]
  164. #
  165. if hop_index == len(streams[measureme_id][stream_id][direction])-1:
  166. guard_fingerprint = streams[measureme_id][stream_id][direction][1]['fingerprint']
  167. if guard_fingerprint not in assigned_colors:
  168. assigned_colors.append(guard_fingerprint)
  169. #
  170. if transmission == 'sent':
  171. color_index = assigned_colors.index(guard_fingerprint)
  172. else:
  173. color_index = get_complimentary_color(assigned_colors.index(guard_fingerprint))
  174. #
  175. else:
  176. color_index = hop_index*2 + ('received', 'sent').index(transmission)
  177. #
  178. is_main_plot = (hop_index == len(streams[measureme_id][stream_id][direction])-1 and transmission == 'sent')
  179. direction_label = direction_shortforms[direction]
  180. transmission_label = transmission_shortforms[transmission]
  181. label = 'hop={}, {:.4}, {:.4}, mid={}, sid={}'.format(hop_index, direction_label, transmission_label, measureme_id, stream_id)
  182. line = plot_cells(data, ax, time_offset=start_time, clickable=is_main_plot, label=label, color=colormap(color_index))
  183. if not is_main_plot:
  184. line.set_visible(False)
  185. #
  186. lines[line] = {'measureme_id':measureme_id, 'stream_id':stream_id, 'direction':direction,
  187. 'hop_index':hop_index, 'transmission':transmission, 'is_main_plot':is_main_plot}
  188. #
  189. #
  190. #
  191. #
  192. ax.set_xlabel('Time (s)')
  193. ax.set_ylabel('Data (MiB)')
  194. ax.set_title('Test')
  195. fig.tight_layout(pad=0)
  196. #ax.set_ylim(0, None)
  197. #
  198. fig.canvas.mpl_connect('resize_event', onresize)
  199. fig.canvas.mpl_connect('scroll_event', onscroll)
  200. fig.canvas.mpl_connect('pick_event', lambda event,lines=lines: onpick(event, lines))
  201. fig.canvas.mpl_connect('button_press_event', lambda event,lines=lines: onclick(event, lines))
  202. fig.canvas.mpl_connect('motion_notify_event', lambda event,pan_settings={'start_x':0,'start_y':0}: onmotion(event, pan_settings))
  203. fig.canvas.mpl_connect('key_press_event', lambda event,lines=lines: onkeypress(event, lines))
  204. fig.canvas.mpl_connect('key_release_event', lambda event,lines=lines: onkeyrelease(event, lines))
  205. #
  206. plt.show(fig)
  207. #