]> git.cworth.org Git - apitrace/blob - scripts/tracediff.py
Merge branch 'master' into d3dretrace
[apitrace] / scripts / tracediff.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2011 Jose Fonseca
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
28 import platform
29 import optparse
30 import os.path
31 import shutil
32 import subprocess
33 import sys
34 import tempfile
35
36
37 class Dumper:
38
39     def __init__(self, trace, calls):
40         self.output = tempfile.NamedTemporaryFile()
41
42         dump_args = [
43             options.apitrace,
44             'dump',
45             '--color=never',
46             '--call-nos=no',
47             '--arg-names=no',
48             '--calls=' + calls,
49             trace
50         ]
51
52         self.dump = subprocess.Popen(
53             args = dump_args,
54             stdout = self.output,
55             universal_newlines = True,
56         )
57
58
59 if platform.system() == 'Windows':
60     start_delete = ''
61     end_delete   = ''
62     start_insert = ''
63     end_insert   = ''
64 else:
65     start_delete = '\33[9m\33[31m'
66     end_delete   = '\33[0m'
67     start_insert = '\33[32m'
68     end_insert   = '\33[0m'
69
70
71 def diff(ref_trace, src_trace):
72
73     ref_dumper = Dumper(ref_trace, options.ref_calls)
74     src_dumper = Dumper(src_trace, options.src_calls)
75
76     # TODO use difflib instead
77     if options.diff == 'diff':
78         diff_args = [
79                 'diff',
80                 '--speed-large-files',
81                 '--old-line-format=' + start_delete + '%l' + end_delete + '\n',
82                 '--new-line-format=' + start_insert + '%l' + end_insert + '\n',
83             ]
84     elif options.diff == 'sdiff':
85         diff_args = [
86                 'sdiff',
87                 '--width=%u' % options.width,
88                 '--speed-large-files',
89             ]
90     elif options.diff == 'wdiff':
91         diff_args = [
92                 'wdiff',
93                 #'--terminal',
94                 '--avoid-wraps',
95                 '--start-delete=' + start_delete,
96                 '--end-delete=' + end_delete,
97                 '--start-insert=' + start_insert,
98                 '--end-insert=' + end_insert,
99             ]
100     else:
101         assert False
102     diff_args += [ref_dumper.output.name, src_dumper.output.name]
103
104     ref_dumper.dump.wait()
105     src_dumper.dump.wait()
106
107     less = None
108     if sys.stdout.isatty():
109         less = subprocess.Popen(
110             args = ['less', '-FRXn'],
111             stdin = subprocess.PIPE
112         )
113
114         diff_stdout = less.stdin
115     else:
116         diff_stdout = None
117
118     diff = subprocess.Popen(
119         args = diff_args,
120         stdout = diff_stdout,
121         universal_newlines = True,
122     )
123
124     diff.wait()
125
126     if less is not None:
127         less.wait()
128
129
130 def which(executable):
131     '''Search for the executable on the PATH.'''
132
133     if platform.system() == 'Windows':
134         exts = ['.exe']
135     else:
136         exts = ['']
137     dirs = os.environ['PATH'].split(os.path.pathsep)
138     for dir in dirs:
139         path = os.path.join(dir, executable)
140         for ext in exts:
141             if os.path.exists(path + ext):
142                 return True
143     return False
144
145
146 def columns():
147     import curses
148     curses.setupterm()
149     return curses.tigetnum('cols')
150
151
152 def main():
153     '''Main program.
154     '''
155
156     # Determine default options
157     default_width = columns()
158
159     # Parse command line options
160     optparser = optparse.OptionParser(
161         usage='\n\t%prog [options] -- TRACE_FILE TRACE_FILE',
162         version='%%prog')
163     optparser.add_option(
164         '-a', '--apitrace', metavar='PROGRAM',
165         type='string', dest='apitrace', default='apitrace',
166         help='apitrace command [default: %default]')
167     optparser.add_option(
168         '-d', '--diff',
169         type="choice", choices=('diff', 'sdiff', 'wdiff'),
170         dest="diff", default=None,
171         help="diff program: wdiff, sdiff, or diff [default: auto]")
172     optparser.add_option(
173         '-c', '--calls', metavar='CALLSET',
174         type="string", dest="calls", default='1-10000',
175         help="calls to compare [default: %default]")
176     optparser.add_option(
177         '--ref-calls', metavar='CALLSET',
178         type="string", dest="ref_calls", default=None,
179         help="calls to compare from reference trace")
180     optparser.add_option(
181         '--src-calls', metavar='CALLSET',
182         type="string", dest="src_calls", default=None,
183         help="calls to compare from source trace")
184     optparser.add_option(
185         '-w', '--width', metavar='NUM',
186         type="string", dest="width", default=default_width,
187         help="columns [default: %default]")
188
189     global options
190     (options, args) = optparser.parse_args(sys.argv[1:])
191     if len(args) != 2:
192         optparser.error("incorrect number of arguments")
193
194     if options.diff is None:
195         if which('wdiff'):
196             options.diff = 'wdiff'
197         else:
198             sys.stderr.write('warning: wdiff not found\n')
199             if which('sdiff'):
200                 options.diff = 'sdiff'
201             else:
202                 sys.stderr.write('warning: sdiff not found\n')
203                 options.diff = 'diff'
204
205     if options.ref_calls is None:
206         options.ref_calls = options.calls
207     if options.src_calls is None:
208         options.src_calls = options.calls
209
210     diff(*args)
211
212
213 if __name__ == '__main__':
214     main()