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