diff mbox

[Google/4_8] Support for embedding build info into gcda files

Message ID CAAe5K+UwtzHBqMG+ai9nWyWrACKR=ph8B1-ndXfL2PrdCyc7eg@mail.gmail.com
State New
Headers show

Commit Message

Teresa Johnson May 23, 2014, 6:08 p.m. UTC
Support for embedding arbitrary build information from the profile-generate
compile into the gcda file in a new BUILD_INFO record. Lines from a file
passed to the -fprofile-generate compile via a new
-fprofile-generate-buildinfo=filename option are embedded as strings
in the gcov_info struct and emitted as-is to a new GCOV_TAG_BUILD_INFO
record. They are ignored on profile-use compiles, but emitted by gcov-dump.
This is useful for recording information about, for example, source revision
info that can be helpful for diagnosing profile mis-matches.

For example:

$ cat buildinfo.txt
Build timestamp xxxx
Build source revision r12345
Other random build data

$ g++ foo.cc -fprofile-generate -fprofile-generate-buildinfo=buildinfo.txt

$ a.out
$ gcov-dump foo.gcda
foo.gcda:data:magic `gcda':version `408*'
foo.gcda:stamp 708902860
foo.gcda: a3000000:  22:PROGRAM_SUMMARY checksum=0x86a3bc55
foo.gcda:               counts=1, runs=1, sum_all=1, run_max=1, sum_max=1
foo.gcda:               counter histogram:
foo.gcda:               1: num counts=1, min counter=1, cum_counter=1
foo.gcda: a7000000:  24:BUILD INFO num_strings=3
foo.gcda:               Build timestamp xxxx
foo.gcda:               Build source revision r12345
foo.gcda:               Other random build data
foo.gcda: 01000000:   3:FUNCTION ident=1, lineno_checksum=0x17c79156,
cfg_checksum=0xdb5de9e8
foo.gcda:  01a10000:   2:COUNTERS arcs 1 counts

Tested manually, passes regression tests. Ok for Google/4_8?

Thanks,
Teresa

Comments

Xinliang David Li May 23, 2014, 6:35 p.m. UTC | #1
The change makes gcov_info a variable length array, which is not ideal.

Better just add one more field (instead of two):

struct gcov_info {
   ...
  char ** build_info;
};

For regular case, it is null, for case where the build info is
available, make it point to a string array (with an null end marker
string).

David





On Fri, May 23, 2014 at 11:08 AM, Teresa Johnson <tejohnson@google.com> wrote:
> Support for embedding arbitrary build information from the profile-generate
> compile into the gcda file in a new BUILD_INFO record. Lines from a file
> passed to the -fprofile-generate compile via a new
> -fprofile-generate-buildinfo=filename option are embedded as strings
> in the gcov_info struct and emitted as-is to a new GCOV_TAG_BUILD_INFO
> record. They are ignored on profile-use compiles, but emitted by gcov-dump.
> This is useful for recording information about, for example, source revision
> info that can be helpful for diagnosing profile mis-matches.
>
> For example:
>
> $ cat buildinfo.txt
> Build timestamp xxxx
> Build source revision r12345
> Other random build data
>
> $ g++ foo.cc -fprofile-generate -fprofile-generate-buildinfo=buildinfo.txt
>
> $ a.out
> $ gcov-dump foo.gcda
> foo.gcda:data:magic `gcda':version `408*'
> foo.gcda:stamp 708902860
> foo.gcda: a3000000:  22:PROGRAM_SUMMARY checksum=0x86a3bc55
> foo.gcda:               counts=1, runs=1, sum_all=1, run_max=1, sum_max=1
> foo.gcda:               counter histogram:
> foo.gcda:               1: num counts=1, min counter=1, cum_counter=1
> foo.gcda: a7000000:  24:BUILD INFO num_strings=3
> foo.gcda:               Build timestamp xxxx
> foo.gcda:               Build source revision r12345
> foo.gcda:               Other random build data
> foo.gcda: 01000000:   3:FUNCTION ident=1, lineno_checksum=0x17c79156,
> cfg_checksum=0xdb5de9e8
> foo.gcda:  01a10000:   2:COUNTERS arcs 1 counts
>
> Tested manually, passes regression tests. Ok for Google/4_8?
>
> Thanks,
> Teresa
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
diff mbox

