]> git.cworth.org Git - apitrace/blob - scripts/tracediff.py
Use wdiff by default.
[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
31 import shutil
32 import subprocess
33 import sys
34 import tempfile
35 import time
36
37
38 def stripdump(trace, fifo):
39     dump = subprocess.Popen(
40         args = [
41             options.apitrace,
42             'dump',
43             '--color=never',
44             '--arg-names=no',
45             '--calls=' + options.calls,
46             trace
47         ],
48         stdout = subprocess.PIPE,
49         universal_newlines = True,
50     )
51
52     sed = subprocess.Popen(
53         args = [
54             'sed',
55             '-e', r's/\r$//g',
56             '-e', r's/^[0-9]\+ //',
57             '-e', r's/hdc = \w\+/hdc/g',
58         ],
59         stdin = dump.stdout,
60         stdout = open(fifo, 'wt'),
61         universal_newlines = True,
62     )
63
64     # XXX: Avoid a weird race condition
65     time.sleep(0.01)
66
67
68 if platform.system() == 'Windows':
69     start_delete = ''
70     end_delete   = ''
71     start_insert = ''
72     end_insert   = ''
73 else:
74     start_delete = '\33[9m\33[31m'
75     end_delete   = '\33[0m'
76     start_insert = '\33[32m'
77     end_insert   = '\33[0m'
78
79
80 def diff(traces):
81     fifodir = tempfile.mkdtemp()
82     try:
83         fifos = []
84         for i in range(len(traces)):
85             trace = traces[i]
86             fifo = os.path.join(fifodir, str(i))
87             stripdump(trace, fifo)
88             fifos.append(fifo)
89
90         # TODO use difflib instead
91         if options.diff == 'diff':
92             diff_args = [
93                     'diff',
94                     '--speed-large-files',
95                     '--old-line-format=' + start_delete + '%l' + end_delete + '\n',
96                     '--new-line-format=' + start_insert + '%l' + end_insert + '\n',
97                 ]
98         elif options.diff == 'sdiff':
99             diff_args = [
100                     'sdiff',
101                     '--width=%u' % options.width,
102                     '--speed-large-files',
103                 ]
104         elif options.diff == 'wdiff':
105             diff_args = [
106                     'wdiff',
107                     #'--terminal',
108                     '--avoid-wraps',
109                     '--start-delete=' + start_delete,
110                     '--end-delete=' + end_delete,
111                     '--start-insert=' + start_insert,
112                     '--end-insert=' + end_insert,
113                 ]
114         else:
115             assert False
116         diff_args += fifos
117             
118         diff = subprocess.Popen(
119             args = diff_args,
120             stdout = subprocess.PIPE,
121             universal_newlines = True,
122         )
123
124         less = subprocess.Popen(
125             args = ['less', '-FRXn'],
126             stdin = diff.stdout
127         )
128
129         less.wait()
130
131     finally:
132         shutil.rmtree(fifodir)
133
134
135 def which(executable):
136     '''Search for the executable on the PATH.'''
137
138     if platform.system() == 'Windows':
139         exts = ['.exe']
140     else:
141         exts = ['']
142     dirs = os.environ['PATH'].split(os.path.pathsep)
143     for dir in dirs:
144         path = os.path.join(dir, executable)
145         for ext in exts:
146             if os.path.exists(path + ext):
147                 return True
148     return False
149
150
151 def columns():
152     import curses
153     curses.setupterm()
154     return curses.tigetnum('cols')
155
156
157 def main():
158     '''Main program.
159     '''
160
161     if which('wdiff'):
162         default_diff = 'wdiff'
163     elif which('sdiff'):
164         default_diff = 'sdiff'
165     else:
166         default_diff = 'diff'
167
168     default_width = columns()
169
170     # Parse command line options
171     optparser = optparse.OptionParser(
172         usage='\n\t%prog [options] -- TRACE_FILE TRACE_FILE',
173         version='%%prog')
174     optparser.add_option(
175         '-a', '--apitrace', metavar='PROGRAM',
176         type='string', dest='apitrace', default='apitrace',
177         help='apitrace command [default: %default]')
178     optparser.add_option(
179         '-d', '--diff',
180         type="choice", choices=('diff', 'sdiff', 'wdiff'),
181         dest="diff", default=default_diff,
182         help="diff program: diff, sdiff, or wdiff [default: %default]")
183     optparser.add_option(
184         '-c', '--calls', metavar='CALLSET',
185         type="string", dest="calls", default='1-10000',
186         help="calls to compare [default: %default]")
187     optparser.add_option(
188         '-w', '--width', metavar='NUM',
189         type="string", dest="width", default=default_width,
190         help="columns [default: %default]")
191
192     global options
193     (options, args) = optparser.parse_args(sys.argv[1:])
194     if len(args) != 2:
195         optparser.error("incorrect number of arguments")
196
197     diff(args)
198
199
200 if __name__ == '__main__':
201     main()