xref: /aosp_15_r20/external/mesa3d/src/gallium/tools/trace/pytracediff.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1*61046927SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*61046927SAndroid Build Coastguard Worker# coding=utf-8
3*61046927SAndroid Build Coastguard Worker##########################################################################
4*61046927SAndroid Build Coastguard Worker#
5*61046927SAndroid Build Coastguard Worker# pytracediff - Compare Gallium XML trace files
6*61046927SAndroid Build Coastguard Worker# (C) Copyright 2022 Matti 'ccr' Hämäläinen <[email protected]>
7*61046927SAndroid Build Coastguard Worker#
8*61046927SAndroid Build Coastguard Worker# Permission is hereby granted, free of charge, to any person obtaining a copy
9*61046927SAndroid Build Coastguard Worker# of this software and associated documentation files (the "Software"), to deal
10*61046927SAndroid Build Coastguard Worker# in the Software without restriction, including without limitation the rights
11*61046927SAndroid Build Coastguard Worker# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12*61046927SAndroid Build Coastguard Worker# copies of the Software, and to permit persons to whom the Software is
13*61046927SAndroid Build Coastguard Worker# furnished to do so, subject to the following conditions:
14*61046927SAndroid Build Coastguard Worker#
15*61046927SAndroid Build Coastguard Worker# The above copyright notice and this permission notice shall be included in
16*61046927SAndroid Build Coastguard Worker# all copies or substantial portions of the Software.
17*61046927SAndroid Build Coastguard Worker#
18*61046927SAndroid Build Coastguard Worker# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19*61046927SAndroid Build Coastguard Worker# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20*61046927SAndroid Build Coastguard Worker# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21*61046927SAndroid Build Coastguard Worker# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22*61046927SAndroid Build Coastguard Worker# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23*61046927SAndroid Build Coastguard Worker# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24*61046927SAndroid Build Coastguard Worker# THE SOFTWARE.
25*61046927SAndroid Build Coastguard Worker#
26*61046927SAndroid Build Coastguard Worker##########################################################################
27*61046927SAndroid Build Coastguard Worker
28*61046927SAndroid Build Coastguard Workerfrom parse import *
29*61046927SAndroid Build Coastguard Workerimport os
30*61046927SAndroid Build Coastguard Workerimport sys
31*61046927SAndroid Build Coastguard Workerimport re
32*61046927SAndroid Build Coastguard Workerimport signal
33*61046927SAndroid Build Coastguard Workerimport functools
34*61046927SAndroid Build Coastguard Workerimport argparse
35*61046927SAndroid Build Coastguard Workerimport difflib
36*61046927SAndroid Build Coastguard Workerimport subprocess
37*61046927SAndroid Build Coastguard Worker
38*61046927SAndroid Build Coastguard Workerassert sys.version_info >= (3, 6), 'Python >= 3.6 required'
39*61046927SAndroid Build Coastguard Worker
40*61046927SAndroid Build Coastguard Worker
41*61046927SAndroid Build Coastguard Worker###
42*61046927SAndroid Build Coastguard Worker### ANSI color codes
43*61046927SAndroid Build Coastguard Worker###
44*61046927SAndroid Build Coastguard WorkerPKK_ANSI_ESC       = '\33['
45*61046927SAndroid Build Coastguard WorkerPKK_ANSI_NORMAL    = '0m'
46*61046927SAndroid Build Coastguard WorkerPKK_ANSI_RED       = '31m'
47*61046927SAndroid Build Coastguard WorkerPKK_ANSI_GREEN     = '32m'
48*61046927SAndroid Build Coastguard WorkerPKK_ANSI_YELLOW    = '33m'
49*61046927SAndroid Build Coastguard WorkerPKK_ANSI_PURPLE    = '35m'
50*61046927SAndroid Build Coastguard WorkerPKK_ANSI_BOLD      = '1m'
51*61046927SAndroid Build Coastguard WorkerPKK_ANSI_ITALIC    = '3m'
52*61046927SAndroid Build Coastguard Worker
53*61046927SAndroid Build Coastguard Worker
54*61046927SAndroid Build Coastguard Worker###
55*61046927SAndroid Build Coastguard Worker### Utility functions and classes
56*61046927SAndroid Build Coastguard Worker###
57*61046927SAndroid Build Coastguard Workerdef pkk_fatal(msg):
58*61046927SAndroid Build Coastguard Worker    print(f"ERROR: {msg}", file=sys.stderr)
59*61046927SAndroid Build Coastguard Worker    if outpipe is not None:
60*61046927SAndroid Build Coastguard Worker        outpipe.terminate()
61*61046927SAndroid Build Coastguard Worker    sys.exit(1)
62*61046927SAndroid Build Coastguard Worker
63*61046927SAndroid Build Coastguard Worker
64*61046927SAndroid Build Coastguard Workerdef pkk_info(msg):
65*61046927SAndroid Build Coastguard Worker    print(msg, file=sys.stderr)
66*61046927SAndroid Build Coastguard Worker
67*61046927SAndroid Build Coastguard Worker
68*61046927SAndroid Build Coastguard Workerdef pkk_output(outpipe, msg):
69*61046927SAndroid Build Coastguard Worker    if outpipe is not None:
70*61046927SAndroid Build Coastguard Worker        print(msg, file=outpipe.stdin)
71*61046927SAndroid Build Coastguard Worker    else:
72*61046927SAndroid Build Coastguard Worker        print(msg)
73*61046927SAndroid Build Coastguard Worker
74*61046927SAndroid Build Coastguard Worker
75*61046927SAndroid Build Coastguard Workerdef pkk_signal_handler(signal, frame):
76*61046927SAndroid Build Coastguard Worker    print("\nQuitting due to SIGINT / Ctrl+C!")
77*61046927SAndroid Build Coastguard Worker    if outpipe is not None:
78*61046927SAndroid Build Coastguard Worker        outpipe.terminate()
79*61046927SAndroid Build Coastguard Worker    sys.exit(1)
80*61046927SAndroid Build Coastguard Worker
81*61046927SAndroid Build Coastguard Worker
82*61046927SAndroid Build Coastguard Workerdef pkk_arg_range(vstr, vmin, vmax):
83*61046927SAndroid Build Coastguard Worker    try:
84*61046927SAndroid Build Coastguard Worker        value = int(vstr)
85*61046927SAndroid Build Coastguard Worker    except Exception as e:
86*61046927SAndroid Build Coastguard Worker        raise argparse.ArgumentTypeError(f"value '{vstr}' is not an integer")
87*61046927SAndroid Build Coastguard Worker
88*61046927SAndroid Build Coastguard Worker    value = int(vstr)
89*61046927SAndroid Build Coastguard Worker    if value < vmin or value > vmax:
90*61046927SAndroid Build Coastguard Worker        raise argparse.ArgumentTypeError(f"value {value} not in range {vmin}-{vmax}")
91*61046927SAndroid Build Coastguard Worker    else:
92*61046927SAndroid Build Coastguard Worker        return value
93*61046927SAndroid Build Coastguard Worker
94*61046927SAndroid Build Coastguard Worker
95*61046927SAndroid Build Coastguard Workerclass PKKArgumentParser(argparse.ArgumentParser):
96*61046927SAndroid Build Coastguard Worker    def print_help(self):
97*61046927SAndroid Build Coastguard Worker        print("pytracediff - Compare Gallium XML trace files\n"
98*61046927SAndroid Build Coastguard Worker        "(C) Copyright 2022 Matti 'ccr' Hämäläinen <[email protected]>\n")
99*61046927SAndroid Build Coastguard Worker        super().print_help()
100*61046927SAndroid Build Coastguard Worker        print("\nList of junk calls:")
101*61046927SAndroid Build Coastguard Worker        for klass, call in sorted(trace_ignore_calls):
102*61046927SAndroid Build Coastguard Worker            print(f"  {klass}::{call}")
103*61046927SAndroid Build Coastguard Worker
104*61046927SAndroid Build Coastguard Worker    def error(self, msg):
105*61046927SAndroid Build Coastguard Worker        self.print_help()
106*61046927SAndroid Build Coastguard Worker        print(f"\nERROR: {msg}", file=sys.stderr)
107*61046927SAndroid Build Coastguard Worker        sys.exit(2)
108*61046927SAndroid Build Coastguard Worker
109*61046927SAndroid Build Coastguard Worker
110*61046927SAndroid Build Coastguard Workerclass PKKTraceParser(TraceParser):
111*61046927SAndroid Build Coastguard Worker    def __init__(self, stream, options, state):
112*61046927SAndroid Build Coastguard Worker        TraceParser.__init__(self, stream, options, state)
113*61046927SAndroid Build Coastguard Worker        self.call_stack = []
114*61046927SAndroid Build Coastguard Worker
115*61046927SAndroid Build Coastguard Worker    def handle_call(self, call):
116*61046927SAndroid Build Coastguard Worker        self.call_stack.append(call)
117*61046927SAndroid Build Coastguard Worker
118*61046927SAndroid Build Coastguard Worker
119*61046927SAndroid Build Coastguard Workerclass PKKPrettyPrinter(PrettyPrinter):
120*61046927SAndroid Build Coastguard Worker    def __init__(self, options):
121*61046927SAndroid Build Coastguard Worker        self.options = options
122*61046927SAndroid Build Coastguard Worker
123*61046927SAndroid Build Coastguard Worker    def entry_start(self, show_args):
124*61046927SAndroid Build Coastguard Worker        self.data = []
125*61046927SAndroid Build Coastguard Worker        self.line = ""
126*61046927SAndroid Build Coastguard Worker        self.show_args = show_args
127*61046927SAndroid Build Coastguard Worker
128*61046927SAndroid Build Coastguard Worker    def entry_get(self):
129*61046927SAndroid Build Coastguard Worker        if self.line != "":
130*61046927SAndroid Build Coastguard Worker            self.data.append(self.line)
131*61046927SAndroid Build Coastguard Worker        return self.data
132*61046927SAndroid Build Coastguard Worker
133*61046927SAndroid Build Coastguard Worker    def text(self, text):
134*61046927SAndroid Build Coastguard Worker        self.line += text
135*61046927SAndroid Build Coastguard Worker
136*61046927SAndroid Build Coastguard Worker    def literal(self, text):
137*61046927SAndroid Build Coastguard Worker        self.line += text
138*61046927SAndroid Build Coastguard Worker
139*61046927SAndroid Build Coastguard Worker    def function(self, text):
140*61046927SAndroid Build Coastguard Worker        self.line += text
141*61046927SAndroid Build Coastguard Worker
142*61046927SAndroid Build Coastguard Worker    def variable(self, text):
143*61046927SAndroid Build Coastguard Worker        self.line += text
144*61046927SAndroid Build Coastguard Worker
145*61046927SAndroid Build Coastguard Worker    def address(self, text):
146*61046927SAndroid Build Coastguard Worker        self.line += text
147*61046927SAndroid Build Coastguard Worker
148*61046927SAndroid Build Coastguard Worker    def newline(self):
149*61046927SAndroid Build Coastguard Worker        self.data.append(self.line)
150*61046927SAndroid Build Coastguard Worker        self.line = ""
151*61046927SAndroid Build Coastguard Worker
152*61046927SAndroid Build Coastguard Worker
153*61046927SAndroid Build Coastguard Worker    def visit_literal(self, node):
154*61046927SAndroid Build Coastguard Worker        if node.value is None:
155*61046927SAndroid Build Coastguard Worker            self.literal("NULL")
156*61046927SAndroid Build Coastguard Worker        elif isinstance(node.value, str):
157*61046927SAndroid Build Coastguard Worker            self.literal('"' + node.value + '"')
158*61046927SAndroid Build Coastguard Worker        else:
159*61046927SAndroid Build Coastguard Worker            self.literal(repr(node.value))
160*61046927SAndroid Build Coastguard Worker
161*61046927SAndroid Build Coastguard Worker    def visit_blob(self, node):
162*61046927SAndroid Build Coastguard Worker        self.address("blob()")
163*61046927SAndroid Build Coastguard Worker
164*61046927SAndroid Build Coastguard Worker    def visit_named_constant(self, node):
165*61046927SAndroid Build Coastguard Worker        self.literal(node.name)
166*61046927SAndroid Build Coastguard Worker
167*61046927SAndroid Build Coastguard Worker    def visit_array(self, node):
168*61046927SAndroid Build Coastguard Worker        self.text("{")
169*61046927SAndroid Build Coastguard Worker        sep = ""
170*61046927SAndroid Build Coastguard Worker        for value in node.elements:
171*61046927SAndroid Build Coastguard Worker            self.text(sep)
172*61046927SAndroid Build Coastguard Worker            if sep != "":
173*61046927SAndroid Build Coastguard Worker                self.newline()
174*61046927SAndroid Build Coastguard Worker            value.visit(self)
175*61046927SAndroid Build Coastguard Worker            sep = ", "
176*61046927SAndroid Build Coastguard Worker        self.text("}")
177*61046927SAndroid Build Coastguard Worker
178*61046927SAndroid Build Coastguard Worker    def visit_struct(self, node):
179*61046927SAndroid Build Coastguard Worker        self.text("{")
180*61046927SAndroid Build Coastguard Worker        sep = ""
181*61046927SAndroid Build Coastguard Worker        for name, value in node.members:
182*61046927SAndroid Build Coastguard Worker            self.text(sep)
183*61046927SAndroid Build Coastguard Worker            if sep != "":
184*61046927SAndroid Build Coastguard Worker                self.newline()
185*61046927SAndroid Build Coastguard Worker            self.variable(name)
186*61046927SAndroid Build Coastguard Worker            self.text(" = ")
187*61046927SAndroid Build Coastguard Worker            value.visit(self)
188*61046927SAndroid Build Coastguard Worker            sep = ", "
189*61046927SAndroid Build Coastguard Worker        self.text("}")
190*61046927SAndroid Build Coastguard Worker
191*61046927SAndroid Build Coastguard Worker    def visit_pointer(self, node):
192*61046927SAndroid Build Coastguard Worker        if self.options.named_ptrs:
193*61046927SAndroid Build Coastguard Worker            self.address(node.named_address())
194*61046927SAndroid Build Coastguard Worker        else:
195*61046927SAndroid Build Coastguard Worker            self.address(node.address)
196*61046927SAndroid Build Coastguard Worker
197*61046927SAndroid Build Coastguard Worker    def visit_call(self, node):
198*61046927SAndroid Build Coastguard Worker        if not self.options.suppress_variants:
199*61046927SAndroid Build Coastguard Worker            self.text(f"[{node.no:8d}] ")
200*61046927SAndroid Build Coastguard Worker
201*61046927SAndroid Build Coastguard Worker        if node.klass is not None:
202*61046927SAndroid Build Coastguard Worker            self.function(node.klass +"::"+ node.method)
203*61046927SAndroid Build Coastguard Worker        else:
204*61046927SAndroid Build Coastguard Worker            self.function(node.method)
205*61046927SAndroid Build Coastguard Worker
206*61046927SAndroid Build Coastguard Worker        if not self.options.method_only or self.show_args:
207*61046927SAndroid Build Coastguard Worker            self.text("(")
208*61046927SAndroid Build Coastguard Worker            if len(node.args):
209*61046927SAndroid Build Coastguard Worker                self.newline()
210*61046927SAndroid Build Coastguard Worker                sep = ""
211*61046927SAndroid Build Coastguard Worker                for name, value in node.args:
212*61046927SAndroid Build Coastguard Worker                    self.text(sep)
213*61046927SAndroid Build Coastguard Worker                    if sep != "":
214*61046927SAndroid Build Coastguard Worker                        self.newline()
215*61046927SAndroid Build Coastguard Worker                    self.variable(name)
216*61046927SAndroid Build Coastguard Worker                    self.text(" = ")
217*61046927SAndroid Build Coastguard Worker                    value.visit(self)
218*61046927SAndroid Build Coastguard Worker                    sep = ", "
219*61046927SAndroid Build Coastguard Worker                self.newline()
220*61046927SAndroid Build Coastguard Worker
221*61046927SAndroid Build Coastguard Worker            self.text(")")
222*61046927SAndroid Build Coastguard Worker
223*61046927SAndroid Build Coastguard Worker            if node.ret is not None:
224*61046927SAndroid Build Coastguard Worker                self.text(" = ")
225*61046927SAndroid Build Coastguard Worker                node.ret.visit(self)
226*61046927SAndroid Build Coastguard Worker
227*61046927SAndroid Build Coastguard Worker        if not self.options.suppress_variants and node.time is not None:
228*61046927SAndroid Build Coastguard Worker            self.text(" // time ")
229*61046927SAndroid Build Coastguard Worker            node.time.visit(self)
230*61046927SAndroid Build Coastguard Worker
231*61046927SAndroid Build Coastguard Worker
232*61046927SAndroid Build Coastguard Workerdef pkk_parse_trace(filename, options, state):
233*61046927SAndroid Build Coastguard Worker    pkk_info(f"Parsing {filename} ...")
234*61046927SAndroid Build Coastguard Worker    try:
235*61046927SAndroid Build Coastguard Worker        if filename.endswith(".gz"):
236*61046927SAndroid Build Coastguard Worker            from gzip import GzipFile
237*61046927SAndroid Build Coastguard Worker            stream = io.TextIOWrapper(GzipFile(filename, "rb"))
238*61046927SAndroid Build Coastguard Worker        elif filename.endswith(".bz2"):
239*61046927SAndroid Build Coastguard Worker            from bz2 import BZ2File
240*61046927SAndroid Build Coastguard Worker            stream = io.TextIOWrapper(BZ2File(filename, "rb"))
241*61046927SAndroid Build Coastguard Worker        else:
242*61046927SAndroid Build Coastguard Worker            stream = open(filename, "rt")
243*61046927SAndroid Build Coastguard Worker
244*61046927SAndroid Build Coastguard Worker    except OSError as e:
245*61046927SAndroid Build Coastguard Worker        pkk_fatal(str(e))
246*61046927SAndroid Build Coastguard Worker
247*61046927SAndroid Build Coastguard Worker    parser = PKKTraceParser(stream, options, state)
248*61046927SAndroid Build Coastguard Worker    parser.parse()
249*61046927SAndroid Build Coastguard Worker
250*61046927SAndroid Build Coastguard Worker    return parser.call_stack
251*61046927SAndroid Build Coastguard Worker
252*61046927SAndroid Build Coastguard Worker
253*61046927SAndroid Build Coastguard Workerdef pkk_get_line(data, nline):
254*61046927SAndroid Build Coastguard Worker    if nline < len(data):
255*61046927SAndroid Build Coastguard Worker        return data[nline]
256*61046927SAndroid Build Coastguard Worker    else:
257*61046927SAndroid Build Coastguard Worker        return None
258*61046927SAndroid Build Coastguard Worker
259*61046927SAndroid Build Coastguard Worker
260*61046927SAndroid Build Coastguard Workerdef pkk_format_line(line, indent, width):
261*61046927SAndroid Build Coastguard Worker    if line is not None:
262*61046927SAndroid Build Coastguard Worker        tmp = indent + line
263*61046927SAndroid Build Coastguard Worker        if len(tmp) > width:
264*61046927SAndroid Build Coastguard Worker            return tmp[0:(width - 3)] + "..."
265*61046927SAndroid Build Coastguard Worker        else:
266*61046927SAndroid Build Coastguard Worker            return tmp
267*61046927SAndroid Build Coastguard Worker    else:
268*61046927SAndroid Build Coastguard Worker        return ""
269*61046927SAndroid Build Coastguard Worker
270*61046927SAndroid Build Coastguard Worker
271*61046927SAndroid Build Coastguard Worker###
272*61046927SAndroid Build Coastguard Worker### Main program starts
273*61046927SAndroid Build Coastguard Worker###
274*61046927SAndroid Build Coastguard Workerif __name__ == "__main__":
275*61046927SAndroid Build Coastguard Worker    ### Check if output is a terminal
276*61046927SAndroid Build Coastguard Worker    outpipe = None
277*61046927SAndroid Build Coastguard Worker    redirect = False
278*61046927SAndroid Build Coastguard Worker
279*61046927SAndroid Build Coastguard Worker    try:
280*61046927SAndroid Build Coastguard Worker        defwidth = os.get_terminal_size().columns
281*61046927SAndroid Build Coastguard Worker        redirect = True
282*61046927SAndroid Build Coastguard Worker    except OSError:
283*61046927SAndroid Build Coastguard Worker        defwidth = 80
284*61046927SAndroid Build Coastguard Worker
285*61046927SAndroid Build Coastguard Worker    signal.signal(signal.SIGINT, pkk_signal_handler)
286*61046927SAndroid Build Coastguard Worker
287*61046927SAndroid Build Coastguard Worker    ### Parse arguments
288*61046927SAndroid Build Coastguard Worker    optparser = PKKArgumentParser(
289*61046927SAndroid Build Coastguard Worker        usage="%(prog)s [options] <tracefile #1> <tracefile #2>\n")
290*61046927SAndroid Build Coastguard Worker
291*61046927SAndroid Build Coastguard Worker    optparser.add_argument("filename1",
292*61046927SAndroid Build Coastguard Worker        type=str, action="store",
293*61046927SAndroid Build Coastguard Worker        metavar="<tracefile #1>",
294*61046927SAndroid Build Coastguard Worker        help="Gallium trace XML filename (plain or .gz, .bz2)")
295*61046927SAndroid Build Coastguard Worker
296*61046927SAndroid Build Coastguard Worker    optparser.add_argument("filename2",
297*61046927SAndroid Build Coastguard Worker        type=str, action="store",
298*61046927SAndroid Build Coastguard Worker        metavar="<tracefile #2>",
299*61046927SAndroid Build Coastguard Worker        help="Gallium trace XML filename (plain or .gz, .bz2)")
300*61046927SAndroid Build Coastguard Worker
301*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-p", "--plain",
302*61046927SAndroid Build Coastguard Worker        dest="plain",
303*61046927SAndroid Build Coastguard Worker        action="store_true",
304*61046927SAndroid Build Coastguard Worker        help="disable ANSI color etc. formatting")
305*61046927SAndroid Build Coastguard Worker
306*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-S", "--sup-variants",
307*61046927SAndroid Build Coastguard Worker        dest="suppress_variants",
308*61046927SAndroid Build Coastguard Worker        action="store_true",
309*61046927SAndroid Build Coastguard Worker        help="suppress some variants in output")
310*61046927SAndroid Build Coastguard Worker
311*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-C", "--sup-common",
312*61046927SAndroid Build Coastguard Worker        dest="suppress_common",
313*61046927SAndroid Build Coastguard Worker        action="store_true",
314*61046927SAndroid Build Coastguard Worker        help="suppress common sections completely")
315*61046927SAndroid Build Coastguard Worker
316*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-N", "--named",
317*61046927SAndroid Build Coastguard Worker        dest="named_ptrs",
318*61046927SAndroid Build Coastguard Worker        action="store_true",
319*61046927SAndroid Build Coastguard Worker        help="generate symbolic names for raw pointer values")
320*61046927SAndroid Build Coastguard Worker
321*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-M", "--method-only",
322*61046927SAndroid Build Coastguard Worker        dest="method_only",
323*61046927SAndroid Build Coastguard Worker        action="store_true",
324*61046927SAndroid Build Coastguard Worker        help="output only call names without arguments")
325*61046927SAndroid Build Coastguard Worker
326*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-I", "--ignore-junk",
327*61046927SAndroid Build Coastguard Worker        dest="ignore_junk",
328*61046927SAndroid Build Coastguard Worker        action="store_true",
329*61046927SAndroid Build Coastguard Worker        help="filter out/ignore junk calls (see below)")
330*61046927SAndroid Build Coastguard Worker
331*61046927SAndroid Build Coastguard Worker    optparser.add_argument("-w", "--width",
332*61046927SAndroid Build Coastguard Worker        dest="output_width",
333*61046927SAndroid Build Coastguard Worker        type=functools.partial(pkk_arg_range, vmin=16, vmax=512), default=defwidth,
334*61046927SAndroid Build Coastguard Worker        metavar="N",
335*61046927SAndroid Build Coastguard Worker        help="output width (default: %(default)s)")
336*61046927SAndroid Build Coastguard Worker
337*61046927SAndroid Build Coastguard Worker    options = optparser.parse_args()
338*61046927SAndroid Build Coastguard Worker
339*61046927SAndroid Build Coastguard Worker    ### Parse input files
340*61046927SAndroid Build Coastguard Worker    stack1 = pkk_parse_trace(options.filename1, options, TraceStateData())
341*61046927SAndroid Build Coastguard Worker    stack2 = pkk_parse_trace(options.filename2, options, TraceStateData())
342*61046927SAndroid Build Coastguard Worker
343*61046927SAndroid Build Coastguard Worker    ### Perform diffing
344*61046927SAndroid Build Coastguard Worker    pkk_info("Matching trace sequences ...")
345*61046927SAndroid Build Coastguard Worker    sequence = difflib.SequenceMatcher(lambda x : x.is_junk, stack1, stack2, autojunk=False)
346*61046927SAndroid Build Coastguard Worker
347*61046927SAndroid Build Coastguard Worker    pkk_info("Sequencing diff ...")
348*61046927SAndroid Build Coastguard Worker    opcodes = sequence.get_opcodes()
349*61046927SAndroid Build Coastguard Worker    if len(opcodes) == 1 and opcodes[0][0] == "equal":
350*61046927SAndroid Build Coastguard Worker        print("The files are identical.")
351*61046927SAndroid Build Coastguard Worker        sys.exit(0)
352*61046927SAndroid Build Coastguard Worker
353*61046927SAndroid Build Coastguard Worker    ### Redirect output to 'less' if stdout is a tty
354*61046927SAndroid Build Coastguard Worker    try:
355*61046927SAndroid Build Coastguard Worker        if redirect:
356*61046927SAndroid Build Coastguard Worker            outpipe = subprocess.Popen(["less", "-S", "-R"], stdin=subprocess.PIPE, encoding="utf8")
357*61046927SAndroid Build Coastguard Worker
358*61046927SAndroid Build Coastguard Worker        ### Output results
359*61046927SAndroid Build Coastguard Worker        pkk_info("Outputting diff ...")
360*61046927SAndroid Build Coastguard Worker        colwidth = int((options.output_width - 3) / 2)
361*61046927SAndroid Build Coastguard Worker        colfmt   = "{}{:"+ str(colwidth) +"s}{} {}{}{} {}{:"+ str(colwidth) +"s}{}"
362*61046927SAndroid Build Coastguard Worker
363*61046927SAndroid Build Coastguard Worker        printer = PKKPrettyPrinter(options)
364*61046927SAndroid Build Coastguard Worker
365*61046927SAndroid Build Coastguard Worker        prevtag = ""
366*61046927SAndroid Build Coastguard Worker        for tag, start1, end1, start2, end2 in opcodes:
367*61046927SAndroid Build Coastguard Worker            if tag == "equal":
368*61046927SAndroid Build Coastguard Worker                show_args = False
369*61046927SAndroid Build Coastguard Worker                if options.suppress_common:
370*61046927SAndroid Build Coastguard Worker                    if tag != prevtag:
371*61046927SAndroid Build Coastguard Worker                        pkk_output(outpipe, "[...]")
372*61046927SAndroid Build Coastguard Worker                    continue
373*61046927SAndroid Build Coastguard Worker
374*61046927SAndroid Build Coastguard Worker                sep = "|"
375*61046927SAndroid Build Coastguard Worker                ansi1 = ansi2 = ansiend = ""
376*61046927SAndroid Build Coastguard Worker                show_args = False
377*61046927SAndroid Build Coastguard Worker            elif tag == "insert":
378*61046927SAndroid Build Coastguard Worker                sep = "+"
379*61046927SAndroid Build Coastguard Worker                ansi1 = ""
380*61046927SAndroid Build Coastguard Worker                ansi2 = PKK_ANSI_ESC + PKK_ANSI_GREEN
381*61046927SAndroid Build Coastguard Worker                show_args = True
382*61046927SAndroid Build Coastguard Worker            elif tag == "delete":
383*61046927SAndroid Build Coastguard Worker                sep = "-"
384*61046927SAndroid Build Coastguard Worker                ansi1 = PKK_ANSI_ESC + PKK_ANSI_RED
385*61046927SAndroid Build Coastguard Worker                ansi2 = ""
386*61046927SAndroid Build Coastguard Worker                show_args = True
387*61046927SAndroid Build Coastguard Worker            elif tag == "replace":
388*61046927SAndroid Build Coastguard Worker                sep = ">"
389*61046927SAndroid Build Coastguard Worker                ansi1 = ansi2 = PKK_ANSI_ESC + PKK_ANSI_BOLD
390*61046927SAndroid Build Coastguard Worker                show_args = True
391*61046927SAndroid Build Coastguard Worker            else:
392*61046927SAndroid Build Coastguard Worker                pkk_fatal(f"Internal error, unsupported difflib.SequenceMatcher operation '{tag}'.")
393*61046927SAndroid Build Coastguard Worker
394*61046927SAndroid Build Coastguard Worker            # No ANSI, please
395*61046927SAndroid Build Coastguard Worker            if options.plain:
396*61046927SAndroid Build Coastguard Worker                ansi1 = ansisep = ansi2 = ansiend = ""
397*61046927SAndroid Build Coastguard Worker            else:
398*61046927SAndroid Build Coastguard Worker                ansisep = PKK_ANSI_ESC + PKK_ANSI_PURPLE
399*61046927SAndroid Build Coastguard Worker                ansiend = PKK_ANSI_ESC + PKK_ANSI_NORMAL
400*61046927SAndroid Build Coastguard Worker
401*61046927SAndroid Build Coastguard Worker
402*61046927SAndroid Build Coastguard Worker            # Print out the block
403*61046927SAndroid Build Coastguard Worker            ncall1 = start1
404*61046927SAndroid Build Coastguard Worker            ncall2 = start2
405*61046927SAndroid Build Coastguard Worker            last1 = last2 = False
406*61046927SAndroid Build Coastguard Worker            while True:
407*61046927SAndroid Build Coastguard Worker                # Get line data
408*61046927SAndroid Build Coastguard Worker                if ncall1 < end1:
409*61046927SAndroid Build Coastguard Worker                    if not options.ignore_junk or not stack1[ncall1].is_junk:
410*61046927SAndroid Build Coastguard Worker                        printer.entry_start(show_args)
411*61046927SAndroid Build Coastguard Worker                        stack1[ncall1].visit(printer)
412*61046927SAndroid Build Coastguard Worker                        data1 = printer.entry_get()
413*61046927SAndroid Build Coastguard Worker                    else:
414*61046927SAndroid Build Coastguard Worker                        data1 = []
415*61046927SAndroid Build Coastguard Worker                    ncall1 += 1
416*61046927SAndroid Build Coastguard Worker                else:
417*61046927SAndroid Build Coastguard Worker                    data1 = []
418*61046927SAndroid Build Coastguard Worker                    last1 = True
419*61046927SAndroid Build Coastguard Worker
420*61046927SAndroid Build Coastguard Worker                if ncall2 < end2:
421*61046927SAndroid Build Coastguard Worker                    if not options.ignore_junk or not stack2[ncall2].is_junk:
422*61046927SAndroid Build Coastguard Worker                        printer.entry_start(show_args)
423*61046927SAndroid Build Coastguard Worker                        stack2[ncall2].visit(printer)
424*61046927SAndroid Build Coastguard Worker                        data2 = printer.entry_get()
425*61046927SAndroid Build Coastguard Worker                    else:
426*61046927SAndroid Build Coastguard Worker                        data2 = []
427*61046927SAndroid Build Coastguard Worker                    ncall2 += 1
428*61046927SAndroid Build Coastguard Worker                else:
429*61046927SAndroid Build Coastguard Worker                    data2 = []
430*61046927SAndroid Build Coastguard Worker                    last2 = True
431*61046927SAndroid Build Coastguard Worker
432*61046927SAndroid Build Coastguard Worker                # Check if we are at last call of both
433*61046927SAndroid Build Coastguard Worker                if last1 and last2:
434*61046927SAndroid Build Coastguard Worker                    break
435*61046927SAndroid Build Coastguard Worker
436*61046927SAndroid Build Coastguard Worker                nline = 0
437*61046927SAndroid Build Coastguard Worker                while nline < len(data1) or nline < len(data2):
438*61046927SAndroid Build Coastguard Worker                    # Determine line start indentation
439*61046927SAndroid Build Coastguard Worker                    if nline > 0:
440*61046927SAndroid Build Coastguard Worker                        if options.suppress_variants:
441*61046927SAndroid Build Coastguard Worker                            indent = " "*8
442*61046927SAndroid Build Coastguard Worker                        else:
443*61046927SAndroid Build Coastguard Worker                            indent = " "*12
444*61046927SAndroid Build Coastguard Worker                    else:
445*61046927SAndroid Build Coastguard Worker                        indent = ""
446*61046927SAndroid Build Coastguard Worker
447*61046927SAndroid Build Coastguard Worker                    line1 = pkk_get_line(data1, nline)
448*61046927SAndroid Build Coastguard Worker                    line2 = pkk_get_line(data2, nline)
449*61046927SAndroid Build Coastguard Worker
450*61046927SAndroid Build Coastguard Worker                    # Highlight differing lines if not plain
451*61046927SAndroid Build Coastguard Worker                    if not options.plain and line1 != line2:
452*61046927SAndroid Build Coastguard Worker                        if tag == "insert" or tag == "delete":
453*61046927SAndroid Build Coastguard Worker                            ansi1 = ansi1 + PKK_ANSI_ESC + PKK_ANSI_BOLD
454*61046927SAndroid Build Coastguard Worker                        elif tag == "replace":
455*61046927SAndroid Build Coastguard Worker                            ansi1 = ansi2 = ansi1 + PKK_ANSI_ESC + PKK_ANSI_YELLOW
456*61046927SAndroid Build Coastguard Worker
457*61046927SAndroid Build Coastguard Worker                    # Output line
458*61046927SAndroid Build Coastguard Worker                    pkk_output(outpipe, colfmt.format(
459*61046927SAndroid Build Coastguard Worker                        ansi1, pkk_format_line(line1, indent, colwidth), ansiend,
460*61046927SAndroid Build Coastguard Worker                        ansisep, sep, ansiend,
461*61046927SAndroid Build Coastguard Worker                        ansi2, pkk_format_line(line2, indent, colwidth), ansiend).
462*61046927SAndroid Build Coastguard Worker                        rstrip())
463*61046927SAndroid Build Coastguard Worker
464*61046927SAndroid Build Coastguard Worker                    nline += 1
465*61046927SAndroid Build Coastguard Worker
466*61046927SAndroid Build Coastguard Worker            if tag == "equal" and options.suppress_common:
467*61046927SAndroid Build Coastguard Worker                pkk_output(outpipe, "[...]")
468*61046927SAndroid Build Coastguard Worker
469*61046927SAndroid Build Coastguard Worker            prevtag = tag
470*61046927SAndroid Build Coastguard Worker
471*61046927SAndroid Build Coastguard Worker    except Exception as e:
472*61046927SAndroid Build Coastguard Worker        pkk_fatal(str(e))
473*61046927SAndroid Build Coastguard Worker
474*61046927SAndroid Build Coastguard Worker    if outpipe is not None:
475*61046927SAndroid Build Coastguard Worker        outpipe.communicate()
476