Patch

Index: gcc/common.opt
===================================================================
--- gcc/common.opt	(revision 210862)
+++ gcc/common.opt	(working copy)
@@ -1798,6 +1798,10 @@  fprofile-generate-sampling
 Common Var(flag_profile_generate_sampling)
 Turn on instrumentation sampling with -fprofile-generate with rate set by --param profile-generate-sampling-rate or environment variable GCOV_SAMPLING_RATE
 
+fprofile-generate-buildinfo=
+Common RejectNegative Joined Var(flag_profile_generate_buildinfo)
+-fprofile-generate-buildinfo=filename	Read build info to include in gcda file from filename
+
 femit-function-names
 Common Var(flag_emit_function_names)
 Print to stderr the mapping from module name and function id to assembler
Index: gcc/coverage.c
===================================================================
--- gcc/coverage.c	(revision 210862)
+++ gcc/coverage.c	(working copy)
@@ -154,6 +154,10 @@  static unsigned num_cpp_defines = 0;
 static struct str_list *cpp_includes_head = NULL, *cpp_includes_tail = NULL;
 static unsigned num_cpp_includes = 0;
 
+/* List of lines read from -fprofile-generate-buildinfo=filename.  */
+struct str_list *build_info_array_head = NULL, *build_info_array_tail = NULL;
+static unsigned num_build_info = 0;
+
 /* True if the current module has any asm statements.  */
 static bool has_asm_statement;
 
@@ -792,6 +796,15 @@  read_counts_file (const char *da_file_name, unsign
 	}
       else if (tag == GCOV_TAG_PARAMETERS)
         gcov_parameter_values = gcov_read_parameters (length);
+      else if (tag == GCOV_TAG_BUILD_INFO)
+        {
+          gcov_unsigned_t num_strings;
+          char **build_info_strings = gcov_read_build_info (length,
+                                                            &num_strings);
+          for (unsigned i = 0; i < num_strings; i++)
+            free (build_info_strings[i]);
+          free (build_info_strings);
+        }
       else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
 	{
 	  counts_entry_t **slot, *entry, elt;
@@ -1699,6 +1712,7 @@  build_info_type (tree type, tree fn_info_ptr_type)
 {
   tree field, fields = NULL_TREE;
   tree merge_fn_type, mod_type;
+  tree string_type, index_type, string_array_type;
 
   /* Version ident */
   field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
@@ -1766,6 +1780,23 @@  build_info_type (tree type, tree fn_info_ptr_type)
   DECL_CHAIN (field) = fields;
   fields = field;
 
+  /* n_build_info */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+		      get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
+  fields = field;
+
+  /* build_info string array */
+  index_type = build_index_type (build_int_cst (NULL_TREE, num_build_info));
+  string_type = build_pointer_type (
+      build_qualified_type (char_type_node,
+                            TYPE_QUAL_CONST));
+  string_array_type = build_array_type (string_type, index_type);
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+                      NULL_TREE, string_array_type);
+  DECL_CHAIN (field) = fields;
+  fields = field;
+
   finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
 }
 
@@ -2178,6 +2209,7 @@  build_info (tree info_type, tree fn_ary)
   int da_file_name_len;
   vec<constructor_elt, va_gc> *v1 = NULL;
   vec<constructor_elt, va_gc> *v2 = NULL;
+  tree string_type, string_array_type;
 
   /* Version ident */
   CONSTRUCTOR_APPEND_ELT (v1, info_fields,
@@ -2204,6 +2236,7 @@  build_info (tree info_type, tree fn_ary)
   info_fields = DECL_CHAIN (info_fields);
 
   /* Filename */
+  string_type = TREE_TYPE (info_fields);
   da_file_name_len = strlen (da_file_name);
   filename_string = build_string (da_file_name_len + 1, da_file_name);
   TREE_TYPE (filename_string) = build_array_type
@@ -2256,6 +2289,21 @@  build_info (tree info_type, tree fn_ary)
 			  build1 (ADDR_EXPR, TREE_TYPE (info_fields), fn_ary));
   info_fields = DECL_CHAIN (info_fields);
 
