]> git.cworth.org Git - apitrace/blob - scripts/tracecheck.py
Use skiplist-based FastCallSet within trace::CallSet
[apitrace] / scripts / tracecheck.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2011 VMware, Inc.
5 # All Rights Reserved.
6 #
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the 'Software'), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
13 #
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
16 #
17 # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 # THE SOFTWARE.
24 #
25 ##########################################################################/
26
27 '''Check a trace replays successfully or not.
28
29 It is meant to be used with git bisect.  See git bisect manpage for more
30 details.
31 '''
32
33
34 import optparse
35 import os.path
36 import platform
37 import re
38 import subprocess
39 import sys
40 import traceback
41
42
43 def good():
44     '''Tell git-bisect that this commit is good.'''
45
46     sys.stdout.write('GOOD\n')
47     sys.exit(0)
48
49
50 def bad():
51     '''Tell git-bisect that this commit is bad.'''
52
53     sys.stdout.write('BAD\n')
54     sys.exit(1)
55
56
57 def skip():
58     '''Tell git-bisect to skip this commit.'''
59
60     sys.stdout.write('SKIP\n')
61     sys.exit(125)
62
63
64 def abort():
65     '''Tell git-bisect to abort.'''
66
67     sys.stdout.write('ABORT\n')
68     sys.exit(-1)
69
70
71 def which(executable):
72     '''Search for the executable on the PATH.'''
73
74     if platform.system() == 'Windows':
75         exts = ['.exe']
76     else:
77         exts = ['']
78     dirs = os.environ['PATH'].split(os.path.pathsep)
79     for dir in dirs:
80         path = os.path.join(dir, executable)
81         for ext in exts:
82             if os.path.exists(path + ext):
83                 return True
84     return False
85
86
87 def main():
88     '''Main program.
89
90     It will always invoke sys.exit(), and never return normally.
91     '''
92
93     # Try to guess the build command.
94     if os.path.exists('SConstruct'):
95         default_build = 'scons'
96     elif os.path.exists('Makefile'):
97         default_build = 'make'
98     else:
99         default_build = None
100
101     # Parse command line options
102     optparser = optparse.OptionParser(
103         usage='\n\tgit bisect run %prog [options] -- [glretrace options] <trace>',
104         version='%%prog')
105     optparser.add_option(
106         '-b', '--build', metavar='COMMAND',
107         type='string', dest='build', default=default_build,
108         help='build command [default: %default]')
109     optparser.add_option(
110         '-r', '--retrace', metavar='PROGRAM',
111         type='string', dest='retrace', default='glretrace',
112         help='retrace command [default: %default]')
113     optparser.add_option(
114         '-c', '--compare', metavar='PREFIX',
115         type='string', dest='compare_prefix', default=None,
116         help='snapshot comparison prefix')
117     optparser.add_option(
118         '--precision-threshold', metavar='BITS',
119         type='float', dest='precision_threshold', default=8.0,
120         help='precision threshold in bits [default: %default]')
121     optparser.add_option(
122         '--gl-renderer', metavar='REGEXP',
123         type='string', dest='gl_renderer_re', default='^.*$',
124         help='require a matching GL_RENDERER string [default: %default]')
125
126     (options, args) = optparser.parse_args(sys.argv[1:])
127     if not args:
128         optparser.error("incorrect number of arguments")
129
130     # Build the source
131     if options.build:
132         sys.stdout.write(options.build + '\n')
133         sys.stdout.flush()
134         returncode = subprocess.call(options.build, shell=True)
135         if returncode:
136             skip()
137
138     # TODO: For this to be useful on Windows we'll also need an installation
139     # procedure here.
140
141     # Do some sanity checks.  In particular we want to make sure that the GL
142     # implementation is usable, and is the right one (i.e., we didn't fallback
143     # to a different OpenGL implementation due to missing symbols).
144     if platform.system() != 'Windows' and which('glxinfo'):
145         p = subprocess.Popen(['glxinfo'], stdout=subprocess.PIPE)
146         stdout, stderr = p.communicate()
147         if p.returncode:
148             skip()
149
150         # Search for the GL_RENDERER string
151         gl_renderer_header = 'OpenGL renderer string: '
152         gl_renderer = ''
153         for line in stdout.split('\n'):
154             if line.startswith(gl_renderer_header):
155                 gl_renderer = line[len(gl_renderer_header):]
156             if line.startswith('direct rendering: No'):
157                 sys.stderr.write('Indirect rendering.\n')
158                 skip()
159
160         # and match it against the regular expression specified in the command
161         # line.
162         if not re.search(options.gl_renderer_re, gl_renderer):
163             sys.stderr.write("GL_RENDERER mismatch: %r !~ /%s/\n"  % (gl_renderer, options.gl_renderer_re))
164             skip()
165
166     # Run glretrace
167     command = [options.retrace]
168     if options.compare_prefix:
169         command += ['-c', options.compare_prefix]
170     else:
171         command += ['-b']
172     command += args
173     sys.stdout.write(' '.join(command) + '\n')
174     sys.stdout.flush()
175     p = subprocess.Popen(command, stdout=subprocess.PIPE)
176     stdout, stderr = p.communicate()
177     if p.returncode:
178         if not options.compare_prefix:
179             bad()
180         else:
181             skip()
182
183     if options.compare_prefix:
184         failed = False
185         precision_re = re.compile('^Snapshot (\S+) average precision of (\S+) bits$')
186         for line in stdout.split('\n'):
187             mo = precision_re.match(line)
188             if mo:
189                 print line
190                 call_no = int(mo.group(1))
191                 precision = float(mo.group(2))
192                 if precision < options.precision_threshold:
193                     failed = True
194         if failed:
195             bad()
196
197     # TODO: allow more criterias here, such as, performance threshold
198
199     # Success
200     good()
201
202
203 # Invoke main program, aborting the bisection on Ctrl+C or any uncaught Python
204 # exception.
205 if __name__ == '__main__':
206     try:
207         main()
208     except SystemExit:
209         raise
210     except KeyboardInterrupt:
211         abort()
212     except:
213         traceback.print_exc()
214         abort()