blob: c6cc4ba0d260873f2310aef26fe695031f328ddf [file] [log] [blame]
Kostya Serebryanyb4150322013-11-15 11:51:08 +00001#!/usr/bin/env python
2# Merge or print the coverage data collected by asan's coverage.
3# Input files are sequences of 4-byte integers.
4# We need to merge these integers into a set and then
5# either print them (as hex) or dump them into another file.
6import array
Timur Iskhodzhanov882bc562015-04-01 14:46:10 +00007import bisect
8import glob
9import os.path
Sergey Matveev6cb47a082014-05-19 12:53:03 +000010import struct
Sergey Matveevc2de3462015-05-06 20:48:29 +000011import subprocess
Kostya Serebryanyb4150322013-11-15 11:51:08 +000012import sys
13
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000014prog_name = ""
Kostya Serebryanyb4150322013-11-15 11:51:08 +000015
16def Usage():
17 print >> sys.stderr, "Usage: \n" + \
Sergey Matveevc2de3462015-05-06 20:48:29 +000018 " " + prog_name + " merge FILE [FILE...] > OUTPUT\n" \
19 " " + prog_name + " print FILE [FILE...]\n" \
20 " " + prog_name + " unpack FILE [FILE...]\n" \
21 " " + prog_name + " rawunpack FILE [FILE ...]\n" \
22 " " + prog_name + " missing BINARY < LIST_OF_PCS\n"
Kostya Serebryanyb4150322013-11-15 11:51:08 +000023 exit(1)
24
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000025def CheckBits(bits):
26 if bits != 32 and bits != 64:
Bill Seurer92760a32015-03-25 14:56:02 +000027 raise Exception("Wrong bitness: %d" % bits)
Kostya Serebryany9f1243e2015-03-17 22:09:19 +000028
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000029def TypeCodeForBits(bits):
30 CheckBits(bits)
31 return 'L' if bits == 64 else 'I'
32
Kostya Serebryanyeaec5b62015-03-19 19:52:30 +000033kMagic32SecondHalf = 0xFFFFFF32;
34kMagic64SecondHalf = 0xFFFFFF64;
35kMagicFirstHalf = 0xC0BFFFFF;
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000036
37def MagicForBits(bits):
38 CheckBits(bits)
Bill Seurer92760a32015-03-25 14:56:02 +000039 if sys.byteorder == 'little':
40 return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf]
41 else:
42 return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf]
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000043
Kostya Serebryanydcb54db2015-03-19 21:01:27 +000044def ReadMagicAndReturnBitness(f, path):
Kostya Serebryanyeaec5b62015-03-19 19:52:30 +000045 magic_bytes = f.read(8)
46 magic_words = struct.unpack('II', magic_bytes);
47 bits = 0
Bill Seurer92760a32015-03-25 14:56:02 +000048 idx = 1 if sys.byteorder == 'little' else 0
49 if magic_words[idx] == kMagicFirstHalf:
50 if magic_words[1-idx] == kMagic64SecondHalf:
Kostya Serebryanyeaec5b62015-03-19 19:52:30 +000051 bits = 64
Bill Seurer92760a32015-03-25 14:56:02 +000052 elif magic_words[1-idx] == kMagic32SecondHalf:
Kostya Serebryanyeaec5b62015-03-19 19:52:30 +000053 bits = 32
54 if bits == 0:
Kostya Serebryanydcb54db2015-03-19 21:01:27 +000055 raise Exception('Bad magic word in %s' % path)
Kostya Serebryanyeaec5b62015-03-19 19:52:30 +000056 return bits
57
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000058def ReadOneFile(path):
Sergey Matveev6cb47a082014-05-19 12:53:03 +000059 with open(path, mode="rb") as f:
60 f.seek(0, 2)
61 size = f.tell()
62 f.seek(0, 0)
Kostya Serebryanydcb54db2015-03-19 21:01:27 +000063 if size < 8:
64 raise Exception('File %s is short (< 8 bytes)' % path)
65 bits = ReadMagicAndReturnBitness(f, path)
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000066 size -= 8
Kostya Serebryanycba49d42015-03-18 00:23:44 +000067 s = array.array(TypeCodeForBits(bits), f.read(size))
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000068 print >>sys.stderr, "%s: read %d %d-bit PCs from %s" % (prog_name, size * 8 / bits, bits, path)
Kostya Serebryanyb4150322013-11-15 11:51:08 +000069 return s
70
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000071def Merge(files):
Kostya Serebryanyb4150322013-11-15 11:51:08 +000072 s = set()
73 for f in files:
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000074 s = s.union(set(ReadOneFile(f)))
Kostya Serebryanyb4150322013-11-15 11:51:08 +000075 print >> sys.stderr, "%s: %d files merged; %d PCs total" % \
76 (prog_name, len(files), len(s))
77 return sorted(s)
78
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000079def PrintFiles(files):
Kostya Serebryanycba49d42015-03-18 00:23:44 +000080 if len(files) > 1:
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000081 s = Merge(files)
Kostya Serebryanycba49d42015-03-18 00:23:44 +000082 else: # If there is just on file, print the PCs in order.
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000083 s = ReadOneFile(files[0])
Kostya Serebryanydcb54db2015-03-19 21:01:27 +000084 print >> sys.stderr, "%s: 1 file merged; %d PCs total" % \
85 (prog_name, len(s))
Kostya Serebryanyb4150322013-11-15 11:51:08 +000086 for i in s:
87 print "0x%x" % i
88
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000089def MergeAndPrint(files):
Kostya Serebryanyb4150322013-11-15 11:51:08 +000090 if sys.stdout.isatty():
91 Usage()
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000092 s = Merge(files)
93 bits = 32
Kostya Serebryany2d56aba2015-03-18 22:03:39 +000094 if max(s) > 0xFFFFFFFF:
95 bits = 64
Kostya Serebryanydcb54db2015-03-19 21:01:27 +000096 array.array('I', MagicForBits(bits)).tofile(sys.stdout)
Kostya Serebryany9f1243e2015-03-17 22:09:19 +000097 a = array.array(TypeCodeForBits(bits), s)
Kostya Serebryanyb4150322013-11-15 11:51:08 +000098 a.tofile(sys.stdout)
99
Sergey Matveev6cb47a082014-05-19 12:53:03 +0000100
101def UnpackOneFile(path):
102 with open(path, mode="rb") as f:
103 print >> sys.stderr, "%s: unpacking %s" % (prog_name, path)
104 while True:
105 header = f.read(12)
106 if not header: return
107 if len(header) < 12:
108 break
109 pid, module_length, blob_size = struct.unpack('iII', header)
110 module = f.read(module_length)
111 blob = f.read(blob_size)
112 assert(len(module) == module_length)
113 assert(len(blob) == blob_size)
114 extracted_file = "%s.%d.sancov" % (module, pid)
115 print >> sys.stderr, "%s: extracting %s" % \
116 (prog_name, extracted_file)
117 # The packed file may contain multiple blobs for the same pid/module
118 # pair. Append to the end of the file instead of overwriting.
119 with open(extracted_file, 'ab') as f2:
120 f2.write(blob)
121 # fail
122 raise Exception('Error reading file %s' % path)
123
124
125def Unpack(files):
126 for f in files:
127 UnpackOneFile(f)
128
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000129def UnpackOneRawFile(path, map_path):
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000130 mem_map = []
131 with open(map_path, mode="rt") as f_map:
132 print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path)
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000133 bits = int(f_map.readline())
134 if bits != 32 and bits != 64:
Kostya Serebryany9f1243e2015-03-17 22:09:19 +0000135 raise Exception('Wrong bits size in the map')
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000136 for line in f_map:
137 parts = line.rstrip().split()
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000138 mem_map.append((int(parts[0], 16),
139 int(parts[1], 16),
140 int(parts[2], 16),
Evgeniy Stepanov937afa12014-06-03 15:25:43 +0000141 ' '.join(parts[3:])))
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000142 mem_map.sort(key=lambda m : m[0])
143 mem_map_keys = [m[0] for m in mem_map]
144
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000145 with open(path, mode="rb") as f:
146 print >> sys.stderr, "%s: unpacking %s" % (prog_name, path)
147
148 f.seek(0, 2)
149 size = f.tell()
150 f.seek(0, 0)
Kostya Serebryany9f1243e2015-03-17 22:09:19 +0000151 pcs = array.array(TypeCodeForBits(bits), f.read(size))
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000152 mem_map_pcs = [[] for i in range(0, len(mem_map))]
153
154 for pc in pcs:
155 if pc == 0: continue
156 map_idx = bisect.bisect(mem_map_keys, pc) - 1
157 (start, end, base, module_path) = mem_map[map_idx]
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000158 assert pc >= start
159 if pc >= end:
160 print >> sys.stderr, "warning: %s: pc %x outside of any known mapping" % (prog_name, pc)
161 continue
162 mem_map_pcs[map_idx].append(pc - base)
163
164 for ((start, end, base, module_path), pc_list) in zip(mem_map, mem_map_pcs):
165 if len(pc_list) == 0: continue
166 assert path.endswith('.sancov.raw')
167 dst_path = module_path + '.' + os.path.basename(path)[:-4]
Evgeniy Stepanovb7238342014-12-25 16:03:24 +0000168 print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path)
Kostya Serebryany9f1243e2015-03-17 22:09:19 +0000169 arr = array.array(TypeCodeForBits(bits))
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000170 arr.fromlist(sorted(pc_list))
171 with open(dst_path, 'ab') as f2:
Kostya Serebryanydcb54db2015-03-19 21:01:27 +0000172 array.array('I', MagicForBits(bits)).tofile(f2)
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000173 arr.tofile(f2)
174
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000175def RawUnpack(files):
Evgeniy Stepanov567e5162014-05-27 12:37:52 +0000176 for f in files:
177 if not f.endswith('.sancov.raw'):
178 raise Exception('Unexpected raw file name %s' % f)
179 f_map = f[:-3] + 'map'
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000180 UnpackOneRawFile(f, f_map)
Sergey Matveev6cb47a082014-05-19 12:53:03 +0000181
Sergey Matveevc2de3462015-05-06 20:48:29 +0000182def GetInstrumentedPCs(binary):
183 cmd = "objdump -d %s | " \
184 "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(@plt\|\)>' | " \
185 "grep '^\s\+[0-9a-f]\+' -o" % binary
186 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
187 shell=True)
188 proc.stdin.close()
189 # The PCs we get from objdump are off by 4 bytes, as they point to the
190 # beginning of the callq instruction. Empirically this is true on x86 and
191 # x86_64.
192 return set(int(line.strip(), 16) + 4 for line in proc.stdout)
193
194def PrintMissing(binary):
195 if not os.path.isfile(binary):
196 raise Exception('File not found: %s' % binary)
197 instrumented = GetInstrumentedPCs(binary)
198 print >> sys.stderr, "%s: found %d instrumented PCs in %s" % (prog_name,
199 len(instrumented),
200 binary)
201 covered = set(int(line, 16) for line in sys.stdin)
202 print >> sys.stderr, "%s: read %d PCs from stdin" % (prog_name, len(covered))
203 missing = instrumented - covered
204 print >> sys.stderr, "%s: %d PCs missing from coverage" % (prog_name, len(missing))
205 if (len(missing) > len(instrumented) - len(covered)):
206 print >> sys.stderr, \
207 "%s: WARNING: stdin contains PCs not found in binary" % prog_name
208 for pc in sorted(missing):
209 print "0x%x" % pc
210
Kostya Serebryanyb4150322013-11-15 11:51:08 +0000211if __name__ == '__main__':
212 prog_name = sys.argv[0]
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000213 if len(sys.argv) <= 2:
Kostya Serebryanyb4150322013-11-15 11:51:08 +0000214 Usage();
Kostya Serebryany9f1243e2015-03-17 22:09:19 +0000215
Sergey Matveevc2de3462015-05-06 20:48:29 +0000216 if sys.argv[1] == "missing":
217 if len(sys.argv) != 3:
218 Usage()
219 PrintMissing(sys.argv[2])
220 exit(0)
221
Timur Iskhodzhanov882bc562015-04-01 14:46:10 +0000222 file_list = []
223 for f in sys.argv[2:]:
224 file_list += glob.glob(f)
225 if not file_list:
226 Usage()
227
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000228 if sys.argv[1] == "print":
Timur Iskhodzhanov882bc562015-04-01 14:46:10 +0000229 PrintFiles(file_list)
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000230 elif sys.argv[1] == "merge":
Timur Iskhodzhanov882bc562015-04-01 14:46:10 +0000231 MergeAndPrint(file_list)
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000232 elif sys.argv[1] == "unpack":
Timur Iskhodzhanov882bc562015-04-01 14:46:10 +0000233 Unpack(file_list)
Kostya Serebryany2d56aba2015-03-18 22:03:39 +0000234 elif sys.argv[1] == "rawunpack":
Timur Iskhodzhanov882bc562015-04-01 14:46:10 +0000235 RawUnpack(file_list)
Kostya Serebryanyb4150322013-11-15 11:51:08 +0000236 else:
237 Usage()