gcpausevis.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. #!/usr/bin/env python
  2. import matplotlib.pyplot as plt
  3. from matplotlib.dates import DateFormatter, MinuteLocator, SecondLocator
  4. import numpy as np
  5. from StringIO import StringIO
  6. import os
  7. import re
  8. import sys
  9. from optparse import OptionParser
  10. import subprocess
  11. parser = OptionParser (usage = "Usage: %prog [options] BINARY-PROTOCOL")
  12. parser.add_option ('--histogram', action = 'store_true', dest = 'histogram', help = "pause time histogram")
  13. parser.add_option ('--minor', action = 'store_true', dest = 'minor', help = "only show minor collections in histogram")
  14. parser.add_option ('--major', action = 'store_true', dest = 'major', help = "only show major collections in histogram")
  15. (options, files) = parser.parse_args ()
  16. show_histogram = False
  17. show_minor = True
  18. show_major = True
  19. if options.minor:
  20. show_histogram = True
  21. show_major = False
  22. if options.major:
  23. show_histogram = True
  24. show_minor = False
  25. if options.histogram:
  26. show_histogram = True
  27. script_path = os.path.realpath (__file__)
  28. sgen_grep_path = os.path.join (os.path.dirname (script_path), 'sgen-grep-binprot')
  29. if not os.path.isfile (sgen_grep_path):
  30. sys.stderr.write ('Error: `%s` does not exist.\n' % sgen_grep_path)
  31. sys.exit (1)
  32. if len (files) != 1:
  33. parser.print_help ()
  34. sys.exit (1)
  35. data = []
  36. class Event:
  37. def __init__(self, **kwargs):
  38. self.minor_work = kwargs['minor_work']
  39. self.major_work = kwargs['major_work']
  40. self.start = kwargs['start']
  41. self.stop = kwargs['stop']
  42. self.gc_type = kwargs['gc_type']
  43. def __repr__(self):
  44. return 'Event(minor_work={}, major_work={}, start={}, stop={}, gc_type={})'.format(
  45. self.minor_work,
  46. self.major_work,
  47. self.start,
  48. self.stop,
  49. self.gc_type,
  50. )
  51. grep_input = open (files [0])
  52. proc = subprocess.Popen ([sgen_grep_path, '--pause-times'], stdin = grep_input, stdout = subprocess.PIPE)
  53. for line in iter (proc.stdout.readline, ''):
  54. m = re.match ('^pause-time (\d+) (\d+) (\d+) (\d+) (\d+)', line)
  55. if m:
  56. minor_work = major_work = False
  57. generation = int (m.group (1))
  58. concurrent = int (m.group (2)) != 0
  59. finish = int (m.group (3)) != 0
  60. msecs = int (m.group (4)) / 10.0 / 1000.0
  61. start = int (m.group (5)) / 10.0 / 1000.0
  62. if concurrent:
  63. kind = "CONC"
  64. else:
  65. kind = "SYNC"
  66. if generation == 0:
  67. minor_work = True
  68. if concurrent:
  69. major_work = True
  70. gc_type = "nursery+update"
  71. else:
  72. gc_type = "nursery"
  73. else:
  74. major_work = True
  75. if concurrent:
  76. if finish:
  77. minor_work = True
  78. gc_type = "nursery+finish"
  79. else:
  80. gc_type = "start"
  81. else:
  82. gc_type = "full"
  83. rec = Event(
  84. minor_work=minor_work,
  85. major_work=major_work,
  86. start=start,
  87. stop=start + msecs,
  88. kind=kind,
  89. gc_type=gc_type,
  90. )
  91. print rec
  92. data.append (rec)
  93. class MajorGCEventGroup:
  94. pass
  95. class FullMajorGCEventGroup(MajorGCEventGroup):
  96. def __init__(self, event):
  97. self.event = event
  98. def __repr__(self):
  99. return 'FullMajorGCEventGroup({})'.format(
  100. self.event,
  101. )
  102. class ConcurrentMajorGCEventGroup(MajorGCEventGroup):
  103. def __init__(self, start, updates, finish):
  104. self.start = start
  105. self.updates = updates
  106. self.finish = finish
  107. def __repr__(self):
  108. return 'ConcurrentMajorEventGroup({}, {}, {})'.format(
  109. self.start,
  110. self.updates,
  111. self.finish,
  112. )
  113. # ([Event], int) -> (MajorGCEventGroup, int) | None
  114. def parse_next_major_gc(data, i):
  115. assert i >= 0
  116. # Find start or full event.
  117. while i < len(data) and data[i].gc_type not in ['start', 'full']:
  118. i += 1
  119. if i == len(data):
  120. return None
  121. # If full event, done.
  122. if data[i].gc_type == 'full':
  123. return (FullMajorGCEventGroup(data[i]), i + 1)
  124. start_event = data[i]
  125. update_events = []
  126. # Filter update events and find finish event.
  127. while i < len(data) and data[i].gc_type != 'nursery+finish':
  128. if data[i].gc_type == 'nursery+update':
  129. update_events.append(data[i])
  130. i += 1
  131. if i == len(data):
  132. return None
  133. finish_event = data[i]
  134. i += 1
  135. return (ConcurrentMajorGCEventGroup(start_event, update_events, finish_event), i)
  136. # [Event] -> [MajorGCEventGroup]
  137. def parse_major_gcs(data):
  138. major_gc_events = []
  139. i = 0
  140. while True:
  141. maybe_event_group = parse_next_major_gc(data, i)
  142. if maybe_event_group is None:
  143. return major_gc_events
  144. event_group, i = maybe_event_group
  145. major_gc_events.append(event_group)
  146. if show_histogram:
  147. minor_pausetimes = []
  148. major_pausetimes = []
  149. for rec in data:
  150. pause = rec.stop - rec.start
  151. if rec.minor_work and rec.major_work and show_minor and show_major:
  152. major_pausetimes.append (pause)
  153. else:
  154. if rec.minor_work:
  155. minor_pausetimes.append (pause)
  156. if rec.major_work:
  157. major_pausetimes.append (pause)
  158. pausetimes = []
  159. colors = []
  160. if show_minor:
  161. pausetimes.append(minor_pausetimes)
  162. colors.append('blue')
  163. if show_major:
  164. pausetimes.append(major_pausetimes)
  165. colors.append('red')
  166. plt.hist (pausetimes, 100, stacked=True, log=True, color=colors)
  167. plt.xlabel ('Pause time in msec')
  168. else:
  169. major_gc_event_groups = parse_major_gcs(data)
  170. def bar(**kwargs):
  171. indices = kwargs['indices']
  172. pauses = kwargs['pauses']
  173. color = kwargs['color']
  174. if 'bottom' in kwargs:
  175. bottom = kwargs['bottom']
  176. else:
  177. bottom = 0
  178. plt.bar(
  179. [index for index in indices if pauses[index] is not None],
  180. np.array([pause for pause in pauses if pause is not None]),
  181. color=color,
  182. bottom=bottom,
  183. )
  184. indices = np.arange(len(major_gc_event_groups))
  185. start_pauses = [
  186. event_group.start.stop - event_group.start.start
  187. if isinstance(event_group, ConcurrentMajorGCEventGroup) else None
  188. for event_group in major_gc_event_groups
  189. ]
  190. bar(
  191. indices=indices,
  192. pauses=start_pauses,
  193. color='red',
  194. )
  195. update_pauses = [
  196. sum([
  197. update_event.stop - update_event.start
  198. for update_event in event_group.updates
  199. ]) if isinstance(event_group, ConcurrentMajorGCEventGroup) else None
  200. for event_group in major_gc_event_groups
  201. ]
  202. bar(
  203. indices=indices,
  204. pauses=update_pauses,
  205. color='green',
  206. bottom=[pause for pause in start_pauses if pause is not None],
  207. )
  208. finish_pauses = [
  209. event_group.finish.stop - event_group.finish.start
  210. if isinstance(event_group, ConcurrentMajorGCEventGroup) else None
  211. for event_group in major_gc_event_groups
  212. ]
  213. start_update_pauses = [
  214. a + b
  215. for a, b in zip(start_pauses, update_pauses)
  216. if a is not None and b is not None
  217. ]
  218. bar(
  219. indices=indices,
  220. pauses=finish_pauses,
  221. color='blue',
  222. bottom=start_update_pauses,
  223. )
  224. full_pauses = [
  225. event_group.event.stop - event_group.event.start
  226. if isinstance(event_group, FullMajorGCEventGroup) else None
  227. for event_group in major_gc_event_groups
  228. ]
  229. bar(
  230. indices=indices,
  231. pauses=full_pauses,
  232. color='black',
  233. )
  234. plt.ylabel("Pause Time (ms)")
  235. plt.xlabel("Collection")
  236. plt.show()