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 uint32_t gcov_tag = 465 gcov_version >= 90 ? GCOV_TAG_OBJECT_SUMMARY : GCOV_TAG_PROGRAM_SUMMARY; 466 if (val != gcov_tag) { 467 fprintf(stderr, 468 "profiling: %s: cannot merge previous run count: " 469 "corrupt object tag (0x%08x)\n", 470 filename, val); 471 return; 472 } 473 474 val = read_32bit_value(); /* length */ 475 uint32_t prev_runs; 476 if (gcov_version < 90) { 477 read_32bit_value(); 478 read_32bit_value(); 479 prev_runs = read_32bit_value(); 480 } else { 481 prev_runs = read_32bit_value(); 482 read_32bit_value(); 483 } 484 for (uint32_t i = gcov_version < 90 ? 3 : 2; i < val; ++i) 485 read_32bit_value(); 486 /* Add previous run count to new counter, if not already counted before. */ 487 runs = run_counted ? prev_runs : prev_runs + 1; 488 } 489 490 cur_pos = save_cur_pos; 491 492 if (gcov_version >= 90) { 493 write_32bit_value(GCOV_TAG_OBJECT_SUMMARY); 494 write_32bit_value(2); 495 write_32bit_value(runs); 496 write_32bit_value(0); // sum_max 497 } else { 498 // Before gcov 4.8 (r190952), GCOV_TAG_SUMMARY_LENGTH was 9. r190952 set 499 // GCOV_TAG_SUMMARY_LENGTH to 22. We simply use the smallest length which 500 // can make gcov read "Runs:". 501 write_32bit_value(GCOV_TAG_PROGRAM_SUMMARY); 502 write_32bit_value(3); 503 write_32bit_value(0); 504 write_32bit_value(0); 505 write_32bit_value(runs); 506 } 507 508 run_counted = 1; 509 510 #ifdef DEBUG_GCDAPROFILING 511 fprintf(stderr, "llvmgcda: %u runs\n", runs); 512 #endif 513 } 514 515 COMPILER_RT_VISIBILITY 516 void llvm_gcda_end_file() { 517 /* Write out EOF record. */ 518 if (output_file) { 519 write_bytes("\0\0\0\0\0\0\0\0", 8); 520 521 if (new_file) { 522 fwrite(write_buffer, cur_pos, 1, output_file); 523 free(write_buffer); 524 } else { 525 unmap_file(); 526 } 527 528 fflush(output_file); 529 lprofUnlockFd(fd); 530 fclose(output_file); 531 output_file = NULL; 532 write_buffer = NULL; 533 } 534 free(filename); 535 536 #ifdef DEBUG_GCDAPROFILING 537 fprintf(stderr, "llvmgcda: -----\n"); 538 #endif 539 } 540 541 COMPILER_RT_VISIBILITY 542 void llvm_register_writeout_function(fn_ptr fn) { 543 fn_list_insert(&writeout_fn_list, fn); 544 } 545 546 COMPILER_RT_VISIBILITY 547 void llvm_writeout_files(void) { 548 struct fn_node *curr = writeout_fn_list.head; 549 550 while (curr) { 551 if (curr->id == CURRENT_ID) { 552 curr->fn(); 553 } 554 curr = curr->next; 555 } 556 } 557 558 #ifndef _WIN32 559 // __attribute__((destructor)) and destructors whose priorities are greater than 560 // 100 run before this function and can thus be tracked. The priority is 561 // compatible with GCC 7 onwards. 562 #if __GNUC__ >= 9 563 #pragma GCC diagnostic ignored "-Wprio-ctor-dtor" 564 #endif 565 __attribute__((destructor(100))) 566 #endif 567 static void llvm_writeout_and_clear(void) { 568 llvm_writeout_files(); 569 fn_list_remove(&writeout_fn_list); 570 } 571 572 COMPILER_RT_VISIBILITY 573 void llvm_register_reset_function(fn_ptr fn) { 574 fn_list_insert(&reset_fn_list, fn); 575 } 576 577 COMPILER_RT_VISIBILITY 578 void llvm_delete_reset_function_list(void) { fn_list_remove(&reset_fn_list); } 579 580 COMPILER_RT_VISIBILITY 581 void llvm_reset_counters(void) { 582 struct fn_node *curr = reset_fn_list.head; 583 584 while (curr) { 585 if (curr->id == CURRENT_ID) { 586 curr->fn(); 587 } 588 curr = curr->next; 589 } 590 } 591 592 #if !defined(_WIN32) 593 COMPILER_RT_VISIBILITY 594 pid_t __gcov_fork() { 595 pid_t parent_pid = getpid(); 596 pid_t pid = fork(); 597 598 if (pid == 0) { 599 pid_t child_pid = getpid(); 600 if (child_pid != parent_pid) { 601 // The pid changed so we've a fork (one could have its own fork function) 602 // Just reset the counters for this child process 603 // threads. 604 llvm_reset_counters(); 605 } 606 } 607 return pid; 608 } 609 #endif 610 611 COMPILER_RT_VISIBILITY 612 void llvm_gcov_init(fn_ptr wfn, fn_ptr rfn) { 613 static int atexit_ran = 0; 614 615 if (wfn) 616 llvm_register_writeout_function(wfn); 617 618 if (rfn) 619 llvm_register_reset_function(rfn); 620 621 if (atexit_ran == 0) { 622 atexit_ran = 1; 623 624 /* Make sure we write out the data and delete the data structures. */ 625 atexit(llvm_delete_reset_function_list); 626 #ifdef _WIN32 627 atexit(llvm_writeout_and_clear); 628 #endif 629 } 630 } 631 632 void __gcov_dump(void) { 633 for (struct fn_node *f = writeout_fn_list.head; f; f = f->next) 634 f->fn(); 635 } 636 637 void __gcov_reset(void) { 638 for (struct fn_node *f = reset_fn_list.head; f; f = f->next) 639 f->fn(); 640 } 641 642 #endif 643