+  /* n_build_info */
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+			  build_int_cstu (TREE_TYPE (info_fields),
+					  num_build_info));
+  info_fields = DECL_CHAIN (info_fields);
+
+  /* build_info string array */
+  string_array_type = TREE_TYPE (info_fields);
+  vec<constructor_elt,va_gc> *path_v = NULL;
+  build_str_array_value (string_type, &path_v,
+                         build_info_array_head);
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+                          build_constructor (string_array_type, path_v));
+  info_fields = DECL_CHAIN (info_fields);
+
   gcc_assert (!info_fields);
   return build_constructor (info_type, v1);
 }
@@ -2286,6 +2334,59 @@  build_init_ctor (tree gcov_info_type)
   cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
 }
 
+/* Add S to the end of the string-list, the head and tail of which are
+   pointed-to by HEAD and TAIL, respectively.  */
+
+static void
+str_list_append (struct str_list **head, struct str_list **tail, const char *s)
+{
+  struct str_list *e = XNEW (struct str_list);
+  e->str = XNEWVEC (char, strlen (s) + 1);
+  strcpy (e->str, s);
+  e->next = NULL;
+  if (*tail)
+    (*tail)->next = e;
+  else
+    *head = e;
+  *tail = e;
+}
+
+/* Read file specified to -fprofile-generate-buildinfo=filename option and
+   create a list of strings to include in build_info array.  */
+
+static void
+read_buildinfo (void)
+{
+  char buf[1024];
+  FILE *buildinfo = fopen (flag_profile_generate_buildinfo, "r");
+  if (!buildinfo)
+    {
+      error ("could not open -fprofile-generate-buildinfo file %qs: %m",
+             flag_profile_generate_buildinfo);
+    }
+
+  while (fgets (buf, sizeof buf, buildinfo) != NULL)
+    {
+      /* Remove end of line.  */
+      int len = strlen (buf);
+      if (len >= 1 && buf[len - 1] =='\n')
+        buf[len - 1] = '\0';
+      str_list_append (&build_info_array_head, &build_info_array_tail, buf);
+      num_build_info++;
+    }
+  if (ferror (buildinfo))
+    {
+      error ("error reading -fprofile-generate-buildinfo file %qs: %m",
+             flag_profile_generate_buildinfo);
+    }
+
+  if (fclose (buildinfo))
+    {
+      error ("could not close -fprofile-generate-buildinfo file %qs: %m",
+             flag_profile_generate_buildinfo);
+    }
+}
+
 /* Create the gcov_info types and object. Does not generate the initializer
    for the object.  Returns TRUE if coverage data is being emitted.  */
 
@@ -2307,6 +2408,9 @@  coverage_obj_init (void)
   if (cgraph_dump_file)
     fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
 
+  if (flag_profile_generate_buildinfo)
+    read_buildinfo ();
+
   /* Prune functions.  */
   if (!flag_dyn_ipa)
     /* in lipo mode, coverage_finish is called when function struct is cleared,
@@ -2812,23 +2916,6 @@  set_profile_parameters (struct cpp_reader *parse_i
   dump_finish (pass_profile.pass.static_pass_number);
 }
 
-/* Add S to the end of the string-list, the head and tail of which are
-   pointed-to by HEAD and TAIL, respectively.  */
-
-static void
-str_list_append (struct str_list **head, struct str_list **tail, const char *s)
-{
-  struct str_list *e = XNEW (struct str_list);
-  e->str = XNEWVEC (char, strlen (s) + 1);
-  strcpy (e->str, s);
-  e->next = NULL;
-  if (*tail)
-    (*tail)->next = e;
-  else
-    *head = e;
-  *tail = e;
-}
-
 /* Copies the macro def or undef CPP_DEF and saves the copy
    in a list. IS_DEF is a flag indicating if CPP_DEF represents
    a -D or -U.  */
Index: gcc/gcov.c
===================================================================
--- gcc/gcov.c	(revision 210862)
+++ gcc/gcov.c	(working copy)
@@ -1432,6 +1432,15 @@  read_count_file (function_t *fns)
 	}
       else if (tag == GCOV_TAG_PARAMETERS)
         gcov_read_parameters (length);
