diff options
5 files changed, 106 insertions, 10 deletions
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h index 1b720aa9cc9..d55bfef0356 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -445,6 +445,9 @@ class InternalMmapVectorNoCtor { const T *data() const { return data_; } + T *data() { + return data_; + } uptr capacity() const { return capacity_; } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index 29f8e49cc61..6ef8bf48754 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -352,6 +352,32 @@ void CoverageData::InitializeGuards(s32 *guards, uptr n, 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) { @@ -367,8 +393,8 @@ 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. @@ -555,7 +581,7 @@ void CoverageData::DumpTrace() { 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); } @@ -681,7 +707,7 @@ void CoverageData::DumpAsBitSet() { 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++; @@ -711,12 +737,18 @@ void CoverageData::DumpOffsets() { 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) { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc index 983af072fb8..884602f7955 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc @@ -111,10 +111,12 @@ COMMON_FLAG( 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.") diff --git a/compiler-rt/lib/sanitizer_common/scripts/sancov.py b/compiler-rt/lib/sanitizer_common/scripts/sancov.py index 1614877ab5e..443521633ca 100755 --- a/compiler-rt/lib/sanitizer_common/scripts/sancov.py +++ b/compiler-rt/lib/sanitizer_common/scripts/sancov.py @@ -30,20 +30,23 @@ def ReadOneFile(path, bits): 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 diff --git a/compiler-rt/test/asan/TestCases/Linux/coverage-order-pcs.cc b/compiler-rt/test/asan/TestCases/Linux/coverage-order-pcs.cc new file mode 100644 index 00000000000..4377b8f00c6 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Linux/coverage-order-pcs.cc @@ -0,0 +1,56 @@ +// 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]] |