aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Makefile.inc1
-rw-r--r--src/lib/cbmem.c2
-rw-r--r--src/lib/gcov-glue.c153
-rw-r--r--src/lib/gcov-io.c552
-rw-r--r--src/lib/gcov-io.h639
-rw-r--r--src/lib/gcov-iov.h4
-rw-r--r--src/lib/hardwaremain.c5
-rw-r--r--src/lib/libgcov.c1157
-rw-r--r--src/lib/selfboot.c4
9 files changed, 2517 insertions, 0 deletions
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc
index 6b3f0d8a59..67964482b1 100644
--- a/src/lib/Makefile.inc
+++ b/src/lib/Makefile.inc
@@ -70,6 +70,7 @@ ramstage-$(CONFIG_USBDEBUG) += usbdebug.c
ramstage-$(CONFIG_BOOTSPLASH) += jpeg.c
ramstage-$(CONFIG_TRACE) += trace.c
ramstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c
+ramstage-$(CONFIG_COVERAGE) += libgcov.c
ramstage-$(CONFIG_CONSOLE_NE2K) += ne2k.c
diff --git a/src/lib/cbmem.c b/src/lib/cbmem.c
index 7378d8c02c..f270f5d89b 100644
--- a/src/lib/cbmem.c
+++ b/src/lib/cbmem.c
@@ -260,6 +260,8 @@ void cbmem_list(void)
case CBMEM_ID_TIMESTAMP: printk(BIOS_DEBUG, "TIME STAMP "); break;
case CBMEM_ID_MRCDATA: printk(BIOS_DEBUG, "MRC DATA "); break;
case CBMEM_ID_CONSOLE: printk(BIOS_DEBUG, "CONSOLE "); break;
+ case CBMEM_ID_ELOG: printk(BIOS_DEBUG, "ELOG "); break;
+ case CBMEM_ID_COVERAGE: printk(BIOS_DEBUG, "COVERAGE "); break;
default: printk(BIOS_DEBUG, "%08x ", cbmem_toc[i].id);
}
printk(BIOS_DEBUG, "%08llx ", cbmem_toc[i].base);
diff --git a/src/lib/gcov-glue.c b/src/lib/gcov-glue.c
new file mode 100644
index 0000000000..7c069cbbca
--- /dev/null
+++ b/src/lib/gcov-glue.c
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Google, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <cbmem.h>
+#include <coverage.h>
+
+typedef struct file {
+ uint32_t magic;
+ struct file *next;
+ char *filename;
+ char *data;
+ int offset;
+ int len;
+} FILE;
+
+#define SEEK_SET 0 /* Seek from beginning of file. */
+
+#define DIR_SEPARATOR '/'
+#define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#define HAS_DRIVE_SPEC(f) (0)
+
+#define COVERAGE_SIZE (32*1024)
+
+static FILE *current_file = NULL;
+static FILE *previous_file = NULL;
+
+static FILE *fopen(const char *path, const char *mode)
+{
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "fopen %s with mode %s\n",
+ path, mode);
+#endif
+ if (!current_file) {
+ current_file = cbmem_add(CBMEM_ID_COVERAGE, 32*1024);
+ } else {
+ previous_file = current_file;
+ current_file = (FILE *)(ALIGN(((unsigned long)previous_file->data + previous_file->len), 16));
+ }
+
+ // TODO check if we're at the end of the CBMEM region (ENOMEM)
+ if (current_file) {
+ current_file->magic = 0x584d4153;
+ current_file->next = NULL;
+ if (previous_file)
+ previous_file->next = current_file;
+ current_file->filename = (char *)&current_file[1];
+ strcpy(current_file->filename, path);
+ current_file->data = (char *)ALIGN(((unsigned long)current_file->filename + strlen(path) + 1), 16);
+ current_file->offset = 0;
+ current_file->len = 0;
+ }
+
+ return current_file;
+}
+
+static int fclose(FILE *stream)
+{
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "fclose %s\n", stream->filename);
+#endif
+ return 0;
+}
+
+static int fseek(FILE *stream, long offset, int whence)
+{
+ /* fseek should only be called with offset==0 and whence==SEEK_SET
+ * to a freshly opened file. */
+ gcc_assert (offset == 0 && whence == SEEK_SET);
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "fseek %s offset=%d whence=%d\n",
+ stream->filename, offset, whence);
+#endif
+ return 0;
+}
+
+static long ftell(FILE *stream)
+{
+ /* ftell should currently not be called */
+ gcc_assert(0);
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "ftell %s\n", stream->filename);
+#endif
+ return 0;
+}
+
+static size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "fread: ptr=%p size=%zd nmemb=%zd FILE*=%p\n",
+ ptr, size, nmemb, stream);
+#endif
+ return 0;
+}
+
+static size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "fwrite: %zd * 0x%zd bytes to file %s\n",
+ nmemb, size, stream->filename);
+#endif
+ // TODO check if file is last opened file and fail otherwise.
+
+ memcpy(stream->data + stream->offset, ptr, size * nmemb);
+ stream->len += (nmemb * size) - (stream->len - stream->offset);
+ stream->offset += nmemb * size;
+ return nmemb;
+}
+
+static void setbuf(FILE *stream, char *buf)
+{
+ gcc_assert(buf == 0);
+}
+
+void coverage_init(void)
+{
+ extern long __CTOR_LIST__;
+ typedef void (*func_ptr)(void) ;
+ func_ptr *ctor = (func_ptr*) &__CTOR_LIST__;
+ if (ctor == NULL)
+ return;
+
+ for ( ; *ctor != (func_ptr) 0; ctor++) {
+ (*ctor)();
+ }
+}
+
+void __gcov_flush(void);
+void coverage_exit(void)
+{
+#if CONFIG_DEBUG_COVERAGE
+ printk(BIOS_DEBUG, "Syncing coverage data.\n");
+#endif
+ __gcov_flush();
+}
+
+
diff --git a/src/lib/gcov-io.c b/src/lib/gcov-io.c
new file mode 100644
index 0000000000..37c1c3e350
--- /dev/null
+++ b/src/lib/gcov-io.c
@@ -0,0 +1,552 @@
+/* File format for coverage information
+ Copyright (C) 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005, 2007,
+ 2008 Free Software Foundation, Inc.
+ Contributed by Bob Manson <manson@cygnus.com>.
+ Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* Routines declared in gcov-io.h. This file should be #included by
+ another source file, after having #included gcov-io.h. */
+
+#if !IN_GCOV
+static void gcov_write_block (unsigned);
+static gcov_unsigned_t *gcov_write_words (unsigned);
+#endif
+static const gcov_unsigned_t *gcov_read_words (unsigned);
+#if !IN_LIBGCOV
+static void gcov_allocate (unsigned);
+#endif
+
+static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
+{
+#if !IN_LIBGCOV
+ if (gcov_var.endian)
+ {
+ value = (value >> 16) | (value << 16);
+ value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
+ }
+#endif
+ return value;
+}
+
+/* Open a gcov file. NAME is the name of the file to open and MODE
+ indicates whether a new file should be created, or an existing file
+ opened. If MODE is >= 0 an existing file will be opened, if
+ possible, and if MODE is <= 0, a new file will be created. Use
+ MODE=0 to attempt to reopen an existing file and then fall back on
+ creating a new one. If MODE < 0, the file will be opened in
+ read-only mode. Otherwise it will be opened for modification.
+ Return zero on failure, >0 on opening an existing file and <0 on
+ creating a new one. */
+
+GCOV_LINKAGE int
+#if IN_LIBGCOV
+gcov_open (const char *name)
+#else
+gcov_open (const char *name, int mode)
+#endif
+{
+#if IN_LIBGCOV
+ const int mode = 0;
+#endif
+#if GCOV_LOCKED
+ struct flock s_flock;
+ int fd;
+
+ s_flock.l_whence = SEEK_SET;
+ s_flock.l_start = 0;
+ s_flock.l_len = 0; /* Until EOF. */
+ s_flock.l_pid = getpid ();
+#endif
+
+ gcc_assert (!gcov_var.file);
+ gcov_var.start = 0;
+ gcov_var.offset = gcov_var.length = 0;
+ gcov_var.overread = -1u;
+ gcov_var.error = 0;
+#if !IN_LIBGCOV
+ gcov_var.endian = 0;
+#endif
+#if GCOV_LOCKED
+ if (mode > 0)
+ {
+ /* Read-only mode - acquire a read-lock. */
+ s_flock.l_type = F_RDLCK;
+ fd = open (name, O_RDONLY);
+ }
+ else
+ {
+ /* Write mode - acquire a write-lock. */
+ s_flock.l_type = F_WRLCK;
+ fd = open (name, O_RDWR | O_CREAT, 0666);
+ }
+ if (fd < 0)
+ return 0;
+
+ while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
+ continue;
+
+ gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
+
+ if (!gcov_var.file)
+ {
+ close (fd);
+ return 0;
+ }
+
+ if (mode > 0)
+ gcov_var.mode = 1;
+ else if (mode == 0)
+ {
+ struct stat st;
+
+ if (fstat (fd, &st) < 0)
+ {
+ fclose (gcov_var.file);
+ gcov_var.file = 0;
+ return 0;
+ }
+ if (st.st_size != 0)
+ gcov_var.mode = 1;
+ else
+ gcov_var.mode = mode * 2 + 1;
+ }
+ else
+ gcov_var.mode = mode * 2 + 1;
+#else
+ if (mode >= 0)
+ gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
+
+ if (gcov_var.file)
+ gcov_var.mode = 1;
+ else if (mode <= 0)
+ {
+ gcov_var.file = fopen (name, "w+b");
+ if (gcov_var.file)
+ gcov_var.mode = mode * 2 + 1;
+ }
+ if (!gcov_var.file)
+ return 0;
+#endif
+
+ setbuf (gcov_var.file, (char *)0);
+
+ return 1;
+}
+
+/* Close the current gcov file. Flushes data to disk. Returns nonzero
+ on failure or error flag set. */
+
+GCOV_LINKAGE int
+gcov_close (void)
+{
+ if (gcov_var.file)
+ {
+#if !IN_GCOV
+ if (gcov_var.offset && gcov_var.mode < 0)
+ gcov_write_block (gcov_var.offset);
+#endif
+ fclose (gcov_var.file);
+ gcov_var.file = 0;
+ gcov_var.length = 0;
+ }
+#if !IN_LIBGCOV
+ free (gcov_var.buffer);
+ gcov_var.alloc = 0;
+ gcov_var.buffer = 0;
+#endif
+ gcov_var.mode = 0;
+ return gcov_var.error;
+}
+
+#if !IN_LIBGCOV
+/* Check if MAGIC is EXPECTED. Use it to determine endianness of the
+ file. Returns +1 for same endian, -1 for other endian and zero for
+ not EXPECTED. */
+
+GCOV_LINKAGE int
+gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
+{
+ if (magic == expected)
+ return 1;
+ magic = (magic >> 16) | (magic << 16);
+ magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
+ if (magic == expected)
+ {
+ gcov_var.endian = 1;
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+#if !IN_LIBGCOV
+static void
+gcov_allocate (unsigned length)
+{
+ size_t new_size = gcov_var.alloc;
+
+ if (!new_size)
+ new_size = GCOV_BLOCK_SIZE;
+ new_size += length;
+ new_size *= 2;
+
+ gcov_var.alloc = new_size;
+ gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
+}
+#endif
+
+#if !IN_GCOV
+/* Write out the current block, if needs be. */
+
+static void
+gcov_write_block (unsigned size)
+{
+ if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
+ gcov_var.error = 1;
+ gcov_var.start += size;
+ gcov_var.offset -= size;
+}
+
+/* Allocate space to write BYTES bytes to the gcov file. Return a
+ pointer to those bytes, or NULL on failure. */
+
+static gcov_unsigned_t *
+gcov_write_words (unsigned words)
+{
+ gcov_unsigned_t *result;
+
+ gcc_assert (gcov_var.mode < 0);
+#if IN_LIBGCOV
+ if (gcov_var.offset >= GCOV_BLOCK_SIZE)
+ {
+ gcov_write_block (GCOV_BLOCK_SIZE);
+ if (gcov_var.offset)
+ {
+ gcc_assert (gcov_var.offset == 1);
+ memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
+ }
+ }
+#else
+ if (gcov_var.offset + words > gcov_var.alloc)
+ gcov_allocate (gcov_var.offset + words);
+#endif
+ result = &gcov_var.buffer[gcov_var.offset];
+ gcov_var.offset += words;
+
+ return result;
+}
+
+/* Write unsigned VALUE to coverage file. Sets error flag
+ appropriately. */
+
+GCOV_LINKAGE void
+gcov_write_unsigned (gcov_unsigned_t value)
+{
+ gcov_unsigned_t *buffer = gcov_write_words (1);
+
+ buffer[0] = value;
+}
+
+/* Write counter VALUE to coverage file. Sets error flag
+ appropriately. */
+
+#if IN_LIBGCOV
+GCOV_LINKAGE void
+gcov_write_counter (gcov_type value)
+{
+ gcov_unsigned_t *buffer = gcov_write_words (2);
+
+ buffer[0] = (gcov_unsigned_t) value;
+ if (sizeof (value) > sizeof (gcov_unsigned_t))
+ buffer[1] = (gcov_unsigned_t) (value >> 32);
+ else
+ buffer[1] = 0;
+}
+#endif /* IN_LIBGCOV */
+
+#if !IN_LIBGCOV
+/* Write STRING to coverage file. Sets error flag on file
+ error, overflow flag on overflow */
+
+GCOV_LINKAGE void
+gcov_write_string (const char *string)
+{
+ unsigned length = 0;
+ unsigned alloc = 0;
+ gcov_unsigned_t *buffer;
+
+ if (string)
+ {
+ length = strlen (string);
+ alloc = (length + 4) >> 2;
+ }
+
+ buffer = gcov_write_words (1 + alloc);
+
+ buffer[0] = alloc;
+ buffer[alloc] = 0;
+ memcpy (&buffer[1], string, length);
+}
+#endif
+
+#if !IN_LIBGCOV
+/* Write a tag TAG and reserve space for the record length. Return a
+ value to be used for gcov_write_length. */
+
+GCOV_LINKAGE gcov_position_t
+gcov_write_tag (gcov_unsigned_t tag)
+{
+ gcov_position_t result = gcov_var.start + gcov_var.offset;
+ gcov_unsigned_t *buffer = gcov_write_words (2);
+
+ buffer[0] = tag;
+ buffer[1] = 0;
+
+ return result;
+}
+
+/* Write a record length using POSITION, which was returned by
+ gcov_write_tag. The current file position is the end of the
+ record, and is restored before returning. Returns nonzero on
+ overflow. */
+
+GCOV_LINKAGE void
+gcov_write_length (gcov_position_t position)
+{
+ unsigned offset;
+ gcov_unsigned_t length;
+ gcov_unsigned_t *buffer;
+
+ gcc_assert (gcov_var.mode < 0);
+ gcc_assert (position + 2 <= gcov_var.start + gcov_var.offset);
+ gcc_assert (position >= gcov_var.start);
+ offset = position - gcov_var.start;
+ length = gcov_var.offset - offset - 2;
+ buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
+ buffer[1] = length;
+ if (gcov_var.offset >= GCOV_BLOCK_SIZE)
+ gcov_write_block (gcov_var.offset);
+}
+
+#else /* IN_LIBGCOV */
+
+/* Write a tag TAG and length LENGTH. */
+
+GCOV_LINKAGE void
+gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
+{
+ gcov_unsigned_t *buffer = gcov_write_words (2);
+
+ buffer[0] = tag;
+ buffer[1] = length;
+}
+
+/* Write a summary structure to the gcov file. Return nonzero on
+ overflow. */
+
+GCOV_LINKAGE void
+gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
+{
+ unsigned ix;
+ const struct gcov_ctr_summary *csum;
+
+ gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
+ gcov_write_unsigned (summary->checksum);
+ for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
+ {
+ gcov_write_unsigned (csum->num);
+ gcov_write_unsigned (csum->runs);
+ gcov_write_counter (csum->sum_all);
+ gcov_write_counter (csum->run_max);
+ gcov_write_counter (csum->sum_max);
+ }
+}
+#endif /* IN_LIBGCOV */
+
+#endif /*!IN_GCOV */
+
+/* Return a pointer to read BYTES bytes from the gcov file. Returns
+ NULL on failure (read past EOF). */
+
+static const gcov_unsigned_t *
+gcov_read_words (unsigned words)
+{
+ const gcov_unsigned_t *result;
+ unsigned excess = gcov_var.length - gcov_var.offset;
+
+ gcc_assert (gcov_var.mode > 0);
+ if (excess < words)
+ {
+ gcov_var.start += gcov_var.offset;
+#if IN_LIBGCOV
+ if (excess)
+ {
+ gcc_assert (excess == 1);
+ memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
+ }
+#else
+ memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4);
+#endif
+ gcov_var.offset = 0;
+ gcov_var.length = excess;
+#if IN_LIBGCOV
+ gcc_assert (!gcov_var.length || gcov_var.length == 1);
+ excess = GCOV_BLOCK_SIZE;
+#else
+ if (gcov_var.length + words > gcov_var.alloc)
+ gcov_allocate (gcov_var.length + words);
+ excess = gcov_var.alloc - gcov_var.length;
+#endif
+ excess = fread (gcov_var.buffer + gcov_var.length,
+ 1, excess << 2, gcov_var.file) >> 2;
+ gcov_var.length += excess;
+ if (gcov_var.length < words)
+ {
+ gcov_var.overread += words - gcov_var.length;
+ gcov_var.length = 0;
+ return 0;
+ }
+ }
+ result = &gcov_var.buffer[gcov_var.offset];
+ gcov_var.offset += words;
+ return result;
+}
+
+/* Read unsigned value from a coverage file. Sets error flag on file
+ error, overflow flag on overflow */
+
+GCOV_LINKAGE gcov_unsigned_t
+gcov_read_unsigned (void)
+{
+ gcov_unsigned_t value;
+ const gcov_unsigned_t *buffer = gcov_read_words (1);
+
+ if (!buffer)
+ return 0;
+ value = from_file (buffer[0]);
+ return value;
+}
+
+/* Read counter value from a coverage file. Sets error flag on file
+ error, overflow flag on overflow */
+
+GCOV_LINKAGE gcov_type
+gcov_read_counter (void)
+{
+ gcov_type value;
+ const gcov_unsigned_t *buffer = gcov_read_words (2);
+
+ if (!buffer)
+ return 0;
+ value = from_file (buffer[0]);
+ if (sizeof (value) > sizeof (gcov_unsigned_t))
+ value |= ((gcov_type) from_file (buffer[1])) << 32;
+ else if (buffer[1])
+ gcov_var.error = -1;
+
+ return value;
+}
+
+/* Read string from coverage file. Returns a pointer to a static
+ buffer, or NULL on empty string. You must copy the string before
+ calling another gcov function. */
+
+#if !IN_LIBGCOV
+GCOV_LINKAGE const char *
+gcov_read_string (void)
+{
+ unsigned length = gcov_read_unsigned ();
+
+ if (!length)
+ return 0;
+
+ return (const char *) gcov_read_words (length);
+}
+#endif
+
+GCOV_LINKAGE void
+gcov_read_summary (struct gcov_summary *summary)
+{
+ unsigned ix;
+ struct gcov_ctr_summary *csum;
+
+ summary->checksum = gcov_read_unsigned ();
+ for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
+ {
+ csum->num = gcov_read_unsigned ();
+ csum->runs = gcov_read_unsigned ();
+ csum->sum_all = gcov_read_counter ();
+ csum->run_max = gcov_read_counter ();
+ csum->sum_max = gcov_read_counter ();
+ }
+}
+
+#if !IN_LIBGCOV
+/* Reset to a known position. BASE should have been obtained from
+ gcov_position, LENGTH should be a record length. */
+
+GCOV_LINKAGE void
+gcov_sync (gcov_position_t base, gcov_unsigned_t length)
+{
+ gcc_assert (gcov_var.mode > 0);
+ base += length;
+ if (base - gcov_var.start <= gcov_var.length)
+ gcov_var.offset = base - gcov_var.start;
+ else
+ {
+ gcov_var.offset = gcov_var.length = 0;
+ fseek (gcov_var.file, base << 2, SEEK_SET);
+ gcov_var.start = ftell (gcov_var.file) >> 2;
+ }
+}
+#endif
+
+#if IN_LIBGCOV
+/* Move to a given position in a gcov file. */
+
+GCOV_LINKAGE void
+gcov_seek (gcov_position_t base)
+{
+ gcc_assert (gcov_var.mode < 0);
+ if (gcov_var.offset)
+ gcov_write_block (gcov_var.offset);
+ fseek (gcov_var.file, base << 2, SEEK_SET);
+ gcov_var.start = ftell (gcov_var.file) >> 2;
+}
+#endif
+
+#if IN_GCOV > 0
+/* Return the modification time of the current gcov file. */
+
+GCOV_LINKAGE time_t
+gcov_time (void)
+{
+ struct stat status;
+
+ if (fstat (fileno (gcov_var.file), &status))
+ return 0;
+ else
+ return status.st_mtime;
+}
+#endif /* IN_GCOV */
diff --git a/src/lib/gcov-io.h b/src/lib/gcov-io.h
new file mode 100644
index 0000000000..4502bd6f94
--- /dev/null
+++ b/src/lib/gcov-io.h
@@ -0,0 +1,639 @@
+/* File format for coverage information
+ Copyright (C) 1996, 1997, 1998, 2000, 2002,
+ 2003, 2004, 2005, 2008, 2009 Free Software Foundation, Inc.
+ Contributed by Bob Manson <manson@cygnus.com>.
+ Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* Coverage information is held in two files. A notes file, which is
+ generated by the compiler, and a data file, which is generated by
+ the program under test. Both files use a similar structure. We do
+ not attempt to make these files backwards compatible with previous
+ versions, as you only need coverage information when developing a
+ program. We do hold version information, so that mismatches can be
+ detected, and we use a format that allows tools to skip information
+ they do not understand or are not interested in.
+
+ Numbers are recorded in the 32 bit unsigned binary form of the
+ endianness of the machine generating the file. 64 bit numbers are
+ stored as two 32 bit numbers, the low part first. Strings are
+ padded with 1 to 4 NUL bytes, to bring the length up to a multiple
+ of 4. The number of 4 bytes is stored, followed by the padded
+ string. Zero length and NULL strings are simply stored as a length
+ of zero (they have no trailing NUL or padding).
+
+ int32: byte3 byte2 byte1 byte0 | byte0 byte1 byte2 byte3
+ int64: int32:low int32:high
+ string: int32:0 | int32:length char* char:0 padding
+ padding: | char:0 | char:0 char:0 | char:0 char:0 char:0
+ item: int32 | int64 | string
+
+ The basic format of the files is
+
+ file : int32:magic int32:version int32:stamp record*
+
+ The magic ident is different for the notes and the data files. The
+ magic ident is used to determine the endianness of the file, when
+ reading. The version is the same for both files and is derived
+ from gcc's version number. The stamp value is used to synchronize
+ note and data files and to synchronize merging within a data
+ file. It need not be an absolute time stamp, merely a ticker that
+ increments fast enough and cycles slow enough to distinguish
+ different compile/run/compile cycles.
+
+ Although the ident and version are formally 32 bit numbers, they
+ are derived from 4 character ASCII strings. The version number
+ consists of the single character major version number, a two
+ character minor version number (leading zero for versions less than
+ 10), and a single character indicating the status of the release.
+ That will be 'e' experimental, 'p' prerelease and 'r' for release.
+ Because, by good fortune, these are in alphabetical order, string
+ collating can be used to compare version strings. Be aware that
+ the 'e' designation will (naturally) be unstable and might be
+ incompatible with itself. For gcc 3.4 experimental, it would be
+ '304e' (0x33303465). When the major version reaches 10, the
+ letters A-Z will be used. Assuming minor increments releases every
+ 6 months, we have to make a major increment every 50 years.
+ Assuming major increments releases every 5 years, we're ok for the
+ next 155 years -- good enough for me.
+
+ A record has a tag, length and variable amount of data.
+
+ record: header data
+ header: int32:tag int32:length
+ data: item*
+
+ Records are not nested, but there is a record hierarchy. Tag
+ numbers reflect this hierarchy. Tags are unique across note and
+ data files. Some record types have a varying amount of data. The
+ LENGTH is the number of 4bytes that follow and is usually used to
+ determine how much data. The tag value is split into 4 8-bit
+ fields, one for each of four possible levels. The most significant
+ is allocated first. Unused levels are zero. Active levels are
+ odd-valued, so that the LSB of the level is one. A sub-level
+ incorporates the values of its superlevels. This formatting allows
+ you to determine the tag hierarchy, without understanding the tags
+ themselves, and is similar to the standard section numbering used
+ in technical documents. Level values [1..3f] are used for common
+ tags, values [41..9f] for the notes file and [a1..ff] for the data
+ file.
+
+ The basic block graph file contains the following records
+ note: unit function-graph*
+ unit: header int32:checksum string:source
+ function-graph: announce_function basic_blocks {arcs | lines}*
+ announce_function: header int32:ident
+ int32:lineno_checksum int32:cfg_checksum
+ string:name string:source int32:lineno
+ basic_block: header int32:flags*
+ arcs: header int32:block_no arc*
+ arc: int32:dest_block int32:flags
+ lines: header int32:block_no line*
+ int32:0 string:NULL
+ line: int32:line_no | int32:0 string:filename
+
+ The BASIC_BLOCK record holds per-bb flags. The number of blocks
+ can be inferred from its data length. There is one ARCS record per
+ basic block. The number of arcs from a bb is implicit from the
+ data length. It enumerates the destination bb and per-arc flags.
+ There is one LINES record per basic block, it enumerates the source
+ lines which belong to that basic block. Source file names are
+ introduced by a line number of 0, following lines are from the new
+ source file. The initial source file for the function is NULL, but
+ the current source file should be remembered from one LINES record
+ to the next. The end of a block is indicated by an empty filename
+ - this does not reset the current source file. Note there is no
+ ordering of the ARCS and LINES records: they may be in any order,
+ interleaved in any manner. The current filename follows the order
+ the LINES records are stored in the file, *not* the ordering of the
+ blocks they are for.
+
+ The data file contains the following records.
+ data: {unit summary:object summary:program* function-data*}*
+ unit: header int32:checksum
+ function-data: announce_function present counts
+ announce_function: header int32:ident
+ int32:lineno_checksum int32:cfg_checksum
+ present: header int32:present
+ counts: header int64:count*
+ summary: int32:checksum {count-summary}GCOV_COUNTERS_SUMMABLE
+ count-summary: int32:num int32:runs int64:sum
+ int64:max int64:sum_max
+
+ The ANNOUNCE_FUNCTION record is the same as that in the note file,
+ but without the source location. The COUNTS gives the
+ counter values for instrumented features. The about the whole
+ program. The checksum is used for whole program summaries, and
+ disambiguates different programs which include the same
+ instrumented object file. There may be several program summaries,
+ each with a unique checksum. The object summary's checksum is
+ zero. Note that the data file might contain information from
+ several runs concatenated, or the data might be merged.
+
+ This file is included by both the compiler, gcov tools and the
+ runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
+ distinguish which case is which. If IN_LIBGCOV is nonzero,
+ libgcov is being built. If IN_GCOV is nonzero, the gcov tools are
+ being built. Otherwise the compiler is being built. IN_GCOV may be
+ positive or negative. If positive, we are compiling a tool that
+ requires additional functions (see the code for knowledge of what
+ those functions are). */
+
+#ifndef GCC_GCOV_IO_H
+#define GCC_GCOV_IO_H
+
+#ifdef __COREBOOT__
+#define GCOV_LINKAGE /* nothing */
+/* We need the definitions for
+ BITS_PER_UNIT and
+ LONG_LONG_TYPE_SIZE
+ They are defined in gcc/defaults.h and gcc/config/<arch_depend_files>
+ (like, gcc/config/i386/i386.h). And it can be overridden by setting
+ in build scripts. Here I hardcoded the value for x86. */
+#define BITS_PER_UNIT 8
+#define LONG_LONG_TYPE_SIZE 64
+
+/* There are many gcc_assertions. Set the vaule to 1 if we want a warning
+ message if the assertion fails. */
+#ifndef ENABLE_ASSERT_CHECKING
+#define ENABLE_ASSERT_CHECKING 1
+#endif
+#endif /* __COREBOOT__ */
+
+#if IN_LIBGCOV
+/* About the target */
+
+#if BITS_PER_UNIT == 8
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (DI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (SI)));
+#endif
+#else
+#if BITS_PER_UNIT == 16
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (SI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (HI)));
+#endif
+#else
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
+typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
+#if LONG_LONG_TYPE_SIZE > 32
+typedef signed gcov_type __attribute__ ((mode (HI)));
+#else
+typedef signed gcov_type __attribute__ ((mode (QI)));
+#endif
+#endif
+#endif
+
+
+#if defined (TARGET_POSIX_IO)
+#define GCOV_LOCKED 1
+#else
+#define GCOV_LOCKED 0
+#endif
+
+#else /* !IN_LIBGCOV */
+/* About the host */
+
+typedef unsigned gcov_unsigned_t;
+typedef unsigned gcov_position_t;
+/* gcov_type is typedef'd elsewhere for the compiler */
+#if IN_GCOV
+#define GCOV_LINKAGE static
+typedef HOST_WIDEST_INT gcov_type;
+#if IN_GCOV > 0
+#include <sys/types.h>
+#endif
+#else /*!IN_GCOV */
+#define GCOV_TYPE_SIZE (LONG_LONG_TYPE_SIZE > 32 ? 64 : 32)
+#endif
+
+#if defined (HOST_HAS_F_SETLKW)
+#define GCOV_LOCKED 1
+#else
+#define GCOV_LOCKED 0
+#endif
+
+#endif /* !IN_LIBGCOV */
+
+/* In gcov we want function linkage to be static. In the compiler we want
+ it extern, so that they can be accessed from elsewhere. In libgcov we
+ need these functions to be extern, so prefix them with __gcov. In
+ libgcov they must also be hidden so that the instance in the executable
+ is not also used in a DSO. */
+#if IN_LIBGCOV
+
+#ifndef __COREBOOT__
+#include "tconfig.h"
+#endif /* __COREBOOT__ */
+
+#define gcov_var __gcov_var
+#define gcov_open __gcov_open
+#define gcov_close __gcov_close
+#define gcov_write_tag_length __gcov_write_tag_length
+#define gcov_position __gcov_position
+#define gcov_seek __gcov_seek
+#define gcov_rewrite __gcov_rewrite
+#define gcov_is_error __gcov_is_error
+#define gcov_write_unsigned __gcov_write_unsigned
+#define gcov_write_counter __gcov_write_counter
+#define gcov_write_summary __gcov_write_summary
+#define gcov_read_unsigned __gcov_read_unsigned
+#define gcov_read_counter __gcov_read_counter
+#define gcov_read_summary __gcov_read_summary
+
+/* Poison these, so they don't accidentally slip in. */
+#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
+#pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic
+
+#ifdef HAVE_GAS_HIDDEN
+#define ATTRIBUTE_HIDDEN __attribute__ ((__visibility__ ("hidden")))
+#else
+#define ATTRIBUTE_HIDDEN
+#endif
+
+#else
+
+#define ATTRIBUTE_HIDDEN
+
+#endif
+
+#ifndef GCOV_LINKAGE
+#define GCOV_LINKAGE extern
+#endif
+
+/* File suffixes. */
+#define GCOV_DATA_SUFFIX ".gcda"
+#define GCOV_NOTE_SUFFIX ".gcno"
+
+/* File magic. Must not be palindromes. */
+#define GCOV_DATA_MAGIC ((gcov_unsigned_t)0x67636461) /* "gcda" */
+#define GCOV_NOTE_MAGIC ((gcov_unsigned_t)0x67636e6f) /* "gcno" */
+
+/* gcov-iov.h is automatically generated by the makefile from
+ version.c, it looks like
+ #define GCOV_VERSION ((gcov_unsigned_t)0x89abcdef)
+*/
+#include "gcov-iov.h"
+
+/* Convert a magic or version number to a 4 character string. */
+#define GCOV_UNSIGNED2STRING(ARRAY,VALUE) \
+ ((ARRAY)[0] = (char)((VALUE) >> 24), \
+ (ARRAY)[1] = (char)((VALUE) >> 16), \
+ (ARRAY)[2] = (char)((VALUE) >> 8), \
+ (ARRAY)[3] = (char)((VALUE) >> 0))
+
+/* The record tags. Values [1..3f] are for tags which may be in either
+ file. Values [41..9f] for those in the note file and [a1..ff] for
+ the data file. The tag value zero is used as an explicit end of
+ file marker -- it is not required to be present. */
+
+#define GCOV_TAG_FUNCTION ((gcov_unsigned_t)0x01000000)
+#define GCOV_TAG_FUNCTION_LENGTH (3)
+#define GCOV_TAG_BLOCKS ((gcov_unsigned_t)0x01410000)
+#define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM)
+#define GCOV_TAG_BLOCKS_NUM(LENGTH) (LENGTH)
+#define GCOV_TAG_ARCS ((gcov_unsigned_t)0x01430000)
+#define GCOV_TAG_ARCS_LENGTH(NUM) (1 + (NUM) * 2)
+#define GCOV_TAG_ARCS_NUM(LENGTH) (((LENGTH) - 1) / 2)
+#define GCOV_TAG_LINES ((gcov_unsigned_t)0x01450000)
+#define GCOV_TAG_COUNTER_BASE ((gcov_unsigned_t)0x01a10000)
+#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2)
+#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
+#define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) /* Obsolete */
+#define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
+#define GCOV_TAG_SUMMARY_LENGTH \
+ (1 + GCOV_COUNTERS_SUMMABLE * (2 + 3 * 2))
+
+/* Counters that are collected. */
+#define GCOV_COUNTER_ARCS 0 /* Arc transitions. */
+#define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be
+ summaried. */
+#define GCOV_FIRST_VALUE_COUNTER 1 /* The first of counters used for value
+ profiling. They must form a consecutive
+ interval and their order must match
+ the order of HIST_TYPEs in
+ value-prof.h. */
+#define GCOV_COUNTER_V_INTERVAL 1 /* Histogram of value inside an interval. */
+#define GCOV_COUNTER_V_POW2 2 /* Histogram of exact power2 logarithm
+ of a value. */
+#define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */
+#define GCOV_COUNTER_V_DELTA 4 /* The most common difference between
+ consecutive values of expression. */
+
+#define GCOV_COUNTER_V_INDIR 5 /* The most common indirect address */
+#define GCOV_COUNTER_AVERAGE 6 /* Compute average value passed to the
+ counter. */
+#define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to
+ counter. */
+#define GCOV_LAST_VALUE_COUNTER 7 /* The last of counters used for value
+ profiling. */
+#define GCOV_COUNTERS 8
+
+/* Number of counters used for value profiling. */
+#define GCOV_N_VALUE_COUNTERS \
+ (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1)
+
+ /* A list of human readable names of the counters */
+#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \
+ "delta", "indirect_call", "average", "ior"}
+
+ /* Names of merge functions for counters. */
+#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \
+ "__gcov_merge_add", \
+ "__gcov_merge_add", \
+ "__gcov_merge_single", \
+ "__gcov_merge_delta", \
+ "__gcov_merge_single", \
+ "__gcov_merge_add", \
+ "__gcov_merge_ior"}
+
+/* Convert a counter index to a tag. */
+#define GCOV_TAG_FOR_COUNTER(COUNT) \
+ (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))
+/* Convert a tag to a counter. */
+#define GCOV_COUNTER_FOR_TAG(TAG) \
+ ((unsigned)(((TAG) - GCOV_TAG_COUNTER_BASE) >> 17))
+/* Check whether a tag is a counter tag. */
+#define GCOV_TAG_IS_COUNTER(TAG) \
+ (!((TAG) & 0xFFFF) && GCOV_COUNTER_FOR_TAG (TAG) < GCOV_COUNTERS)
+
+/* The tag level mask has 1's in the position of the inner levels, &
+ the lsb of the current level, and zero on the current and outer
+ levels. */
+#define GCOV_TAG_MASK(TAG) (((TAG) - 1) ^ (TAG))
+
+/* Return nonzero if SUB is an immediate subtag of TAG. */
+#define GCOV_TAG_IS_SUBTAG(TAG,SUB) \
+ (GCOV_TAG_MASK (TAG) >> 8 == GCOV_TAG_MASK (SUB) \
+ && !(((SUB) ^ (TAG)) & ~GCOV_TAG_MASK(TAG)))
+
+/* Return nonzero if SUB is at a sublevel to TAG. */
+#define GCOV_TAG_IS_SUBLEVEL(TAG,SUB) \
+ (GCOV_TAG_MASK (TAG) > GCOV_TAG_MASK (SUB))
+
+/* Basic block flags. */
+#define GCOV_BLOCK_UNEXPECTED (1 << 1)
+
+/* Arc flags. */
+#define GCOV_ARC_ON_TREE (1 << 0)
+#define GCOV_ARC_FAKE (1 << 1)
+#define GCOV_ARC_FALLTHROUGH (1 << 2)
+
+/* Structured records. */
+
+/* Cumulative counter data. */
+struct gcov_ctr_summary
+{
+ gcov_unsigned_t num; /* number of counters. */
+ gcov_unsigned_t runs; /* number of program runs */
+ gcov_type sum_all; /* sum of all counters accumulated. */
+ gcov_type run_max; /* maximum value on a single run. */
+ gcov_type sum_max; /* sum of individual run max values. */
+};
+
+/* Object & program summary record. */
+struct gcov_summary
+{
+ gcov_unsigned_t checksum; /* checksum of program */
+ struct gcov_ctr_summary ctrs[GCOV_COUNTERS_SUMMABLE];
+};
+
+/* Structures embedded in coveraged program. The structures generated
+ by write_profile must match these. */
+
+#if IN_LIBGCOV
+/* Information about counters for a single function. */
+struct gcov_ctr_info
+{
+ gcov_unsigned_t num; /* number of counters. */
+ gcov_type *values; /* their values. */
+};
+
+/* Information about a single function. This uses the trailing array
+ idiom. The number of counters is determined from the merge pointer
+ array in gcov_info. The key is used to detect which of a set of
+ comdat functions was selected -- it points to the gcov_info object
+ of the object file containing the selected comdat function. */
+
+struct gcov_fn_info
+{
+ const struct gcov_info *key; /* comdat key */
+ gcov_unsigned_t ident; /* unique ident of function */
+ gcov_unsigned_t lineno_checksum; /* function lineo_checksum */
+ gcov_unsigned_t cfg_checksum; /* function cfg checksum */
+ struct gcov_ctr_info ctrs[0]; /* instrumented counters */
+};
+
+/* Type of function used to merge counters. */
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+
+/* Information about a single object file. */
+struct gcov_info
+{
+ gcov_unsigned_t version; /* expected version number */
+ struct gcov_info *next; /* link to next, used by libgcov */
+
+ gcov_unsigned_t stamp; /* uniquifying time stamp */
+ const char *filename; /* output file name */
+
+ gcov_merge_fn merge[GCOV_COUNTERS]; /* merge functions (null for
+ unused) */
+
+ unsigned n_functions; /* number of functions */
+ const struct gcov_fn_info *const *functions; /* pointer to pointers
+ to function information */
+};
+
+/* Register a new object file module. */
+extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN;
+
+#ifndef __COREBOOT__
+/* Called before fork, to avoid double counting. */
+extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
+#endif
+
+/* The merge function that just sums the counters. */
+extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to choose the most common value. */
+extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function to choose the most common difference between
+ consecutive values. */
+extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The merge function that just ors the counters together. */
+extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
+/* The profiler functions. */
+extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
+extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
+extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler (gcov_type *, gcov_type, void *, void *);
+extern void __gcov_average_profiler (gcov_type *, gcov_type);
+extern void __gcov_ior_profiler (gcov_type *, gcov_type);
+
+#ifndef inhibit_libc
+/* The wrappers around some library functions.. */
+extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN;
+extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN;
+extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN;
+extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN;
+extern int __gcov_execve (const char *, char *const [], char *const [])
+ ATTRIBUTE_HIDDEN;
+#endif
+
+#endif /* IN_LIBGCOV */
+
+#if IN_LIBGCOV >= 0
+
+/* Optimum number of gcov_unsigned_t's read from or written to disk. */
+#define GCOV_BLOCK_SIZE (1 << 10)
+
+GCOV_LINKAGE struct gcov_var
+{
+ FILE *file;
+ gcov_position_t start; /* Position of first byte of block */
+ unsigned offset; /* Read/write position within the block. */
+ unsigned length; /* Read limit in the block. */
+ unsigned overread; /* Number of words overread. */
+ int error; /* < 0 overflow, > 0 disk error. */
+ int mode; /* < 0 writing, > 0 reading */
+#if IN_LIBGCOV
+ /* Holds one block plus 4 bytes, thus all coverage reads & writes
+ fit within this buffer and we always can transfer GCOV_BLOCK_SIZE
+ to and from the disk. libgcov never backtracks and only writes 4
+ or 8 byte objects. */
+ gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1];
+#else
+ int endian; /* Swap endianness. */
+ /* Holds a variable length block, as the compiler can write
+ strings and needs to backtrack. */
+ size_t alloc;
+ gcov_unsigned_t *buffer;
+#endif
+} gcov_var ATTRIBUTE_HIDDEN;
+
+/* Functions for reading and writing gcov files. In libgcov you can
+ open the file for reading then writing. Elsewhere you can open the
+ file either for reading or for writing. When reading a file you may
+ use the gcov_read_* functions, gcov_sync, gcov_position, &
+ gcov_error. When writing a file you may use the gcov_write
+ functions, gcov_seek & gcov_error. When a file is to be rewritten
+ you use the functions for reading, then gcov_rewrite then the
+ functions for writing. Your file may become corrupted if you break
+ these invariants. */
+#if IN_LIBGCOV
+GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
+#else
+GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/);
+GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t);
+#endif
+GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN;
+
+/* Available everywhere. */
+static gcov_position_t gcov_position (void);
+static int gcov_is_error (void);
+
+GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
+
+#if IN_LIBGCOV
+/* Available only in libgcov */
+GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
+ ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
+ const struct gcov_summary *)
+ ATTRIBUTE_HIDDEN;
+static void gcov_rewrite (void);
+GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
+#else
+/* Available outside libgcov */
+GCOV_LINKAGE const char *gcov_read_string (void);
+GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
+ gcov_unsigned_t /*length */);
+#endif
+
+#if !IN_GCOV
+/* Available outside gcov */
+GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
+#endif
+
+#if !IN_GCOV && !IN_LIBGCOV
+/* Available only in compiler */
+GCOV_LINKAGE void gcov_write_string (const char *);
+GCOV_LINKAGE gcov_position_t gcov_write_tag (gcov_unsigned_t);
+GCOV_LINKAGE void gcov_write_length (gcov_position_t /*position*/);
+#endif
+
+#if IN_GCOV > 0
+/* Available in gcov */
+GCOV_LINKAGE time_t gcov_time (void);
+#endif
+
+/* Save the current position in the gcov file. */
+
+static inline gcov_position_t
+gcov_position (void)
+{
+ gcc_assert (gcov_var.mode > 0);
+ return gcov_var.start + gcov_var.offset;
+}
+
+/* Return nonzero if the error flag is set. */
+
+static inline int
+gcov_is_error (void)
+{
+ return gcov_var.file ? gcov_var.error : 1;
+}
+
+#if IN_LIBGCOV
+/* Move to beginning of file and initialize for writing. */
+
+static inline void
+gcov_rewrite (void)
+{
+ gcc_assert (gcov_var.mode > 0);
+ gcov_var.mode = -1;
+ gcov_var.start = 0;
+ gcov_var.offset = 0;
+ fseek (gcov_var.file, 0L, SEEK_SET);
+}
+#endif
+
+#endif /* IN_LIBGCOV >= 0 */
+
+#endif /* GCC_GCOV_IO_H */
diff --git a/src/lib/gcov-iov.h b/src/lib/gcov-iov.h
new file mode 100644
index 0000000000..09951fbbe7
--- /dev/null
+++ b/src/lib/gcov-iov.h
@@ -0,0 +1,4 @@
+/* Generated automatically by the program `build/gcov-iov'
+ from `4.7.2 (4 7) and (*)'. */
+
+#define GCOV_VERSION ((gcov_unsigned_t)0x3430372a) /* 407* */
diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c
index 206e82b066..eed243ad75 100644
--- a/src/lib/hardwaremain.c
+++ b/src/lib/hardwaremain.c
@@ -42,6 +42,7 @@ it with the version available from LANL.
#if CONFIG_WRITE_HIGH_TABLES
#include <cbmem.h>
#endif
+#include <coverage.h>
#include <timestamp.h>
/**
@@ -62,6 +63,10 @@ void hardwaremain(int boot_complete)
timestamp_stash(TS_START_RAMSTAGE);
post_code(POST_ENTRY_RAMSTAGE);
+#if CONFIG_COVERAGE
+ coverage_init();
+#endif
+
/* console_init() MUST PRECEDE ALL printk()! */
console_init();
diff --git a/src/lib/libgcov.c b/src/lib/libgcov.c
new file mode 100644
index 0000000000..4adf75ebff
--- /dev/null
+++ b/src/lib/libgcov.c
@@ -0,0 +1,1157 @@
+/* Routines required for instrumenting a program. */
+/* Compile this one with gcc. */
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2010, 2011
+ Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#define __COREBOOT__
+#ifdef __COREBOOT__
+#include <stdlib.h>
+#include <string.h>
+#include <console/console.h>
+#include <assert.h>
+typedef s32 pid_t;
+#define gcc_assert(x) ASSERT(x)
+#define fprintf(file, x...) printk(BIOS_ERR, x)
+#define alloca(size) __builtin_alloca (size)
+#include "gcov-glue.c"
+
+/* Define MACROs to be used by coreboot compilation. */
+# define L_gcov
+# define L_gcov_interval_profiler
+# define L_gcov_pow2_profiler
+# define L_gcov_one_value_profiler
+# define L_gcov_indirect_call_profiler
+# define L_gcov_average_profiler
+# define L_gcov_ior_profiler
+
+# define HAVE_CC_TLS 0
+# define __GCOV_KERNEL__
+
+# define IN_LIBGCOV 1
+# define IN_GCOV 0
+#else /* __COREBOOT__ */
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#endif /* __COREBOOT__ */
+
+#ifndef __COREBOOT__
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
+#include <stdio.h>
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#endif /* __COREBOOT__ */
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions. */
+
+#ifdef L_gcov
+void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
+void __gcov_flush (void) {}
+#endif
+
+#ifdef L_gcov_merge_add
+void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_single
+void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_delta
+void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused))) {}
+#endif
+
+#else
+
+#ifndef __COREBOOT__
+#include <string.h>
+#if GCOV_LOCKED
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#endif
+#else
+void __gcov_merge_add(gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused))) {}
+#endif /* __COREBOOT__ */
+
+#ifdef L_gcov
+#include "gcov-io.c"
+
+struct gcov_fn_buffer
+{
+ struct gcov_fn_buffer *next;
+ unsigned fn_ix;
+ struct gcov_fn_info info;
+ /* note gcov_fn_info ends in a trailing array. */
+};
+
+/* Chain of per-object gcov structures. */
+static struct gcov_info *gcov_list;
+
+/* Size of the longest file name. */
+static size_t gcov_max_filename = 0;
+
+/* Make sure path component of the given FILENAME exists, create
+ missing directories. FILENAME must be writable.
+ Returns zero on success, or -1 if an error occurred. */
+
+static int
+create_file_directory (char *filename)
+{
+#ifdef __COREBOOT__
+ (void) filename;
+ return 0;
+#else
+#if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
+ (void) filename;
+ return -1;
+#else
+ char *s;
+
+ s = filename;
+
+ if (HAS_DRIVE_SPEC(s))
+ s += 2;
+ if (IS_DIR_SEPARATOR(*s))
+ ++s;
+ for (; *s != '\0'; s++)
+ if (IS_DIR_SEPARATOR(*s))
+ {
+ char sep = *s;
+ *s = '\0';
+
+ /* Try to make directory if it doesn't already exist. */
+ if (access (filename, F_OK) == -1
+#ifdef TARGET_POSIX_IO
+ && mkdir (filename, 0755) == -1
+#else
+ && mkdir (filename) == -1
+#endif
+ /* The directory might have been made by another process. */
+ && errno != EEXIST)
+ {
+ fprintf (stderr, "profiling:%s:Cannot create directory\n",
+ filename);
+ *s = sep;
+ return -1;
+ };
+
+ *s = sep;
+ };
+ return 0;
+#endif
+#endif
+}
+
+static struct gcov_fn_buffer *
+free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
+ unsigned limit)
+{
+ struct gcov_fn_buffer *next;
+ unsigned ix, n_ctr = 0;
+
+ if (!buffer)
+ return 0;
+ next = buffer->next;
+
+ for (ix = 0; ix != limit; ix++)
+ if (gi_ptr->merge[ix])
+ free (buffer->info.ctrs[n_ctr++].values);
+ free (buffer);
+ return next;
+}
+
+static struct gcov_fn_buffer **
+buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
+ struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
+{
+ unsigned n_ctrs = 0, ix = 0;
+ struct gcov_fn_buffer *fn_buffer;
+ unsigned len;
+
+ for (ix = GCOV_COUNTERS; ix--;)
+ if (gi_ptr->merge[ix])
+ n_ctrs++;
+
+ len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
+ fn_buffer = (struct gcov_fn_buffer *)malloc (len);
+
+ if (!fn_buffer)
+ goto fail;
+
+ fn_buffer->next = 0;
+ fn_buffer->fn_ix = fn_ix;
+ fn_buffer->info.ident = gcov_read_unsigned ();
+ fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
+ fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
+
+ for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
+ {
+ gcov_unsigned_t length;
+ gcov_type *values;
+
+ if (!gi_ptr->merge[ix])
+ continue;
+
+ if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
+ {
+ len = 0;
+ goto fail;
+ }
+
+ length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
+ len = length * sizeof (gcov_type);
+ values = (gcov_type *)malloc (len);
+ if (!values)
+ goto fail;
+
+ fn_buffer->info.ctrs[n_ctrs].num = length;
+ fn_buffer->info.ctrs[n_ctrs].values = values;
+
+ while (length--)
+ *values++ = gcov_read_counter ();
+ n_ctrs++;
+ }
+
+ *end_ptr = fn_buffer;
+ return &fn_buffer->next;
+
+fail:
+ fprintf (stderr, "profiling:%s:Function %u %s %u \n", filename, fn_ix,
+ len ? "cannot allocate" : "counter mismatch", len ? len : ix);
+
+ return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
+}
+
+/* Add an unsigned value to the current crc */
+
+static gcov_unsigned_t
+crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
+{
+ unsigned ix;
+
+ for (ix = 32; ix--; value <<= 1)
+ {
+ unsigned feedback;
+
+ feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
+ crc32 <<= 1;
+ crc32 ^= feedback;
+ }
+
+ return crc32;
+}
+
+/* Check if VERSION of the info block PTR matches libgcov one.
+ Return 1 on success, or zero in case of versions mismatch.
+ If FILENAME is not NULL, its value used for reporting purposes
+ instead of value from the info block. */
+
+static int
+gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
+ const char *filename)
+{
+ if (version != GCOV_VERSION)
+ {
+ char v[4], e[4];
+
+ GCOV_UNSIGNED2STRING (v, version);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+ fprintf (stderr,
+ "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
+ filename? filename : ptr->filename, e, v);
+ return 0;
+ }
+ return 1;
+}
+
+/* Dump the coverage counts. We merge with existing counts when
+ possible, to avoid growing the .da files ad infinitum. We use this
+ program's checksum to make sure we only accumulate whole program
+ statistics to the correct summary. An object file might be embedded
+ in two separate programs, and we must keep the two program
+ summaries separate. */
+
+static void
+gcov_exit (void)
+{
+ struct gcov_info *gi_ptr;
+ const struct gcov_fn_info *gfi_ptr;
+ struct gcov_summary this_prg; /* summary for program. */
+ struct gcov_summary all_prg; /* summary for all instances of program. */
+ struct gcov_ctr_summary *cs_ptr;
+ const struct gcov_ctr_info *ci_ptr;
+ unsigned t_ix;
+ int f_ix = 0;
+ gcov_unsigned_t c_num;
+ const char *gcov_prefix;
+ int gcov_prefix_strip = 0;
+ size_t prefix_length;
+ char *gi_filename, *gi_filename_up;
+ gcov_unsigned_t crc32 = 0;
+
+ memset (&all_prg, 0, sizeof (all_prg));
+ /* Find the totals for this execution. */
+ memset (&this_prg, 0, sizeof (this_prg));
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
+ crc32 = crc32_unsigned (crc32, gi_ptr->n_functions);
+
+ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
+ {
+ gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (gfi_ptr && gfi_ptr->key != gi_ptr)
+ gfi_ptr = 0;
+
+ crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0);
+ crc32 = crc32_unsigned (crc32,
+ gfi_ptr ? gfi_ptr->lineno_checksum : 0);
+ if (!gfi_ptr)
+ continue;
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
+ {
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ cs_ptr = &this_prg.ctrs[t_ix];
+ cs_ptr->num += ci_ptr->num;
+ crc32 = crc32_unsigned (crc32, ci_ptr->num);
+
+ for (c_num = 0; c_num < ci_ptr->num; c_num++)
+ {
+ cs_ptr->sum_all += ci_ptr->values[c_num];
+ if (cs_ptr->run_max < ci_ptr->values[c_num])
+ cs_ptr->run_max = ci_ptr->values[c_num];
+ }
+ ci_ptr++;
+ }
+ }
+ }
+
+#ifndef __COREBOOT__
+ {
+ /* Check if the level of dirs to strip off specified. */
+ char *tmp = getenv("GCOV_PREFIX_STRIP");
+ if (tmp)
+ {
+ gcov_prefix_strip = atoi (tmp);
+ /* Do not consider negative values. */
+ if (gcov_prefix_strip < 0)
+ gcov_prefix_strip = 0;
+ }
+ }
+
+ /* Get file name relocation prefix. Non-absolute values are ignored. */
+ gcov_prefix = getenv("GCOV_PREFIX");
+ if (gcov_prefix)
+ {
+ prefix_length = strlen(gcov_prefix);
+
+ /* Remove an unnecessary trailing '/' */
+ if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
+ prefix_length--;
+ }
+ else
+#endif
+ prefix_length = 0;
+
+ /* If no prefix was specified and a prefix stip, then we assume
+ relative. */
+ if (gcov_prefix_strip != 0 && prefix_length == 0)
+ {
+ gcov_prefix = ".";
+ prefix_length = 1;
+ }
+ /* Allocate and initialize the filename scratch space plus one. */
+ gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 2);
+ if (prefix_length)
+ memcpy (gi_filename, gcov_prefix, prefix_length);
+ gi_filename_up = gi_filename + prefix_length;
+
+ /* Now merge each file. */
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ unsigned n_counts;
+ struct gcov_summary prg; /* summary for this object over all
+ program. */
+ struct gcov_ctr_summary *cs_prg, *cs_tprg, *cs_all;
+ int error = 0;
+ gcov_unsigned_t tag, length;
+ gcov_position_t summary_pos = 0;
+ gcov_position_t eof_pos = 0;
+ const char *fname, *s;
+ struct gcov_fn_buffer *fn_buffer = 0;
+ struct gcov_fn_buffer **fn_tail = &fn_buffer;
+
+ fname = gi_ptr->filename;
+
+ /* Avoid to add multiple drive letters into combined path. */
+ if (prefix_length != 0 && HAS_DRIVE_SPEC(fname))
+ fname += 2;
+
+ /* Build relocated filename, stripping off leading
+ directories from the initial filename if requested. */
+ if (gcov_prefix_strip > 0)
+ {
+ int level = 0;
+ s = fname;
+ if (IS_DIR_SEPARATOR(*s))
+ ++s;
+
+ /* Skip selected directory levels. */
+ for (; (*s != '\0') && (level < gcov_prefix_strip); s++)
+ if (IS_DIR_SEPARATOR(*s))
+ {
+ fname = s;
+ level++;
+ }
+ }
+
+ /* Update complete filename with stripped original. */
+ if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname))
+ {
+ /* If prefix is given, add directory separator. */
+ strcpy (gi_filename_up, "/");
+ strcpy (gi_filename_up + 1, fname);
+ }
+ else
+ strcpy (gi_filename_up, fname);
+
+ if (!gcov_open (gi_filename))
+ {
+ /* Open failed likely due to missed directory.
+ Create directory and retry to open file. */
+ if (create_file_directory (gi_filename))
+ {
+ fprintf (stderr, "profiling:%s:Skip\n", gi_filename);
+ continue;
+ }
+ if (!gcov_open (gi_filename))
+ {
+ fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
+ continue;
+ }
+ }
+
+ tag = gcov_read_unsigned ();
+ if (tag)
+ {
+ /* Merge data from file. */
+ if (tag != GCOV_DATA_MAGIC)
+ {
+ fprintf (stderr, "profiling:%s:Not a gcov data file\n",
+ gi_filename);
+ goto read_fatal;
+ }
+ length = gcov_read_unsigned ();
+ if (!gcov_version (gi_ptr, length, gi_filename))
+ goto read_fatal;
+
+ length = gcov_read_unsigned ();
+ if (length != gi_ptr->stamp)
+ /* Read from a different compilation. Overwrite the file. */
+ goto rewrite;
+
+ /* Look for program summary. */
+ for (f_ix = 0;;)
+ {
+ struct gcov_summary tmp;
+
+ eof_pos = gcov_position ();
+ tag = gcov_read_unsigned ();
+ if (tag != GCOV_TAG_PROGRAM_SUMMARY)
+ break;
+
+ f_ix--;
+ length = gcov_read_unsigned ();
+ if (length != GCOV_TAG_SUMMARY_LENGTH)
+ goto read_mismatch;
+ gcov_read_summary (&tmp);
+ if ((error = gcov_is_error ()))
+ goto read_error;
+ if (summary_pos || tmp.checksum != crc32)
+ goto next_summary;
+
+ for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
+ if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
+ goto next_summary;
+ prg = tmp;
+ summary_pos = eof_pos;
+
+ next_summary:;
+ }
+
+ /* Merge execution counts for each function. */
+ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
+ f_ix++, tag = gcov_read_unsigned ())
+ {
+ gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (tag != GCOV_TAG_FUNCTION)
+ goto read_mismatch;
+
+ length = gcov_read_unsigned ();
+ if (!length)
+ /* This function did not appear in the other program.
+ We have nothing to merge. */
+ continue;
+
+ if (length != GCOV_TAG_FUNCTION_LENGTH)
+ goto read_mismatch;
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ {
+ /* This function appears in the other program. We
+ need to buffer the information in order to write
+ it back out -- we'll be inserting data before
+ this point, so cannot simply keep the data in the
+ file. */
+ fn_tail = buffer_fn_data (gi_filename,
+ gi_ptr, fn_tail, f_ix);
+ if (!fn_tail)
+ goto read_mismatch;
+ continue;
+ }
+
+ length = gcov_read_unsigned ();
+ if (length != gfi_ptr->ident)
+ goto read_mismatch;
+
+ length = gcov_read_unsigned ();
+ if (length != gfi_ptr->lineno_checksum)
+ goto read_mismatch;
+
+ length = gcov_read_unsigned ();
+ if (length != gfi_ptr->cfg_checksum)
+ goto read_mismatch;
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+ {
+ gcov_merge_fn merge = gi_ptr->merge[t_ix];
+
+ if (!merge)
+ continue;
+
+ tag = gcov_read_unsigned ();
+ length = gcov_read_unsigned ();
+ if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
+ || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
+ goto read_mismatch;
+ (*merge) (ci_ptr->values, ci_ptr->num);
+ ci_ptr++;
+ }
+ if ((error = gcov_is_error ()))
+ goto read_error;
+ }
+
+ if (tag)
+ {
+ read_mismatch:;
+ fprintf (stderr, "profiling:%s:Merge mismatch for %s %u\n",
+ gi_filename, f_ix >= 0 ? "function" : "summary",
+ f_ix < 0 ? -1 - f_ix : f_ix);
+ goto read_fatal;
+ }
+ }
+ goto rewrite;
+
+ read_error:;
+ fprintf (stderr, "profiling:%s:%s merging\n", gi_filename,
+ error < 0 ? "Overflow": "Error");
+
+ goto read_fatal;
+
+ rewrite:;
+ gcov_rewrite ();
+ if (!summary_pos)
+ {
+ memset (&prg, 0, sizeof (prg));
+ summary_pos = eof_pos;
+ }
+
+ /* Merge the summaries. */
+ for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+ {
+ cs_prg = &prg.ctrs[t_ix];
+ cs_tprg = &this_prg.ctrs[t_ix];
+ cs_all = &all_prg.ctrs[t_ix];
+
+ if (gi_ptr->merge[t_ix])
+ {
+ if (!cs_prg->runs++)
+ cs_prg->num = cs_tprg->num;
+ cs_prg->sum_all += cs_tprg->sum_all;
+ if (cs_prg->run_max < cs_tprg->run_max)
+ cs_prg->run_max = cs_tprg->run_max;
+ cs_prg->sum_max += cs_tprg->run_max;
+ }
+ else if (cs_prg->runs)
+ goto read_mismatch;
+
+ if (!cs_all->runs && cs_prg->runs)
+ memcpy (cs_all, cs_prg, sizeof (*cs_all));
+ else if (!all_prg.checksum
+ && (!GCOV_LOCKED || cs_all->runs == cs_prg->runs)
+ && memcmp (cs_all, cs_prg, sizeof (*cs_all)))
+ {
+ fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s\n",
+ gi_filename, GCOV_LOCKED
+ ? "" : " or concurrently updated without locking support");
+ all_prg.checksum = ~0u;
+ }
+ }
+
+ prg.checksum = crc32;
+
+ /* Write out the data. */
+ if (!eof_pos)
+ {
+ gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
+ gcov_write_unsigned (gi_ptr->stamp);
+ }
+
+ if (summary_pos)
+ gcov_seek (summary_pos);
+
+ /* Generate whole program statistics. */
+ gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &prg);
+
+ if (summary_pos < eof_pos)
+ gcov_seek (eof_pos);
+
+ /* Write execution counts for each function. */
+ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
+ {
+ unsigned buffered = 0;
+
+ if (fn_buffer && fn_buffer->fn_ix == (unsigned)f_ix)
+ {
+ /* Buffered data from another program. */
+ buffered = 1;
+ gfi_ptr = &fn_buffer->info;
+ length = GCOV_TAG_FUNCTION_LENGTH;
+ }
+ else
+ {
+ gfi_ptr = gi_ptr->functions[f_ix];
+ if (gfi_ptr && gfi_ptr->key == gi_ptr)
+ length = GCOV_TAG_FUNCTION_LENGTH;
+ else
+ length = 0;
+ }
+
+ gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
+ if (!length)
+ continue;
+
+ gcov_write_unsigned (gfi_ptr->ident);
+ gcov_write_unsigned (gfi_ptr->lineno_checksum);
+ gcov_write_unsigned (gfi_ptr->cfg_checksum);
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+ {
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ n_counts = ci_ptr->num;
+ gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+ GCOV_TAG_COUNTER_LENGTH (n_counts));
+ gcov_type *c_ptr = ci_ptr->values;
+ while (n_counts--)
+ gcov_write_counter (*c_ptr++);
+ ci_ptr++;
+ }
+ if (buffered)
+ fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+ }
+
+ gcov_write_unsigned (0);
+
+ read_fatal:;
+ while (fn_buffer)
+ fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+
+ if ((error = gcov_close ()))
+ fprintf (stderr, error < 0 ?
+ "profiling:%s:Overflow writing\n" :
+ "profiling:%s:Error writing\n",
+ gi_filename);
+ }
+}
+
+/* Add a new object file onto the bb chain. Invoked automatically
+ when running an object file's global ctors. */
+
+void
+__gcov_init (struct gcov_info *info)
+{
+ if (!info->version || !info->n_functions)
+ return;
+ if (gcov_version (info, info->version, 0))
+ {
+ size_t filename_length = strlen(info->filename);
+
+ /* Refresh the longest file name information */
+ if (filename_length > gcov_max_filename)
+ gcov_max_filename = filename_length;
+
+#ifndef __COREBOOT__
+ if (!gcov_list)
+ atexit (gcov_exit);
+#endif
+
+ info->next = gcov_list;
+ gcov_list = info;
+ }
+ info->version = 0;
+}
+
+/* Called before fork or exec - write out profile information gathered so
+ far and reset it to zero. This avoids duplication or loss of the
+ profile information gathered so far. */
+
+void
+__gcov_flush (void)
+{
+ const struct gcov_info *gi_ptr;
+
+ gcov_exit ();
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ unsigned f_ix;
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ unsigned t_ix;
+ const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ continue;
+ const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+ {
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
+ ci_ptr++;
+ }
+ }
+ }
+}
+
+#endif /* L_gcov */
+
+#ifdef L_gcov_merge_add
+/* The profile merging function that just adds the counters. It is given
+ an array COUNTERS of N_COUNTERS old counters and it reads the same number
+ of counters from the gcov file. */
+void
+__gcov_merge_add (gcov_type *counters, unsigned n_counters)
+{
+ for (; n_counters; counters++, n_counters--)
+ *counters += gcov_read_counter ();
+}
+#endif /* L_gcov_merge_add */
+
+#ifdef L_gcov_merge_ior
+/* The profile merging function that just adds the counters. It is given
+ an array COUNTERS of N_COUNTERS old counters and it reads the same number
+ of counters from the gcov file. */
+void
+__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
+{
+ for (; n_counters; counters++, n_counters--)
+ *counters |= gcov_read_counter ();
+}
+#endif
+
+#ifdef L_gcov_merge_single
+/* The profile merging function for choosing the most common value.
+ It is given an array COUNTERS of N_COUNTERS old counters and it
+ reads the same number of counters from the gcov file. The counters
+ are split into 3-tuples where the members of the tuple have
+ meanings:
+
+ -- the stored candidate on the most common value of the measured entity
+ -- counter
+ -- total number of evaluations of the value */
+void
+__gcov_merge_single (gcov_type *counters, unsigned n_counters)
+{
+ unsigned i, n_measures;
+ gcov_type value, counter, all;
+
+ gcc_assert (!(n_counters % 3));
+ n_measures = n_counters / 3;
+ for (i = 0; i < n_measures; i++, counters += 3)
+ {
+ value = gcov_read_counter ();
+ counter = gcov_read_counter ();
+ all = gcov_read_counter ();
+
+ if (counters[0] == value)
+ counters[1] += counter;
+ else if (counter > counters[1])
+ {
+ counters[0] = value;
+ counters[1] = counter - counters[1];
+ }
+ else
+ counters[1] -= counter;
+ counters[2] += all;
+ }
+}
+#endif /* L_gcov_merge_single */
+
+#ifdef L_gcov_merge_delta
+/* The profile merging function for choosing the most common
+ difference between two consecutive evaluations of the value. It is
+ given an array COUNTERS of N_COUNTERS old counters and it reads the
+ same number of counters from the gcov file. The counters are split
+ into 4-tuples where the members of the tuple have meanings:
+
+ -- the last value of the measured entity
+ -- the stored candidate on the most common difference
+ -- counter
+ -- total number of evaluations of the value */
+void
+__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
+{
+ unsigned i, n_measures;
+ gcov_type value, counter, all;
+
+ gcc_assert (!(n_counters % 4));
+ n_measures = n_counters / 4;
+ for (i = 0; i < n_measures; i++, counters += 4)
+ {
+ /* last = */ gcov_read_counter ();
+ value = gcov_read_counter ();
+ counter = gcov_read_counter ();
+ all = gcov_read_counter ();
+
+ if (counters[1] == value)
+ counters[2] += counter;
+ else if (counter > counters[2])
+ {
+ counters[1] = value;
+ counters[2] = counter - counters[2];
+ }
+ else
+ counters[2] -= counter;
+ counters[3] += all;
+ }
+}
+#endif /* L_gcov_merge_delta */
+
+#ifdef L_gcov_interval_profiler
+/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
+ corresponding counter in COUNTERS. If the VALUE is above or below
+ the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
+ instead. */
+
+void
+__gcov_interval_profiler (gcov_type *counters, gcov_type value,
+ int start, unsigned steps)
+{
+ gcov_type delta = value - start;
+ if (delta < 0)
+ counters[steps + 1]++;
+ else if (delta >= steps)
+ counters[steps]++;
+ else
+ counters[delta]++;
+}
+#endif
+
+#ifdef L_gcov_pow2_profiler
+/* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
+ COUNTERS[0] is incremented. */
+
+void
+__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
+{
+ if (value & (value - 1))
+ counters[0]++;
+ else
+ counters[1]++;
+}
+#endif
+
+/* Tries to determine the most common value among its inputs. Checks if the
+ value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1]
+ is incremented. If this is not the case and COUNTERS[1] is not zero,
+ COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
+ VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this
+ function is called more than 50% of the time with one value, this value
+ will be in COUNTERS[0] in the end.
+
+ In any case, COUNTERS[2] is incremented. */
+
+static inline void
+__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
+{
+ if (value == counters[0])
+ counters[1]++;
+ else if (counters[1] == 0)
+ {
+ counters[1] = 1;
+ counters[0] = value;
+ }
+ else
+ counters[1]--;
+ counters[2]++;
+}
+
+#ifdef L_gcov_one_value_profiler
+void
+__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
+{
+ __gcov_one_value_profiler_body (counters, value);
+}
+#endif
+
+#ifdef L_gcov_indirect_call_profiler
+
+/* By default, the C++ compiler will use function addresses in the
+ vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+ tells the compiler to use function descriptors instead. The value
+ of this macro says how many words wide the descriptor is (normally 2),
+ but it may be dependent on target flags. Since we do not have access
+ to the target flags here we just check to see if it is set and use
+ that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+ It is assumed that the address of a function descriptor may be treated
+ as a pointer to a function. */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
+ void* cur_func, void* callee_func)
+{
+ /* If the C++ virtual tables contain function descriptors then one
+ function may have multiple descriptors and we need to dereference
+ the descriptors to see if they point to the same function. */
+ if (cur_func == callee_func
+ || (VTABLE_USES_DESCRIPTORS && callee_func
+ && *(void **) cur_func == *(void **) callee_func))
+ __gcov_one_value_profiler_body (counter, value);
+}
+#endif
+
+
+#ifdef L_gcov_average_profiler
+/* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
+ to saturate up. */
+
+void
+__gcov_average_profiler (gcov_type *counters, gcov_type value)
+{
+ counters[0] += value;
+ counters[1] ++;
+}
+#endif
+
+#ifdef L_gcov_ior_profiler
+/* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
+ to saturate up. */
+
+void
+__gcov_ior_profiler (gcov_type *counters, gcov_type value)
+{
+ *counters |= value;
+}
+#endif
+
+#ifdef L_gcov_fork
+/* A wrapper for the fork function. Flushes the accumulated profiling data, so
+ that they are not counted twice. */
+
+pid_t
+__gcov_fork (void)
+{
+ __gcov_flush ();
+ return fork ();
+}
+#endif
+
+#ifdef L_gcov_execl
+/* A wrapper for the execl function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execl (const char *path, char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = (char **) alloca (length * sizeof (void *));
+ args[0] = arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ va_end (aq);
+
+ return execv (path, args);
+}
+#endif
+
+#ifdef L_gcov_execlp
+/* A wrapper for the execlp function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execlp (const char *path, char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = (char **) alloca (length * sizeof (void *));
+ args[0] = arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ va_end (aq);
+
+ return execvp (path, args);
+}
+#endif
+
+#ifdef L_gcov_execle
+/* A wrapper for the execle function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execle (const char *path, char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+ char **envp;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = (char **) alloca (length * sizeof (void *));
+ args[0] = arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ envp = va_arg (aq, char **);
+ va_end (aq);
+
+ return execve (path, args, envp);
+}
+#endif
+
+#ifdef L_gcov_execv
+/* A wrapper for the execv function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execv (const char *path, char *const argv[])
+{
+ __gcov_flush ();
+ return execv (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execvp
+/* A wrapper for the execvp function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execvp (const char *path, char *const argv[])
+{
+ __gcov_flush ();
+ return execvp (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execve
+/* A wrapper for the execve function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execve (const char *path, char *const argv[], char *const envp[])
+{
+ __gcov_flush ();
+ return execve (path, argv, envp);
+}
+#endif
+#endif /* inhibit_libc */
diff --git a/src/lib/selfboot.c b/src/lib/selfboot.c
index 2556a14d6e..c5fb62adca 100644
--- a/src/lib/selfboot.c
+++ b/src/lib/selfboot.c
@@ -32,6 +32,7 @@
#if CONFIG_COLLECT_TIMESTAMPS
#include <timestamp.h>
#endif
+#include <coverage.h>
/* Maximum physical address we can use for the coreboot bounce buffer. */
#ifndef MAX_ADDR
@@ -518,6 +519,9 @@ int selfboot(struct lb_memory *mem, struct cbfs_payload *payload)
#if CONFIG_COLLECT_TIMESTAMPS
timestamp_add_now(TS_SELFBOOT_JUMP);
#endif
+#if CONFIG_COVERAGE
+ coverage_exit();
+#endif
/* Before we go off to run the payload, see if
* we stayed within our bounds.