Home | History | Annotate | Download | only in gcov-src
      1 /* File format for coverage information
      2    Copyright (C) 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005, 2007,
      3    2008  Free Software Foundation, Inc.
      4    Contributed by Bob Manson <manson (at) cygnus.com>.
      5    Completely remangled by Nathan Sidwell <nathan (at) codesourcery.com>.
      6 
      7 This file is part of GCC.
      8 
      9 GCC is free software; you can redistribute it and/or modify it under
     10 the terms of the GNU General Public License as published by the Free
     11 Software Foundation; either version 3, or (at your option) any later
     12 version.
     13 
     14 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     17 for more details.
     18 
     19 Under Section 7 of GPL version 3, you are granted additional
     20 permissions described in the GCC Runtime Library Exception, version
     21 3.1, as published by the Free Software Foundation.
     22 
     23 You should have received a copy of the GNU General Public License and
     24 a copy of the GCC Runtime Library Exception along with this program;
     25 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     26 <http://www.gnu.org/licenses/>.  */
     27 
     28 /* Routines declared in gcov-io.h.  This file should be #included by
     29    another source file, after having #included gcov-io.h.  */
     30 
     31 /* Redefine these here, rather than using the ones in system.h since
     32  * including system.h leads to conflicting definitions of other
     33  * symbols and macros.  */
     34 #undef MIN
     35 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
     36 
     37 #if !IN_GCOV
     38 static void gcov_write_block (unsigned);
     39 static gcov_unsigned_t *gcov_write_words (unsigned);
     40 #endif
     41 static const gcov_unsigned_t *gcov_read_words (unsigned);
     42 #if !IN_LIBGCOV
     43 static void gcov_allocate (unsigned);
     44 #endif
     45 
     46 #ifdef __GCOV_KERNEL__
     47 struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
     48 #endif
     49 
     50 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
     51 {
     52 #if !IN_LIBGCOV
     53   if (gcov_var.endian)
     54     {
     55       value = (value >> 16) | (value << 16);
     56       value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
     57     }
     58 #endif
     59   return value;
     60 }
     61 
     62 /* Open a gcov file. NAME is the name of the file to open and MODE
     63    indicates whether a new file should be created, or an existing file
     64    opened. If MODE is >= 0 an existing file will be opened, if
     65    possible, and if MODE is <= 0, a new file will be created. Use
     66    MODE=0 to attempt to reopen an existing file and then fall back on
     67    creating a new one.  If MODE < 0, the file will be opened in
     68    read-only mode.  Otherwise it will be opened for modification.
     69    Return zero on failure, >0 on opening an existing file and <0 on
     70    creating a new one.  */
     71 
     72 #ifndef __GCOV_KERNEL__
     73 GCOV_LINKAGE int
     74 #if IN_LIBGCOV
     75 gcov_open (const char *name)
     76 #else
     77 gcov_open (const char *name, int mode)
     78 #endif
     79 {
     80 #if IN_LIBGCOV
     81   const int mode = 0;
     82 #endif
     83 #if GCOV_LOCKED
     84   struct flock s_flock;
     85   int fd;
     86 
     87   s_flock.l_whence = SEEK_SET;
     88   s_flock.l_start = 0;
     89   s_flock.l_len = 0; /* Until EOF.  */
     90   s_flock.l_pid = getpid ();
     91 #endif
     92 
     93   gcc_assert (!gcov_var.file);
     94   gcov_var.start = 0;
     95   gcov_var.offset = gcov_var.length = 0;
     96   gcov_var.overread = -1u;
     97   gcov_var.error = 0;
     98 #if !IN_LIBGCOV
     99   gcov_var.endian = 0;
    100 #endif
    101 #if GCOV_LOCKED
    102   if (mode > 0)
    103     {
    104       /* Read-only mode - acquire a read-lock.  */
    105       s_flock.l_type = F_RDLCK;
    106       fd = open (name, O_RDONLY);
    107     }
    108   else
    109     {
    110       /* Write mode - acquire a write-lock.  */
    111       s_flock.l_type = F_WRLCK;
    112       fd = open (name, O_RDWR | O_CREAT, 0666);
    113     }
    114   if (fd < 0)
    115     return 0;
    116 
    117   while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
    118     continue;
    119 
    120   gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
    121 
    122   if (!gcov_var.file)
    123     {
    124       close (fd);
    125       return 0;
    126     }
    127 
    128   if (mode > 0)
    129     gcov_var.mode = 1;
    130   else if (mode == 0)
    131     {
    132       struct stat st;
    133 
    134       if (fstat (fd, &st) < 0)
    135 	{
    136 	  fclose (gcov_var.file);
    137 	  gcov_var.file = 0;
    138 	  return 0;
    139 	}
    140       if (st.st_size != 0)
    141 	gcov_var.mode = 1;
    142       else
    143 	gcov_var.mode = mode * 2 + 1;
    144     }
    145   else
    146     gcov_var.mode = mode * 2 + 1;
    147 #else
    148   if (mode >= 0)
    149     gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
    150 
    151   if (gcov_var.file)
    152     gcov_var.mode = 1;
    153   else if (mode <= 0)
    154     {
    155       gcov_var.file = fopen (name, "w+b");
    156       if (gcov_var.file)
    157 	gcov_var.mode = mode * 2 + 1;
    158     }
    159   if (!gcov_var.file)
    160     return 0;
    161 #endif
    162 
    163   setbuf (gcov_var.file, (char *)0);
    164 
    165   return 1;
    166 }
    167 #else /* __GCOV_KERNEL__ */
    168 
    169 extern _GCOV_FILE *gcov_current_file;
    170 
    171 GCOV_LINKAGE int
    172 gcov_open (const char *name)
    173 {
    174   gcov_var.start = 0;
    175   gcov_var.offset = gcov_var.length = 0;
    176   gcov_var.overread = -1u;
    177   gcov_var.error = 0;
    178   gcov_var.file = gcov_current_file;
    179   gcov_var.mode = 1;
    180 
    181   return 1;
    182 }
    183 #endif /* __GCOV_KERNEL__ */
    184 
    185 /* Close the current gcov file. Flushes data to disk. Returns nonzero
    186    on failure or error flag set.  */
    187 
    188 GCOV_LINKAGE int
    189 gcov_close (void)
    190 {
    191   if (gcov_var.file)
    192     {
    193 #if !IN_GCOV
    194       if (gcov_var.offset && gcov_var.mode < 0)
    195 	gcov_write_block (gcov_var.offset);
    196 #endif
    197       _GCOV_fclose (gcov_var.file);
    198       gcov_var.file = 0;
    199       gcov_var.length = 0;
    200     }
    201 #if !IN_LIBGCOV
    202   free (gcov_var.buffer);
    203   gcov_var.alloc = 0;
    204   gcov_var.buffer = 0;
    205 #endif
    206   gcov_var.mode = 0;
    207   return gcov_var.error;
    208 }
    209 
    210 #if !IN_LIBGCOV
    211 /* Modify FILENAME to a canonical form after stripping known prefixes
    212    in place.  It removes '/proc/self/cwd' and '/proc/self/cwd/.'.
    213    Returns the in-place modified filename.  */
    214 
    215 GCOV_LINKAGE char *
    216 gcov_canonical_filename (char *filename)
    217 {
    218   static char cwd_dot_str[] = "/proc/self/cwd/./";
    219   int cwd_dot_len = strlen (cwd_dot_str);
    220   int cwd_len = cwd_dot_len - 2; /* without trailing './' */
    221   int filename_len = strlen (filename);
    222   /* delete the longer prefix first */
    223   if (0 == strncmp (filename, cwd_dot_str, cwd_dot_len))
    224     {
    225       memmove (filename, filename + cwd_dot_len, filename_len - cwd_dot_len);
    226       filename[filename_len - cwd_dot_len] = '\0';
    227       return filename;
    228     }
    229 
    230   if (0 == strncmp (filename, cwd_dot_str, cwd_len))
    231     {
    232       memmove (filename, filename + cwd_len, filename_len - cwd_len);
    233       filename[filename_len - cwd_len] = '\0';
    234       return filename;
    235     }
    236   return filename;
    237 }
    238 
    239 /* Read LEN words and construct load latency info LL_INFO.  */
    240 
    241 GCOV_LINKAGE void
    242 gcov_read_pmu_load_latency_info (gcov_pmu_ll_info_t *ll_info,
    243                                  gcov_unsigned_t len ATTRIBUTE_UNUSED)
    244 {
    245   const char *filename;
    246   ll_info->counts = gcov_read_unsigned ();
    247   ll_info->self = gcov_read_unsigned ();
    248   ll_info->cum = gcov_read_unsigned ();
    249   ll_info->lt_10 = gcov_read_unsigned ();
    250   ll_info->lt_32 = gcov_read_unsigned ();
    251   ll_info->lt_64 = gcov_read_unsigned ();
    252   ll_info->lt_256 = gcov_read_unsigned ();
    253   ll_info->lt_1024 = gcov_read_unsigned ();
    254   ll_info->gt_1024 = gcov_read_unsigned ();
    255   ll_info->wself = gcov_read_unsigned ();
    256   ll_info->code_addr = gcov_read_counter ();
    257   ll_info->line = gcov_read_unsigned ();
    258   ll_info->discriminator = gcov_read_unsigned ();
    259   filename = gcov_read_string ();
    260   if (filename)
    261     ll_info->filename = gcov_canonical_filename (xstrdup (filename));
    262   else
    263     ll_info->filename = 0;
    264 }
    265 
    266 /* Read LEN words and construct branch mispredict info BRM_INFO.  */
    267 
    268 GCOV_LINKAGE void
    269 gcov_read_pmu_branch_mispredict_info (gcov_pmu_brm_info_t *brm_info,
    270                                       gcov_unsigned_t len ATTRIBUTE_UNUSED)
    271 {
    272   const char *filename;
    273   brm_info->counts = gcov_read_unsigned ();
    274   brm_info->self = gcov_read_unsigned ();
    275   brm_info->cum = gcov_read_unsigned ();
    276   brm_info->code_addr = gcov_read_counter ();
    277   brm_info->line = gcov_read_unsigned ();
    278   brm_info->discriminator = gcov_read_unsigned ();
    279   filename = gcov_read_string ();
    280   if (filename)
    281     brm_info->filename = gcov_canonical_filename (xstrdup (filename));
    282   else
    283     brm_info->filename = 0;
    284 }
    285 
    286 /* Read LEN words from an open gcov file and construct data into pmu
    287    tool header TOOL_HEADER.  */
    288 
    289 GCOV_LINKAGE void gcov_read_pmu_tool_header (gcov_pmu_tool_header_t *header,
    290                                            gcov_unsigned_t len ATTRIBUTE_UNUSED)
    291 {
    292   const char *str;
    293   str = gcov_read_string ();
    294   header->host_cpu = str ? xstrdup (str) : 0;
    295   str = gcov_read_string ();
    296   header->hostname = str ? xstrdup (str) : 0;
    297   str = gcov_read_string ();
    298   header->kernel_version = str ? xstrdup (str) : 0;
    299   str = gcov_read_string ();
    300   header->column_header = str ? xstrdup (str) : 0;
    301   str = gcov_read_string ();
    302   header->column_description = str ? xstrdup (str) : 0;
    303   str = gcov_read_string ();
    304   header->full_header = str ? xstrdup (str) : 0;
    305 }
    306 #endif
    307 
    308 #if !IN_LIBGCOV
    309 /* Check if MAGIC is EXPECTED. Use it to determine endianness of the
    310    file. Returns +1 for same endian, -1 for other endian and zero for
    311    not EXPECTED.  */
    312 
    313 GCOV_LINKAGE int
    314 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
    315 {
    316   if (magic == expected)
    317     return 1;
    318   magic = (magic >> 16) | (magic << 16);
    319   magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
    320   if (magic == expected)
    321     {
    322       gcov_var.endian = 1;
    323       return -1;
    324     }
    325   return 0;
    326 }
    327 #endif
    328 
    329 #if !IN_LIBGCOV
    330 static void
    331 gcov_allocate (unsigned length)
    332 {
    333   size_t new_size = gcov_var.alloc;
    334 
    335   if (!new_size)
    336     new_size = GCOV_BLOCK_SIZE;
    337   new_size += length;
    338   new_size *= 2;
    339 
    340   gcov_var.alloc = new_size;
    341   gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
    342 }
    343 #endif
    344 
    345 #if !IN_GCOV
    346 /* Write out the current block, if needs be.  */
    347 
    348 static void
    349 gcov_write_block (unsigned size)
    350 {
    351   if (_GCOV_fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
    352     gcov_var.error = 1;
    353   gcov_var.start += size;
    354   gcov_var.offset -= size;
    355 }
    356 
    357 #if IN_LIBGCOV
    358 /* Return the number of words STRING would need including the length
    359    field in the output stream itself.  This should be identical to
    360    "alloc" calculation in gcov_write_string().  */
    361 
    362 GCOV_LINKAGE gcov_unsigned_t
    363 gcov_string_length (const char *string)
    364 {
    365   gcov_unsigned_t len = (string) ? strlen (string) : 0;
    366   /* + 1 because of the length field.  */
    367   gcov_unsigned_t alloc = 1 + ((len + 4) >> 2);
    368 
    369   /* Can not write a bigger than GCOV_BLOCK_SIZE string yet */
    370   gcc_assert (alloc < GCOV_BLOCK_SIZE);
    371   return alloc;
    372 }
    373 #endif
    374 
    375 /* Allocate space to write BYTES bytes to the gcov file. Return a
    376    pointer to those bytes, or NULL on failure.  */
    377 
    378 static gcov_unsigned_t *
    379 gcov_write_words (unsigned words)
    380 {
    381   gcov_unsigned_t *result;
    382 
    383   gcc_assert (gcov_var.mode < 0);
    384 #if IN_LIBGCOV
    385   if (gcov_var.offset + words >= GCOV_BLOCK_SIZE)
    386     {
    387       gcov_write_block (MIN (gcov_var.offset, GCOV_BLOCK_SIZE));
    388       if (gcov_var.offset)
    389 	{
    390 	  gcc_assert (gcov_var.offset < GCOV_BLOCK_SIZE);
    391 	  memcpy (gcov_var.buffer,
    392                   gcov_var.buffer + GCOV_BLOCK_SIZE,
    393                   gcov_var.offset << 2);
    394 	}
    395     }
    396 #else
    397   if (gcov_var.offset + words > gcov_var.alloc)
    398     gcov_allocate (gcov_var.offset + words);
    399 #endif
    400   result = &gcov_var.buffer[gcov_var.offset];
    401   gcov_var.offset += words;
    402 
    403   return result;
    404 }
    405 
    406 /* Write unsigned VALUE to coverage file.  Sets error flag
    407    appropriately.  */
    408 
    409 GCOV_LINKAGE void
    410 gcov_write_unsigned (gcov_unsigned_t value)
    411 {
    412   gcov_unsigned_t *buffer = gcov_write_words (1);
    413 
    414   buffer[0] = value;
    415 }
    416 
    417 /* Write counter VALUE to coverage file.  Sets error flag
    418    appropriately.  */
    419 
    420 #if IN_LIBGCOV
    421 GCOV_LINKAGE void
    422 gcov_write_counter (gcov_type value)
    423 {
    424   gcov_unsigned_t *buffer = gcov_write_words (2);
    425 
    426   buffer[0] = (gcov_unsigned_t) value;
    427   if (sizeof (value) > sizeof (gcov_unsigned_t))
    428     buffer[1] = (gcov_unsigned_t) (value >> 32);
    429   else
    430     buffer[1] = 0;
    431 }
    432 #endif /* IN_LIBGCOV */
    433 
    434 /* Write STRING to coverage file.  Sets error flag on file
    435    error, overflow flag on overflow */
    436 
    437 GCOV_LINKAGE void
    438 gcov_write_string (const char *string)
    439 {
    440   unsigned length = 0;
    441   unsigned alloc = 0;
    442   gcov_unsigned_t *buffer;
    443 
    444   if (string)
    445     {
    446       length = strlen (string);
    447       alloc = (length + 4) >> 2;
    448     }
    449 
    450   buffer = gcov_write_words (1 + alloc);
    451 
    452   buffer[0] = alloc;
    453   buffer[alloc] = 0;
    454   memcpy (&buffer[1], string, length);
    455 }
    456 
    457 #if !IN_LIBGCOV
    458 /* Write a tag TAG and reserve space for the record length. Return a
    459    value to be used for gcov_write_length.  */
    460 
    461 GCOV_LINKAGE gcov_position_t
    462 gcov_write_tag (gcov_unsigned_t tag)
    463 {
    464   gcov_position_t result = gcov_var.start + gcov_var.offset;
    465   gcov_unsigned_t *buffer = gcov_write_words (2);
    466 
    467   buffer[0] = tag;
    468   buffer[1] = 0;
    469 
    470   return result;
    471 }
    472 
    473 /* Write a record length using POSITION, which was returned by
    474    gcov_write_tag.  The current file position is the end of the
    475    record, and is restored before returning.  Returns nonzero on
    476    overflow.  */
    477 
    478 GCOV_LINKAGE void
    479 gcov_write_length (gcov_position_t position)
    480 {
    481   unsigned offset;
    482   gcov_unsigned_t length;
    483   gcov_unsigned_t *buffer;
    484 
    485   gcc_assert (gcov_var.mode < 0);
    486   gcc_assert (position + 2 <= gcov_var.start + gcov_var.offset);
    487   gcc_assert (position >= gcov_var.start);
    488   offset = position - gcov_var.start;
    489   length = gcov_var.offset - offset - 2;
    490   buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
    491   buffer[1] = length;
    492   if (gcov_var.offset >= GCOV_BLOCK_SIZE)
    493     gcov_write_block (gcov_var.offset);
    494 }
    495 
    496 #else /* IN_LIBGCOV */
    497 
    498 /* Write a tag TAG and length LENGTH.  */
    499 
    500 GCOV_LINKAGE void
    501 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
    502 {
    503   gcov_unsigned_t *buffer = gcov_write_words (2);
    504 
    505   buffer[0] = tag;
    506   buffer[1] = length;
    507 }
    508 
    509 /* Write a summary structure to the gcov file.  Return nonzero on
    510    overflow.  */
    511 
    512 GCOV_LINKAGE void
    513 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
    514 {
    515   unsigned ix;
    516   const struct gcov_ctr_summary *csum;
    517 
    518   gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
    519   gcov_write_unsigned (summary->checksum);
    520   for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
    521     {
    522       gcov_write_unsigned (csum->num);
    523       gcov_write_unsigned (csum->runs);
    524       gcov_write_counter (csum->sum_all);
    525       gcov_write_counter (csum->run_max);
    526       gcov_write_counter (csum->sum_max);
    527     }
    528 }
    529 #endif /* IN_LIBGCOV */
    530 
    531 #endif /*!IN_GCOV */
    532 
    533 /* Return a pointer to read BYTES bytes from the gcov file. Returns
    534    NULL on failure (read past EOF).  */
    535 
    536 static const gcov_unsigned_t *
    537 gcov_read_words (unsigned words)
    538 {
    539   const gcov_unsigned_t *result;
    540   unsigned excess = gcov_var.length - gcov_var.offset;
    541 
    542   gcc_assert (gcov_var.mode > 0);
    543   gcc_assert (words < GCOV_BLOCK_SIZE);
    544   if (excess < words)
    545     {
    546       gcov_var.start += gcov_var.offset;
    547 #if IN_LIBGCOV
    548       if (excess)
    549 	{
    550 	  gcc_assert (excess < GCOV_BLOCK_SIZE);
    551 	  memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4);
    552 	}
    553 #else
    554       memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4);
    555 #endif
    556       gcov_var.offset = 0;
    557       gcov_var.length = excess;
    558 #if IN_LIBGCOV
    559       excess = (sizeof (gcov_var.buffer) / sizeof (gcov_var.buffer[0])) - gcov_var.length;
    560 #else
    561       if (gcov_var.length + words > gcov_var.alloc)
    562 	gcov_allocate (gcov_var.length + words);
    563       excess = gcov_var.alloc - gcov_var.length;
    564 #endif
    565       excess = _GCOV_fread (gcov_var.buffer + gcov_var.length,
    566 		      1, excess << 2, gcov_var.file) >> 2;
    567       gcov_var.length += excess;
    568       if (gcov_var.length < words)
    569 	{
    570 	  gcov_var.overread += words - gcov_var.length;
    571 	  gcov_var.length = 0;
    572 	  return 0;
    573 	}
    574     }
    575   result = &gcov_var.buffer[gcov_var.offset];
    576   gcov_var.offset += words;
    577   return result;
    578 }
    579 
    580 /* Read unsigned value from a coverage file. Sets error flag on file
    581    error, overflow flag on overflow */
    582 
    583 GCOV_LINKAGE gcov_unsigned_t
    584 gcov_read_unsigned (void)
    585 {
    586   gcov_unsigned_t value;
    587   const gcov_unsigned_t *buffer = gcov_read_words (1);
    588 
    589   if (!buffer)
    590     return 0;
    591   value = from_file (buffer[0]);
    592   return value;
    593 }
    594 
    595 /* Read counter value from a coverage file. Sets error flag on file
    596    error, overflow flag on overflow */
    597 
    598 GCOV_LINKAGE gcov_type
    599 gcov_read_counter (void)
    600 {
    601   gcov_type value;
    602   const gcov_unsigned_t *buffer = gcov_read_words (2);
    603 
    604   if (!buffer)
    605     return 0;
    606   value = from_file (buffer[0]);
    607   if (sizeof (value) > sizeof (gcov_unsigned_t))
    608     value |= ((gcov_type) from_file (buffer[1])) << 32;
    609   else if (buffer[1])
    610     gcov_var.error = -1;
    611 
    612   return value;
    613 }
    614 
    615 /* Read string from coverage file. Returns a pointer to a static
    616    buffer, or NULL on empty string. You must copy the string before
    617    calling another gcov function.  */
    618 
    619 GCOV_LINKAGE const char *
    620 gcov_read_string (void)
    621 {
    622   unsigned length = gcov_read_unsigned ();
    623 
    624   if (!length)
    625     return 0;
    626 
    627   return (const char *) gcov_read_words (length);
    628 }
    629 
    630 GCOV_LINKAGE void
    631 gcov_read_summary (struct gcov_summary *summary)
    632 {
    633   unsigned ix;
    634   struct gcov_ctr_summary *csum;
    635 
    636   summary->checksum = gcov_read_unsigned ();
    637   for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
    638     {
    639       csum->num = gcov_read_unsigned ();
    640       csum->runs = gcov_read_unsigned ();
    641       csum->sum_all = gcov_read_counter ();
    642       csum->run_max = gcov_read_counter ();
    643       csum->sum_max = gcov_read_counter ();
    644     }
    645 }
    646 
    647 #if !IN_LIBGCOV && IN_GCOV != 1
    648 /* Read LEN words (unsigned type) and construct MOD_INFO.  */
    649 
    650 GCOV_LINKAGE void
    651 gcov_read_module_info (struct gcov_module_info *mod_info,
    652                        gcov_unsigned_t len)
    653 {
    654   gcov_unsigned_t src_filename_len, filename_len, i, j, num_strings;
    655   mod_info->ident = gcov_read_unsigned ();
    656   mod_info->is_primary = gcov_read_unsigned ();
    657   mod_info->is_exported = gcov_read_unsigned ();
    658   mod_info->lang  = gcov_read_unsigned ();
    659   mod_info->num_quote_paths = gcov_read_unsigned ();
    660   mod_info->num_bracket_paths = gcov_read_unsigned ();
    661   mod_info->num_cpp_defines = gcov_read_unsigned ();
    662   mod_info->num_cpp_includes = gcov_read_unsigned ();
    663   mod_info->num_cl_args = gcov_read_unsigned ();
    664   len -= 9;
    665 
    666   filename_len = gcov_read_unsigned ();
    667   mod_info->da_filename = (char *) xmalloc (filename_len *
    668                                             sizeof (gcov_unsigned_t));
    669   for (i = 0; i < filename_len; i++)
    670     ((gcov_unsigned_t *) mod_info->da_filename)[i] = gcov_read_unsigned ();
    671   len -= (filename_len + 1);
    672 
    673   src_filename_len = gcov_read_unsigned ();
    674   mod_info->source_filename = (char *) xmalloc (src_filename_len *
    675 						sizeof (gcov_unsigned_t));
    676   for (i = 0; i < src_filename_len; i++)
    677     ((gcov_unsigned_t *) mod_info->source_filename)[i] = gcov_read_unsigned ();
    678   len -= (src_filename_len + 1);
    679 
    680   num_strings = mod_info->num_quote_paths + mod_info->num_bracket_paths +
    681     mod_info->num_cpp_defines + mod_info->num_cpp_includes +
    682     mod_info->num_cl_args;
    683   for (j = 0; j < num_strings; j++)
    684    {
    685      gcov_unsigned_t string_len = gcov_read_unsigned ();
    686      mod_info->string_array[j] =
    687        (char *) xmalloc (string_len * sizeof (gcov_unsigned_t));
    688      for (i = 0; i < string_len; i++)
    689        ((gcov_unsigned_t *) mod_info->string_array[j])[i] =
    690 	 gcov_read_unsigned ();
    691      len -= (string_len + 1);
    692    }
    693   gcc_assert (!len);
    694 }
    695 #endif
    696 
    697 #if !IN_LIBGCOV
    698 /* Reset to a known position.  BASE should have been obtained from
    699    gcov_position, LENGTH should be a record length.  */
    700 
    701 GCOV_LINKAGE void
    702 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
    703 {
    704 #ifdef __GCOV_KERNEL__
    705   /* should not reach this point */
    706   gcc_assert (0);
    707 #else /* __GCOV_KERNEL__ */
    708   gcc_assert (gcov_var.mode > 0);
    709   base += length;
    710   if (base - gcov_var.start <= gcov_var.length)
    711     gcov_var.offset = base - gcov_var.start;
    712   else
    713     {
    714       gcov_var.offset = gcov_var.length = 0;
    715       _GCOV_fseek (gcov_var.file, base << 2, SEEK_SET);
    716       gcov_var.start = _GCOV_ftell (gcov_var.file) >> 2;
    717     }
    718 #endif /* __GCOV_KERNEL__ */
    719 }
    720 #endif
    721 
    722 #if IN_LIBGCOV
    723 /* Move to a given position in a gcov file.  */
    724 
    725 GCOV_LINKAGE void
    726 gcov_seek (gcov_position_t base)
    727 {
    728   gcc_assert (gcov_var.mode < 0);
    729   if (gcov_var.offset)
    730     gcov_write_block (gcov_var.offset);
    731   _GCOV_fseek (gcov_var.file, base << 2, SEEK_SET);
    732   gcov_var.start = _GCOV_ftell (gcov_var.file) >> 2;
    733 }
    734 
    735 /* Truncate the gcov file at the current position.  */
    736 
    737 GCOV_LINKAGE void
    738 gcov_truncate (void)
    739 {
    740 #ifdef __GCOV_KERNEL__
    741   /* should not reach this point */
    742   gcc_assert (0);
    743 #else /* __GCOV_KERNEL__ */
    744   long offs;
    745   int filenum;
    746   gcc_assert (gcov_var.mode < 0);
    747   if (gcov_var.offset)
    748     gcov_write_block (gcov_var.offset);
    749   offs = ftell (gcov_var.file);
    750   filenum = fileno (gcov_var.file);
    751   if (offs == -1 || filenum == -1 || ftruncate (filenum, offs))
    752     gcov_var.error = 1;
    753 #endif /* __GCOV_KERNEL__ */
    754 }
    755 #endif
    756 
    757 #ifndef __GCOV_KERNEL__
    758 /* Convert an unsigned NUMBER to a percentage after dividing by
    759    100.  */
    760 
    761 GCOV_LINKAGE float
    762 convert_unsigned_to_pct (const unsigned number)
    763 {
    764   return (float)number / 100.0f;
    765 }
    766 #endif
    767 
    768 #if !IN_LIBGCOV && IN_GCOV != 1
    769 /* Print load latency information given by LL_INFO in a human readable
    770    format into an open output file pointed by FP. NEWLINE specifies
    771    whether or not to print a trailing newline.  */
    772 
    773 GCOV_LINKAGE void
    774 print_load_latency_line (FILE *fp, const gcov_pmu_ll_info_t *ll_info,
    775                          const enum print_newline newline)
    776 {
    777   if (!ll_info)
    778     return;
    779   fprintf (fp, " %u %.2f%% %.2f%% %.2f%% %.2f%% %.2f%% %.2f%% %.2f%% "
    780            "%.2f%% %.2f%% " HOST_WIDEST_INT_PRINT_HEX " %s %d %d",
    781            ll_info->counts,
    782            convert_unsigned_to_pct (ll_info->self),
    783            convert_unsigned_to_pct (ll_info->cum),
    784            convert_unsigned_to_pct (ll_info->lt_10),
    785            convert_unsigned_to_pct (ll_info->lt_32),
    786            convert_unsigned_to_pct (ll_info->lt_64),
    787            convert_unsigned_to_pct (ll_info->lt_256),
    788            convert_unsigned_to_pct (ll_info->lt_1024),
    789            convert_unsigned_to_pct (ll_info->gt_1024),
    790            convert_unsigned_to_pct (ll_info->wself),
    791            ll_info->code_addr,
    792            ll_info->filename,
    793            ll_info->line,
    794            ll_info->discriminator);
    795   if (newline == add_newline)
    796     fprintf (fp, "\n");
    797 }
    798 
    799 /* Print BRM_INFO into the file pointed by FP.  NEWLINE specifies
    800    whether or not to print a trailing newline.  */
    801 
    802 GCOV_LINKAGE void
    803 print_branch_mispredict_line (FILE *fp, const gcov_pmu_brm_info_t *brm_info,
    804                               const enum print_newline newline)
    805 {
    806   if (!brm_info)
    807     return;
    808   fprintf (fp, " %u %.2f%% %.2f%% " HOST_WIDEST_INT_PRINT_HEX " %s %d %d",
    809            brm_info->counts,
    810            convert_unsigned_to_pct (brm_info->self),
    811            convert_unsigned_to_pct (brm_info->cum),
    812            brm_info->code_addr,
    813            brm_info->filename,
    814            brm_info->line,
    815            brm_info->discriminator);
    816   if (newline == add_newline)
    817     fprintf (fp, "\n");
    818 }
    819 
    820 /* Print TOOL_HEADER into the file pointed by FP.  NEWLINE specifies
    821    whether or not to print a trailing newline.  */
    822 
    823 GCOV_LINKAGE void
    824 print_pmu_tool_header (FILE *fp, gcov_pmu_tool_header_t *tool_header,
    825                        const enum print_newline newline)
    826 {
    827   if (!tool_header)
    828     return;
    829   fprintf (fp, "\nhost_cpu: %s\n", tool_header->host_cpu);
    830   fprintf (fp, "hostname: %s\n", tool_header->hostname);
    831   fprintf (fp, "kernel_version: %s\n", tool_header->kernel_version);
    832   fprintf (fp, "column_header: %s\n", tool_header->column_header);
    833   fprintf (fp, "column_description: %s\n", tool_header->column_description);
    834   fprintf (fp, "full_header: %s\n", tool_header->full_header);
    835   if (newline == add_newline)
    836     fprintf (fp, "\n");
    837 }
    838 #endif
    839 
    840 #if IN_GCOV > 0
    841 /* Return the modification time of the current gcov file.  */
    842 
    843 GCOV_LINKAGE time_t
    844 gcov_time (void)
    845 {
    846   struct stat status;
    847 
    848   if (fstat (fileno (gcov_var.file), &status))
    849     return 0;
    850   else
    851     return status.st_mtime;
    852 }
    853 #endif /* IN_GCOV */
    854 
    855 #ifdef __GCOV_KERNEL__
    856 
    857 /* File fclose operation in kernel mode.  */
    858 
    859 int
    860 kernel_file_fclose (gcov_kernel_vfile *fp)
    861 {
    862   return 0;
    863 }
    864 
    865 /* File ftell operation in kernel mode. It currently should not
    866    be called.  */
    867 
    868 long
    869 kernel_file_ftell (gcov_kernel_vfile *fp)
    870 {
    871   gcc_assert (0);  /* should not reach here */
    872   return 0;
    873 }
    874 
    875 /* File fseek operation in kernel mode. It should only be called
    876    with OFFSET==0 and WHENCE==0 to a freshly opened file.  */
    877 
    878 int
    879 kernel_file_fseek (gcov_kernel_vfile *fp, long offset, int whence)
    880 {
    881   gcc_assert (offset == 0 && whence == 0 && fp->count == 0);
    882   return 0;
    883 }
    884 
    885 /* File ftruncate operation in kernel mode. It currently should not
    886    be called.  */
    887 
    888 int
    889 kernel_file_ftruncate (gcov_kernel_vfile *fp, off_t value)
    890 {
    891   gcc_assert (0);  /* should not reach here */
    892   return 0;
    893 }
    894 
    895 /* File fread operation in kernel mode. It currently should not
    896    be called.  */
    897 
    898 int
    899 kernel_file_fread (void *ptr, size_t size, size_t nitems,
    900                   gcov_kernel_vfile *fp)
    901 {
    902   gcc_assert (0);  /* should not reach here */
    903   return 0;
    904 }
    905 
    906 /* File fwrite operation in kernel mode. It outputs the data
    907    to a buffer in the virual file.  */
    908 
    909 int
    910 kernel_file_fwrite (const void *ptr, size_t size,
    911                    size_t nitems, gcov_kernel_vfile *fp)
    912 {
    913   char *vbuf;
    914   unsigned vsize, vpos;
    915   unsigned len;
    916 
    917   if (!fp) return 0;
    918 
    919   vbuf = fp->buf;
    920   vsize = fp->size;
    921   vpos = fp->count;
    922 
    923   if (vsize <= vpos)
    924     {
    925       printk (KERN_ERR
    926          "GCOV_KERNEL: something wrong: vbuf=%p vsize=%u vpos=%u\n",
    927           vbuf, vsize, vpos);
    928       return 0;
    929     }
    930   len = vsize - vpos;
    931   len /= size;
    932 
    933   if (len > nitems)
    934     len = nitems;
    935 
    936   memcpy (vbuf+vpos, ptr, size*len);
    937   fp->count += len*size;
    938 
    939   if (len != nitems)
    940     printk (KERN_ERR
    941         "GCOV_KERNEL: something wrong: size=%lu nitems=%lu ret=%d\n",
    942         size, nitems, len);
    943   return len;
    944 }
    945 
    946 /* File fileno operation in kernel mode. It currently should not
    947    be called.  */
    948 
    949 int
    950 kernel_file_fileno (gcov_kernel_vfile *fp)
    951 {
    952   gcc_assert (0);  /* should not reach here */
    953   return 0;
    954 }
    955 #else /* __GCOV_KERNEL__ */
    956 
    957 #if IN_GCOV != 1
    958 /* Delete pmu tool header TOOL_HEADER.  */
    959 
    960 GCOV_LINKAGE void
    961 destroy_pmu_tool_header (gcov_pmu_tool_header_t *tool_header)
    962 {
    963   if (!tool_header)
    964     return;
    965   if (tool_header->host_cpu)
    966     free (tool_header->host_cpu);
    967   if (tool_header->hostname)
    968     free (tool_header->hostname);
    969   if (tool_header->kernel_version)
    970     free (tool_header->kernel_version);
    971   if (tool_header->column_header)
    972     free (tool_header->column_header);
    973   if (tool_header->column_description)
    974     free (tool_header->column_description);
    975   if (tool_header->full_header)
    976     free (tool_header->full_header);
    977 }
    978 #endif
    979 
    980 #endif /* GCOV_KERNEL */
    981