const T *data() const {
return data_;
}
+ T *data() {
+ return data_;
+ }
uptr capacity() const {
return capacity_;
}
UpdateModuleNameVec(caller_pc, range_beg, range_end);
}
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return pc;
+ static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+ if (counter > kMaxCounter)
+ counter = kMaxCounter;
+ CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+ return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return bundle;
+ return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return 0;
+ return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
+}
+
// If guard is negative, atomically set it to -guard and store the PC in
// pc_array.
void CoverageData::Add(uptr pc, u32 *guard) {
return; // May happen after fork when pc_array_index becomes 0.
CHECK_LT(idx * sizeof(uptr),
atomic_load(&pc_array_size, memory_order_acquire));
- pc_array[idx] = pc;
- atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ pc_array[idx] = BundlePcAndCounter(pc, counter);
}
// Registers a pair caller=>callee.
for (uptr i = 0, n = size(); i < n; i++) {
const char *module_name = "<unknown>";
uptr module_address = 0;
- sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name,
+ sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
&module_address);
out.append("%s 0x%zx\n", module_name, module_address);
}
CHECK_LE(r.beg, r.end);
CHECK_LE(r.end, size());
for (uptr i = r.beg; i < r.end; i++) {
- uptr pc = data()[i];
+ uptr pc = UnbundlePc(pc_array[i]);
out[i] = pc ? '1' : '0';
if (pc)
n_set_bits++;
CHECK_LE(r.end, size());
const char *module_name = "<unknown>";
for (uptr i = r.beg; i < r.end; i++) {
- uptr pc = data()[i];
+ uptr pc = UnbundlePc(pc_array[i]);
+ uptr counter = UnbundleCounter(pc_array[i]);
if (!pc) continue; // Not visited.
uptr offset = 0;
sym->GetModuleNameAndOffsetForPC(pc, &module_name, &offset);
- offsets.push_back(offset);
+ offsets.push_back(BundlePcAndCounter(offset, counter));
}
+
+ SortArray(offsets.data(), offsets.size());
+ for (uptr i = 0; i < offsets.size(); i++)
+ offsets[i] = UnbundlePc(offsets[i]);
+
module_name = StripModuleName(r.name);
if (cov_sandboxed) {
if (cov_fd >= 0) {
bool, coverage, false,
"If set, coverage information will be dumped at program shutdown (if the "
"coverage instrumentation was enabled at compile time).")
-// On by default, but works only if coverage == true.
COMMON_FLAG(bool, coverage_pcs, true,
"If set (and if 'coverage' is set too), the coverage information "
"will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+ "If true, the PCs will be dumped in the order they've"
+ " appeared during the execution.")
COMMON_FLAG(bool, coverage_bitset, false,
"If set (and if 'coverage' is set too), the coverage information "
"will also be dumped as a bitset to a separate file.")
f.seek(0, 2)
size = f.tell()
f.seek(0, 0)
- s = set(array.array(TypeCodeForBits(bits), f.read(size)))
+ s = array.array(TypeCodeForBits(bits), f.read(size))
print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size * 8 / bits, path)
return s
def Merge(files, bits):
s = set()
for f in files:
- s = s.union(ReadOneFile(f, bits))
+ s = s.union(set(ReadOneFile(f, bits)))
print >> sys.stderr, "%s: %d files merged; %d PCs total" % \
(prog_name, len(files), len(s))
return sorted(s)
def PrintFiles(files, bits):
- s = Merge(files, bits)
+ if len(files) > 1:
+ s = Merge(files, bits)
+ else: # If there is just on file, print the PCs in order.
+ s = ReadOneFile(files[0], bits)
for i in s:
print "0x%x" % i
--- /dev/null
+// Test coverage_order_pcs=1 flag which orders the PCs by their appearance.
+// RUN: DIR=%T/coverage-order-pcs
+// RUN: rm -rf $DIR
+// RUN: mkdir $DIR
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %t
+// RUN: mv $DIR/*sancov $DIR/A
+
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %t 1
+// RUN: mv $DIR/*sancov $DIR/B
+
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %t
+// RUN: mv $DIR/*sancov $DIR/C
+
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %t 1
+// RUN: mv $DIR/*sancov $DIR/D
+//
+// RUN: (%sancov print $DIR/A; %sancov print $DIR/B; %sancov print $DIR/C; %sancov print $DIR/D) | FileCheck %s
+//
+// RUN: rm -rf $DIR
+// Ordering works only in 64-bit mode for now.
+// REQUIRES: asan-64-bits
+#include <stdio.h>
+
+void foo() { fprintf(stderr, "FOO\n"); }
+void bar() { fprintf(stderr, "BAR\n"); }
+
+int main(int argc, char **argv) {
+ if (argc == 2) {
+ foo();
+ bar();
+ } else {
+ bar();
+ foo();
+ }
+}
+
+// Run A: no ordering
+// CHECK: [[FOO:0x[0-9a-f]*]]
+// CHECK-NEXT: [[BAR:0x[0-9a-f]*]]
+// CHECK-NEXT: [[MAIN:0x[0-9a-f]*]]
+//
+// Run B: still no ordering
+// CHECK-NEXT: [[FOO]]
+// CHECK-NEXT: [[BAR]]
+// CHECK-NEXT: [[MAIN]]
+//
+// Run C: MAIN, BAR, FOO
+// CHECK-NEXT: [[MAIN]]
+// CHECK-NEXT: [[BAR]]
+// CHECK-NEXT: [[FOO]]
+//
+// Run D: MAIN, FOO, BAR
+// CHECK-NEXT: [[MAIN]]
+// CHECK-NEXT: [[FOO]]
+// CHECK-NEXT: [[BAR]]