GCDAProfiling.c revision 276851
1103856Stjr/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\
2103856Stjr|*
3103856Stjr|*                     The LLVM Compiler Infrastructure
4103856Stjr|*
5103856Stjr|* This file is distributed under the University of Illinois Open Source
6103856Stjr|* License. See LICENSE.TXT for details.
7103856Stjr|*
8103856Stjr|*===----------------------------------------------------------------------===*|
9103856Stjr|*
10103856Stjr|* This file implements the call back routines for the gcov profiling
11103856Stjr|* instrumentation pass. Link against this library when running code through
12103856Stjr|* the -insert-gcov-profiling LLVM pass.
13103856Stjr|*
14103856Stjr|* We emit files in a corrupt version of GCOV's "gcda" file format. These files
15103856Stjr|* are only close enough that LCOV will happily parse them. Anything that lcov
16103856Stjr|* ignores is missing.
17103856Stjr|*
18103856Stjr|* TODO: gcov is multi-process safe by having each exit open the existing file
19103856Stjr|* and append to it. We'd like to achieve that and be thread-safe too.
20103856Stjr|*
21103856Stjr\*===----------------------------------------------------------------------===*/
22103856Stjr
23103856Stjr#include <errno.h>
24103856Stjr#include <fcntl.h>
25103856Stjr#include <stdio.h>
26103856Stjr#include <stdlib.h>
27103856Stjr#include <string.h>
28103856Stjr#include <sys/mman.h>
29103856Stjr#ifdef _WIN32
30103856Stjr#include <direct.h>
31103856Stjr#endif
32103856Stjr
33103856Stjr#define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__))
34103856Stjr
35103856Stjr#if !I386_FREEBSD
36103856Stjr#include <sys/stat.h>
37103856Stjr#include <sys/types.h>
38128844Sobrien#endif
39103856Stjr
40103856Stjr#if !defined(_MSC_VER) && !I386_FREEBSD
41103856Stjr#include <stdint.h>
42103856Stjr#endif
43103856Stjr
44149313Sstefanf#if defined(_MSC_VER)
45103856Stjrtypedef unsigned int uint32_t;
46103856Stjrtypedef unsigned long long uint64_t;
47103856Stjr#elif I386_FREEBSD
48103856Stjr/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to
49103856Stjr * FreeBSD 10, r232261) when compiled in 32-bit mode.
50103856Stjr */
51103856Stjrtypedef unsigned char uint8_t;
52103856Stjrtypedef unsigned int uint32_t;
53103856Stjrtypedef unsigned long long uint64_t;
54103856Stjrint mkdir(const char*, unsigned short);
55103856Stjr#endif
56103856Stjr
57128822Sdas/* #define DEBUG_GCDAPROFILING */
58103856Stjr
59103856Stjr/*
60103856Stjr * --- GCOV file format I/O primitives ---
61103856Stjr */
62103856Stjr
63103856Stjr/*
64103856Stjr * The current file name we're outputting. Used primarily for error logging.
65103856Stjr */
66103856Stjrstatic char *filename = NULL;
67103856Stjr
68103856Stjr/*
69103856Stjr * The current file we're outputting.
70103856Stjr */
71103856Stjrstatic FILE *output_file = NULL;
72103856Stjr
73103856Stjr/*
74103856Stjr * Buffer that we write things into.
75103856Stjr */
76103856Stjr#define WRITE_BUFFER_SIZE (128 * 1024)
77103856Stjrstatic char *write_buffer = NULL;
78103856Stjrstatic uint64_t cur_buffer_size = 0;
79103856Stjrstatic uint64_t cur_pos = 0;
80117249Stjrstatic uint64_t file_size = 0;
81117249Stjrstatic int new_file = 0;
82103856Stjrstatic int fd = -1;
83103856Stjr
84103856Stjr/*
85103856Stjr * A list of functions to write out the data.
86103856Stjr */
87125283Sdastypedef void (*writeout_fn)();
88103856Stjr
89103856Stjrstruct writeout_fn_node {
90103856Stjr  writeout_fn fn;
91103856Stjr  struct writeout_fn_node *next;
92103856Stjr};
93103856Stjr
94103856Stjrstatic struct writeout_fn_node *writeout_fn_head = NULL;
95103856Stjrstatic struct writeout_fn_node *writeout_fn_tail = NULL;
96103856Stjr
97103856Stjr/*
98157381Sphk *  A list of flush functions that our __gcov_flush() function should call.
99117249Stjr */
100157381Sphktypedef void (*flush_fn)();
101117249Stjr
102103856Stjrstruct flush_fn_node {
103103856Stjr  flush_fn fn;
104103856Stjr  struct flush_fn_node *next;
105103856Stjr};
106187422Sdas
107187422Sdasstatic struct flush_fn_node *flush_fn_head = NULL;
108103856Stjrstatic struct flush_fn_node *flush_fn_tail = NULL;
109103856Stjr
110103856Stjrstatic void resize_write_buffer(uint64_t size) {
111103856Stjr  if (!new_file) return;
112103856Stjr  size += cur_pos;
113103856Stjr  if (size <= cur_buffer_size) return;
114103856Stjr  size = (size - 1) / WRITE_BUFFER_SIZE + 1;
115103856Stjr  size *= WRITE_BUFFER_SIZE;
116103856Stjr  write_buffer = realloc(write_buffer, size);
117103856Stjr  cur_buffer_size = size;
118103856Stjr}
119103856Stjr
120103856Stjrstatic void write_bytes(const char *s, size_t len) {
121103856Stjr  resize_write_buffer(len);
122103856Stjr  memcpy(&write_buffer[cur_pos], s, len);
123103856Stjr  cur_pos += len;
124103856Stjr}
125103856Stjr
126103856Stjrstatic void write_32bit_value(uint32_t i) {
127103856Stjr  write_bytes((char*)&i, 4);
128103856Stjr}
129103856Stjr
130103856Stjrstatic void write_64bit_value(uint64_t i) {
131103856Stjr  write_bytes((char*)&i, 8);
132103856Stjr}
133103856Stjr
134103856Stjrstatic uint32_t length_of_string(const char *s) {
135103856Stjr  return (strlen(s) / 4) + 1;
136103856Stjr}
137103856Stjr
138103856Stjrstatic void write_string(const char *s) {
139103856Stjr  uint32_t len = length_of_string(s);
140103856Stjr  write_32bit_value(len);
141103856Stjr  write_bytes(s, strlen(s));
142103856Stjr  write_bytes("\0\0\0\0", 4 - (strlen(s) % 4));
143103856Stjr}
144103856Stjr
145103856Stjrstatic uint32_t read_32bit_value() {
146105317Stjr  uint32_t val;
147128002Stjr
148103856Stjr  if (new_file)
149103856Stjr    return (uint32_t)-1;
150103856Stjr
151103856Stjr  val = *(uint32_t*)&write_buffer[cur_pos];
152103856Stjr  cur_pos += 4;
153103856Stjr  return val;
154103856Stjr}
155103856Stjr
156103856Stjrstatic uint64_t read_64bit_value() {
157103856Stjr  uint64_t val;
158103856Stjr
159103856Stjr  if (new_file)
160103856Stjr    return (uint64_t)-1;
161103856Stjr
162103856Stjr  val = *(uint64_t*)&write_buffer[cur_pos];
163103856Stjr  cur_pos += 8;
164103856Stjr  return val;
165103856Stjr}
166103856Stjr
167103856Stjrstatic char *mangle_filename(const char *orig_filename) {
168103856Stjr  char *new_filename;
169103856Stjr  size_t filename_len, prefix_len;
170103856Stjr  int prefix_strip;
171103856Stjr  int level = 0;
172103856Stjr  const char *fname, *ptr;
173103856Stjr  const char *prefix = getenv("GCOV_PREFIX");
174103856Stjr  const char *prefix_strip_str = getenv("GCOV_PREFIX_STRIP");
175103856Stjr
176103856Stjr  if (prefix == NULL || prefix[0] == '\0')
177103856Stjr    return strdup(orig_filename);
178103856Stjr
179103856Stjr  if (prefix_strip_str) {
180103856Stjr    prefix_strip = atoi(prefix_strip_str);
181103856Stjr
182103856Stjr    /* Negative GCOV_PREFIX_STRIP values are ignored */
183103856Stjr    if (prefix_strip < 0)
184103856Stjr      prefix_strip = 0;
185103856Stjr  } else {
186103856Stjr    prefix_strip = 0;
187103856Stjr  }
188103856Stjr
189103856Stjr  fname = orig_filename;
190103856Stjr  for (level = 0, ptr = fname + 1; level < prefix_strip; ++ptr) {
191103856Stjr    if (*ptr == '\0')
192103856Stjr      break;
193103856Stjr    if (*ptr != '/')
194103856Stjr      continue;
195103856Stjr    fname = ptr;
196103856Stjr    ++level;
197103856Stjr  }
198103856Stjr
199103856Stjr  filename_len = strlen(fname);
200103856Stjr  prefix_len = strlen(prefix);
201103856Stjr  new_filename = malloc(prefix_len + 1 + filename_len + 1);
202103856Stjr  memcpy(new_filename, prefix, prefix_len);
203103856Stjr
204103856Stjr  if (prefix[prefix_len - 1] != '/')
205103856Stjr    new_filename[prefix_len++] = '/';
206103856Stjr  memcpy(new_filename + prefix_len, fname, filename_len + 1);
207103856Stjr
208103856Stjr  return new_filename;
209103856Stjr}
210103856Stjr
211103856Stjrstatic void recursive_mkdir(char *path) {
212103856Stjr  int i;
213103856Stjr
214103856Stjr  for (i = 1; path[i] != '\0'; ++i) {
215103856Stjr    if (path[i] != '/') continue;
216103856Stjr    path[i] = '\0';
217103856Stjr#ifdef _WIN32
218103856Stjr    _mkdir(path);
219103856Stjr#else
220103856Stjr    mkdir(path, 0755);  /* Some of these will fail, ignore it. */
221103856Stjr#endif
222103856Stjr    path[i] = '/';
223103856Stjr  }
224103856Stjr}
225103856Stjr
226103856Stjrstatic int map_file() {
227103856Stjr  fseek(output_file, 0L, SEEK_END);
228103856Stjr  file_size = ftell(output_file);
229103856Stjr
230103856Stjr  /* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an
231103856Stjr   * error message because it should "just work" for the user. */
232103856Stjr  if (file_size == 0)
233103856Stjr    return -1;
234103856Stjr
235103856Stjr  write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE,
236103856Stjr                      MAP_FILE | MAP_SHARED, fd, 0);
237103856Stjr  if (write_buffer == (void *)-1) {
238103856Stjr    int errnum = errno;
239103856Stjr    fprintf(stderr, "profiling: %s: cannot map: %s\n", filename,
240103856Stjr            strerror(errnum));
241103856Stjr    return -1;
242103856Stjr  }
243103856Stjr  return 0;
244103856Stjr}
245103856Stjr
246103856Stjrstatic void unmap_file() {
247103856Stjr  if (msync(write_buffer, file_size, MS_SYNC) == -1) {
248103856Stjr    int errnum = errno;
249103856Stjr    fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename,
250103856Stjr            strerror(errnum));
251103856Stjr  }
252103856Stjr
253103856Stjr  /* We explicitly ignore errors from unmapping because at this point the data
254103856Stjr   * is written and we don't care.
255103856Stjr   */
256103856Stjr  (void)munmap(write_buffer, file_size);
257103856Stjr  write_buffer = NULL;
258103856Stjr  file_size = 0;
259103856Stjr}
260103856Stjr
261128822Sdas/*
262117249Stjr * --- LLVM line counter API ---
263117249Stjr */
264103856Stjr
265103856Stjr/* A file in this case is a translation unit. Each .o file built with line
266103856Stjr * profiling enabled will emit to a different file. Only one file may be
267103856Stjr * started at a time.
268103856Stjr */
269103856Stjrvoid llvm_gcda_start_file(const char *orig_filename, const char version[4],
270103856Stjr                          uint32_t checksum) {
271103856Stjr  const char *mode = "r+b";
272103856Stjr  filename = mangle_filename(orig_filename);
273103856Stjr
274103856Stjr  /* Try just opening the file. */
275103856Stjr  new_file = 0;
276103856Stjr  fd = open(filename, O_RDWR);
277103856Stjr
278103856Stjr  if (fd == -1) {
279103856Stjr    /* Try opening the file, creating it if necessary. */
280103856Stjr    new_file = 1;
281103856Stjr    mode = "w+b";
282103856Stjr    fd = open(filename, O_RDWR | O_CREAT, 0644);
283103856Stjr    if (fd == -1) {
284103856Stjr      /* Try creating the directories first then opening the file. */
285103856Stjr      recursive_mkdir(filename);
286103856Stjr      fd = open(filename, O_RDWR | O_CREAT, 0644);
287103856Stjr      if (fd == -1) {
288103856Stjr        /* Bah! It's hopeless. */
289103856Stjr        int errnum = errno;
290103856Stjr        fprintf(stderr, "profiling: %s: cannot open: %s\n", filename,
291103856Stjr                strerror(errnum));
292103856Stjr        return;
293103856Stjr      }
294103856Stjr    }
295103856Stjr  }
296103856Stjr
297103856Stjr  output_file = fdopen(fd, mode);
298103856Stjr
299103856Stjr  /* Initialize the write buffer. */
300103856Stjr  write_buffer = NULL;
301103856Stjr  cur_buffer_size = 0;
302103856Stjr  cur_pos = 0;
303103856Stjr
304103856Stjr  if (new_file) {
305103856Stjr    resize_write_buffer(WRITE_BUFFER_SIZE);
306103856Stjr    memset(write_buffer, 0, WRITE_BUFFER_SIZE);
307103856Stjr  } else {
308103856Stjr    if (map_file() == -1) {
309103856Stjr      /* mmap failed, try to recover by clobbering */
310103856Stjr      new_file = 1;
311103856Stjr      write_buffer = NULL;
312103856Stjr      cur_buffer_size = 0;
313103856Stjr      resize_write_buffer(WRITE_BUFFER_SIZE);
314103856Stjr      memset(write_buffer, 0, WRITE_BUFFER_SIZE);
315103856Stjr    }
316103856Stjr  }
317103856Stjr
318103856Stjr  /* gcda file, version, stamp checksum. */
319103856Stjr  write_bytes("adcg", 4);
320103856Stjr  write_bytes(version, 4);
321103856Stjr  write_32bit_value(checksum);
322103856Stjr
323103856Stjr#ifdef DEBUG_GCDAPROFILING
324103856Stjr  fprintf(stderr, "llvmgcda: [%s]\n", orig_filename);
325103856Stjr#endif
326103856Stjr}
327103856Stjr
328103856Stjr/* Given an array of pointers to counters (counters), increment the n-th one,
329103856Stjr * where we're also given a pointer to n (predecessor).
330103856Stjr */
331103856Stjrvoid llvm_gcda_increment_indirect_counter(uint32_t *predecessor,
332103856Stjr                                          uint64_t **counters) {
333103856Stjr  uint64_t *counter;
334103856Stjr  uint32_t pred;
335103856Stjr
336103856Stjr  pred = *predecessor;
337103856Stjr  if (pred == 0xffffffff)
338103856Stjr    return;
339103856Stjr  counter = counters[pred];
340103856Stjr
341103856Stjr  /* Don't crash if the pred# is out of sync. This can happen due to threads,
342103856Stjr     or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */
343103856Stjr  if (counter)
344103856Stjr    ++*counter;
345103856Stjr#ifdef DEBUG_GCDAPROFILING
346103856Stjr  else
347103856Stjr    fprintf(stderr,
348103856Stjr            "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n",
349103856Stjr            *counter, *predecessor);
350103856Stjr#endif
351103856Stjr}
352103856Stjr
353103856Stjrvoid llvm_gcda_emit_function(uint32_t ident, const char *function_name,
354103856Stjr                             uint32_t func_checksum, uint8_t use_extra_checksum,
355103856Stjr                             uint32_t cfg_checksum) {
356103856Stjr  uint32_t len = 2;
357103856Stjr
358103856Stjr  if (use_extra_checksum)
359103856Stjr    len++;
360105317Stjr#ifdef DEBUG_GCDAPROFILING
361105317Stjr  fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident,
362105317Stjr          function_name ? function_name : "NULL");
363103856Stjr#endif
364103856Stjr  if (!output_file) return;
365103856Stjr
366105317Stjr  /* function tag */
367105317Stjr  write_bytes("\0\0\0\1", 4);
368103856Stjr  if (function_name)
369103856Stjr    len += 1 + length_of_string(function_name);
370103856Stjr  write_32bit_value(len);
371103856Stjr  write_32bit_value(ident);
372103856Stjr  write_32bit_value(func_checksum);
373105317Stjr  if (use_extra_checksum)
374105317Stjr    write_32bit_value(cfg_checksum);
375103856Stjr  if (function_name)
376105317Stjr    write_string(function_name);
377105317Stjr}
378103856Stjr
379187422Sdasvoid llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) {
380105317Stjr  uint32_t i;
381103856Stjr  uint64_t *old_ctrs = NULL;
382105317Stjr  uint32_t val = 0;
383105317Stjr  uint64_t save_cur_pos = cur_pos;
384128002Stjr
385105317Stjr  if (!output_file) return;
386105317Stjr
387105317Stjr  val = read_32bit_value();
388105317Stjr
389128002Stjr  if (val != (uint32_t)-1) {
390105317Stjr    /* There are counters present in the file. Merge them. */
391105317Stjr    if (val != 0x01a10000) {
392105317Stjr      fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: "
393105317Stjr                      "corrupt arc tag (0x%08x)\n",
394105317Stjr              filename, val);
395105317Stjr      return;
396105317Stjr    }
397105317Stjr
398105317Stjr    val = read_32bit_value();
399105317Stjr    if (val == (uint32_t)-1 || val / 2 != num_counters) {
400105317Stjr      fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: "
401105317Stjr                      "mismatched number of counters (%d)\n",
402105317Stjr              filename, val);
403103856Stjr      return;
404103856Stjr    }
405103856Stjr
406103856Stjr    old_ctrs = malloc(sizeof(uint64_t) * num_counters);
407103856Stjr    for (i = 0; i < num_counters; ++i)
408105317Stjr      old_ctrs[i] = read_64bit_value();
409105317Stjr  }
410103856Stjr
411103856Stjr  cur_pos = save_cur_pos;
412103856Stjr
413103856Stjr  /* Counter #1 (arcs) tag */
414103856Stjr  write_bytes("\0\0\xa1\1", 4);
415103856Stjr  write_32bit_value(num_counters * 2);
416103856Stjr  for (i = 0; i < num_counters; ++i) {
417103856Stjr    counters[i] += (old_ctrs ? old_ctrs[i] : 0);
418103856Stjr    write_64bit_value(counters[i]);
419105317Stjr  }
420103856Stjr
421103856Stjr  free(old_ctrs);
422103856Stjr
423103856Stjr#ifdef DEBUG_GCDAPROFILING
424103856Stjr  fprintf(stderr, "llvmgcda:   %u arcs\n", num_counters);
425103856Stjr  for (i = 0; i < num_counters; ++i)
426103856Stjr    fprintf(stderr, "llvmgcda:   %llu\n", (unsigned long long)counters[i]);
427103856Stjr#endif
428103856Stjr}
429103856Stjr
430103856Stjrvoid llvm_gcda_summary_info() {
431103856Stjr  const uint32_t obj_summary_len = 9; /* Length for gcov compatibility. */
432103856Stjr  uint32_t i;
433103856Stjr  uint32_t runs = 1;
434103856Stjr  uint32_t val = 0;
435103856Stjr  uint64_t save_cur_pos = cur_pos;
436103856Stjr
437103856Stjr  if (!output_file) return;
438103856Stjr
439103856Stjr  val = read_32bit_value();
440103856Stjr
441105317Stjr  if (val != (uint32_t)-1) {
442105317Stjr    /* There are counters present in the file. Merge them. */
443103856Stjr    if (val != 0xa1000000) {
444187422Sdas      fprintf(stderr, "profiling: %s: cannot merge previous run count: "
445103856Stjr                      "corrupt object tag (0x%08x)\n",
446105317Stjr              filename, val);
447105317Stjr      return;
448105317Stjr    }
449128002Stjr
450105317Stjr    val = read_32bit_value(); /* length */
451105317Stjr    if (val != obj_summary_len) {
452105317Stjr      fprintf(stderr, "profiling: %s: cannot merge previous run count: "
453105317Stjr                      "mismatched object length (%d)\n",
454128002Stjr              filename, val);
455105317Stjr      return;
456105317Stjr    }
457105317Stjr
458105317Stjr    read_32bit_value(); /* checksum, unused */
459105317Stjr    read_32bit_value(); /* num, unused */
460105317Stjr    runs += read_32bit_value(); /* Add previous run count to new counter. */
461105317Stjr  }
462105317Stjr
463105317Stjr  cur_pos = save_cur_pos;
464105317Stjr
465105317Stjr  /* Object summary tag */
466103856Stjr  write_bytes("\0\0\0\xa1", 4);
467103856Stjr  write_32bit_value(obj_summary_len);
468103856Stjr  write_32bit_value(0); /* checksum, unused */
469103856Stjr  write_32bit_value(0); /* num, unused */
470105317Stjr  write_32bit_value(runs);
471105317Stjr  for (i = 3; i < obj_summary_len; ++i)
472105317Stjr    write_32bit_value(0);
473105317Stjr
474103856Stjr  /* Program summary tag */
475103856Stjr  write_bytes("\0\0\0\xa3", 4); /* tag indicates 1 program */
476103856Stjr  write_32bit_value(0); /* 0 length */
477103856Stjr
478103856Stjr#ifdef DEBUG_GCDAPROFILING
479103856Stjr  fprintf(stderr, "llvmgcda:   %u runs\n", runs);
480103856Stjr#endif
481103856Stjr}
482103856Stjr
483105317Stjrvoid llvm_gcda_end_file() {
484103856Stjr  /* Write out EOF record. */
485103856Stjr  if (output_file) {
486103856Stjr    write_bytes("\0\0\0\0\0\0\0\0", 8);
487103856Stjr
488103856Stjr    if (new_file) {
489103856Stjr      fwrite(write_buffer, cur_pos, 1, output_file);
490103856Stjr      free(write_buffer);
491103856Stjr    } else {
492103856Stjr      unmap_file();
493103856Stjr    }
494103856Stjr
495103856Stjr    fclose(output_file);
496103856Stjr    output_file = NULL;
497103856Stjr    write_buffer = NULL;
498103856Stjr  }
499103856Stjr  free(filename);
500103856Stjr
501103856Stjr#ifdef DEBUG_GCDAPROFILING
502103856Stjr  fprintf(stderr, "llvmgcda: -----\n");
503105317Stjr#endif
504105317Stjr}
505187422Sdas
506103856Stjrvoid llvm_register_writeout_function(writeout_fn fn) {
507105317Stjr  struct writeout_fn_node *new_node = malloc(sizeof(struct writeout_fn_node));
508103856Stjr  new_node->fn = fn;
509105317Stjr  new_node->next = NULL;
510105317Stjr
511128002Stjr  if (!writeout_fn_head) {
512105317Stjr    writeout_fn_head = writeout_fn_tail = new_node;
513105317Stjr  } else {
514105317Stjr    writeout_fn_tail->next = new_node;
515105317Stjr    writeout_fn_tail = new_node;
516128002Stjr  }
517105317Stjr}
518105317Stjr
519105317Stjrvoid llvm_writeout_files() {
520105317Stjr  struct writeout_fn_node *curr = writeout_fn_head;
521105317Stjr
522105317Stjr  while (curr) {
523105317Stjr    curr->fn();
524105317Stjr    curr = curr->next;
525105317Stjr  }
526105317Stjr}
527105317Stjr
528103856Stjrvoid llvm_delete_writeout_function_list() {
529103856Stjr  while (writeout_fn_head) {
530103856Stjr    struct writeout_fn_node *node = writeout_fn_head;
531103856Stjr    writeout_fn_head = writeout_fn_head->next;
532105317Stjr    free(node);
533105317Stjr  }
534105317Stjr
535105317Stjr  writeout_fn_head = writeout_fn_tail = NULL;
536103856Stjr}
537103856Stjr
538103856Stjrvoid llvm_register_flush_function(flush_fn fn) {
539103856Stjr  struct flush_fn_node *new_node = malloc(sizeof(struct flush_fn_node));
540103856Stjr  new_node->fn = fn;
541103856Stjr  new_node->next = NULL;
542117250Stjr
543117250Stjr  if (!flush_fn_head) {
544117250Stjr    flush_fn_head = flush_fn_tail = new_node;
545103856Stjr  } else {
546103856Stjr    flush_fn_tail->next = new_node;
547103856Stjr    flush_fn_tail = new_node;
548103856Stjr  }
549103856Stjr}
550103856Stjr
551103856Stjrvoid __gcov_flush() {
552103856Stjr  struct flush_fn_node *curr = flush_fn_head;
553103856Stjr
554103856Stjr  while (curr) {
555103856Stjr    curr->fn();
556103856Stjr    curr = curr->next;
557103856Stjr  }
558103856Stjr}
559103856Stjr
560103856Stjrvoid llvm_delete_flush_function_list() {
561103856Stjr  while (flush_fn_head) {
562103856Stjr    struct flush_fn_node *node = flush_fn_head;
563103856Stjr    flush_fn_head = flush_fn_head->next;
564103856Stjr    free(node);
565103856Stjr  }
566103856Stjr
567103856Stjr  flush_fn_head = flush_fn_tail = NULL;
568103856Stjr}
569103856Stjr
570103856Stjrvoid llvm_gcov_init(writeout_fn wfn, flush_fn ffn) {
571103856Stjr  static int atexit_ran = 0;
572103856Stjr
573103856Stjr  if (wfn)
574103856Stjr    llvm_register_writeout_function(wfn);
575103856Stjr
576103856Stjr  if (ffn)
577103856Stjr    llvm_register_flush_function(ffn);
578103856Stjr
579103856Stjr  if (atexit_ran == 0) {
580103856Stjr    atexit_ran = 1;
581103856Stjr
582103856Stjr    /* Make sure we write out the data and delete the data structures. */
583103856Stjr    atexit(llvm_delete_flush_function_list);
584103856Stjr    atexit(llvm_delete_writeout_function_list);
585103856Stjr    atexit(llvm_writeout_files);
586103856Stjr  }
587103856Stjr}
588103856Stjr