]> git.cworth.org Git - apitrace/blob - scripts/tracediff.py
Merge branch 'master' into d2d
[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     isatty = sys.stdout.isatty()
74
75     ref_dumper = Dumper(ref_trace, options.ref_calls)
76     src_dumper = Dumper(src_trace, options.src_calls)
77
78     # TODO use difflib instead
79     if options.diff == 'diff':
80         diff_args = [
81                 'diff',
82                 '--speed-large-files',
83             ]
84         if isatty:
85             diff_args += [
86                 '--old-line-format=' + start_delete + '%l' + end_delete + '\n',
87                 '--new-line-format=' + start_insert + '%l' + end_insert + '\n',
88             ]
89     elif options.diff == 'sdiff':
90         diff_args = [
91                 'sdiff',
92                 '--width=%u' % options.width,
93                 '--speed-large-files',
94             ]
95     elif options.diff == 'wdiff':
96         diff_args = [
97                 'wdiff',
98                 #'--terminal',
99                 '--avoid-wraps',
100             ]
101         if isatty:
102             diff_args += [
103                 '--start-delete=' + start_delete,
104                 '--end-delete=' + end_delete,
105                 '--start-insert=' + start_insert,
106                 '--end-insert=' + end_insert,
107             ]
108     else:
109         assert False
110     diff_args += [ref_dumper.output.name, src_dumper.output.name]
111
112     ref_dumper.dump.wait()
113     src_dumper.dump.wait()
114
115     less = None
116     if isatty:
117         less = subprocess.Popen(
118             args = ['less', '-FRXn'],
119             stdin = subprocess.PIPE
120         )
121
122         diff_stdout = less.stdin
123     else:
124         diff_stdout = None
125
126     diff = subprocess.Popen(
127         args = diff_args,
128         stdout = diff_stdout,
129         universal_newlines = True,
130     )
131
132     diff.wait()
133
134     if less is not None:
135         less.stdin.close()
136         less.wait()
137
138
139 def which(executable):
140     '''Search for the executable on the PATH.'''
141
142     if platform.system() == 'Windows':
143         exts = ['.exe']
144     else:
145         exts = ['']
146     dirs = os.environ['PATH'].split(os.path.pathsep)
147     for dir in dirs:
148         path = os.path.join(dir, executable)
149         for ext in exts:
150             if os.path.exists(path + ext):
151                 return True
152     return False
153
154
155 def columns():
156     import curses
157     curses.setupterm()
158     return curses.tigetnum('cols')
159
160
161 def main():
162     '''Main program.
163     '''
164
165     # Determine default options
166     default_width = columns()
167
168     # Parse command line options
169     optparser = optparse.OptionParser(
170         usage='\n\t%prog [options] -- TRACE_FILE TRACE_FILE',
171         version='%%prog')
172     optparser.add_option(
173         '-a', '--apitrace', metavar='PROGRAM',
174         type='string', dest='apitrace', default='apitrace',
175         help='apitrace command [default: %default]')
176     optparser.add_option(
177         '-d', '--diff',
178         type="choice", choices=('diff', 'sdiff', 'wdiff'),
179         dest="diff", default=None,
180         help="diff program: wdiff, sdiff, or diff [default: auto]")
181     optparser.add_option(
182         '-c', '--calls', metavar='CALLSET',
183         type="string", dest="calls", default='1-10000',
184         help="calls to compare [default: %default]")
185     optparser.add_option(
186         '--ref-calls', metavar='CALLSET',
187         type="string", dest="ref_calls", default=None,
188         help="calls to compare from reference trace")
189     optparser.add_option(
190         '--src-calls', metavar='CALLSET',
191         type="string", dest="src_calls", default=None,
192         help="calls to compare from source trace")
193     optparser.add_option(
194         '-w', '--width', metavar='NUM',
195         type="int", dest="width", default=default_width,
196         help="columns [default: %default]")
197
198     global options
199     (options, args) = optparser.parse_args(sys.argv[1:])
200     if len(args) != 2:
201         optparser.error("incorrect number of arguments")
202
203     if options.diff is None:
204         if which('wdiff'):
205             options.diff = 'wdiff'
206         else:
207             sys.stderr.write('warning: wdiff not found\n')
208             if which('sdiff'):
209                 options.diff = 'sdiff'
210             else:
211                 sys.stderr.write('warning: sdiff not found\n')
212                 options.diff = 'diff'
213
214     if options.ref_calls is None:
215         options.ref_calls = options.calls
216     if options.src_calls is None:
217         options.src_calls = options.calls
218
219     diff(*args)
220
221
222 if __name__ == '__main__':
223     main()