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@cygnus.com>.
5   Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
6
7This file is part of GCC.
8
9GCC is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
11Software Foundation; either version 3, or (at your option) any later
12version.
13
14GCC is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17for more details.
18
19Under Section 7 of GPL version 3, you are granted additional
20permissions described in the GCC Runtime Library Exception, version
213.1, as published by the Free Software Foundation.
22
23You should have received a copy of the GNU General Public License and
24a copy of the GCC Runtime Library Exception along with this program;
25see 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
38static void gcov_write_block (unsigned);
39static gcov_unsigned_t *gcov_write_words (unsigned);
40#endif
41static const gcov_unsigned_t *gcov_read_words (unsigned);
42#if !IN_LIBGCOV
43static void gcov_allocate (unsigned);
44#endif
45
46#ifdef __GCOV_KERNEL__
47struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
48#endif
49
50static 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__
73GCOV_LINKAGE int
74#if IN_LIBGCOV
75gcov_open (const char *name)
76#else
77gcov_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
169extern _GCOV_FILE *gcov_current_file;
170
171GCOV_LINKAGE int
172gcov_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
188GCOV_LINKAGE int
189gcov_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
215GCOV_LINKAGE char *
216gcov_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
241GCOV_LINKAGE void
242gcov_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
268GCOV_LINKAGE void
269gcov_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
289GCOV_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
313GCOV_LINKAGE int
314gcov_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
330static void
331gcov_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
348static void
349gcov_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
362GCOV_LINKAGE gcov_unsigned_t
363gcov_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
378static gcov_unsigned_t *
379gcov_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
409GCOV_LINKAGE void
410gcov_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
421GCOV_LINKAGE void
422gcov_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
437GCOV_LINKAGE void
438gcov_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
461GCOV_LINKAGE gcov_position_t
462gcov_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
478GCOV_LINKAGE void
479gcov_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
500GCOV_LINKAGE void
501gcov_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
512GCOV_LINKAGE void
513gcov_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
536static const gcov_unsigned_t *
537gcov_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
583GCOV_LINKAGE gcov_unsigned_t
584gcov_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
598GCOV_LINKAGE gcov_type
599gcov_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
619GCOV_LINKAGE const char *
620gcov_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
630GCOV_LINKAGE void
631gcov_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
650GCOV_LINKAGE void
651gcov_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
701GCOV_LINKAGE void
702gcov_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
725GCOV_LINKAGE void
726gcov_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
737GCOV_LINKAGE void
738gcov_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
761GCOV_LINKAGE float
762convert_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
773GCOV_LINKAGE void
774print_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
802GCOV_LINKAGE void
803print_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
823GCOV_LINKAGE void
824print_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
843GCOV_LINKAGE time_t
844gcov_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
859int
860kernel_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
868long
869kernel_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
878int
879kernel_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
888int
889kernel_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
898int
899kernel_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
909int
910kernel_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
949int
950kernel_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
960GCOV_LINKAGE void
961destroy_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