plot_traces 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/env python3
  2. # Plot the network traces output when TRACE_SOCKIO is set to 1
  3. # ("make TRACE_SOCKIO=1" to build in this configuration)
  4. # Args: log_directory
  5. # Outputs trace.tex (and the files pdflatex builds, including the target
  6. # trace.pdf) into the log directory
  7. import glob
  8. import os
  9. import re
  10. import sys
  11. if len(sys.argv) != 2:
  12. print(f"Usage: {sys.argv[0]} log_directory", file=sys.stderr)
  13. sys.exit(1)
  14. # This will throw an exception if the directory does not exist
  15. os.chdir(sys.argv[1])
  16. nodelogs = glob.glob("s*.log")
  17. nodelogs.sort(key=lambda filename : int(filename[1:-4]))
  18. # Pass 1: For each sender and receiver, make a list of each message
  19. # queued from that sender to receiver, noting its queue start time,
  20. # queue end time, size, and type
  21. queued_messages = {}
  22. min_ts = None
  23. for nodelog in nodelogs:
  24. node = nodelog[:-4]
  25. with open(nodelog) as logf:
  26. queueing = {}
  27. for logline in logf:
  28. logline = logline.rstrip()
  29. if "RTE" in logline:
  30. if "queueing" in logline:
  31. matches = re.match(
  32. r'(\d+\.\d+): RTE queueing (\d+) bytes to (\S+)',
  33. logline)
  34. [ts, size, recv] = matches.groups()
  35. assert(recv not in queueing)
  36. tsf = float(ts)
  37. queueing[recv] = \
  38. { 'queue_start': tsf, 'size': size }
  39. if min_ts is None or min_ts > tsf:
  40. min_ts = tsf
  41. if "queued" in logline:
  42. matches = re.match(
  43. r'(\d+\.\d+): RTE queued (\d+) bytes to (\S+)',
  44. logline)
  45. [ts, size, recv] = matches.groups()
  46. assert(recv in queueing)
  47. assert(queueing[recv]['size'] == size)
  48. if (node, recv) not in queued_messages:
  49. queued_messages[(node, recv)] = []
  50. msg = {
  51. 'queue_start': queueing[recv]['queue_start'],
  52. 'queue_end': float(ts),
  53. 'size': size,
  54. 'type': 'RTE',
  55. }
  56. queued_messages[(node, recv)].append(msg)
  57. del queueing[recv]
  58. # Pass 2: For each sender and receiver, note the receive start time and
  59. # receive end time for each message in the queued_messages list
  60. messages = {}
  61. max_ts = None
  62. for nodelog in nodelogs:
  63. node = nodelog[:-4]
  64. with open(nodelog) as logf:
  65. receiving = {}
  66. for logline in logf:
  67. logline = logline.rstrip()
  68. if "RTE" in logline:
  69. if "receiving" in logline:
  70. matches = re.match(
  71. r'(\d+\.\d+): RTE receiving (\d+) bytes from (\S+)',
  72. logline)
  73. [ts, size, snd] = matches.groups()
  74. assert(snd not in receiving)
  75. receiving[snd] = \
  76. { 'recv_start': float(ts), 'size': size }
  77. if "received" in logline:
  78. matches = re.match(
  79. r'(\d+\.\d+): RTE received (\d+) bytes from (\S+)',
  80. logline)
  81. [ts, size, snd] = matches.groups()
  82. assert(snd in receiving)
  83. assert(receiving[snd]['size'] == size)
  84. assert(queued_messages[(snd, node)][0]['size'] == size)
  85. tsf = float(ts)
  86. if (snd, node) not in messages:
  87. messages[(snd, node)] = []
  88. msg = queued_messages[(snd, node)].pop(0)
  89. msg['recv_start'] = receiving[snd]['recv_start']
  90. msg['recv_end'] = tsf
  91. if max_ts is None or max_ts < tsf:
  92. max_ts = tsf
  93. messages[(snd, node)].append(msg)
  94. del receiving[snd]
  95. # Write a latex file that draws the messages
  96. # Timescale (cm per second)
  97. timescale = 2
  98. # Nodescale (cm between nodes)
  99. nodescale = 1
  100. with open("trace.tex", "w") as tf:
  101. print(r'''\documentclass{article}
  102. \usepackage[paperwidth=%fcm,paperheight=%fcm,margin=1cm]{geometry}
  103. \usepackage{tikz}
  104. \setlength\parindent{0pt}
  105. \pagestyle{empty}
  106. \begin{document}
  107. \begin{tikzpicture}''' % (((max_ts-min_ts)*timescale)+2.5,
  108. (len(nodelogs)+1)*nodescale+2.5), file=tf)
  109. nodenum = 0
  110. nodepos = {}
  111. for nodelog in nodelogs:
  112. node = nodelog[:-4]
  113. nodenum += 1
  114. nodepos[node] = -nodenum * nodescale
  115. print(r'''\node [anchor=east] at (0,%f) { %s };
  116. \draw[thick] (0,%f) -- ++(%f,0);''' %
  117. (nodepos[node], node, nodepos[node],
  118. (max_ts-min_ts)*timescale), file=tf)
  119. for (snd,recv) in messages:
  120. for msg in messages[(snd,recv)]:
  121. print(msg)
  122. print(r'''\fill [fill=%s,fill opacity=.2] (%f,%f) -- (%f,%f) -- (%f,%f) -- (%f,%f) -- cycle;''' %
  123. ('black',
  124. (msg['queue_start']-min_ts)*timescale, nodepos[snd],
  125. (msg['queue_end']-min_ts)*timescale, nodepos[snd],
  126. (msg['recv_end']-min_ts)*timescale, nodepos[recv],
  127. (msg['recv_start']-min_ts)*timescale, nodepos[recv]),
  128. file=tf)
  129. print(r'''\end{tikzpicture}
  130. \end{document}''', file=tf)
  131. os.system("pdflatex trace")