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 (&current_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