+      else if (tag == GCOV_TAG_BUILD_INFO)
+        {
+          gcov_unsigned_t num_strings;
+          char **build_info_strings = gcov_read_build_info (length,
+                                                            &num_strings);
+          for (unsigned i = 0; i < num_strings; i++)
+            free (build_info_strings[i]);
+          free (build_info_strings);
+        }
       else if (tag == GCOV_TAG_FUNCTION && !length)
 	; /* placeholder  */
       else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
Index: gcc/gcov-dump.c
===================================================================
--- gcc/gcov-dump.c	(revision 210862)
+++ gcc/gcov-dump.c	(working copy)
@@ -40,6 +40,7 @@  static void tag_lines (const char *, unsigned, uns
 static void tag_counters (const char *, unsigned, unsigned);
 static void tag_summary (const char *, unsigned, unsigned);
 static void tag_parameters (const char *, unsigned, unsigned);
+static void tag_build_info (const char *, unsigned, unsigned);
 static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
                                const struct gcov_ctr_summary *summary);
 static void tag_module_info (const char *, unsigned, unsigned);
@@ -80,6 +81,7 @@  static const tag_format_t tag_table[] =
   {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
   {GCOV_TAG_PARAMETERS, "PARAMETERS", tag_parameters},
   {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
+  {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
   {0, NULL, NULL}
 };
 
@@ -596,6 +598,28 @@  tag_module_info (const char *filename ATTRIBUTE_UN
 }
 
 static void
+tag_build_info (const char *filename,
+		unsigned tag ATTRIBUTE_UNUSED, unsigned length)
+{
+  gcov_unsigned_t num_strings = 0;
+  char **build_info_strings = gcov_read_build_info (length, &num_strings);
+  if (!build_info_strings)
+    {
+      printf ("%s:error reading build info\n", filename);
+      return;
+    }
+  printf (" num_strings=%u", num_strings);
+  for (unsigned i = 0; i < num_strings; i++)
+    {
+      printf ("\n");
+      print_prefix (filename, 0, 0);
+      printf ("\t\t%s", build_info_strings[i]);
+      free (build_info_strings[i]);
+    }
+  free (build_info_strings);
+}
+
+static void
 dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
                    const struct gcov_ctr_summary *summary)
 {
Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 210862)
+++ gcc/gcov-io.c	(working copy)
@@ -341,6 +341,47 @@  gcov_write_unsigned (gcov_unsigned_t value)
   buffer[0] = value;
 }
 
+/* Compute the total length in words required to write NUM_STRINGS
+   in STRING_ARRAY as unsigned.  */
+
+GCOV_LINKAGE gcov_unsigned_t
+gcov_compute_string_array_len (char **string_array,
+                               gcov_unsigned_t num_strings)
+{
+  gcov_unsigned_t len = 0, i;
+  for (i = 0; i < num_strings; i++)
+    {
+      gcov_unsigned_t string_len
+          = (strlen (string_array[i]) + sizeof (gcov_unsigned_t))
+          / sizeof (gcov_unsigned_t);
+      len += string_len;
+      len += 1; /* Each string is lead by a length.  */
+    }
+  return len;
+}
+
+/* Write NUM_STRINGS in STRING_ARRAY as unsigned.  */
+
+GCOV_LINKAGE void
+gcov_write_string_array (char **string_array, gcov_unsigned_t num_strings)
+{
+  gcov_unsigned_t i, j;
+  for (j = 0; j < num_strings; j++)
+    {
+      gcov_unsigned_t *aligned_string;
+      gcov_unsigned_t string_len =
+	(strlen (string_array[j]) + sizeof (gcov_unsigned_t)) /
+	sizeof (gcov_unsigned_t);
+      aligned_string = (gcov_unsigned_t *)
+	alloca ((string_len + 1) * sizeof (gcov_unsigned_t));
+      memset (aligned_string, 0, (string_len + 1) * sizeof (gcov_unsigned_t));
+      aligned_string[0] = string_len;
+      strcpy ((char*) (aligned_string + 1), string_array[j]);
+      for (i = 0; i < (string_len + 1); i++)
+        gcov_write_unsigned (aligned_string[i]);
+    }
+}
+
 /* Write counter VALUE to coverage file.  Sets error flag
    appropriately.  */
 
