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
44 '''Tell git-bisect that this commit is good.'''
46 sys.stdout.write('GOOD\n')
51 '''Tell git-bisect that this commit is bad.'''
53 sys.stdout.write('BAD\n')
58 '''Tell git-bisect to skip this commit.'''
60 sys.stdout.write('SKIP\n')
65 '''Tell git-bisect to abort.'''
67 sys.stdout.write('ABORT\n')
71 def which(executable):
72 '''Search for the executable on the PATH.'''
74 if platform.system() == 'Windows':
78 dirs = os.environ['PATH'].split(os.path.pathsep)
80 path = os.path.join(dir, executable)
82 if os.path.exists(path + ext):
90 It will always invoke sys.exit(), and never return normally.
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'
101 # Parse command line options
102 optparser = optparse.OptionParser(
103 usage='\n\tgit bisect run %prog [options] -- [glretrace options] <trace>',
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]')
126 (options, args) = optparser.parse_args(sys.argv[1:])
128 optparser.error("incorrect number of arguments")
132 sys.stdout.write(options.build + '\n')
134 returncode = subprocess.call(options.build, shell=True)
138 # TODO: For this to be useful on Windows we'll also need an installation
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()
150 # Search for the GL_RENDERER string
151 gl_renderer_header = 'OpenGL renderer string: '
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')
160 # and match it against the regular expression specified in the command
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))
167 command = [options.retrace]
168 if options.compare_prefix:
169 command += ['-c', options.compare_prefix]
173 sys.stdout.write(' '.join(command) + '\n')
175 p = subprocess.Popen(command, stdout=subprocess.PIPE)
176 stdout, stderr = p.communicate()
178 if not options.compare_prefix:
183 if options.compare_prefix:
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)
190 call_no = int(mo.group(1))
191 precision = float(mo.group(2))
192 if precision < options.precision_threshold:
197 # TODO: allow more criterias here, such as, performance threshold
203 # Invoke main program, aborting the bisection on Ctrl+C or any uncaught Python
205 if __name__ == '__main__':
210 except KeyboardInterrupt:
213 traceback.print_exc()