37 self.stats = {} |
40 self.stats = {} |
38 self.failcount = 0 |
41 self.failcount = 0 |
39 self.failtime = 0.0 |
42 self.failtime = 0.0 |
40 self.failtypes = {} |
43 self.failtypes = {} |
41 self.retryfails = 0 |
44 self.retryfails = 0 |
|
45 self.hosts = {} |
42 |
46 |
43 def add(self, starttime, duration, name, status): |
47 def add(self, starttime, duration, name, status, host, phase): |
44 if status != BuildStats.STAT_OK: |
48 if status != BuildStats.STAT_OK: |
45 self.failcount += 1 |
49 self.failcount += 1 |
46 if name in self.failtypes: |
50 if name in self.failtypes: |
47 self.failtypes[name] += 1 |
51 self.failtypes[name] += 1 |
48 else: |
52 else: |
56 r = self.stats[name] |
60 r = self.stats[name] |
57 r.add(duration) |
61 r.add(duration) |
58 else: |
62 else: |
59 self.stats[name] = RecipeStats(name,1,duration) |
63 self.stats[name] = RecipeStats(name,1,duration) |
60 |
64 |
|
65 hp=host |
|
66 if hp in self.hosts: |
|
67 self.hosts[hp] += 1 |
|
68 else: |
|
69 self.hosts[hp] = 1 |
|
70 |
61 def recipe_csv(self): |
71 def recipe_csv(self): |
62 s = '"name", "time", "count"\n' |
72 s = '"name", "time", "count"\n' |
63 l = sorted(self.stats.values(), key= lambda r: r.time, reverse=True) |
73 l = sorted(self.stats.values(), key= lambda r: r.time, reverse=True) |
64 for r in l: |
74 for r in l: |
65 s += '"%s",%s,%d\n' % (r.name, str(r.time), r.count) |
75 s += '"%s",%s,%d\n' % (r.name, str(r.time), r.count) |
66 return s |
76 return s |
67 |
77 |
|
78 def hosts_csv(self): |
|
79 s='"host","recipecount"\n' |
|
80 hs = self.hosts |
|
81 for h in sorted(hs.keys()): |
|
82 s += '"%s",%d\n' % (h,hs[h]) |
|
83 return s |
68 |
84 |
69 |
85 |
70 import sys |
86 import sys |
71 import re |
87 import re |
|
88 import os |
|
89 from optparse import OptionParser # for parsing command line parameters |
72 |
90 |
73 def main(): |
91 def main(): |
|
92 recipe_re = re.compile(".*<recipe name='([^']+)'.*host='([^']+)'.*") |
|
93 time_re = re.compile(".*<time start='([0-9]+\.[0-9]+)' *elapsed='([0-9]+\.[0-9]+)'.*") |
|
94 status_re = re.compile(".*<status exit='(?P<exit>(ok|failed))'( *code='(?P<code>[0-9]+)')?.*") |
|
95 phase_re = re.compile(".*<info>Making.*?([^\.]+\.[^\.]+)</info>") |
74 |
96 |
75 f = sys.stdin |
97 parser = OptionParser(prog = "recipestats", |
|
98 usage = """%prog --help [-b] [-f <logfilename>]""") |
|
99 |
|
100 parser.add_option("-b","--buildhosts",action="store_true",dest="buildhosts_flag", |
|
101 help="Lists which build hosts were active in each invocation of the build engine and how many recipes ran on each.", default = False) |
|
102 parser.add_option("-f","--logfile",action="store",dest="logfilename", help="Read from the file, not stdin", default = None) |
|
103 |
|
104 |
|
105 (options, stuff) = parser.parse_args(sys.argv[1:]) |
|
106 |
|
107 if options.logfilename is None: |
|
108 f = sys.stdin |
|
109 else: |
|
110 f = open(options.logfilename,"r") |
|
111 |
76 st = BuildStats() |
112 st = BuildStats() |
77 |
113 |
78 recipe_re = re.compile(".*<recipe name='([^']+)'.*") |
|
79 time_re = re.compile(".*<time start='([0-9]+\.[0-9]+)' *elapsed='([0-9]+\.[0-9]+)'.*") |
|
80 status_re = re.compile(".*<status exit='(?P<exit>(ok|failed))'( *code='(?P<code>[0-9]+)')?.*") |
|
81 |
114 |
82 alternating = 0 |
115 alternating = 0 |
83 start_time = 0.0 |
116 start_time = 0.0 |
84 |
117 |
85 |
118 phase=None |
86 for l in f.xreadlines(): |
119 for l in f: |
87 l2 = l.rstrip("\n\r") |
120 l2 = l.rstrip("\n\r") |
|
121 |
88 rm = recipe_re.match(l2) |
122 rm = recipe_re.match(l2) |
89 |
123 |
90 if rm is not None: |
124 if rm is not None: |
91 rname = rm.groups()[0] |
125 (rname,host) = rm.groups() |
92 continue |
126 continue |
93 |
127 |
|
128 pm = phase_re.match(l2) |
|
129 |
|
130 if pm is not None: |
|
131 if phase is not None: |
|
132 if options.buildhosts_flag: |
|
133 print('"%s"\n' % phase) |
|
134 print(st.hosts_csv()) |
|
135 st.hosts = {} |
|
136 phase = pm.groups()[0] |
|
137 continue |
94 |
138 |
95 tm = time_re.match(l2) |
139 tm = time_re.match(l2) |
96 if tm is not None: |
140 if tm is not None: |
97 try: |
141 try: |
98 s = float(tm.groups()[0]) |
142 s = float(tm.groups()[0]) |
118 if sm.groupdict()['exit'] == 'ok': |
162 if sm.groupdict()['exit'] == 'ok': |
119 status = 0 |
163 status = 0 |
120 else: |
164 else: |
121 status = int(sm.groupdict()['code']) |
165 status = int(sm.groupdict()['code']) |
122 |
166 |
123 st.add(s, elapsed, rname, status) |
167 st.add(s, elapsed, rname, status, host, phase) |
124 |
168 |
125 print(st.recipe_csv()) |
169 if options.buildhosts_flag: |
|
170 print('"%s"\n' % phase) |
|
171 print(st.hosts_csv()) |
|
172 else: |
|
173 print(st.recipe_csv()) |
126 |
174 |
127 |
175 |
128 if __name__ == '__main__': main() |
176 if __name__ == '__main__': main() |