]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //===-- sanitizer_coverage.cc ---------------------------------------------===// |
2 | // | |
3 | // The LLVM Compiler Infrastructure | |
4 | // | |
5 | // This file is distributed under the University of Illinois Open Source | |
6 | // License. See LICENSE.TXT for details. | |
7 | // | |
8 | //===----------------------------------------------------------------------===// | |
9 | // | |
10 | // Sanitizer Coverage. | |
11 | // This file implements run-time support for a poor man's coverage tool. | |
12 | // | |
13 | // Compiler instrumentation: | |
14 | // For every interesting basic block the compiler injects the following code: | |
15 | // if (*Guard) { | |
16 | // __sanitizer_cov(); | |
17 | // *Guard = 1; | |
18 | // } | |
19 | // It's fine to call __sanitizer_cov more than once for a given block. | |
20 | // | |
21 | // Run-time: | |
22 | // - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC). | |
23 | // - __sanitizer_cov_dump: dump the coverage data to disk. | |
24 | // For every module of the current process that has coverage data | |
25 | // this will create a file module_name.PID.sancov. The file format is simple: | |
26 | // it's just a sorted sequence of 4-byte offsets in the module. | |
27 | // | |
28 | // Eventually, this coverage implementation should be obsoleted by a more | |
29 | // powerful general purpose Clang/LLVM coverage instrumentation. | |
30 | // Consider this implementation as prototype. | |
31 | // | |
32 | // FIXME: support (or at least test with) dlclose. | |
33 | //===----------------------------------------------------------------------===// | |
34 | ||
35 | #include "sanitizer_allocator_internal.h" | |
36 | #include "sanitizer_common.h" | |
37 | #include "sanitizer_libc.h" | |
38 | #include "sanitizer_mutex.h" | |
39 | #include "sanitizer_procmaps.h" | |
40 | #include "sanitizer_stacktrace.h" | |
41 | #include "sanitizer_flags.h" | |
42 | ||
43 | atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. | |
44 | ||
45 | // pc_array is the array containing the covered PCs. | |
46 | // To make the pc_array thread- and AS- safe it has to be large enough. | |
47 | // 128M counters "ought to be enough for anybody" (4M on 32-bit). | |
48 | // pc_array is allocated with MmapNoReserveOrDie and so it uses only as | |
49 | // much RAM as it really needs. | |
50 | static const uptr kPcArraySize = FIRST_32_SECOND_64(1 << 22, 1 << 27); | |
51 | static uptr *pc_array; | |
52 | static atomic_uintptr_t pc_array_index; | |
53 | ||
54 | namespace __sanitizer { | |
55 | ||
56 | // Simply add the pc into the vector under lock. If the function is called more | |
57 | // than once for a given PC it will be inserted multiple times, which is fine. | |
58 | static void CovAdd(uptr pc) { | |
59 | if (!pc_array) return; | |
60 | uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); | |
61 | CHECK_LT(idx, kPcArraySize); | |
62 | pc_array[idx] = pc; | |
63 | } | |
64 | ||
65 | void CovInit() { | |
66 | pc_array = reinterpret_cast<uptr *>( | |
67 | MmapNoReserveOrDie(sizeof(uptr) * kPcArraySize, "CovInit")); | |
68 | } | |
69 | ||
70 | static inline bool CompareLess(const uptr &a, const uptr &b) { | |
71 | return a < b; | |
72 | } | |
73 | ||
74 | // Dump the coverage on disk. | |
75 | void CovDump() { | |
76 | #if !SANITIZER_WINDOWS | |
77 | if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) | |
78 | return; | |
79 | uptr size = atomic_load(&pc_array_index, memory_order_relaxed); | |
80 | InternalSort(&pc_array, size, CompareLess); | |
81 | InternalMmapVector<u32> offsets(size); | |
82 | const uptr *vb = pc_array; | |
83 | const uptr *ve = vb + size; | |
84 | MemoryMappingLayout proc_maps(/*cache_enabled*/false); | |
85 | uptr mb, me, off, prot; | |
86 | InternalScopedBuffer<char> module(4096); | |
87 | InternalScopedBuffer<char> path(4096 * 2); | |
88 | for (int i = 0; | |
89 | proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); | |
90 | i++) { | |
91 | if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) | |
92 | continue; | |
93 | if (vb >= ve) break; | |
94 | if (mb <= *vb && *vb < me) { | |
95 | offsets.clear(); | |
96 | const uptr *old_vb = vb; | |
97 | CHECK_LE(off, *vb); | |
98 | for (; vb < ve && *vb < me; vb++) { | |
99 | uptr diff = *vb - (i ? mb : 0) + off; | |
100 | CHECK_LE(diff, 0xffffffffU); | |
101 | offsets.push_back(static_cast<u32>(diff)); | |
102 | } | |
103 | char *module_name = StripModuleName(module.data()); | |
104 | internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov", | |
105 | module_name, internal_getpid()); | |
106 | InternalFree(module_name); | |
107 | uptr fd = OpenFile(path.data(), true); | |
108 | if (internal_iserror(fd)) { | |
109 | Report(" CovDump: failed to open %s for writing\n", path.data()); | |
110 | } else { | |
111 | internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); | |
112 | internal_close(fd); | |
113 | VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb); | |
114 | } | |
115 | } | |
116 | } | |
117 | #endif // !SANITIZER_WINDOWS | |
118 | } | |
119 | ||
120 | } // namespace __sanitizer | |
121 | ||
122 | extern "C" { | |
123 | SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() { | |
124 | CovAdd(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC())); | |
125 | } | |
126 | SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } | |
127 | SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { CovInit(); } | |
128 | } // extern "C" |