1/* gmon_io.c - Input and output from/to gmon.out files.
2
3   Copyright (C) 1999-2022 Free Software Foundation, Inc.
4
5   This file is part of GNU Binutils.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
20   02110-1301, USA.  */
21
22#include "gprof.h"
23#include "binary-io.h"
24#include "search_list.h"
25#include "source.h"
26#include "symtab.h"
27#include "cg_arcs.h"
28#include "basic_blocks.h"
29#include "corefile.h"
30#include "call_graph.h"
31#include "gmon_io.h"
32#include "gmon_out.h"
33#include "gmon.h"		/* Fetch header for old format.  */
34#include "hertz.h"
35#include "hist.h"
36#include "libiberty.h"
37
38enum gmon_ptr_size {
39  ptr_32bit,
40  ptr_64bit
41};
42
43enum gmon_ptr_signedness {
44  ptr_signed,
45  ptr_unsigned
46};
47
48static enum gmon_ptr_size gmon_get_ptr_size (void);
49static enum gmon_ptr_signedness gmon_get_ptr_signedness (void);
50
51static int gmon_io_read_64 (FILE *, uint64_t *);
52static int gmon_io_write_64 (FILE *, uint64_t);
53static int gmon_read_raw_arc
54  (FILE *, bfd_vma *, bfd_vma *, unsigned long *);
55static int gmon_write_raw_arc
56  (FILE *, bfd_vma, bfd_vma, unsigned long);
57
58int gmon_input = 0;
59int gmon_file_version = 0;	/* 0 == old (non-versioned) file format.  */
60
61static enum gmon_ptr_size
62gmon_get_ptr_size (void)
63{
64  int size;
65
66  /* Pick best size for pointers.  Start with the ELF size, and if not
67     elf go with the architecture's address size.  */
68  size = bfd_get_arch_size (core_bfd);
69  if (size == -1)
70    size = bfd_arch_bits_per_address (core_bfd);
71
72  switch (size)
73    {
74    case 32:
75      return ptr_32bit;
76
77    case 64:
78      return ptr_64bit;
79
80    default:
81      fprintf (stderr, _("%s: address size has unexpected value of %u\n"),
82	       whoami, size);
83      done (1);
84    }
85}
86
87static enum gmon_ptr_signedness
88gmon_get_ptr_signedness (void)
89{
90  int sext;
91
92  /* Figure out whether to sign extend.  If BFD doesn't know, assume no.  */
93  sext = bfd_get_sign_extend_vma (core_bfd);
94  if (sext == -1)
95    return ptr_unsigned;
96  return (sext ? ptr_signed : ptr_unsigned);
97}
98
99int
100gmon_io_read_32 (FILE *ifp, unsigned int *valp)
101{
102  char buf[4];
103
104  if (fread (buf, 1, 4, ifp) != 4)
105    return 1;
106  *valp = bfd_get_32 (core_bfd, buf);
107  return 0;
108}
109
110static int
111gmon_io_read_64 (FILE *ifp, uint64_t *valp)
112{
113  char buf[8];
114
115  if (fread (buf, 1, 8, ifp) != 8)
116    return 1;
117  *valp = bfd_get_64 (core_bfd, buf);
118  return 0;
119}
120
121int
122gmon_io_read_vma (FILE *ifp, bfd_vma *valp)
123{
124  unsigned int val32;
125  uint64_t val64;
126
127  switch (gmon_get_ptr_size ())
128    {
129    case ptr_32bit:
130      if (gmon_io_read_32 (ifp, &val32))
131	return 1;
132      if (gmon_get_ptr_signedness () == ptr_signed)
133	*valp = (int) val32;
134      else
135	*valp = val32;
136      break;
137
138    case ptr_64bit:
139      if (gmon_io_read_64 (ifp, &val64))
140	return 1;
141      if (gmon_get_ptr_signedness () == ptr_signed)
142	*valp = (int64_t) val64;
143      else
144	*valp = val64;
145      break;
146    }
147  return 0;
148}
149
150int
151gmon_io_read (FILE *ifp, char *buf, size_t n)
152{
153  if (fread (buf, 1, n, ifp) != n)
154    return 1;
155  return 0;
156}
157
158int
159gmon_io_write_32 (FILE *ofp, unsigned int val)
160{
161  char buf[4];
162
163  bfd_put_32 (core_bfd, (bfd_vma) val, buf);
164  if (fwrite (buf, 1, 4, ofp) != 4)
165    return 1;
166  return 0;
167}
168
169static int
170gmon_io_write_64 (FILE *ofp, uint64_t val)
171{
172  char buf[8];
173
174  bfd_put_64 (core_bfd, (bfd_vma) val, buf);
175  if (fwrite (buf, 1, 8, ofp) != 8)
176    return 1;
177  return 0;
178}
179
180int
181gmon_io_write_vma (FILE *ofp, bfd_vma val)
182{
183
184  switch (gmon_get_ptr_size ())
185    {
186    case ptr_32bit:
187      if (gmon_io_write_32 (ofp, (unsigned int) val))
188	return 1;
189      break;
190
191    case ptr_64bit:
192      if (gmon_io_write_64 (ofp, (uint64_t) val))
193	return 1;
194      break;
195    }
196  return 0;
197}
198
199int
200gmon_io_write_8 (FILE *ofp, unsigned int val)
201{
202  char buf[1];
203
204  bfd_put_8 (core_bfd, val, buf);
205  if (fwrite (buf, 1, 1, ofp) != 1)
206    return 1;
207  return 0;
208}
209
210int
211gmon_io_write (FILE *ofp, char *buf, size_t n)
212{
213  if (fwrite (buf, 1, n, ofp) != n)
214    return 1;
215  return 0;
216}
217
218static int
219gmon_read_raw_arc (FILE *ifp, bfd_vma *fpc, bfd_vma *spc, unsigned long *cnt)
220{
221  uint64_t cnt64;
222  unsigned int cnt32;
223
224  if (gmon_io_read_vma (ifp, fpc)
225      || gmon_io_read_vma (ifp, spc))
226    return 1;
227
228  switch (gmon_get_ptr_size ())
229    {
230    case ptr_32bit:
231      if (gmon_io_read_32 (ifp, &cnt32))
232	return 1;
233      *cnt = cnt32;
234      break;
235
236    case ptr_64bit:
237      if (gmon_io_read_64 (ifp, &cnt64))
238	return 1;
239      *cnt = cnt64;
240      break;
241
242    default:
243      return 1;
244    }
245  return 0;
246}
247
248static int
249gmon_write_raw_arc (FILE *ofp, bfd_vma fpc, bfd_vma spc, unsigned long cnt)
250{
251
252  if (gmon_io_write_vma (ofp, fpc)
253      || gmon_io_write_vma (ofp, spc))
254    return 1;
255
256  switch (gmon_get_ptr_size ())
257    {
258    case ptr_32bit:
259      if (gmon_io_write_32 (ofp, (unsigned int) cnt))
260	return 1;
261      break;
262
263    case ptr_64bit:
264      if (gmon_io_write_64 (ofp, (uint64_t) cnt))
265	return 1;
266      break;
267    }
268  return 0;
269}
270
271void
272gmon_out_read (const char *filename)
273{
274  FILE *ifp;
275  struct gmon_hdr ghdr;
276  unsigned char tag;
277  int nhist = 0, narcs = 0, nbbs = 0;
278
279  /* Open gmon.out file.  */
280  if (strcmp (filename, "-") == 0)
281    {
282      ifp = stdin;
283      SET_BINARY (fileno (stdin));
284    }
285  else
286    {
287      ifp = fopen (filename, FOPEN_RB);
288
289      if (!ifp)
290	{
291	  perror (filename);
292	  done (1);
293	}
294    }
295
296  if (fread (&ghdr, sizeof (struct gmon_hdr), 1, ifp) != 1)
297    {
298      fprintf (stderr, _("%s: file too short to be a gmon file\n"),
299	       filename);
300      done (1);
301    }
302
303  if ((file_format == FF_MAGIC)
304      || (file_format == FF_AUTO && !strncmp (&ghdr.cookie[0], GMON_MAGIC, 4)))
305    {
306      if (file_format == FF_MAGIC && strncmp (&ghdr.cookie[0], GMON_MAGIC, 4))
307	{
308	  fprintf (stderr, _("%s: file `%s' has bad magic cookie\n"),
309		   whoami, filename);
310	  done (1);
311	}
312
313      /* Right magic, so it's probably really a new gmon.out file.  */
314      gmon_file_version = bfd_get_32 (core_bfd, (bfd_byte *) ghdr.version);
315
316      if (gmon_file_version != GMON_VERSION && gmon_file_version != 0)
317	{
318	  fprintf (stderr,
319		   _("%s: file `%s' has unsupported version %d\n"),
320		   whoami, filename, gmon_file_version);
321	  done (1);
322	}
323
324      /* Read in all the records.  */
325      while (fread (&tag, sizeof (tag), 1, ifp) == 1)
326	{
327	  switch (tag)
328	    {
329	    case GMON_TAG_TIME_HIST:
330	      ++nhist;
331	      gmon_input |= INPUT_HISTOGRAM;
332	      hist_read_rec (ifp, filename);
333	      break;
334
335	    case GMON_TAG_CG_ARC:
336	      ++narcs;
337	      gmon_input |= INPUT_CALL_GRAPH;
338	      cg_read_rec (ifp, filename);
339	      break;
340
341	    case GMON_TAG_BB_COUNT:
342	      ++nbbs;
343	      gmon_input |= INPUT_BB_COUNTS;
344	      bb_read_rec (ifp, filename);
345	      break;
346
347	    default:
348	      fprintf (stderr,
349		       _("%s: %s: found bad tag %d (file corrupted?)\n"),
350		       whoami, filename, tag);
351	      done (1);
352	    }
353	}
354    }
355  else if (file_format == FF_AUTO
356	   || file_format == FF_BSD
357	   || file_format == FF_BSD44)
358    {
359      struct hdr
360      {
361	bfd_vma low_pc;
362	bfd_vma high_pc;
363	unsigned int ncnt;
364      };
365      unsigned int i;
366      int samp_bytes, header_size = 0;
367      unsigned long count;
368      bfd_vma from_pc, self_pc;
369      UNIT raw_bin_count;
370      struct hdr tmp;
371      unsigned int version;
372      unsigned int hist_num_bins;
373
374      /* Information from a gmon.out file is in two parts: an array of
375	 sampling hits within pc ranges, and the arcs.  */
376      gmon_input = INPUT_HISTOGRAM | INPUT_CALL_GRAPH;
377
378      /* This fseek() ought to work even on stdin as long as it's
379	 not an interactive device (heck, is there anybody who would
380	 want to type in a gmon.out at the terminal?).  */
381      if (fseek (ifp, 0, SEEK_SET) < 0)
382	{
383	  perror (filename);
384	  done (1);
385	}
386
387      /* The beginning of the old BSD header and the 4.4BSD header
388	 are the same: lowpc, highpc, ncnt  */
389      if (gmon_io_read_vma (ifp, &tmp.low_pc)
390          || gmon_io_read_vma (ifp, &tmp.high_pc)
391          || gmon_io_read_32 (ifp, &tmp.ncnt))
392	{
393 bad_gmon_file:
394          fprintf (stderr, _("%s: file too short to be a gmon file\n"),
395		   filename);
396	  done (1);
397	}
398
399      /* Check to see if this a 4.4BSD-style header.  */
400      if (gmon_io_read_32 (ifp, &version))
401	goto bad_gmon_file;
402
403      if (version == GMONVERSION)
404	{
405	  unsigned int profrate;
406
407	  /* 4.4BSD format header.  */
408          if (gmon_io_read_32 (ifp, &profrate))
409	    goto bad_gmon_file;
410
411	  if (!histograms)
412	    hz = profrate;
413	  else if (hz != (int) profrate)
414	    {
415	      fprintf (stderr,
416		       _("%s: profiling rate incompatible with first gmon file\n"),
417		       filename);
418	      done (1);
419	    }
420
421	  switch (gmon_get_ptr_size ())
422	    {
423	    case ptr_32bit:
424	      header_size = GMON_HDRSIZE_BSD44_32;
425	      break;
426
427	    case ptr_64bit:
428	      header_size = GMON_HDRSIZE_BSD44_64;
429	      break;
430	    }
431	}
432      else
433	{
434	  /* Old style BSD format.  */
435	  if (file_format == FF_BSD44)
436	    {
437	      fprintf (stderr, _("%s: file `%s' has bad magic cookie\n"),
438		       whoami, filename);
439	      done (1);
440	    }
441
442	  switch (gmon_get_ptr_size ())
443	    {
444	    case ptr_32bit:
445	      header_size = GMON_HDRSIZE_OLDBSD_32;
446	      break;
447
448	    case ptr_64bit:
449	      header_size = GMON_HDRSIZE_OLDBSD_64;
450	      break;
451	    }
452	}
453
454      /* Position the file to after the header.  */
455      if (fseek (ifp, header_size, SEEK_SET) < 0)
456	{
457	  perror (filename);
458	  done (1);
459	}
460
461      samp_bytes = tmp.ncnt - header_size;
462      hist_num_bins = samp_bytes / sizeof (UNIT);
463      if (histograms && (tmp.low_pc != histograms->lowpc
464			 || tmp.high_pc != histograms->highpc
465			 || (hist_num_bins != histograms->num_bins)))
466	{
467	  fprintf (stderr, _("%s: incompatible with first gmon file\n"),
468		   filename);
469	  done (1);
470	}
471
472      if (!histograms)
473	{
474	  num_histograms = 1;
475	  histograms = (struct histogram *) xmalloc (sizeof (struct histogram));
476	  histograms->lowpc = tmp.low_pc;
477	  histograms->highpc = tmp.high_pc;
478	  histograms->num_bins = hist_num_bins;
479	  hist_scale = (double)((tmp.high_pc - tmp.low_pc) / sizeof (UNIT))
480	    / hist_num_bins;
481	  histograms->sample = (int *) xmalloc (hist_num_bins * sizeof (int));
482	  memset (histograms->sample, 0,
483		  hist_num_bins * sizeof (int));
484	}
485
486      DBG (SAMPLEDEBUG,
487	   printf ("[gmon_out_read] lowpc 0x%lx highpc 0x%lx ncnt %d\n",
488		   (unsigned long) tmp.low_pc, (unsigned long) tmp.high_pc,
489		   tmp.ncnt);
490	   printf ("[gmon_out_read] samp_bytes %d hist_num_bins %d\n",
491		   samp_bytes, hist_num_bins));
492
493      /* Make sure that we have sensible values.  */
494      if (samp_bytes < 0 || histograms->lowpc > histograms->highpc)
495	{
496	  fprintf (stderr,
497	    _("%s: file '%s' does not appear to be in gmon.out format\n"),
498	    whoami, filename);
499	  done (1);
500	}
501
502      if (hist_num_bins)
503	++nhist;
504
505      for (i = 0; i < hist_num_bins; ++i)
506	{
507	  if (fread (raw_bin_count, sizeof (raw_bin_count), 1, ifp) != 1)
508	    {
509	      fprintf (stderr,
510		       _("%s: unexpected EOF after reading %d/%d bins\n"),
511		       whoami, --i, hist_num_bins);
512	      done (1);
513	    }
514
515	  histograms->sample[i]
516	    += bfd_get_16 (core_bfd, (bfd_byte *) raw_bin_count);
517	}
518
519      /* The rest of the file consists of a bunch of
520	 <from,self,count> tuples.  */
521      while (gmon_read_raw_arc (ifp, &from_pc, &self_pc, &count) == 0)
522	{
523	  ++narcs;
524
525	  DBG (SAMPLEDEBUG,
526	     printf ("[gmon_out_read] frompc 0x%lx selfpc 0x%lx count %lu\n",
527		     (unsigned long) from_pc, (unsigned long) self_pc, count));
528
529	  /* Add this arc.  */
530	  cg_tally (from_pc, self_pc, count);
531	}
532
533      if (hz == HZ_WRONG)
534	{
535	  /* How many ticks per second?  If we can't tell, report
536	     time in ticks.  */
537	  hz = hertz ();
538
539	  if (hz == HZ_WRONG)
540	    {
541	      hz = 1;
542	      fprintf (stderr, _("time is in ticks, not seconds\n"));
543	    }
544	}
545    }
546  else
547    {
548      fprintf (stderr, _("%s: don't know how to deal with file format %d\n"),
549	       whoami, file_format);
550      done (1);
551    }
552
553  if (ifp != stdin)
554    fclose (ifp);
555
556  if (output_style & STYLE_GMON_INFO)
557    {
558      printf (_("File `%s' (version %d) contains:\n"),
559	      filename, gmon_file_version);
560      printf (nhist == 1 ?
561	      _("\t%d histogram record\n") :
562	      _("\t%d histogram records\n"), nhist);
563      printf (narcs == 1 ?
564	      _("\t%d call-graph record\n") :
565	      _("\t%d call-graph records\n"), narcs);
566      printf (nbbs == 1 ?
567	      _("\t%d basic-block count record\n") :
568	      _("\t%d basic-block count records\n"), nbbs);
569      first_output = false;
570    }
571}
572
573
574void
575gmon_out_write (const char *filename)
576{
577  FILE *ofp;
578  struct gmon_hdr ghdr;
579
580  ofp = fopen (filename, FOPEN_WB);
581  if (!ofp)
582    {
583      perror (filename);
584      done (1);
585    }
586
587  if (file_format == FF_AUTO || file_format == FF_MAGIC)
588    {
589      /* Write gmon header.  */
590
591      memcpy (&ghdr.cookie[0], GMON_MAGIC, 4);
592      bfd_put_32 (core_bfd, (bfd_vma) GMON_VERSION, (bfd_byte *) ghdr.version);
593
594      if (fwrite (&ghdr, sizeof (ghdr), 1, ofp) != 1)
595	{
596	  perror (filename);
597	  done (1);
598	}
599
600      /* Write execution time histogram if we have one.  */
601      if (gmon_input & INPUT_HISTOGRAM)
602	hist_write_hist (ofp, filename);
603
604      /* Write call graph arcs if we have any.  */
605      if (gmon_input & INPUT_CALL_GRAPH)
606	cg_write_arcs (ofp, filename);
607
608      /* Write basic-block info if we have it.  */
609      if (gmon_input & INPUT_BB_COUNTS)
610	bb_write_blocks (ofp, filename);
611    }
612  else if (file_format == FF_BSD || file_format == FF_BSD44)
613    {
614      UNIT raw_bin_count;
615      unsigned int i, hdrsize;
616      unsigned padsize;
617      char pad[3*4];
618      Arc *arc;
619      Sym *sym;
620
621      memset (pad, 0, sizeof (pad));
622
623      hdrsize = 0;
624      /* Decide how large the header will be.  Use the 4.4BSD format
625         header if explicitly specified, or if the profiling rate is
626         non-standard.  Otherwise, use the old BSD format.  */
627      if (file_format == FF_BSD44
628	  || hz != hertz())
629	{
630	  padsize = 3*4;
631	  switch (gmon_get_ptr_size ())
632	    {
633	    case ptr_32bit:
634	      hdrsize = GMON_HDRSIZE_BSD44_32;
635	      break;
636
637	    case ptr_64bit:
638	      hdrsize = GMON_HDRSIZE_BSD44_64;
639	      break;
640	    }
641	}
642      else
643	{
644	  padsize = 0;
645	  switch (gmon_get_ptr_size ())
646	    {
647	    case ptr_32bit:
648	      hdrsize = GMON_HDRSIZE_OLDBSD_32;
649	      break;
650
651	    case ptr_64bit:
652	      hdrsize = GMON_HDRSIZE_OLDBSD_64;
653	      /* FIXME: Checking host compiler defines here means that we can't
654		 use a cross gprof alpha OSF.  */
655#if defined(__alpha__) && defined (__osf__)
656	      padsize = 4;
657#endif
658	      break;
659	    }
660	}
661
662      /* Write the parts of the headers that are common to both the
663	 old BSD and 4.4BSD formats.  */
664      if (gmon_io_write_vma (ofp, histograms->lowpc)
665          || gmon_io_write_vma (ofp, histograms->highpc)
666          || gmon_io_write_32 (ofp, histograms->num_bins
667			       * sizeof (UNIT) + hdrsize))
668	{
669	  perror (filename);
670	  done (1);
671	}
672
673      /* Write out the 4.4BSD header bits, if that's what we're using.  */
674      if (file_format == FF_BSD44
675	  || hz != hertz())
676	{
677          if (gmon_io_write_32 (ofp, GMONVERSION)
678	      || gmon_io_write_32 (ofp, (unsigned int) hz))
679	    {
680	      perror (filename);
681	      done (1);
682	    }
683	}
684
685      /* Now write out any necessary padding after the meaningful
686	 header bits.  */
687      if (padsize != 0
688          && fwrite (pad, 1, padsize, ofp) != padsize)
689        {
690          perror (filename);
691	  done (1);
692	}
693
694      /* Dump the samples.  */
695      for (i = 0; i < histograms->num_bins; ++i)
696	{
697	  bfd_put_16 (core_bfd, (bfd_vma) histograms->sample[i],
698		      (bfd_byte *) &raw_bin_count[0]);
699	  if (fwrite (&raw_bin_count[0], sizeof (raw_bin_count), 1, ofp) != 1)
700	    {
701	      perror (filename);
702	      done (1);
703	    }
704	}
705
706      /* Dump the normalized raw arc information.  */
707      for (sym = symtab.base; sym < symtab.limit; ++sym)
708	{
709	  for (arc = sym->cg.children; arc; arc = arc->next_child)
710	    {
711	      if (gmon_write_raw_arc (ofp, arc->parent->addr,
712				      arc->child->addr, arc->count))
713		{
714		  perror (filename);
715		  done (1);
716		}
717	      DBG (SAMPLEDEBUG,
718		   printf ("[dumpsum] frompc 0x%lx selfpc 0x%lx count %lu\n",
719			   (unsigned long) arc->parent->addr,
720			   (unsigned long) arc->child->addr, arc->count));
721	    }
722	}
723
724      fclose (ofp);
725    }
726  else
727    {
728      fprintf (stderr, _("%s: don't know how to deal with file format %d\n"),
729	       whoami, file_format);
730      done (1);
731    }
732}
733