6 import matplotlib.pyplot as plt
8 parser = argparse.ArgumentParser()
9 parser.add_argument('log_file')
10 parser.add_argument('-q', '--queue', help='plot queue size', action='store_true')
11 parser.add_argument('-e', '--encoder-threads', help='plot encoder thread activity', action='store_true')
12 parser.add_argument('-f', '--plot-first-encoder', help='plot more detailed activity of the first encoder thread', action='store_true')
13 parser.add_argument('-s', '--fps-stats', help='frames-per-second stats', action='store_true')
14 parser.add_argument('--dump-first-encoder', help='dump activity of the first encoder thread', action='store_true')
15 parser.add_argument('--from', help='time in seconds to start at', type=int, dest='from_time')
16 parser.add_argument('--to', help='time in seconds to stop at', type=int, dest='to_time')
17 args = parser.parse_args()
19 def find_nth(haystack, needle, n):
20 start = haystack.find(needle)
21 while start >= 0 and n > 1:
22 start = haystack.find(needle, start+len(needle))
27 def __init__(self, s, m = 0):
32 return '%d:%d' % (self.seconds, self.microseconds)
34 def float_seconds(self):
35 return self.seconds + self.microseconds / 1000000.0
38 m = self.microseconds - x.microseconds
40 return Time(self.seconds - x.seconds - 1, m + 1000000)
42 return Time(self.seconds - x.seconds, m)
46 encoder_thread_events = dict()
48 def add_encoder_thread_event(thread, time, event):
49 global encoder_thread_events
50 if thread in encoder_thread_events:
51 encoder_thread_events[thread].append((time, event))
53 encoder_thread_events[thread] = [(time, event)]
55 def add_general_event(time, event):
57 general_events.append((time, event))
59 f = open(args.log_file)
72 if len(p[0].split(':')) == 2:
75 T = Time(int(x[0]), int(x[1]))
76 message = l[l.find(' ')+1:]
79 s = find_nth(l, ':', 3)
80 T = Time(time.mktime(time.strptime(l[:s])))
89 if message.startswith('['):
90 thread = message.split()[0][1:-1]
91 message = message[message.find(' ')+1:]
93 if message.startswith('adding to queue of '):
94 queue_size.append((T, int(message.split()[4])))
95 elif message.startswith('encoder thread sleeps'):
96 add_encoder_thread_event(thread, T, 'sleep')
97 elif message.startswith('encoder thread wakes'):
98 add_encoder_thread_event(thread, T, 'wake')
99 elif message.startswith('encoder thread begins local encode'):
100 add_encoder_thread_event(thread, T, 'begin_encode')
101 elif message.startswith('MagickImageProxy begins decode and convert') or message.startswith('MagickImageProxy begins read and decode'):
102 add_encoder_thread_event(thread, T, 'magick_begin_decode')
103 elif message.startswith('MagickImageProxy decode finished'):
104 add_encoder_thread_event(thread, T, 'magick_end_decode')
105 elif message.startswith('MagickImageProxy completes decode and convert'):
106 add_encoder_thread_event(thread, T, 'magick_end_unpack')
107 elif message.startswith('encoder thread finishes local encode'):
108 add_encoder_thread_event(thread, T, 'end_encode')
109 elif message.startswith('Finished locally-encoded'):
110 add_general_event(T, 'end_local_encode')
111 elif message.startswith('Finished remotely-encoded'):
112 add_general_event(T, 'end_remote_encode')
113 elif message.startswith('Transcode job starting'):
114 add_general_event(T, 'begin_transcode')
115 elif message.startswith('Transcode job completed successfully'):
116 add_general_event(T, 'end_transcode')
123 x.append(q[0].seconds)
129 elif args.encoder_threads:
131 N = len(encoder_thread_events)
133 for thread, events in encoder_thread_events.iteritems():
139 if args.from_time is not None and e[0].float_seconds() <= args.from_time:
141 if args.to_time is not None and e[0].float_seconds() >= args.to_time:
143 x.append(e[0].float_seconds())
144 x.append(e[0].float_seconds())
150 elif e[1] == 'begin_encode':
152 elif e[1] == 'end_encode':
154 elif e[1] == 'magick_begin_decode':
156 elif e[1] == 'magick_end_decode':
166 elif args.plot_first_encoder:
168 N = len(encoder_thread_events)
170 events = encoder_thread_events.itervalues().next()
174 for t in ['sleep', 'wake', 'begin_encode', 'magick_begin_decode', 'magick_end_decode', 'end_encode']:
179 if args.from_time is not None and e[0].float_seconds() <= args.from_time:
181 if args.to_time is not None and e[0].float_seconds() >= args.to_time:
184 x.append(e[0].float_seconds())
185 x.append(e[0].float_seconds())
186 x.append(e[0].float_seconds())
197 elif args.dump_first_encoder:
198 events = encoder_thread_events.itervalues().next()
201 print e[0].float_seconds(), (e[0].float_seconds() - last), e[1]
202 last = e[0].float_seconds()
209 for e in general_events:
210 if e[1] == 'begin_transcode':
212 elif e[1] == 'end_transcode':
214 elif e[1] == 'end_local_encode':
216 elif e[1] == 'end_remote_encode':
220 print 'Job did not appear to end'
223 duration = end - start
225 print 'Job ran for %fs' % duration.float_seconds()
226 print '%d local and %d remote' % (local, remote)
227 print '%.2f fps local and %.2f fps remote' % (local / duration.float_seconds(), remote / duration.float_seconds())