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 43atomic_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 async-signal-safe it has to be large enough. 47// 128M counters "ought to be enough for anybody" (4M on 32-bit). 48 49// With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file. 50// In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping() 51// dump current memory layout to another file. 52 53static bool cov_sandboxed = false; 54static int cov_fd = kInvalidFd; 55static unsigned int cov_max_block_size = 0; 56 57namespace __sanitizer { 58 59class CoverageData { 60 public: 61 void Init(); 62 void BeforeFork(); 63 void AfterFork(int child_pid); 64 void Extend(uptr npcs); 65 void Add(uptr pc); 66 67 uptr *data(); 68 uptr size(); 69 70 private: 71 // Maximal size pc array may ever grow. 72 // We MmapNoReserve this space to ensure that the array is contiguous. 73 static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); 74 // The amount file mapping for the pc array is grown by. 75 static const uptr kPcArrayMmapSize = 64 * 1024; 76 77 // pc_array is allocated with MmapNoReserveOrDie and so it uses only as 78 // much RAM as it really needs. 79 uptr *pc_array; 80 // Index of the first available pc_array slot. 81 atomic_uintptr_t pc_array_index; 82 // Array size. 83 atomic_uintptr_t pc_array_size; 84 // Current file mapped size of the pc array. 85 uptr pc_array_mapped_size; 86 // Descriptor of the file mapped pc array. 87 int pc_fd; 88 StaticSpinMutex mu; 89 90 void DirectOpen(); 91 void ReInit(); 92}; 93 94static CoverageData coverage_data; 95 96void CoverageData::DirectOpen() { 97 InternalScopedString path(1024); 98 internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", 99 common_flags()->coverage_dir, internal_getpid()); 100 pc_fd = OpenFile(path.data(), true); 101 if (internal_iserror(pc_fd)) { 102 Report(" Coverage: failed to open %s for writing\n", path.data()); 103 Die(); 104 } 105 106 pc_array_mapped_size = 0; 107 CovUpdateMapping(); 108} 109 110void CoverageData::Init() { 111 pc_array = reinterpret_cast<uptr *>( 112 MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); 113 pc_fd = kInvalidFd; 114 if (common_flags()->coverage_direct) { 115 atomic_store(&pc_array_size, 0, memory_order_relaxed); 116 atomic_store(&pc_array_index, 0, memory_order_relaxed); 117 } else { 118 atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); 119 atomic_store(&pc_array_index, 0, memory_order_relaxed); 120 } 121} 122 123void CoverageData::ReInit() { 124 internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); 125 if (pc_fd != kInvalidFd) internal_close(pc_fd); 126 if (common_flags()->coverage_direct) { 127 // In memory-mapped mode we must extend the new file to the known array 128 // size. 129 uptr size = atomic_load(&pc_array_size, memory_order_relaxed); 130 Init(); 131 if (size) Extend(size); 132 } else { 133 Init(); 134 } 135} 136 137void CoverageData::BeforeFork() { 138 mu.Lock(); 139} 140 141void CoverageData::AfterFork(int child_pid) { 142 // We are single-threaded so it's OK to release the lock early. 143 mu.Unlock(); 144 if (child_pid == 0) ReInit(); 145} 146 147// Extend coverage PC array to fit additional npcs elements. 148void CoverageData::Extend(uptr npcs) { 149 if (!common_flags()->coverage_direct) return; 150 SpinMutexLock l(&mu); 151 152 if (pc_fd == kInvalidFd) DirectOpen(); 153 CHECK_NE(pc_fd, kInvalidFd); 154 155 uptr size = atomic_load(&pc_array_size, memory_order_relaxed); 156 size += npcs * sizeof(uptr); 157 158 if (size > pc_array_mapped_size) { 159 uptr new_mapped_size = pc_array_mapped_size; 160 while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize; 161 162 // Extend the file and map the new space at the end of pc_array. 163 uptr res = internal_ftruncate(pc_fd, new_mapped_size); 164 int err; 165 if (internal_iserror(res, &err)) { 166 Printf("failed to extend raw coverage file: %d\n", err); 167 Die(); 168 } 169 void *p = MapWritableFileToMemory(pc_array + pc_array_mapped_size, 170 new_mapped_size - pc_array_mapped_size, 171 pc_fd, pc_array_mapped_size); 172 CHECK_EQ(p, pc_array + pc_array_mapped_size); 173 pc_array_mapped_size = new_mapped_size; 174 } 175 176 atomic_store(&pc_array_size, size, memory_order_release); 177} 178 179// Simply add the pc into the vector under lock. If the function is called more 180// than once for a given PC it will be inserted multiple times, which is fine. 181void CoverageData::Add(uptr pc) { 182 if (!pc_array) return; 183 uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); 184 CHECK_LT(idx * sizeof(uptr), 185 atomic_load(&pc_array_size, memory_order_acquire)); 186 pc_array[idx] = pc; 187} 188 189uptr *CoverageData::data() { 190 return pc_array; 191} 192 193uptr CoverageData::size() { 194 return atomic_load(&pc_array_index, memory_order_relaxed); 195} 196 197// Block layout for packed file format: header, followed by module name (no 198// trailing zero), followed by data blob. 199struct CovHeader { 200 int pid; 201 unsigned int module_name_length; 202 unsigned int data_length; 203}; 204 205static void CovWritePacked(int pid, const char *module, const void *blob, 206 unsigned int blob_size) { 207 if (cov_fd < 0) return; 208 unsigned module_name_length = internal_strlen(module); 209 CovHeader header = {pid, module_name_length, blob_size}; 210 211 if (cov_max_block_size == 0) { 212 // Writing to a file. Just go ahead. 213 internal_write(cov_fd, &header, sizeof(header)); 214 internal_write(cov_fd, module, module_name_length); 215 internal_write(cov_fd, blob, blob_size); 216 } else { 217 // Writing to a socket. We want to split the data into appropriately sized 218 // blocks. 219 InternalScopedBuffer<char> block(cov_max_block_size); 220 CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data()); 221 uptr header_size_with_module = sizeof(header) + module_name_length; 222 CHECK_LT(header_size_with_module, cov_max_block_size); 223 unsigned int max_payload_size = 224 cov_max_block_size - header_size_with_module; 225 char *block_pos = block.data(); 226 internal_memcpy(block_pos, &header, sizeof(header)); 227 block_pos += sizeof(header); 228 internal_memcpy(block_pos, module, module_name_length); 229 block_pos += module_name_length; 230 char *block_data_begin = block_pos; 231 char *blob_pos = (char *)blob; 232 while (blob_size > 0) { 233 unsigned int payload_size = Min(blob_size, max_payload_size); 234 blob_size -= payload_size; 235 internal_memcpy(block_data_begin, blob_pos, payload_size); 236 blob_pos += payload_size; 237 ((CovHeader *)block.data())->data_length = payload_size; 238 internal_write(cov_fd, block.data(), 239 header_size_with_module + payload_size); 240 } 241 } 242} 243 244// If packed = false: <name>.<pid>.<sancov> (name = module name). 245// If packed = true and name == 0: <pid>.<sancov>.<packed>. 246// If packed = true and name != 0: <name>.<sancov>.<packed> (name is 247// user-supplied). 248static int CovOpenFile(bool packed, const char* name) { 249 InternalScopedBuffer<char> path(1024); 250 if (!packed) { 251 CHECK(name); 252 Printf("%s\n", common_flags()->coverage_dir); 253 internal_snprintf((char *)path.data(), path.size(), "%s/%s.%zd.sancov", 254 common_flags()->coverage_dir, name, internal_getpid()); 255 } else { 256 if (!name) 257 internal_snprintf((char *)path.data(), path.size(), 258 "%s/%zd.sancov.packed", common_flags()->coverage_dir, 259 internal_getpid()); 260 else 261 internal_snprintf((char *)path.data(), path.size(), "%s/%s.sancov.packed", 262 common_flags()->coverage_dir, name); 263 } 264 uptr fd = OpenFile(path.data(), true); 265 if (internal_iserror(fd)) { 266 Report(" SanitizerCoverage: failed to open %s for writing\n", path.data()); 267 return -1; 268 } 269 return fd; 270} 271 272// Dump the coverage on disk. 273static void CovDump() { 274 if (!common_flags()->coverage || common_flags()->coverage_direct) return; 275#if !SANITIZER_WINDOWS 276 if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) 277 return; 278 uptr size = coverage_data.size(); 279 InternalMmapVector<u32> offsets(size); 280 uptr *vb = coverage_data.data(); 281 uptr *ve = vb + size; 282 SortArray(vb, size); 283 MemoryMappingLayout proc_maps(/*cache_enabled*/true); 284 uptr mb, me, off, prot; 285 InternalScopedBuffer<char> module(4096); 286 InternalScopedBuffer<char> path(4096 * 2); 287 for (int i = 0; 288 proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); 289 i++) { 290 if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) 291 continue; 292 while (vb < ve && *vb < mb) vb++; 293 if (vb >= ve) break; 294 if (*vb < me) { 295 offsets.clear(); 296 const uptr *old_vb = vb; 297 CHECK_LE(off, *vb); 298 for (; vb < ve && *vb < me; vb++) { 299 uptr diff = *vb - (i ? mb : 0) + off; 300 CHECK_LE(diff, 0xffffffffU); 301 offsets.push_back(static_cast<u32>(diff)); 302 } 303 char *module_name = StripModuleName(module.data()); 304 if (cov_sandboxed) { 305 if (cov_fd >= 0) { 306 CovWritePacked(internal_getpid(), module_name, offsets.data(), 307 offsets.size() * sizeof(u32)); 308 VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb); 309 } 310 } else { 311 // One file per module per process. 312 internal_snprintf((char *)path.data(), path.size(), "%s/%s.%zd.sancov", 313 common_flags()->coverage_dir, module_name, 314 internal_getpid()); 315 int fd = CovOpenFile(false /* packed */, module_name); 316 if (fd > 0) { 317 internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); 318 internal_close(fd); 319 VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), 320 vb - old_vb); 321 } 322 } 323 InternalFree(module_name); 324 } 325 } 326 if (cov_fd >= 0) 327 internal_close(cov_fd); 328#endif // !SANITIZER_WINDOWS 329} 330 331void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { 332 if (!args) return; 333 if (!common_flags()->coverage) return; 334 cov_sandboxed = args->coverage_sandboxed; 335 if (!cov_sandboxed) return; 336 cov_fd = args->coverage_fd; 337 cov_max_block_size = args->coverage_max_block_size; 338 if (cov_fd < 0) 339 // Pre-open the file now. The sandbox won't allow us to do it later. 340 cov_fd = CovOpenFile(true /* packed */, 0); 341} 342 343int MaybeOpenCovFile(const char *name) { 344 CHECK(name); 345 if (!common_flags()->coverage) return -1; 346 return CovOpenFile(true /* packed */, name); 347} 348 349void CovBeforeFork() { 350 coverage_data.BeforeFork(); 351} 352 353void CovAfterFork(int child_pid) { 354 coverage_data.AfterFork(child_pid); 355} 356 357} // namespace __sanitizer 358 359extern "C" { 360SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() { 361 coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC())); 362} 363SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } 364SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { 365 coverage_data.Init(); 366} 367SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(uptr npcs) { 368 if (!common_flags()->coverage || !common_flags()->coverage_direct) return; 369 if (SANITIZER_ANDROID) { 370 // dlopen/dlclose interceptors do not work on Android, so we rely on 371 // Extend() calls to update .sancov.map. 372 CovUpdateMapping(GET_CALLER_PC()); 373 } 374 coverage_data.Extend(npcs); 375} 376SANITIZER_INTERFACE_ATTRIBUTE 377sptr __sanitizer_maybe_open_cov_file(const char *name) { 378 return MaybeOpenCovFile(name); 379} 380} // extern "C" 381