@@ -701,6 +742,44 @@  gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
+/* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
+   the number of words read.  */
+
+GCOV_LINKAGE gcov_unsigned_t
+gcov_read_string_array (char **string_array, gcov_unsigned_t num_strings)
+{
+  gcov_unsigned_t i, j, len = 0;
+
+  for (j = 0; j < num_strings; j++)
+   {
+     gcov_unsigned_t string_len = gcov_read_unsigned ();
+     string_array[j] =
+       (char *) xmalloc (string_len * sizeof (gcov_unsigned_t));
+     for (i = 0; i < string_len; i++)
+       ((gcov_unsigned_t *) string_array[j])[i] = gcov_read_unsigned ();
+     len += (string_len + 1);
+   }
+  return len;
+}
+
+/* Read LENGTH words (unsigned type) from a build info record with the number
+   of strings read saved in NUM_STRINGS.  Returns the string array, which
+   should be deallocated by caller, or NULL on error.  */
+
+GCOV_LINKAGE char **
+gcov_read_build_info (gcov_unsigned_t length, gcov_unsigned_t *num_strings)
+{
+  gcov_unsigned_t num = gcov_read_unsigned ();
+  char **build_info_strings = (char **)
+      xmalloc (sizeof (char *) * num);
+  gcov_unsigned_t len = gcov_read_string_array (build_info_strings,
+                                                num);
+  if (len != length - 1)
+    return NULL;
+  *num_strings = num;
+  return build_info_strings;
+}
+
 #if IN_GCOV_TOOL || !IN_LIBGCOV && IN_GCOV != 1
 /* Read LEN words (unsigned type) and construct MOD_INFO.  */
 
