1#!/usr/bin/env python
2
3import os, sys, re
4
5class multidict:
6    def __init__(self, elts=()):
7        self.data = {}
8        for key,value in elts:
9            self[key] = value
10    
11    def __getitem__(self, item):
12        return self.data[item]
13    def __setitem__(self, key, value):
14        if key in self.data:
15            self.data[key].append(value)
16        else:
17            self.data[key] = [value]
18    def items(self):
19        return self.data.items()
20    def values(self):
21        return self.data.values()
22    def keys(self):
23        return self.data.keys()
24    def __len__(self):
25        return len(self.data)
26
27kDiagnosticRE = re.compile(': (error|warning): (.*)')
28kAssertionRE = re.compile('Assertion failed: (.*, function .*, file .*, line [0-9]+\\.)')
29
30def readInfo(path, opts):
31    lastProgress = [-100,0]
32    def progress(pos):
33        pct = (100. * pos) / (size * 2)
34        if (pct - lastProgress[0]) >= 10:
35            lastProgress[0] = pct
36            print '%d/%d = %.2f%%' % (pos, size*2, pct)
37
38    f = open(path)
39    data = f.read()
40    f.close()
41
42    if opts.truncate != -1:
43        data = data[:opts.truncate]
44
45    size = len(data)
46    warnings = multidict()
47    errors = multidict()
48    for m in kDiagnosticRE.finditer(data):
49        progress(m.end())
50        if m.group(1) == 'error':
51            d = errors
52        else:
53            d = warnings
54        d[m.group(2)] = m
55    warnings = warnings.items()
56    errors = errors.items()
57    assertions = multidict()
58    for m in kAssertionRE.finditer(data):
59        print '%d/%d = %.2f%%' % (size + m.end(), size, (float(m.end()) / (size*2)) * 100.)
60        assertions[m.group(1)] = m
61    assertions = assertions.items()
62
63    # Manual scan for stack traces
64    aborts = multidict()
65    if 0:
66        prevLine = None
67        lnIter = iter(data.split('\n'))
68        for ln in lnIter:
69            m = kStackDumpLineRE.match(ln)
70            if m:
71                stack = [m.group(2)]
72                for ln in lnIter:
73                    m = kStackDumpLineRE.match(ln)
74                    if not m:
75                        break
76                    stack.append(m.group(2))
77                if prevLine is None or not kAssertionRE.match(prevLine):
78                    aborts[tuple(stack)] = stack
79            prevLine = ln
80
81    sections = [
82        (warnings, 'Warnings'),
83        (errors, 'Errors'),
84        (assertions, 'Assertions'),
85        (aborts.items(), 'Aborts'),
86        ]
87
88    if opts.ascending:
89        sections.reverse()
90
91    for l,title in sections:
92        l.sort(key = lambda (a,b): -len(b))
93        if l:
94            print '-- %d %s (%d kinds) --' % (sum([len(b) for a,b in l]), title, len(l))
95            for name,elts in l:
96                print '%5d:' % len(elts), name
97
98def main():
99    global options
100    from optparse import OptionParser
101    parser = OptionParser("usage: %prog [options] {inputs}")
102    parser.add_option("", "--ascending", dest="ascending",
103                      help="Print output in ascending order of severity.",
104                      action="store_true", default=False)
105    parser.add_option("", "--truncate", dest="truncate",
106                      help="Truncate input file (for testing).",
107                      type=int, action="store", default=-1)
108    (opts, args) = parser.parse_args()
109    
110    if not args:
111        parser.error('No inputs specified')
112
113    for arg in args:
114        readInfo(arg, opts)
115
116if __name__=='__main__':
117    main()
118