]> git.cworth.org Git - apitrace/blob - scripts/tracecheck.py
gui: Group on glPush/PopGroupMarkerEXT too.
[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 import snapdiff
43 import retracediff
44
45
46 def good():
47     '''Tell git-bisect that this commit is good.'''
48
49     sys.stdout.write('GOOD\n')
50     sys.exit(0)
51
52
53 def bad():
54     '''Tell git-bisect that this commit is bad.'''
55
56     sys.stdout.write('BAD\n')
57     sys.exit(1)
58
59
60 def skip():
61     '''Tell git-bisect to skip this commit.'''
62
63     sys.stdout.write('SKIP\n')
64     sys.exit(125)
65
66
67 def abort():
68     '''Tell git-bisect to abort.'''
69
70     sys.stdout.write('ABORT\n')
71     sys.exit(-1)
72
73
74 def which(executable):
75     '''Search for the executable on the PATH.'''
76
77     if platform.system() == 'Windows':
78         exts = ['.exe']
79     else:
80         exts = ['']
81     dirs = os.environ['PATH'].split(os.path.pathsep)
82     for dir in dirs:
83         path = os.path.join(dir, executable)
84         for ext in exts:
85             if os.path.exists(path + ext):
86                 return True
87     return False
88
89
90 def main():
91     '''Main program.
92
93     It will always invoke sys.exit(), and never return normally.
94     '''
95
96     # Try to guess the build command.
97     if os.path.exists('SConstruct'):
98         default_build = 'scons'
99     elif os.path.exists('Makefile'):
100         default_build = 'make'
101     else:
102         default_build = None
103
104     # Parse command line options
105     optparser = optparse.OptionParser(
106         usage='\n\tgit bisect run %prog [options] -- [glretrace options] <trace>',
107         version='%%prog')
108     optparser.add_option(
109         '-b', '--build', metavar='COMMAND',
110         type='string', dest='build', default=default_build,
111         help='build command [default: %default]')
112     optparser.add_option(
113         '-r', '--retrace', metavar='PROGRAM',
114         type='string', dest='retrace', default='glretrace',
115         help='retrace command [default: %default]')
116     optparser.add_option(
117         '-c', '--compare', metavar='PREFIX',
118         type='string', dest='compare_prefix', default=None,
119         help='snapshot comparison prefix')
120     optparser.add_option(
121         '--precision-threshold', metavar='BITS',
122         type='float', dest='precision_threshold', default=8.0,
123         help='precision threshold in bits [default: %default]')
124     optparser.add_option(
125         '--gl-renderer', metavar='REGEXP',
126         type='string', dest='gl_renderer_re', default='^.*$',
127         help='require a matching GL_RENDERER string [default: %default]')
128
129     (options, args) = optparser.parse_args(sys.argv[1:])
130     if not args:
131         optparser.error("incorrect number of arguments")
132
133     # Build the source
134     if options.build:
135         sys.stdout.write(options.build + '\n')
136         sys.stdout.flush()
137         returncode = subprocess.call(options.build, shell=True)
138         if returncode:
139             skip()
140
141     # TODO: For this to be useful on Windows we'll also need an installation
142     # procedure here.
143
144     # Do some sanity checks.  In particular we want to make sure that the GL
145     # implementation is usable, and is the right one (i.e., we didn't fallback
146     # to a different OpenGL implementation due to missing symbols).
147     if platform.system() != 'Windows' and which('glxinfo'):
148         glxinfo = subprocess.Popen(['glxinfo'], stdout=subprocess.PIPE)
149         stdout, stderr = glxinfo.communicate()
150         if glxinfo.returncode:
151             skip()
152
153         # Search for the GL_RENDERER string
154         gl_renderer_header = 'OpenGL renderer string: '
155         gl_renderer = ''
156         for line in stdout.split('\n'):
157             if line.startswith(gl_renderer_header):
158                 gl_renderer = line[len(gl_renderer_header):]
159             if line.startswith('direct rendering: No'):
160                 sys.stderr.write('Indirect rendering.\n')
161                 skip()
162
163         # and match it against the regular expression specified in the command
164         # line.
165         if not re.search(options.gl_renderer_re, gl_renderer):
166             sys.stderr.write("GL_RENDERER mismatch: %r !~ /%s/\n"  % (gl_renderer, options.gl_renderer_re))
167             skip()
168
169     # Run glretrace
170     
171     retracer = retracediff.Retracer(options.retrace, args)
172
173     if options.compare_prefix:
174         refImages = {}
175         callNos = []
176             
177         images = snapdiff.find_images(options.compare_prefix)
178         images.sort()
179         for image in images:
180             imageName, ext = os.path.splitext(image)
181             try:
182                 callNo = int(imageName)
183             except ValueError:
184                 continue
185             refImages[callNo] = options.compare_prefix + image
186             callNos.append(callNo)
187
188         run = retracer.snapshot(','.join(map(str, callNos)))
189         while True:
190             srcImage, callNo = run.nextSnapshot()
191             if srcImage is None:
192                 break
193
194             refImage = refImages[callNo]
195
196             # Compare the two images
197             comparer = snapdiff.Comparer(refImage, srcImage)
198             precision = comparer.precision()
199
200             mismatch = precision < options.precision_threshold
201             if mismatch:
202                 bad()
203         run.process.wait()
204         if run.process.returncode:
205             skip()
206     else:
207         returncode = retracer.retrace('-b')
208         if returncode:
209             bad()
210
211     # TODO: allow more criterias here, such as, performance threshold
212
213     # Success
214     good()
215
216
217 # Invoke main program, aborting the bisection on Ctrl+C or any uncaught Python
218 # exception.
219 if __name__ == '__main__':
220     try:
221         main()
222     except SystemExit:
223         raise
224     except KeyboardInterrupt:
225         abort()
226     except:
227         traceback.print_exc()
228         abort()