1229109Sed/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ 2229109Sed|* 3353358Sdim|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim|* See https://llvm.org/LICENSE.txt for license information. 5353358Sdim|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6229109Sed|* 7229109Sed|*===----------------------------------------------------------------------===*| 8229109Sed|* 9229109Sed|* This file implements the call back routines for the gcov profiling 10229109Sed|* instrumentation pass. Link against this library when running code through 11229109Sed|* the -insert-gcov-profiling LLVM pass. 12229109Sed|* 13229109Sed|* We emit files in a corrupt version of GCOV's "gcda" file format. These files 14229109Sed|* are only close enough that LCOV will happily parse them. Anything that lcov 15229109Sed|* ignores is missing. 16229109Sed|* 17229109Sed|* TODO: gcov is multi-process safe by having each exit open the existing file 18229109Sed|* and append to it. We'd like to achieve that and be thread-safe too. 19229109Sed|* 20229109Sed\*===----------------------------------------------------------------------===*/ 21229109Sed 22341825Sdim#if !defined(__Fuchsia__) 23341825Sdim 24274201Sdim#include <errno.h> 25251034Sed#include <fcntl.h> 26229109Sed#include <stdio.h> 27229109Sed#include <stdlib.h> 28229109Sed#include <string.h> 29296417Sdim 30296417Sdim#if defined(_WIN32) 31344779Sdim#define WIN32_LEAN_AND_MEAN 32344779Sdim#include <windows.h> 33296417Sdim#include "WindowsMMap.h" 34296417Sdim#else 35363496Sdim#include <sys/file.h> 36251034Sed#include <sys/mman.h> 37363496Sdim#include <sys/types.h> 38363496Sdim#include <unistd.h> 39296417Sdim#endif 40229109Sed 41309124Sdim#if defined(__FreeBSD__) && defined(__i386__) 42309124Sdim#define I386_FREEBSD 1 43309124Sdim#else 44309124Sdim#define I386_FREEBSD 0 45309124Sdim#endif 46276789Sdim 47276789Sdim#if !defined(_MSC_VER) && !I386_FREEBSD 48229109Sed#include <stdint.h> 49276789Sdim#endif 50276789Sdim 51276789Sdim#if defined(_MSC_VER) 52296417Sdimtypedef unsigned char uint8_t; 53229109Sedtypedef unsigned int uint32_t; 54276789Sdimtypedef unsigned long long uint64_t; 55276789Sdim#elif I386_FREEBSD 56276789Sdim/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to 57276789Sdim * FreeBSD 10, r232261) when compiled in 32-bit mode. 58276789Sdim */ 59276789Sdimtypedef unsigned char uint8_t; 60276789Sdimtypedef unsigned int uint32_t; 61276789Sdimtypedef unsigned long long uint64_t; 62229109Sed#endif 63229109Sed 64327952Sdim#include "InstrProfiling.h" 65327952Sdim#include "InstrProfilingUtil.h" 66327952Sdim 67229109Sed/* #define DEBUG_GCDAPROFILING */ 68229109Sed 69229109Sed/* 70229109Sed * --- GCOV file format I/O primitives --- 71229109Sed */ 72229109Sed 73251034Sed/* 74274201Sdim * The current file name we're outputting. Used primarily for error logging. 75274201Sdim */ 76274201Sdimstatic char *filename = NULL; 77274201Sdim 78274201Sdim/* 79251034Sed * The current file we're outputting. 80251034Sed */ 81229109Sedstatic FILE *output_file = NULL; 82229109Sed 83251034Sed/* 84251034Sed * Buffer that we write things into. 85251034Sed */ 86251034Sed#define WRITE_BUFFER_SIZE (128 * 1024) 87341825Sdimstatic unsigned char *write_buffer = NULL; 88251034Sedstatic uint64_t cur_buffer_size = 0; 89251034Sedstatic uint64_t cur_pos = 0; 90251034Sedstatic uint64_t file_size = 0; 91251034Sedstatic int new_file = 0; 92344779Sdim#if defined(_WIN32) 93344779Sdimstatic HANDLE mmap_handle = NULL; 94344779Sdim#endif 95251034Sedstatic int fd = -1; 96251034Sed 97341825Sdimtypedef void (*fn_ptr)(); 98251034Sed 99341825Sdimtypedef void* dynamic_object_id; 100341825Sdim// The address of this variable identifies a given dynamic object. 101341825Sdimstatic dynamic_object_id current_id; 102341825Sdim#define CURRENT_ID (¤t_id) 103341825Sdim 104341825Sdimstruct fn_node { 105341825Sdim dynamic_object_id id; 106341825Sdim fn_ptr fn; 107341825Sdim struct fn_node* next; 108251034Sed}; 109251034Sed 110341825Sdimstruct fn_list { 111341825Sdim struct fn_node *head, *tail; 112341825Sdim}; 113251034Sed 114251034Sed/* 115341825Sdim * A list of functions to write out the data, shared between all dynamic objects. 116251034Sed */ 117341825Sdimstruct fn_list writeout_fn_list; 118251034Sed 119341825Sdim/* 120341825Sdim * A list of flush functions that our __gcov_flush() function should call, shared between all dynamic objects. 121341825Sdim */ 122341825Sdimstruct fn_list flush_fn_list; 123251034Sed 124363496Sdim/* 125363496Sdim * A list of reset functions, shared between all dynamic objects. 126363496Sdim */ 127363496Sdimstruct fn_list reset_fn_list; 128363496Sdim 129341825Sdimstatic void fn_list_insert(struct fn_list* list, fn_ptr fn) { 130341825Sdim struct fn_node* new_node = malloc(sizeof(struct fn_node)); 131341825Sdim new_node->fn = fn; 132341825Sdim new_node->next = NULL; 133341825Sdim new_node->id = CURRENT_ID; 134251034Sed 135341825Sdim if (!list->head) { 136341825Sdim list->head = list->tail = new_node; 137341825Sdim } else { 138341825Sdim list->tail->next = new_node; 139341825Sdim list->tail = new_node; 140341825Sdim } 141341825Sdim} 142341825Sdim 143341825Sdimstatic void fn_list_remove(struct fn_list* list) { 144341825Sdim struct fn_node* curr = list->head; 145341825Sdim struct fn_node* prev = NULL; 146341825Sdim struct fn_node* next = NULL; 147341825Sdim 148341825Sdim while (curr) { 149341825Sdim next = curr->next; 150341825Sdim 151341825Sdim if (curr->id == CURRENT_ID) { 152341825Sdim if (curr == list->head) { 153341825Sdim list->head = next; 154341825Sdim } 155341825Sdim 156341825Sdim if (curr == list->tail) { 157341825Sdim list->tail = prev; 158341825Sdim } 159341825Sdim 160341825Sdim if (prev) { 161341825Sdim prev->next = next; 162341825Sdim } 163341825Sdim 164341825Sdim free(curr); 165341825Sdim } else { 166341825Sdim prev = curr; 167341825Sdim } 168341825Sdim 169341825Sdim curr = next; 170341825Sdim } 171341825Sdim} 172341825Sdim 173251034Sedstatic void resize_write_buffer(uint64_t size) { 174251034Sed if (!new_file) return; 175251034Sed size += cur_pos; 176251034Sed if (size <= cur_buffer_size) return; 177251034Sed size = (size - 1) / WRITE_BUFFER_SIZE + 1; 178251034Sed size *= WRITE_BUFFER_SIZE; 179251034Sed write_buffer = realloc(write_buffer, size); 180251034Sed cur_buffer_size = size; 181229109Sed} 182229109Sed 183251034Sedstatic void write_bytes(const char *s, size_t len) { 184251034Sed resize_write_buffer(len); 185251034Sed memcpy(&write_buffer[cur_pos], s, len); 186251034Sed cur_pos += len; 187229109Sed} 188229109Sed 189251034Sedstatic void write_32bit_value(uint32_t i) { 190251034Sed write_bytes((char*)&i, 4); 191251034Sed} 192251034Sed 193251034Sedstatic void write_64bit_value(uint64_t i) { 194341825Sdim // GCOV uses a lo-/hi-word format even on big-endian systems. 195341825Sdim // See also GCOVBuffer::readInt64 in LLVM. 196341825Sdim uint32_t lo = (uint32_t) i; 197341825Sdim uint32_t hi = (uint32_t) (i >> 32); 198341825Sdim write_32bit_value(lo); 199341825Sdim write_32bit_value(hi); 200251034Sed} 201251034Sed 202229109Sedstatic uint32_t length_of_string(const char *s) { 203229109Sed return (strlen(s) / 4) + 1; 204229109Sed} 205229109Sed 206229109Sedstatic void write_string(const char *s) { 207229109Sed uint32_t len = length_of_string(s); 208251034Sed write_32bit_value(len); 209251034Sed write_bytes(s, strlen(s)); 210251034Sed write_bytes("\0\0\0\0", 4 - (strlen(s) % 4)); 211229109Sed} 212229109Sed 213251034Sedstatic uint32_t read_32bit_value() { 214251034Sed uint32_t val; 215245614Sandrew 216251034Sed if (new_file) 217245614Sandrew return (uint32_t)-1; 218245614Sandrew 219251034Sed val = *(uint32_t*)&write_buffer[cur_pos]; 220251034Sed cur_pos += 4; 221251034Sed return val; 222245614Sandrew} 223245614Sandrew 224341825Sdimstatic uint32_t read_le_32bit_value() { 225341825Sdim uint32_t val = 0; 226341825Sdim int i; 227245614Sandrew 228251034Sed if (new_file) 229341825Sdim return (uint32_t)-1; 230245614Sandrew 231341825Sdim for (i = 0; i < 4; i++) 232341825Sdim val |= write_buffer[cur_pos++] << (8*i); 233251034Sed return val; 234245614Sandrew} 235245614Sandrew 236341825Sdimstatic uint64_t read_64bit_value() { 237341825Sdim // GCOV uses a lo-/hi-word format even on big-endian systems. 238341825Sdim // See also GCOVBuffer::readInt64 in LLVM. 239341825Sdim uint32_t lo = read_32bit_value(); 240341825Sdim uint32_t hi = read_32bit_value(); 241341825Sdim return ((uint64_t)hi << 32) | ((uint64_t)lo); 242341825Sdim} 243341825Sdim 244229109Sedstatic char *mangle_filename(const char *orig_filename) { 245276789Sdim char *new_filename; 246309124Sdim size_t prefix_len; 247276789Sdim int prefix_strip; 248309124Sdim const char *prefix = lprofGetPathPrefix(&prefix_strip, &prefix_len); 249229109Sed 250309124Sdim if (prefix == NULL) 251229109Sed return strdup(orig_filename); 252229109Sed 253309124Sdim new_filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); 254309124Sdim lprofApplyPathPrefix(new_filename, orig_filename, prefix, prefix_len, 255309124Sdim prefix_strip); 256238901Sandrew 257276789Sdim return new_filename; 258229109Sed} 259229109Sed 260274201Sdimstatic int map_file() { 261251034Sed fseek(output_file, 0L, SEEK_END); 262251034Sed file_size = ftell(output_file); 263251034Sed 264274201Sdim /* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an 265274201Sdim * error message because it should "just work" for the user. */ 266274201Sdim if (file_size == 0) 267274201Sdim return -1; 268274201Sdim 269344779Sdim#if defined(_WIN32) 270344779Sdim HANDLE mmap_fd; 271344779Sdim if (fd == -1) 272344779Sdim mmap_fd = INVALID_HANDLE_VALUE; 273344779Sdim else 274344779Sdim mmap_fd = (HANDLE)_get_osfhandle(fd); 275344779Sdim 276344779Sdim mmap_handle = CreateFileMapping(mmap_fd, NULL, PAGE_READWRITE, DWORD_HI(file_size), DWORD_LO(file_size), NULL); 277344779Sdim if (mmap_handle == NULL) { 278353358Sdim fprintf(stderr, "profiling: %s: cannot create file mapping: %lu\n", 279353358Sdim filename, GetLastError()); 280344779Sdim return -1; 281344779Sdim } 282344779Sdim 283344779Sdim write_buffer = MapViewOfFile(mmap_handle, FILE_MAP_WRITE, 0, 0, file_size); 284344779Sdim if (write_buffer == NULL) { 285353358Sdim fprintf(stderr, "profiling: %s: cannot map: %lu\n", filename, 286344779Sdim GetLastError()); 287344779Sdim CloseHandle(mmap_handle); 288344779Sdim return -1; 289344779Sdim } 290344779Sdim#else 291251034Sed write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE, 292251034Sed MAP_FILE | MAP_SHARED, fd, 0); 293274201Sdim if (write_buffer == (void *)-1) { 294274201Sdim int errnum = errno; 295274201Sdim fprintf(stderr, "profiling: %s: cannot map: %s\n", filename, 296274201Sdim strerror(errnum)); 297274201Sdim return -1; 298274201Sdim } 299344779Sdim#endif 300344779Sdim 301274201Sdim return 0; 302251034Sed} 303251034Sed 304251034Sedstatic void unmap_file() { 305344779Sdim#if defined(_WIN32) 306344779Sdim if (!FlushViewOfFile(write_buffer, file_size)) { 307353358Sdim fprintf(stderr, "profiling: %s: cannot flush mapped view: %lu\n", filename, 308344779Sdim GetLastError()); 309344779Sdim } 310344779Sdim 311344779Sdim if (!UnmapViewOfFile(write_buffer)) { 312353358Sdim fprintf(stderr, "profiling: %s: cannot unmap mapped view: %lu\n", filename, 313344779Sdim GetLastError()); 314344779Sdim } 315344779Sdim 316344779Sdim if (!CloseHandle(mmap_handle)) { 317353358Sdim fprintf(stderr, "profiling: %s: cannot close file mapping handle: %lu\n", 318353358Sdim filename, GetLastError()); 319344779Sdim } 320344779Sdim 321344779Sdim mmap_handle = NULL; 322344779Sdim#else 323274201Sdim if (msync(write_buffer, file_size, MS_SYNC) == -1) { 324274201Sdim int errnum = errno; 325274201Sdim fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename, 326274201Sdim strerror(errnum)); 327274201Sdim } 328274201Sdim 329274201Sdim /* We explicitly ignore errors from unmapping because at this point the data 330274201Sdim * is written and we don't care. 331274201Sdim */ 332274201Sdim (void)munmap(write_buffer, file_size); 333344779Sdim#endif 334344779Sdim 335251034Sed write_buffer = NULL; 336251034Sed file_size = 0; 337251034Sed} 338251034Sed 339229109Sed/* 340229109Sed * --- LLVM line counter API --- 341229109Sed */ 342229109Sed 343229109Sed/* A file in this case is a translation unit. Each .o file built with line 344229109Sed * profiling enabled will emit to a different file. Only one file may be 345229109Sed * started at a time. 346229109Sed */ 347327952SdimCOMPILER_RT_VISIBILITY 348276789Sdimvoid llvm_gcda_start_file(const char *orig_filename, const char version[4], 349276789Sdim uint32_t checksum) { 350251034Sed const char *mode = "r+b"; 351274201Sdim filename = mangle_filename(orig_filename); 352229109Sed 353245614Sandrew /* Try just opening the file. */ 354251034Sed new_file = 0; 355327952Sdim fd = open(filename, O_RDWR | O_BINARY); 356245614Sandrew 357251034Sed if (fd == -1) { 358245614Sandrew /* Try opening the file, creating it if necessary. */ 359251034Sed new_file = 1; 360251034Sed mode = "w+b"; 361327952Sdim fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); 362251034Sed if (fd == -1) { 363245614Sandrew /* Try creating the directories first then opening the file. */ 364288943Sdim __llvm_profile_recursive_mkdir(filename); 365327952Sdim fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); 366274201Sdim if (fd == -1) { 367245614Sandrew /* Bah! It's hopeless. */ 368274201Sdim int errnum = errno; 369274201Sdim fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, 370274201Sdim strerror(errnum)); 371245614Sandrew return; 372245614Sandrew } 373238901Sandrew } 374238901Sandrew } 375238901Sandrew 376288943Sdim /* Try to flock the file to serialize concurrent processes writing out to the 377288943Sdim * same GCDA. This can fail if the filesystem doesn't support it, but in that 378288943Sdim * case we'll just carry on with the old racy behaviour and hope for the best. 379288943Sdim */ 380327952Sdim lprofLockFd(fd); 381251034Sed output_file = fdopen(fd, mode); 382229109Sed 383251034Sed /* Initialize the write buffer. */ 384251034Sed write_buffer = NULL; 385251034Sed cur_buffer_size = 0; 386251034Sed cur_pos = 0; 387251034Sed 388251034Sed if (new_file) { 389251034Sed resize_write_buffer(WRITE_BUFFER_SIZE); 390251034Sed memset(write_buffer, 0, WRITE_BUFFER_SIZE); 391251034Sed } else { 392274201Sdim if (map_file() == -1) { 393274201Sdim /* mmap failed, try to recover by clobbering */ 394274201Sdim new_file = 1; 395274201Sdim write_buffer = NULL; 396274201Sdim cur_buffer_size = 0; 397274201Sdim resize_write_buffer(WRITE_BUFFER_SIZE); 398274201Sdim memset(write_buffer, 0, WRITE_BUFFER_SIZE); 399274201Sdim } 400251034Sed } 401251034Sed 402276789Sdim /* gcda file, version, stamp checksum. */ 403251034Sed write_bytes("adcg", 4); 404251034Sed write_bytes(version, 4); 405276789Sdim write_32bit_value(checksum); 406251034Sed 407229109Sed#ifdef DEBUG_GCDAPROFILING 408245614Sandrew fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); 409229109Sed#endif 410229109Sed} 411229109Sed 412229109Sed/* Given an array of pointers to counters (counters), increment the n-th one, 413229109Sed * where we're also given a pointer to n (predecessor). 414229109Sed */ 415327952SdimCOMPILER_RT_VISIBILITY 416229109Sedvoid llvm_gcda_increment_indirect_counter(uint32_t *predecessor, 417229109Sed uint64_t **counters) { 418229109Sed uint64_t *counter; 419229109Sed uint32_t pred; 420229109Sed 421229109Sed pred = *predecessor; 422229109Sed if (pred == 0xffffffff) 423229109Sed return; 424229109Sed counter = counters[pred]; 425229109Sed 426229109Sed /* Don't crash if the pred# is out of sync. This can happen due to threads, 427229109Sed or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ 428229109Sed if (counter) 429229109Sed ++*counter; 430229109Sed#ifdef DEBUG_GCDAPROFILING 431229109Sed else 432238901Sandrew fprintf(stderr, 433245614Sandrew "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", 434245614Sandrew *counter, *predecessor); 435229109Sed#endif 436229109Sed} 437229109Sed 438327952SdimCOMPILER_RT_VISIBILITY 439251034Sedvoid llvm_gcda_emit_function(uint32_t ident, const char *function_name, 440276789Sdim uint32_t func_checksum, uint8_t use_extra_checksum, 441276789Sdim uint32_t cfg_checksum) { 442251034Sed uint32_t len = 2; 443251034Sed 444251034Sed if (use_extra_checksum) 445251034Sed len++; 446229109Sed#ifdef DEBUG_GCDAPROFILING 447251034Sed fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, 448251034Sed function_name ? function_name : "NULL"); 449229109Sed#endif 450238901Sandrew if (!output_file) return; 451229109Sed 452251034Sed /* function tag */ 453251034Sed write_bytes("\0\0\0\1", 4); 454251034Sed if (function_name) 455251034Sed len += 1 + length_of_string(function_name); 456251034Sed write_32bit_value(len); 457251034Sed write_32bit_value(ident); 458276789Sdim write_32bit_value(func_checksum); 459251034Sed if (use_extra_checksum) 460276789Sdim write_32bit_value(cfg_checksum); 461251034Sed if (function_name) 462251034Sed write_string(function_name); 463229109Sed} 464229109Sed 465327952SdimCOMPILER_RT_VISIBILITY 466229109Sedvoid llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 467229109Sed uint32_t i; 468245614Sandrew uint64_t *old_ctrs = NULL; 469245614Sandrew uint32_t val = 0; 470251034Sed uint64_t save_cur_pos = cur_pos; 471238901Sandrew 472245614Sandrew if (!output_file) return; 473245614Sandrew 474341825Sdim val = read_le_32bit_value(); 475245614Sandrew 476245614Sandrew if (val != (uint32_t)-1) { 477245614Sandrew /* There are counters present in the file. Merge them. */ 478245614Sandrew if (val != 0x01a10000) { 479276789Sdim fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " 480276789Sdim "corrupt arc tag (0x%08x)\n", 481276789Sdim filename, val); 482245614Sandrew return; 483245614Sandrew } 484245614Sandrew 485251034Sed val = read_32bit_value(); 486245614Sandrew if (val == (uint32_t)-1 || val / 2 != num_counters) { 487276789Sdim fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " 488276789Sdim "mismatched number of counters (%d)\n", 489276789Sdim filename, val); 490245614Sandrew return; 491245614Sandrew } 492245614Sandrew 493245614Sandrew old_ctrs = malloc(sizeof(uint64_t) * num_counters); 494251034Sed for (i = 0; i < num_counters; ++i) 495251034Sed old_ctrs[i] = read_64bit_value(); 496245614Sandrew } 497245614Sandrew 498251034Sed cur_pos = save_cur_pos; 499245614Sandrew 500238901Sandrew /* Counter #1 (arcs) tag */ 501251034Sed write_bytes("\0\0\xa1\1", 4); 502251034Sed write_32bit_value(num_counters * 2); 503251034Sed for (i = 0; i < num_counters; ++i) { 504251034Sed counters[i] += (old_ctrs ? old_ctrs[i] : 0); 505251034Sed write_64bit_value(counters[i]); 506251034Sed } 507229109Sed 508245614Sandrew free(old_ctrs); 509245614Sandrew 510229109Sed#ifdef DEBUG_GCDAPROFILING 511245614Sandrew fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); 512238901Sandrew for (i = 0; i < num_counters; ++i) 513245614Sandrew fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); 514229109Sed#endif 515229109Sed} 516229109Sed 517327952SdimCOMPILER_RT_VISIBILITY 518274201Sdimvoid llvm_gcda_summary_info() { 519276789Sdim const uint32_t obj_summary_len = 9; /* Length for gcov compatibility. */ 520274201Sdim uint32_t i; 521274201Sdim uint32_t runs = 1; 522341825Sdim static uint32_t run_counted = 0; // We only want to increase the run count once. 523274201Sdim uint32_t val = 0; 524274201Sdim uint64_t save_cur_pos = cur_pos; 525274201Sdim 526238901Sandrew if (!output_file) return; 527251034Sed 528341825Sdim val = read_le_32bit_value(); 529274201Sdim 530274201Sdim if (val != (uint32_t)-1) { 531274201Sdim /* There are counters present in the file. Merge them. */ 532274201Sdim if (val != 0xa1000000) { 533276789Sdim fprintf(stderr, "profiling: %s: cannot merge previous run count: " 534276789Sdim "corrupt object tag (0x%08x)\n", 535276789Sdim filename, val); 536274201Sdim return; 537274201Sdim } 538274201Sdim 539276789Sdim val = read_32bit_value(); /* length */ 540274201Sdim if (val != obj_summary_len) { 541276789Sdim fprintf(stderr, "profiling: %s: cannot merge previous run count: " 542276789Sdim "mismatched object length (%d)\n", 543276789Sdim filename, val); 544274201Sdim return; 545274201Sdim } 546274201Sdim 547276789Sdim read_32bit_value(); /* checksum, unused */ 548276789Sdim read_32bit_value(); /* num, unused */ 549341825Sdim uint32_t prev_runs = read_32bit_value(); 550341825Sdim /* Add previous run count to new counter, if not already counted before. */ 551341825Sdim runs = run_counted ? prev_runs : prev_runs + 1; 552251034Sed } 553251034Sed 554274201Sdim cur_pos = save_cur_pos; 555229109Sed 556274201Sdim /* Object summary tag */ 557274201Sdim write_bytes("\0\0\0\xa1", 4); 558274201Sdim write_32bit_value(obj_summary_len); 559276789Sdim write_32bit_value(0); /* checksum, unused */ 560276789Sdim write_32bit_value(0); /* num, unused */ 561274201Sdim write_32bit_value(runs); 562276789Sdim for (i = 3; i < obj_summary_len; ++i) 563274201Sdim write_32bit_value(0); 564274201Sdim 565274201Sdim /* Program summary tag */ 566276789Sdim write_bytes("\0\0\0\xa3", 4); /* tag indicates 1 program */ 567276789Sdim write_32bit_value(0); /* 0 length */ 568274201Sdim 569341825Sdim run_counted = 1; 570341825Sdim 571229109Sed#ifdef DEBUG_GCDAPROFILING 572274201Sdim fprintf(stderr, "llvmgcda: %u runs\n", runs); 573274201Sdim#endif 574274201Sdim} 575274201Sdim 576327952SdimCOMPILER_RT_VISIBILITY 577274201Sdimvoid llvm_gcda_end_file() { 578274201Sdim /* Write out EOF record. */ 579274201Sdim if (output_file) { 580274201Sdim write_bytes("\0\0\0\0\0\0\0\0", 8); 581274201Sdim 582274201Sdim if (new_file) { 583274201Sdim fwrite(write_buffer, cur_pos, 1, output_file); 584274201Sdim free(write_buffer); 585274201Sdim } else { 586274201Sdim unmap_file(); 587274201Sdim } 588274201Sdim 589327952Sdim fflush(output_file); 590327952Sdim lprofUnlockFd(fd); 591274201Sdim fclose(output_file); 592274201Sdim output_file = NULL; 593274201Sdim write_buffer = NULL; 594274201Sdim } 595274201Sdim free(filename); 596274201Sdim 597274201Sdim#ifdef DEBUG_GCDAPROFILING 598245614Sandrew fprintf(stderr, "llvmgcda: -----\n"); 599229109Sed#endif 600229109Sed} 601251034Sed 602327952SdimCOMPILER_RT_VISIBILITY 603341825Sdimvoid llvm_register_writeout_function(fn_ptr fn) { 604341825Sdim fn_list_insert(&writeout_fn_list, fn); 605251034Sed} 606251034Sed 607327952SdimCOMPILER_RT_VISIBILITY 608309124Sdimvoid llvm_writeout_files(void) { 609341825Sdim struct fn_node *curr = writeout_fn_list.head; 610251034Sed 611251034Sed while (curr) { 612341825Sdim if (curr->id == CURRENT_ID) { 613341825Sdim curr->fn(); 614341825Sdim } 615251034Sed curr = curr->next; 616251034Sed } 617251034Sed} 618251034Sed 619327952SdimCOMPILER_RT_VISIBILITY 620309124Sdimvoid llvm_delete_writeout_function_list(void) { 621341825Sdim fn_list_remove(&writeout_fn_list); 622251034Sed} 623251034Sed 624327952SdimCOMPILER_RT_VISIBILITY 625341825Sdimvoid llvm_register_flush_function(fn_ptr fn) { 626341825Sdim fn_list_insert(&flush_fn_list, fn); 627251034Sed} 628251034Sed 629251034Sedvoid __gcov_flush() { 630341825Sdim struct fn_node* curr = flush_fn_list.head; 631251034Sed 632251034Sed while (curr) { 633251034Sed curr->fn(); 634251034Sed curr = curr->next; 635251034Sed } 636251034Sed} 637251034Sed 638327952SdimCOMPILER_RT_VISIBILITY 639309124Sdimvoid llvm_delete_flush_function_list(void) { 640341825Sdim fn_list_remove(&flush_fn_list); 641251034Sed} 642251034Sed 643327952SdimCOMPILER_RT_VISIBILITY 644363496Sdimvoid llvm_register_reset_function(fn_ptr fn) { 645363496Sdim fn_list_insert(&reset_fn_list, fn); 646363496Sdim} 647363496Sdim 648363496SdimCOMPILER_RT_VISIBILITY 649363496Sdimvoid llvm_delete_reset_function_list(void) { fn_list_remove(&reset_fn_list); } 650363496Sdim 651363496SdimCOMPILER_RT_VISIBILITY 652363496Sdimvoid llvm_reset_counters(void) { 653363496Sdim struct fn_node *curr = reset_fn_list.head; 654363496Sdim 655363496Sdim while (curr) { 656363496Sdim if (curr->id == CURRENT_ID) { 657363496Sdim curr->fn(); 658363496Sdim } 659363496Sdim curr = curr->next; 660363496Sdim } 661363496Sdim} 662363496Sdim 663363496Sdim#if !defined(_WIN32) 664363496SdimCOMPILER_RT_VISIBILITY 665363496Sdimpid_t __gcov_fork() { 666363496Sdim pid_t parent_pid = getpid(); 667363496Sdim pid_t pid = fork(); 668363496Sdim 669363496Sdim if (pid == 0) { 670363496Sdim pid_t child_pid = getpid(); 671363496Sdim if (child_pid != parent_pid) { 672363496Sdim // The pid changed so we've a fork (one could have its own fork function) 673363496Sdim // Just reset the counters for this child process 674363496Sdim // threads. 675363496Sdim llvm_reset_counters(); 676363496Sdim } 677363496Sdim } 678363496Sdim return pid; 679363496Sdim} 680363496Sdim#endif 681363496Sdim 682363496SdimCOMPILER_RT_VISIBILITY 683363496Sdimvoid llvm_gcov_init(fn_ptr wfn, fn_ptr ffn, fn_ptr rfn) { 684251034Sed static int atexit_ran = 0; 685251034Sed 686251034Sed if (wfn) 687251034Sed llvm_register_writeout_function(wfn); 688251034Sed 689251034Sed if (ffn) 690251034Sed llvm_register_flush_function(ffn); 691251034Sed 692363496Sdim if (rfn) 693363496Sdim llvm_register_reset_function(rfn); 694363496Sdim 695251034Sed if (atexit_ran == 0) { 696251034Sed atexit_ran = 1; 697251034Sed 698251034Sed /* Make sure we write out the data and delete the data structures. */ 699363496Sdim atexit(llvm_delete_reset_function_list); 700251034Sed atexit(llvm_delete_flush_function_list); 701251034Sed atexit(llvm_delete_writeout_function_list); 702251034Sed atexit(llvm_writeout_files); 703251034Sed } 704251034Sed} 705341825Sdim 706341825Sdim#endif 707