@@ -708,7 +787,7 @@  GCOV_LINKAGE void
 gcov_read_module_info (struct gcov_module_info *mod_info,
                        gcov_unsigned_t len)
 {
-  gcov_unsigned_t src_filename_len, filename_len, i, j, num_strings;
+  gcov_unsigned_t src_filename_len, filename_len, i, num_strings;
   mod_info->ident = gcov_read_unsigned ();
   mod_info->is_primary = gcov_read_unsigned ();
   mod_info->flags = gcov_read_unsigned ();
@@ -740,16 +819,7 @@  gcov_read_module_info (struct gcov_module_info *mo
     + mod_info->num_system_paths
     + mod_info->num_cpp_defines + mod_info->num_cpp_includes
     + mod_info->num_cl_args;
-  for (j = 0; j < num_strings; j++)
-   {
-     gcov_unsigned_t string_len = gcov_read_unsigned ();
-     mod_info->string_array[j] =
-       (char *) xmalloc (string_len * sizeof (gcov_unsigned_t));
-     for (i = 0; i < string_len; i++)
-       ((gcov_unsigned_t *) mod_info->string_array[j])[i] =
-	 gcov_read_unsigned ();
-     len -= (string_len + 1);
-   }
+  len -= gcov_read_string_array (mod_info->string_array, num_strings);
   gcc_assert (!len);
 }
 #endif
Index: gcc/gcov-io.h
===================================================================
--- gcc/gcov-io.h	(revision 210862)
+++ gcc/gcov-io.h	(working copy)
@@ -129,7 +129,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
    blocks they are for.
 
    The data file contains the following records.
-        data: {unit summary:program* parameter-data function-data*}*
+        data: {unit summary:program* build_info parameter-data function-data*}*
 	unit: header int32:checksum
         function-data:	announce_function present counts
 	announce_function: header int32:ident
@@ -143,6 +143,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
         histogram-buckets: int32:num int64:min int64:sum
         parameter-data: header parm-value*
         parm-value: string:macro_name int64:value
+        build_info: string:info*
 
    The ANNOUNCE_FUNCTION record is the same as that in the note file,
    but without the source location.  The COUNTS gives the
@@ -153,6 +154,11 @@  see the files COPYING3 and COPYING.RUNTIME respect
    each with a unique checksum.  Note that the data file might contain
    information from several runs concatenated, or the data might be merged.
 
+   BUILD_INFO record contains a list of strings that is used
+   to include in the data file information about the profile generate
+   build.  For example, it can be used to include source revision
+   information that is useful in diagnosing profile mis-matches.
+
    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,
@@ -259,6 +265,7 @@  typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
         (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
 #define GCOV_TAG_PARAMETERS    ((gcov_unsigned_t)0xa5000000)
+#define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
 #define GCOV_TAG_MODULE_INFO ((gcov_unsigned_t)0xab000000)
 #define GCOV_TAG_AFDO_FILE_NAMES ((gcov_unsigned_t)0xaa000000)
 #define GCOV_TAG_AFDO_FUNCTION ((gcov_unsigned_t)0xac000000)
@@ -468,11 +475,15 @@  GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
 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;
+GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
+  ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE struct gcov_parameter_value *gcov_read_parameters (gcov_unsigned_t)
   ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE const char *gcov_read_string (void);
 GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
 			     gcov_unsigned_t /*length */);
+GCOV_LINKAGE gcov_unsigned_t gcov_read_string_array (char **, gcov_unsigned_t)
+  ATTRIBUTE_HIDDEN;
 
 #if !IN_LIBGCOV && IN_GCOV != 1
 GCOV_LINKAGE void gcov_read_module_info (struct gcov_module_info *mod_info,
@@ -482,6 +493,11 @@  GCOV_LINKAGE void gcov_read_module_info (struct gc
 #if !IN_GCOV
 /* Available outside gcov */
 GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE gcov_unsigned_t gcov_compute_string_array_len (char **,
+                                                            gcov_unsigned_t)
+  ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_string_array (char **, gcov_unsigned_t)
+  ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_write_string (const char *);
 #endif
 
Index: libgcc/dyn-ipa.c
===================================================================
--- libgcc/dyn-ipa.c	(revision 210862)
+++ libgcc/dyn-ipa.c	(working copy)
@@ -2233,7 +2233,7 @@  static void
 gcov_write_module_info (const struct gcov_info *mod_info,
                         unsigned is_primary)
 {
-  gcov_unsigned_t len = 0, filename_len = 0, src_filename_len = 0, i, j;
+  gcov_unsigned_t len = 0, filename_len = 0, src_filename_len = 0, i;
   gcov_unsigned_t num_strings;
   gcov_unsigned_t *aligned_fname;
   struct gcov_module_info  *module_info = mod_info->mod_info;
@@ -2248,14 +2248,8 @@  gcov_write_module_info (const struct gcov_info *mo
     + module_info->num_system_paths
     + module_info->num_cpp_defines + module_info->num_cpp_includes
     + module_info->num_cl_args;
-  for (i = 0; i < num_strings; i++)
-    {
-      gcov_unsigned_t string_len
-          = (strlen (module_info->string_array[i]) + sizeof (gcov_unsigned_t))
-          / sizeof (gcov_unsigned_t);
-      len += string_len;
-      len += 1; /* Each string is lead by a length.  */
-    }
+  len += gcov_compute_string_array_len (module_info->string_array,
+                                        num_strings);
 
   len += 11; /* 11 more fields */
 
@@ -2288,20 +2282,7 @@  gcov_write_module_info (const struct gcov_info *mo
     gcov_write_unsigned (aligned_fname[i]);
 
   /* Now write the string array.  */
-  for (j = 0; j < num_strings; j++)
-    {
-      gcov_unsigned_t *aligned_string;
-      gcov_unsigned_t string_len =
-	(strlen (module_info->string_array[j]) + sizeof (gcov_unsigned_t)) /
-	sizeof (gcov_unsigned_t);
-      aligned_string = (gcov_unsigned_t *)
-	alloca ((string_len + 1) * sizeof (gcov_unsigned_t));
-      memset (aligned_string, 0, (string_len + 1) * sizeof (gcov_unsigned_t));
-      aligned_string[0] = string_len;
-      strcpy ((char*) (aligned_string + 1), module_info->string_array[j]);
-      for (i = 0; i < (string_len + 1); i++)
-        gcov_write_unsigned (aligned_string[i]);
-    }
+  gcov_write_string_array (module_info->string_array, num_strings);
 }
 
 /* Write out MOD_INFO and its imported modules into gcda file.  */
Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 210862)
+++ libgcc/libgcov-driver.c	(working copy)
@@ -482,7 +482,7 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
                       struct gcov_parameter_value **merged_parameters)
 {
   gcov_unsigned_t tag, length, version, stamp;
-  unsigned t_ix, f_ix;
+  unsigned t_ix, f_ix, i;
   int error = 0;
   struct gcov_summary_buffer **sum_tail = &sum_buffer;
 
@@ -535,6 +535,44 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
     next_summary:;
     }
 
+  if (tag == GCOV_TAG_BUILD_INFO)
+    {
+      length = gcov_read_unsigned ();
+      gcov_unsigned_t num_strings = 0;
+      char **build_info_strings = gcov_read_build_info (length, &num_strings);
+      if (!build_info_strings)
+        {
+          gcov_error ("profiling:%s:Error reading build info\n", gi_filename);
+          return -1;
+        }
+      /* The build info should match since the compile stamps matched above.  */
+      if (num_strings != gi_ptr->n_build_info)
+        {
+          gcov_error ("profiling:%s:Mismatched build info count (expected %u "
+                      "strings, read %u)\n",
+                      gi_filename, gi_ptr->n_build_info, num_strings);
+          return -1;
+        }
+
+      for (i = 0; i < num_strings; i++)
+        {
+          if (strcmp (build_info_strings[i], gi_ptr->build_info[i]))
+            {
+              gcov_error ("profiling:%s:Mismatched build info string "
+                          "(expected %s, read %s)\n",
+                          gi_filename, gi_ptr->build_info[i],
+                          build_info_strings[i]);
+              return -1;
+            }
+          free (build_info_strings[i]);
+        }
+      free (build_info_strings);
+
+      /* Since the stamps matched if we got here, this should be from
+         the same compilation and the build info strings should match.  */
+      tag = gcov_read_unsigned ();
+    }
+
   if (tag == GCOV_TAG_PARAMETERS)
     {
       length = gcov_read_unsigned ();
@@ -607,6 +645,23 @@  read_error:;
 }
 
 
+/* Write build_info strings from GI_PTR to a gcda file starting from its current
+   location.  */
+
+static void
+gcov_write_build_info (struct gcov_info *gi_ptr)
+{
+  if (gi_ptr->n_build_info)
+    {
+      gcov_unsigned_t len = 1; /* Number of strings.  */
+      len += gcov_compute_string_array_len (gi_ptr->build_info,
+                                            gi_ptr->n_build_info);
+      gcov_write_tag_length (GCOV_TAG_BUILD_INFO, len);
+      gcov_write_unsigned (gi_ptr->n_build_info);
+      gcov_write_string_array (gi_ptr->build_info, gi_ptr->n_build_info);
+    }
+}
+
 /* Write counters in GI_PTR to a gcda file starting from its current
    location.  */
 
@@ -693,6 +748,8 @@  gcov_exit_write_gcda (struct gcov_info *gi_ptr,
       sum_buffer = next_sum_buffer;
     }
 
+  gcov_write_build_info (gi_ptr);
+
   if (merged_parameters)
     {
       gcov_write_parameters (merged_parameters);
Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 210862)
+++ libgcc/libgcov.h	(working copy)
@@ -172,6 +172,7 @@  typedef unsigned gcov_type_unsigned __attribute__
 #define gcov_read_counter __gcov_read_counter
 #define gcov_read_summary __gcov_read_summary
 #define gcov_read_parameters __gcov_read_parameters
+#define gcov_read_buildinfo __gcov_read_buildinfo
 #define gcov_read_string __gcov_read_string
 #define gcov_read_module_info __gcov_read_module_info
 #define gcov_sort_n_vals __gcov_sort_n_vals
@@ -237,6 +238,9 @@  struct gcov_info
 #else
   const struct gcov_fn_info **functions;
 #endif /* !IN_GCOV_TOOL */
+  unsigned n_build_info;        /* number of build_info strings.  */
+  char *build_info[1];          /* strings to include in BUILD_INFO
+                                   section of gcda file.  */
 };
 
 /* Information about a single imported module.  */