xref: /aosp_15_r20/external/clang/utils/check_cfc/obj_diff.py (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li#!/usr/bin/env python2.7
2*67e74705SXin Li
3*67e74705SXin Lifrom __future__ import print_function
4*67e74705SXin Li
5*67e74705SXin Liimport argparse
6*67e74705SXin Liimport difflib
7*67e74705SXin Liimport filecmp
8*67e74705SXin Liimport os
9*67e74705SXin Liimport subprocess
10*67e74705SXin Liimport sys
11*67e74705SXin Li
12*67e74705SXin Lidisassembler = 'objdump'
13*67e74705SXin Li
14*67e74705SXin Lidef keep_line(line):
15*67e74705SXin Li    """Returns true for lines that should be compared in the disassembly
16*67e74705SXin Li    output."""
17*67e74705SXin Li    return "file format" not in line
18*67e74705SXin Li
19*67e74705SXin Lidef disassemble(objfile):
20*67e74705SXin Li    """Disassemble object to a file."""
21*67e74705SXin Li    p = subprocess.Popen([disassembler, '-d', objfile],
22*67e74705SXin Li                         stdout=subprocess.PIPE,
23*67e74705SXin Li                         stderr=subprocess.PIPE)
24*67e74705SXin Li    (out, err) = p.communicate()
25*67e74705SXin Li    if p.returncode or err:
26*67e74705SXin Li        print("Disassemble failed: {}".format(objfile))
27*67e74705SXin Li        sys.exit(1)
28*67e74705SXin Li    return filter(keep_line, out.split(os.linesep))
29*67e74705SXin Li
30*67e74705SXin Lidef dump_debug(objfile):
31*67e74705SXin Li    """Dump all of the debug info from a file."""
32*67e74705SXin Li    p = subprocess.Popen([disassembler, '-WliaprmfsoRt', objfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
33*67e74705SXin Li    (out, err) = p.communicate()
34*67e74705SXin Li    if p.returncode or err:
35*67e74705SXin Li        print("Dump debug failed: {}".format(objfile))
36*67e74705SXin Li        sys.exit(1)
37*67e74705SXin Li    return filter(keep_line, out.split(os.linesep))
38*67e74705SXin Li
39*67e74705SXin Lidef first_diff(a, b, fromfile, tofile):
40*67e74705SXin Li    """Returns the first few lines of a difference, if there is one.  Python
41*67e74705SXin Li    diff can be very slow with large objects and the most interesting changes
42*67e74705SXin Li    are the first ones. Truncate data before sending to difflib.  Returns None
43*67e74705SXin Li    is there is no difference."""
44*67e74705SXin Li
45*67e74705SXin Li    # Find first diff
46*67e74705SXin Li    first_diff_idx = None
47*67e74705SXin Li    for idx, val in enumerate(a):
48*67e74705SXin Li        if val != b[idx]:
49*67e74705SXin Li            first_diff_idx = idx
50*67e74705SXin Li            break
51*67e74705SXin Li
52*67e74705SXin Li    if first_diff_idx == None:
53*67e74705SXin Li        # No difference
54*67e74705SXin Li        return None
55*67e74705SXin Li
56*67e74705SXin Li    # Diff to first line of diff plus some lines
57*67e74705SXin Li    context = 3
58*67e74705SXin Li    diff = difflib.unified_diff(a[:first_diff_idx+context],
59*67e74705SXin Li                                b[:first_diff_idx+context],
60*67e74705SXin Li                                fromfile,
61*67e74705SXin Li                                tofile)
62*67e74705SXin Li    difference = "\n".join(diff)
63*67e74705SXin Li    if first_diff_idx + context < len(a):
64*67e74705SXin Li        difference += "\n*** Diff truncated ***"
65*67e74705SXin Li    return difference
66*67e74705SXin Li
67*67e74705SXin Lidef compare_object_files(objfilea, objfileb):
68*67e74705SXin Li    """Compare disassembly of two different files.
69*67e74705SXin Li       Allowing unavoidable differences, such as filenames.
70*67e74705SXin Li       Return the first difference if the disassembly differs, or None.
71*67e74705SXin Li    """
72*67e74705SXin Li    disa = disassemble(objfilea)
73*67e74705SXin Li    disb = disassemble(objfileb)
74*67e74705SXin Li    return first_diff(disa, disb, objfilea, objfileb)
75*67e74705SXin Li
76*67e74705SXin Lidef compare_debug_info(objfilea, objfileb):
77*67e74705SXin Li    """Compare debug info of two different files.
78*67e74705SXin Li       Allowing unavoidable differences, such as filenames.
79*67e74705SXin Li       Return the first difference if the debug info differs, or None.
80*67e74705SXin Li       If there are differences in the code, there will almost certainly be differences in the debug info too.
81*67e74705SXin Li    """
82*67e74705SXin Li    dbga = dump_debug(objfilea)
83*67e74705SXin Li    dbgb = dump_debug(objfileb)
84*67e74705SXin Li    return first_diff(dbga, dbgb, objfilea, objfileb)
85*67e74705SXin Li
86*67e74705SXin Lidef compare_exact(objfilea, objfileb):
87*67e74705SXin Li    """Byte for byte comparison between object files.
88*67e74705SXin Li       Returns True if equal, False otherwise.
89*67e74705SXin Li    """
90*67e74705SXin Li    return filecmp.cmp(objfilea, objfileb)
91*67e74705SXin Li
92*67e74705SXin Liif __name__ == '__main__':
93*67e74705SXin Li    parser = argparse.ArgumentParser()
94*67e74705SXin Li    parser.add_argument('objfilea', nargs=1)
95*67e74705SXin Li    parser.add_argument('objfileb', nargs=1)
96*67e74705SXin Li    parser.add_argument('-v', '--verbose', action='store_true')
97*67e74705SXin Li    args = parser.parse_args()
98*67e74705SXin Li    diff = compare_object_files(args.objfilea[0], args.objfileb[0])
99*67e74705SXin Li    if diff:
100*67e74705SXin Li        print("Difference detected")
101*67e74705SXin Li        if args.verbose:
102*67e74705SXin Li            print(diff)
103*67e74705SXin Li        sys.exit(1)
104*67e74705SXin Li    else:
105*67e74705SXin Li        print("The same")
106