1 /*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ 2 |* 3 |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 |* See https://llvm.org/LICENSE.txt for license information. 5 |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 |* 7 |*===----------------------------------------------------------------------===*| 8 |* 9 |* This file implements the call back routines for the gcov profiling 10 |* instrumentation pass. Link against this library when running code through 11 |* the -insert-gcov-profiling LLVM pass. 12 |* 13 |* We emit files in a corrupt version of GCOV's "gcda" file format. These files 14 |* are only close enough that LCOV will happily parse them. Anything that lcov 15 |* ignores is missing. 16 |* 17 |* TODO: gcov is multi-process safe by having each exit open the existing file 18 |* and append to it. We'd like to achieve that and be thread-safe too. 19 |* 20 \*===----------------------------------------------------------------------===*/ 21 22 #if !defined(__Fuchsia__) 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdint.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #if defined(_WIN32) 32 #define WIN32_LEAN_AND_MEAN 33 #include <windows.h> 34 #include "WindowsMMap.h" 35 #else 36 #include <sys/file.h> 37 #include <sys/mman.h> 38 #include <sys/types.h> 39 #include <unistd.h> 40 #endif 41 42 #include "InstrProfiling.h" 43 #include "InstrProfilingUtil.h" 44 45 /* #define DEBUG_GCDAPROFILING */ 46 47 enum { 48 GCOV_DATA_MAGIC = 0x67636461, // "gcda" 49 50 GCOV_TAG_FUNCTION = 0x01000000, 51 GCOV_TAG_COUNTER_ARCS = 0x01a10000, 52 // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9. 53 GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, 54 GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, 55 }; 56 57 /* 58 * --- GCOV file format I/O primitives --- 59 */ 60 61 /* 62 * The current file name we're outputting. Used primarily for error logging. 63 */ 64 static char *filename = NULL; 65 66 /* 67 * The current file we're outputting. 68 */ 69 static FILE *output_file = NULL; 70 71 /* 72 * Buffer that we write things into. 73 */ 74 #define WRITE_BUFFER_SIZE (128 * 1024) 75 static unsigned char *write_buffer = NULL; 76 static uint64_t cur_buffer_size = 0; 77 static uint64_t cur_pos = 0; 78 static uint64_t file_size = 0; 79 static int new_file = 0; 80 static int gcov_version; 81 #if defined(_WIN32) 82 static HANDLE mmap_handle = NULL; 83 #endif 84 static int fd = -1; 85 86 typedef void (*fn_ptr)(); 87 88 typedef void* dynamic_object_id; 89 // The address of this variable identifies a given dynamic object. 90 static dynamic_object_id current_id; 91 #define CURRENT_ID (¤t_id) 92 93 struct fn_node { 94 dynamic_object_id id; 95 fn_ptr fn; 96 struct fn_node* next; 97 }; 98 99 struct fn_list { 100 struct fn_node *head, *tail; 101 }; 102 103 /* 104 * A list of functions to write out the data, shared between all dynamic objects. 105 */ 106 struct fn_list writeout_fn_list; 107 108 /* 109 * A list of reset functions, shared between all dynamic objects. 110 */ 111 struct fn_list reset_fn_list; 112 113 static void fn_list_insert(struct fn_list* list, fn_ptr fn) { 114 struct fn_node* new_node = malloc(sizeof(struct fn_node)); 115 new_node->fn = fn; 116 new_node->next = NULL; 117 new_node->id = CURRENT_ID; 118 119 if (!list->head) { 120 list->head = list->tail = new_node; 121 } else { 122 list->tail->next = new_node; 123 list->tail = new_node; 124 } 125 } 126 127 static void fn_list_remove(struct fn_list* list) { 128 struct fn_node* curr = list->head; 129 struct fn_node* prev = NULL; 130 struct fn_node* next = NULL; 131 132 while (curr) { 133 next = curr->next; 134 135 if (curr->id == CURRENT_ID) { 136 if (curr == list->head) { 137 list->head = next; 138 } 139 140 if (curr == list->tail) { 141 list->tail = prev; 142 } 143 144 if (prev) { 145 prev->next = next; 146 } 147 148 free(curr); 149 } else { 150 prev = curr; 151 } 152 153 curr = next; 154 } 155 } 156 157 static void resize_write_buffer(uint64_t size) { 158 if (!new_file) return; 159 size += cur_pos; 160 if (size <= cur_buffer_size) return; 161 size = (size - 1) / WRITE_BUFFER_SIZE + 1; 162 size *= WRITE_BUFFER_SIZE; 163 write_buffer = realloc(write_buffer, size); 164 cur_buffer_size = size; 165 } 166 167 static void write_bytes(const char *s, size_t len) { 168 resize_write_buffer(len); 169 memcpy(&write_buffer[cur_pos], s, len); 170 cur_pos += len; 171 } 172 173 static void write_32bit_value(uint32_t i) { 174 write_bytes((char*)&i, 4); 175 } 176 177 static void write_64bit_value(uint64_t i) { 178 // GCOV uses a lo-/hi-word format even on big-endian systems. 179 // See also GCOVBuffer::readInt64 in LLVM. 180 uint32_t lo = (uint32_t) i; 181 uint32_t hi = (uint32_t) (i >> 32); 182 write_32bit_value(lo); 183 write_32bit_value(hi); 184 } 185 186 static uint32_t read_32bit_value() { 187 uint32_t val; 188 189 if (new_file) 190 return (uint32_t)-1; 191 192 val = *(uint32_t*)&write_buffer[cur_pos]; 193 cur_pos += 4; 194 return val; 195 } 196 197 static uint64_t read_64bit_value() { 198 // GCOV uses a lo-/hi-word format even on big-endian systems. 199 // See also GCOVBuffer::readInt64 in LLVM. 200 uint32_t lo = read_32bit_value(); 201 uint32_t hi = read_32bit_value(); 202 return ((uint64_t)hi << 32) | ((uint64_t)lo); 203 } 204 205 static char *mangle_filename(const char *orig_filename) { 206 char *new_filename; 207 size_t prefix_len; 208 int prefix_strip; 209 const char *prefix = lprofGetPathPrefix(&prefix_strip, &prefix_len); 210 211 if (prefix == NULL) 212 return strdup(orig_filename); 213 214 new_filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); 215 lprofApplyPathPrefix(new_filename, orig_filename, prefix, prefix_len, 216 prefix_strip); 217 218 return new_filename; 219 } 220 221 static int map_file() { 222 fseek(output_file, 0L, SEEK_END); 223 file_size = ftell(output_file); 224 225 /* A size of 0 means the file has been created just now (possibly by another 226 * process in lock-after-open race condition). No need to mmap. */ 227 if (file_size == 0) 228 return -1; 229 230 #if defined(_WIN32) 231 HANDLE mmap_fd; 232 if (fd == -1) 233 mmap_fd = INVALID_HANDLE_VALUE; 234 else 235 mmap_fd = (HANDLE)_get_osfhandle(fd); 236 237 mmap_handle = CreateFileMapping(mmap_fd, NULL, PAGE_READWRITE, DWORD_HI(file_size), DWORD_LO(file_size), NULL); 238 if (mmap_handle == NULL) { 239 fprintf(stderr, "profiling: %s: cannot create file mapping: %lu\n", 240 filename, GetLastError()); 241 return -1; 242 } 243 244 write_buffer = MapViewOfFile(mmap_handle, FILE_MAP_WRITE, 0, 0, file_size); 245 if (write_buffer == NULL) { 246 fprintf(stderr, "profiling: %s: cannot map: %lu\n", filename, 247 GetLastError()); 248 CloseHandle(mmap_handle); 249 return -1; 250 } 251 #else 252 write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE, 253 MAP_FILE | MAP_SHARED, fd, 0); 254 if (write_buffer == (void *)-1) { 255 int errnum = errno; 256 fprintf(stderr, "profiling: %s: cannot map: %s\n", filename, 257 strerror(errnum)); 258 return -1; 259 } 260 #endif 261 262 return 0; 263 } 264 265 static void unmap_file() { 266 #if defined(_WIN32) 267 if (!FlushViewOfFile(write_buffer, file_size)) { 268 fprintf(stderr, "profiling: %s: cannot flush mapped view: %lu\n", filename, 269 GetLastError()); 270 } 271 272 if (!UnmapViewOfFile(write_buffer)) { 273 fprintf(stderr, "profiling: %s: cannot unmap mapped view: %lu\n", filename, 274 GetLastError()); 275 } 276 277 if (!CloseHandle(mmap_handle)) { 278 fprintf(stderr, "profiling: %s: cannot close file mapping handle: %lu\n", 279 filename, GetLastError()); 280 } 281 282 mmap_handle = NULL; 283 #else 284 if (munmap(write_buffer, file_size) == -1) { 285 int errnum = errno; 286 fprintf(stderr, "profiling: %s: cannot munmap: %s\n", filename, 287 strerror(errnum)); 288 } 289 #endif 290 291 write_buffer = NULL; 292 file_size = 0; 293 } 294 295 /* 296 * --- LLVM line counter API --- 297 */ 298 299 /* A file in this case is a translation unit. Each .o file built with line 300 * profiling enabled will emit to a different file. Only one file may be 301 * started at a time. 302 */ 303 COMPILER_RT_VISIBILITY 304 void llvm_gcda_start_file(const char *orig_filename, uint32_t version, 305 uint32_t checksum) { 306 const char *mode = "r+b"; 307 filename = mangle_filename(orig_filename); 308 309 /* Try just opening the file. */ 310 fd = open(filename, O_RDWR | O_BINARY); 311 312 if (fd == -1) { 313 /* Try creating the file. */ 314 fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); 315 if (fd != -1) { 316 mode = "w+b"; 317 } else { 318 /* Try creating the directories first then opening the file. */ 319 __llvm_profile_recursive_mkdir(filename); 320 fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); 321 if (fd != -1) { 322 mode = "w+b"; 323 } else { 324 /* Another process may have created the file just now. 325 * Try opening it without O_CREAT and O_EXCL. */ 326 fd = open(filename, O_RDWR | O_BINARY); 327 if (fd == -1) { 328 /* Bah! It's hopeless. */ 329 int errnum = errno; 330 fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, 331 strerror(errnum)); 332 return; 333 } 334 } 335 } 336 } 337 338 /* Try to flock the file to serialize concurrent processes writing out to the 339 * same GCDA. This can fail if the filesystem doesn't support it, but in that 340 * case we'll just carry on with the old racy behaviour and hope for the best. 341 */ 342 lprofLockFd(fd); 343 output_file = fdopen(fd, mode); 344 345 /* Initialize the write buffer. */ 346 new_file = 0; 347 write_buffer = NULL; 348 cur_buffer_size = 0; 349 cur_pos = 0; 350 351 if (map_file() == -1) { 352 /* The file has been created just now (file_size == 0) or mmap failed 353 * unexpectedly. In the latter case, try to recover by clobbering. */ 354 new_file = 1; 355 write_buffer = NULL; 356 resize_write_buffer(WRITE_BUFFER_SIZE); 357 memset(write_buffer, 0, WRITE_BUFFER_SIZE); 358 } 359 360 /* gcda file, version, stamp checksum. */ 361 { 362 uint8_t c3 = version >> 24; 363 uint8_t c2 = (version >> 16) & 255; 364 uint8_t c1 = (version >> 8) & 255; 365 gcov_version = c3 >= 'A' ? (c3 - 'A') * 100 + (c2 - '0') * 10 + c1 - '0' 366 : (c3 - '0') * 10 + c1 - '0'; 367 } 368 write_32bit_value(GCOV_DATA_MAGIC); 369 write_32bit_value(version); 370 write_32bit_value(checksum); 371 372 #ifdef DEBUG_GCDAPROFILING 373 fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); 374 #endif 375 } 376 377 COMPILER_RT_VISIBILITY 378 void llvm_gcda_emit_function(uint32_t ident, uint32_t func_checksum, 379 uint32_t cfg_checksum) { 380 uint32_t len = 2; 381 int use_extra_checksum = gcov_version >= 47; 382 383 if (use_extra_checksum) 384 len++; 385 #ifdef DEBUG_GCDAPROFILING 386 fprintf(stderr, "llvmgcda: function id=0x%08x\n", ident); 387 #endif 388 if (!output_file) return; 389 390 /* function tag */ 391 write_32bit_value(GCOV_TAG_FUNCTION); 392 write_32bit_value(len); 393 write_32bit_value(ident); 394 write_32bit_value(func_checksum); 395 if (use_extra_checksum) 396 write_32bit_value(cfg_checksum); 397 } 398 399 COMPILER_RT_VISIBILITY 400 void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 401 uint32_t i; 402 uint64_t *old_ctrs = NULL; 403 uint32_t val = 0; 404 uint64_t save_cur_pos = cur_pos; 405 406 if (!output_file) return; 407 408 val = read_32bit_value(); 409 410 if (val != (uint32_t)-1) { 411 /* There are counters present in the file. Merge them. */ 412 if (val != GCOV_TAG_COUNTER_ARCS) { 413 fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " 414 "corrupt arc tag (0x%08x)\n", 415 filename, val); 416 return; 417 } 418 419 val = read_32bit_value(); 420 if (val == (uint32_t)-1 || val / 2 != num_counters) { 421 fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " 422 "mismatched number of counters (%d)\n", 423 filename, val); 424 return; 425 } 426 427 old_ctrs = malloc(sizeof(uint64_t) * num_counters); 428 for (i = 0; i < num_counters; ++i) 429 old_ctrs[i] = read_64bit_value(); 430 } 431 432 cur_pos = save_cur_pos; 433 434 /* Counter #1 (arcs) tag */ 435 write_32bit_value(GCOV_TAG_COUNTER_ARCS); 436 write_32bit_value(num_counters * 2); 437 for (i = 0; i < num_counters; ++i) { 438 counters[i] += (old_ctrs ? old_ctrs[i] : 0); 439 write_64bit_value(counters[i]); 440 } 441 442 free(old_ctrs); 443 444 #ifdef DEBUG_GCDAPROFILING 445 fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); 446 for (i = 0; i < num_counters; ++i) 447 fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); 448 #endif 449 } 450 451 COMPILER_RT_VISIBILITY 452 void llvm_gcda_summary_info() { 453 uint32_t runs = 1; 454 static uint32_t run_counted = 0; // We only want to increase the run count once. 455 uint32_t val = 0; 456 uint64_t save_cur_pos = cur_pos; 457 458 if (!output_file) return; 459 460 val = read_32bit_value(); 461 462 if (val != (uint32_t)-1) { 463 /* There are counters present in the file. Merge them. */ 464 if (val != (gcov_version >= 90 ? GCOV_TAG_OBJECT_SUMMARY 465 : GCOV_TAG_PROGRAM_SUMMARY)) { 466 fprintf(stderr, 467 "profiling: %s: cannot merge previous run count: " 468 "corrupt object tag (0x%08x)\n", 469 filename, val); 470 return; 471 } 472 473 val = read_32bit_value(); /* length */ 474 uint32_t prev_runs; 475 if (gcov_version < 90) { 476 read_32bit_value(); 477 read_32bit_value(); 478 prev_runs = read_32bit_value(); 479 } else { 480 prev_runs = read_32bit_value(); 481 read_32bit_value(); 482 } 483 for (uint32_t i = gcov_version < 90 ? 3 : 2; i < val; ++i) 484 read_32bit_value(); 485 /* Add previous run count to new counter, if not already counted before. */ 486 runs = run_counted ? prev_runs : prev_runs + 1; 487 } 488 489 cur_pos = save_cur_pos; 490 491 if (gcov_version >= 90) { 492 write_32bit_value(GCOV_TAG_OBJECT_SUMMARY); 493 write_32bit_value(2); 494 write_32bit_value(runs); 495 write_32bit_value(0); // sum_max 496 } else { 497 // Before gcov 4.8 (r190952), GCOV_TAG_SUMMARY_LENGTH was 9. r190952 set 498 // GCOV_TAG_SUMMARY_LENGTH to 22. We simply use the smallest length which 499 // can make gcov read "Runs:". 500 write_32bit_value(GCOV_TAG_PROGRAM_SUMMARY); 501 write_32bit_value(3); 502 write_32bit_value(0); 503 write_32bit_value(0); 504 write_32bit_value(runs); 505 } 506 507 run_counted = 1; 508 509 #ifdef DEBUG_GCDAPROFILING 510 fprintf(stderr, "llvmgcda: %u runs\n", runs); 511 #endif 512 } 513 514 COMPILER_RT_VISIBILITY 515 void llvm_gcda_end_file() { 516 /* Write out EOF record. */ 517 if (output_file) { 518 write_bytes("\0\0\0\0\0\0\0\0", 8); 519 520 if (new_file) { 521 fwrite(write_buffer, cur_pos, 1, output_file); 522 free(write_buffer); 523 } else { 524 unmap_file(); 525 } 526 527 fflush(output_file); 528 lprofUnlockFd(fd); 529 fclose(output_file); 530 output_file = NULL; 531 write_buffer = NULL; 532 } 533 free(filename); 534 535 #ifdef DEBUG_GCDAPROFILING 536 fprintf(stderr, "llvmgcda: -----\n"); 537 #endif 538 } 539 540 COMPILER_RT_VISIBILITY 541 void llvm_register_writeout_function(fn_ptr fn) { 542 fn_list_insert(&writeout_fn_list, fn); 543 } 544 545 COMPILER_RT_VISIBILITY 546 void llvm_writeout_files(void) { 547 struct fn_node *curr = writeout_fn_list.head; 548 549 while (curr) { 550 if (curr->id == CURRENT_ID) { 551 curr->fn(); 552 } 553 curr = curr->next; 554 } 555 } 556 557 #ifndef _WIN32 558 // __attribute__((destructor)) and destructors whose priorities are greater than 559 // 100 run before this function and can thus be tracked. The priority is 560 // compatible with GCC 7 onwards. 561 #if __GNUC__ >= 9 562 #pragma GCC diagnostic ignored "-Wprio-ctor-dtor" 563 #endif 564 __attribute__((destructor(100))) 565 #endif 566 static void llvm_writeout_and_clear(void) { 567 llvm_writeout_files(); 568 fn_list_remove(&writeout_fn_list); 569 } 570 571 COMPILER_RT_VISIBILITY 572 void llvm_register_reset_function(fn_ptr fn) { 573 fn_list_insert(&reset_fn_list, fn); 574 } 575 576 COMPILER_RT_VISIBILITY 577 void llvm_delete_reset_function_list(void) { fn_list_remove(&reset_fn_list); } 578 579 COMPILER_RT_VISIBILITY 580 void llvm_reset_counters(void) { 581 struct fn_node *curr = reset_fn_list.head; 582 583 while (curr) { 584 if (curr->id == CURRENT_ID) { 585 curr->fn(); 586 } 587 curr = curr->next; 588 } 589 } 590 591 #if !defined(_WIN32) 592 COMPILER_RT_VISIBILITY 593 pid_t __gcov_fork() { 594 pid_t parent_pid = getpid(); 595 pid_t pid = fork(); 596 597 if (pid == 0) { 598 pid_t child_pid = getpid(); 599 if (child_pid != parent_pid) { 600 // The pid changed so we've a fork (one could have its own fork function) 601 // Just reset the counters for this child process 602 // threads. 603 llvm_reset_counters(); 604 } 605 } 606 return pid; 607 } 608 #endif 609 610 COMPILER_RT_VISIBILITY 611 void llvm_gcov_init(fn_ptr wfn, fn_ptr rfn) { 612 static int atexit_ran = 0; 613 614 if (wfn) 615 llvm_register_writeout_function(wfn); 616 617 if (rfn) 618 llvm_register_reset_function(rfn); 619 620 if (atexit_ran == 0) { 621 atexit_ran = 1; 622 623 /* Make sure we write out the data and delete the data structures. */ 624 atexit(llvm_delete_reset_function_list); 625 #ifdef _WIN32 626 atexit(llvm_writeout_and_clear); 627 #endif 628 } 629 } 630 631 void __gcov_dump(void) { 632 for (struct fn_node *f = writeout_fn_list.head; f; f = f->next) 633 f->fn(); 634 } 635 636 void __gcov_reset(void) { 637 for (struct fn_node *f = reset_fn_list.head; f; f = f->next) 638 f->fn(); 639 } 640 641 #endif 642