1 //===-- sanitizer_coverage.cc ---------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
9 // This file implements run-time support for a poor man's coverage tool.
11 // Compiler instrumentation:
12 // For every interesting basic block the compiler injects the following code:
17 // It's fine to call __sanitizer_cov more than once for a given block.
20 // - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC).
21 // - __sanitizer_cov_dump: dump the coverage data to disk.
22 // For every module of the current process that has coverage data
23 // this will create a file module_name.PID.sancov. The file format is simple:
24 // it's just a sorted sequence of 4-byte offsets in the module.
26 // Eventually, this coverage implementation should be obsoleted by a more
27 // powerful general purpose Clang/LLVM coverage instrumentation.
28 // Consider this implementation as prototype.
30 // FIXME: support (or at least test with) dlclose.
31 //===----------------------------------------------------------------------===//
33 #include "sanitizer_allocator_internal.h"
34 #include "sanitizer_common.h"
35 #include "sanitizer_libc.h"
36 #include "sanitizer_mutex.h"
37 #include "sanitizer_procmaps.h"
38 #include "sanitizer_stacktrace.h"
39 #include "sanitizer_flags.h"
41 atomic_uint32_t dump_once_guard
; // Ensure that CovDump runs only once.
43 // pc_array is the array containing the covered PCs.
44 // To make the pc_array thread- and async-signal-safe it has to be large enough.
45 // 128M counters "ought to be enough for anybody" (4M on 32-bit).
46 // pc_array is allocated with MmapNoReserveOrDie and so it uses only as
47 // much RAM as it really needs.
48 static const uptr kPcArraySize
= FIRST_32_SECOND_64(1 << 22, 1 << 27);
49 static uptr
*pc_array
;
50 static atomic_uintptr_t pc_array_index
;
52 static bool cov_sandboxed
= false;
53 static int cov_fd
= kInvalidFd
;
54 static unsigned int cov_max_block_size
= 0;
56 namespace __sanitizer
{
58 // Simply add the pc into the vector under lock. If the function is called more
59 // than once for a given PC it will be inserted multiple times, which is fine.
60 static void CovAdd(uptr pc
) {
61 if (!pc_array
) return;
62 uptr idx
= atomic_fetch_add(&pc_array_index
, 1, memory_order_relaxed
);
63 CHECK_LT(idx
, kPcArraySize
);
68 pc_array
= reinterpret_cast<uptr
*>(
69 MmapNoReserveOrDie(sizeof(uptr
) * kPcArraySize
, "CovInit"));
72 static inline bool CompareLess(const uptr
&a
, const uptr
&b
) {
76 // Block layout for packed file format: header, followed by module name (no
77 // trailing zero), followed by data blob.
80 unsigned int module_name_length
;
81 unsigned int data_length
;
84 static void CovWritePacked(int pid
, const char *module
, const void *blob
,
85 unsigned int blob_size
) {
87 unsigned module_name_length
= internal_strlen(module
);
88 CovHeader header
= {pid
, module_name_length
, blob_size
};
90 if (cov_max_block_size
== 0) {
91 // Writing to a file. Just go ahead.
92 internal_write(cov_fd
, &header
, sizeof(header
));
93 internal_write(cov_fd
, module
, module_name_length
);
94 internal_write(cov_fd
, blob
, blob_size
);
96 // Writing to a socket. We want to split the data into appropriately sized
98 InternalScopedBuffer
<char> block(cov_max_block_size
);
99 CHECK_EQ((uptr
)block
.data(), (uptr
)(CovHeader
*)block
.data());
100 uptr header_size_with_module
= sizeof(header
) + module_name_length
;
101 CHECK_LT(header_size_with_module
, cov_max_block_size
);
102 unsigned int max_payload_size
=
103 cov_max_block_size
- header_size_with_module
;
104 char *block_pos
= block
.data();
105 internal_memcpy(block_pos
, &header
, sizeof(header
));
106 block_pos
+= sizeof(header
);
107 internal_memcpy(block_pos
, module
, module_name_length
);
108 block_pos
+= module_name_length
;
109 char *block_data_begin
= block_pos
;
110 char *blob_pos
= (char *)blob
;
111 while (blob_size
> 0) {
112 unsigned int payload_size
= Min(blob_size
, max_payload_size
);
113 blob_size
-= payload_size
;
114 internal_memcpy(block_data_begin
, blob_pos
, payload_size
);
115 blob_pos
+= payload_size
;
116 ((CovHeader
*)block
.data())->data_length
= payload_size
;
117 internal_write(cov_fd
, block
.data(),
118 header_size_with_module
+ payload_size
);
123 // Dump the coverage on disk.
124 static void CovDump() {
125 if (!common_flags()->coverage
) return;
126 #if !SANITIZER_WINDOWS
127 if (atomic_fetch_add(&dump_once_guard
, 1, memory_order_relaxed
))
129 uptr size
= atomic_load(&pc_array_index
, memory_order_relaxed
);
130 InternalSort(&pc_array
, size
, CompareLess
);
131 InternalMmapVector
<u32
> offsets(size
);
132 const uptr
*vb
= pc_array
;
133 const uptr
*ve
= vb
+ size
;
134 MemoryMappingLayout
proc_maps(/*cache_enabled*/true);
135 uptr mb
, me
, off
, prot
;
136 InternalScopedBuffer
<char> module(4096);
137 InternalScopedBuffer
<char> path(4096 * 2);
139 proc_maps
.Next(&mb
, &me
, &off
, module
.data(), module
.size(), &prot
);
141 if ((prot
& MemoryMappingLayout::kProtectionExecute
) == 0)
143 while (vb
< ve
&& *vb
< mb
) vb
++;
147 const uptr
*old_vb
= vb
;
149 for (; vb
< ve
&& *vb
< me
; vb
++) {
150 uptr diff
= *vb
- (i
? mb
: 0) + off
;
151 CHECK_LE(diff
, 0xffffffffU
);
152 offsets
.push_back(static_cast<u32
>(diff
));
154 char *module_name
= StripModuleName(module
.data());
156 CovWritePacked(internal_getpid(), module_name
, offsets
.data(),
157 offsets
.size() * sizeof(u32
));
158 VReport(1, " CovDump: %zd PCs written to packed file\n", vb
- old_vb
);
160 // One file per module per process.
161 internal_snprintf((char *)path
.data(), path
.size(), "%s.%zd.sancov",
162 module_name
, internal_getpid());
163 uptr fd
= OpenFile(path
.data(), true);
164 if (internal_iserror(fd
)) {
165 Report(" CovDump: failed to open %s for writing\n", path
.data());
167 internal_write(fd
, offsets
.data(), offsets
.size() * sizeof(u32
));
169 VReport(1, " CovDump: %s: %zd PCs written\n", path
.data(),
173 InternalFree(module_name
);
177 internal_close(cov_fd
);
178 #endif // !SANITIZER_WINDOWS
181 static void OpenPackedFileForWriting() {
182 CHECK(cov_fd
== kInvalidFd
);
183 InternalScopedBuffer
<char> path(1024);
184 internal_snprintf((char *)path
.data(), path
.size(), "%zd.sancov.packed",
186 uptr fd
= OpenFile(path
.data(), true);
187 if (internal_iserror(fd
)) {
188 Report(" Coverage: failed to open %s for writing\n", path
.data());
194 void CovPrepareForSandboxing(__sanitizer_sandbox_arguments
*args
) {
196 if (!common_flags()->coverage
) return;
197 cov_sandboxed
= args
->coverage_sandboxed
;
198 if (!cov_sandboxed
) return;
199 cov_fd
= args
->coverage_fd
;
200 cov_max_block_size
= args
->coverage_max_block_size
;
202 // Pre-open the file now. The sandbox won't allow us to do it later.
203 OpenPackedFileForWriting();
206 } // namespace __sanitizer
209 SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_cov() {
210 CovAdd(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
212 SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_cov_dump() { CovDump(); }
213 SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_cov_init() { CovInit(); }