Debugging: Add code to print backtrace for guest on SIGSEGV
[nativeclient.git] / ncv / discmp.py
blob1faa55aeffaac5f845c717ffe1ce6f219d2baf17
1 #!/usr/bin/python
2 # Copyright 2008, Google Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 """discmp.py - compare disassembler output
34 This script compares output from the Native Client decoder to output
35 from GNU objdump to make sure they agree on instruction lengths. It
36 returns a zero status on success and non-zero on failure.
38 Usage:
39 discmp.py PLATFORM_BASE=<platform_base> <fileordir> <fileordir>
40 for example:
41 discmp.py PLATFORM_BASE=/home/who/proj/nacl/googleclient/native_client/scons-out/dbg-linux ncv/testdata/hanoi
43 """
45 import re
46 import sys
47 import os
49 def Usage():
50 print("""
51 USAGE:
52 %s PLATFORM_BASE=<platform_base> <fileordir> <fileordir>
54 For example:
55 %s PLATFORM_BASE=/home/who/proj/nacl/googleclient/native_client/scons-out/dbg-linux ncv/testdata/hanoi
57 Commonly this installer will be invoked from hammer as:
58 ./hammer decoder_test
59 In this case hammer will take care of the command line args.
60 """ % (sys.argv[0], sys.argv[0]))
61 sys.exit(-1)
63 def FindBinaries(PlatformBase):
64 ncdis = os.path.join(PlatformBase, "staging", "ncdis")
65 # Someday if nacl-objdump gets more permissive we can use it
66 # objdump = os.path.join(nacl.SDK_BASE, "bin", "nacl-objdump")
67 objdump = "/usr/bin/objdump"
68 if not os.path.isfile(ncdis):
69 print "Error: could not find", ncdis
70 sys.exit(-1)
71 if not os.path.isfile(objdump):
72 print "Error: could not find", objdump
73 sys.exit(-1)
74 return ncdis, objdump
76 class DisFile(object):
77 """Processes variants of disassembler output from various sources,
78 providing a list of instruction lengths, based on addresses."""
80 def __init__(self, stream):
81 self.fd = stream
82 self.lastline = None
83 self.lastaddr = None
84 self.thisaddr = None
85 self.thisline = None
86 # note we ignore lines that have only continuation bytes;
87 # no opcode
88 self.dislinefmt = re.compile("\s*([0-9a-f]+):\t(.*\t+.*)")
89 self.data16fmt = re.compile("\s*([0-9a-f]+):\s+66\s+data16")
90 self.waitfmt = re.compile("\s*([0-9a-f]+):\s+9b\s+[f]?wait")
92 def NextDisInst(self):
93 while (True):
94 line = self.fd.readline()
95 if line == "": return 0, None
96 match = self.data16fmt.match(line)
97 if (match):
98 addr = match.group(1)
99 line = self.fd.readline()
100 match = self.dislinefmt.match(line)
101 return (int(addr, 16),
102 " " + addr + ":\t60 " + match.group(2) + "\n")
103 match = self.waitfmt.match(line)
104 if (match):
105 addr = match.group(1)
106 line = self.fd.readline()
107 match = self.dislinefmt.match(line)
108 return (int(addr, 16),
109 " " + addr + ":\t9b " + match.group(2) + "\n")
110 match = self.dislinefmt.match(line)
111 if (match):
112 return int(match.group(1), 16), line
114 def NextInst(self):
115 if self.lastaddr is None:
116 self.lastaddr, self.lastline = self.NextDisInst()
117 else:
118 self.lastaddr = self.thisaddr
119 self.lastline = self.thisline
120 self.thisaddr, self.thisline = self.NextDisInst()
121 if self.thisline is None:
122 # don't know how long the last line was, so just return 1
123 return (1, self.lastline)
124 if self.lastline is None:
125 return 0, None
126 else:
127 return (self.thisaddr - self.lastaddr, self.lastline)
129 def IsElfBinary(fname):
130 fd = open(fname)
131 iselfbinary = fd.readline().startswith("\x7fELF")
132 fd.close()
133 return iselfbinary
135 def DoOneFile(fname, ncdis, objdump):
136 if not IsElfBinary(fname):
137 print("Error:", fname, "is not an ELF binary\nContinuing...")
138 return
139 df1 = DisFile(os.popen(objdump + " -dz " + fname))
140 df2 = DisFile(os.popen(ncdis + " " + fname))
141 instcount = 0
142 while (1):
143 instcount += 1
144 len1, line1 = df1.NextInst()
145 len2, line2 = df2.NextInst()
146 if line1 is None: break
147 if line2 is None: break
148 if (len1 != len2):
149 sys.stdout.write("ERROR: inst length mistmatch %d != %d\n" %
150 (len1, len2))
151 sys.stdout.write(line1)
152 sys.stdout.write(line2)
153 sys.exit(-1)
154 if line1 or line2:
155 sys.stdout.write("ERROR: disasm output is different lengths\n")
156 sys.exit(-1)
157 sys.stdout.write("%s: %d instructions; 0 errors!\n"
158 % (fname, instcount))
160 def DoOneFileOrDir(name, ncdis, objdump):
161 if os.path.isfile(name): DoOneFile(name, ncdis, objdump)
162 elif os.path.isdir(name):
163 for dirpath, dirlist, filelist in os.walk(name):
164 for fname in filelist:
165 DoOneFile(os.path.join(dirpath, fname), ncdis, objdump)
166 else: print "invalid argument", name
168 def ParseArgv():
169 files = []
170 args = {}
171 print "***ARGV:"
172 for arg in sys.argv[1:]:
173 print "*** ", arg
174 if arg.find("=") > 0:
175 name, value = arg.split("=")
176 print("%s=%s" % (name, value))
177 args[name] = value
178 else:
179 files.append(arg)
180 # require MODE and PLATFORM_BASE to be set
181 if "PLATFORM_BASE" not in args: Usage()
182 return args, files
184 def main():
185 args, files = ParseArgv()
186 ncdis, objdump = FindBinaries(args["PLATFORM_BASE"])
187 for name in files:
188 DoOneFileOrDir(name, ncdis, objdump)
189 sys.exit(0)
191 if '__main__' == __name__:
192 main()