2 ##########################################################################
4 # Copyright 2011 VMware, Inc.
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:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
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
25 ##########################################################################/
27 '''Check a trace replays successfully or not.
29 It is meant to be used with git bisect. See git bisect manpage for more
47 '''Tell git-bisect that this commit is good.'''
49 sys.stdout.write('GOOD\n')
54 '''Tell git-bisect that this commit is bad.'''
56 sys.stdout.write('BAD\n')
61 '''Tell git-bisect to skip this commit.'''
63 sys.stdout.write('SKIP\n')
68 '''Tell git-bisect to abort.'''
70 sys.stdout.write('ABORT\n')
74 def which(executable):
75 '''Search for the executable on the PATH.'''
77 if platform.system() == 'Windows':
81 dirs = os.environ['PATH'].split(os.path.pathsep)
83 path = os.path.join(dir, executable)
85 if os.path.exists(path + ext):
93 It will always invoke sys.exit(), and never return normally.
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'
104 # Parse command line options
105 optparser = optparse.OptionParser(
106 usage='\n\tgit bisect run %prog [options] -- [glretrace options] <trace>',
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]')
129 (options, args) = optparser.parse_args(sys.argv[1:])
131 optparser.error("incorrect number of arguments")
135 sys.stdout.write(options.build + '\n')
137 returncode = subprocess.call(options.build, shell=True)
141 # TODO: For this to be useful on Windows we'll also need an installation
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:
153 # Search for the GL_RENDERER string
154 gl_renderer_header = 'OpenGL renderer string: '
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')
163 # and match it against the regular expression specified in the command
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))
171 retracer = retracediff.Retracer(options.retrace, args)
173 if options.compare_prefix:
177 images = snapdiff.find_images(options.compare_prefix)
180 imageName, ext = os.path.splitext(image)
182 callNo = int(imageName)
185 refImages[callNo] = options.compare_prefix + image
186 callNos.append(callNo)
188 run = retracer.snapshot(','.join(map(str, callNos)))
190 srcImage, callNo = run.nextSnapshot()
194 refImage = refImages[callNo]
196 # Compare the two images
197 comparer = snapdiff.Comparer(refImage, srcImage)
198 precision = comparer.precision()
200 mismatch = precision < options.precision_threshold
204 if run.process.returncode:
207 returncode = retracer.retrace('-b')
211 # TODO: allow more criterias here, such as, performance threshold
217 # Invoke main program, aborting the bisection on Ctrl+C or any uncaught Python
219 if __name__ == '__main__':
224 except KeyboardInterrupt:
227 traceback.print_exc()