2000-10-31 Philip Blundell <philb@gnu.org>
[external/binutils.git] / gprof / gmon_io.c
1 /* gmon_io.c - Input and output from/to gmon.out files.
2
3    Copyright (C) 2000  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 2 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., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21 \f
22 #include "cg_arcs.h"
23 #include "basic_blocks.h"
24 #include "bfd.h"
25 #include "corefile.h"
26 #include "call_graph.h"
27 #include "gmon_io.h"
28 #include "gmon_out.h"
29 #include "gmon.h"               /* Fetch header for old format.  */
30 #include "gprof.h"
31 #include "hertz.h"
32 #include "hist.h"
33 #include "libiberty.h"
34
35 int gmon_input = 0;
36 int gmon_file_version = 0;      /* 0 == old (non-versioned) file format.  */
37
38 /* This probably ought to be in libbfd.  */
39
40 bfd_vma
41 DEFUN (get_vma, (abfd, addr), bfd * abfd AND bfd_byte * addr)
42 {
43   switch (sizeof (char*))
44     {
45     case 4:
46       return bfd_get_32 (abfd, addr);
47     case 8:
48       return bfd_get_64 (abfd, addr);
49     default:
50       fprintf (stderr, _("%s: bfd_vma has unexpected size of %ld bytes\n"),
51                whoami, (long) sizeof (char*));
52       done (1);
53     }
54 }
55
56
57 /* This probably ought to be in libbfd.  */
58
59 void
60 DEFUN (put_vma, (abfd, val, addr), bfd * abfd AND bfd_vma val AND bfd_byte * addr)
61 {
62   switch (sizeof (char*))
63     {
64     case 4:
65       bfd_put_32 (abfd, val, addr);
66       break;
67     case 8:
68       bfd_put_64 (abfd, val, addr);
69       break;
70     default:
71       fprintf (stderr, _("%s: bfd_vma has unexpected size of %ld bytes\n"),
72                whoami, (long) sizeof (char*));
73       done (1);
74     }
75 }
76
77
78 void
79 DEFUN (gmon_out_read, (filename), const char *filename)
80 {
81   FILE *ifp;
82   struct gmon_hdr ghdr;
83   unsigned char tag;
84   int nhist = 0, narcs = 0, nbbs = 0;
85
86   /* Open gmon.out file.  */
87   if (strcmp (filename, "-") == 0)
88     {
89       ifp = stdin;
90 #ifdef SET_BINARY
91       SET_BINARY (fileno (stdin));
92 #endif
93     }
94   else
95     {
96       ifp = fopen (filename, FOPEN_RB);
97       
98       if (!ifp)
99         {
100           perror (filename);
101           done (1);
102         }
103     }
104   
105   if (fread (&ghdr, sizeof (struct gmon_hdr), 1, ifp) != 1)
106     {
107       fprintf (stderr, _("%s: file too short to be a gmon file\n"),
108                filename);
109       done (1);
110     }
111
112   if ((file_format == FF_MAGIC) ||
113       (file_format == FF_AUTO && !strncmp (&ghdr.cookie[0], GMON_MAGIC, 4)))
114     {
115       if (file_format == FF_MAGIC && strncmp (&ghdr.cookie[0], GMON_MAGIC, 4))
116         {
117           fprintf (stderr, _("%s: file `%s' has bad magic cookie\n"),
118                    whoami, filename);
119           done (1);
120         }
121
122       /* Right magic, so it's probably really a new gmon.out file.  */
123       gmon_file_version = bfd_get_32 (core_bfd, (bfd_byte *) ghdr.version);
124       
125       if (gmon_file_version != GMON_VERSION && gmon_file_version != 0)
126         {
127           fprintf (stderr,
128                    _("%s: file `%s' has unsupported version %d\n"),
129                    whoami, filename, gmon_file_version);
130           done (1);
131         }
132
133       /* Read in all the records.  */
134       while (fread (&tag, sizeof (tag), 1, ifp) == 1)
135         {
136           switch (tag)
137             {
138             case GMON_TAG_TIME_HIST:
139               ++nhist;
140               gmon_input |= INPUT_HISTOGRAM;
141               hist_read_rec (ifp, filename);
142               break;
143
144             case GMON_TAG_CG_ARC:
145               ++narcs;
146               gmon_input |= INPUT_CALL_GRAPH;
147               cg_read_rec (ifp, filename);
148               break;
149
150             case GMON_TAG_BB_COUNT:
151               ++nbbs;
152               gmon_input |= INPUT_BB_COUNTS;
153               bb_read_rec (ifp, filename);
154               break;
155
156             default:
157               fprintf (stderr,
158                        _("%s: %s: found bad tag %d (file corrupted?)\n"),
159                        whoami, filename, tag);
160               done (1);
161             }
162         }
163     }
164   else if (file_format == FF_AUTO
165            || file_format == FF_BSD
166            || file_format == FF_BSD44)
167     {
168       struct hdr
169       {
170         bfd_vma low_pc;
171         bfd_vma high_pc;
172         int ncnt;
173       };
174       int i, samp_bytes, header_size;
175       unsigned long count;
176       bfd_vma from_pc, self_pc;
177       struct raw_arc raw_arc;
178       struct raw_phdr raw;
179       static struct hdr h;
180       UNIT raw_bin_count;
181       struct hdr tmp;
182
183       /* Information from a gmon.out file is in two parts: an array of
184          sampling hits within pc ranges, and the arcs.  */
185       gmon_input = INPUT_HISTOGRAM | INPUT_CALL_GRAPH;
186
187       /* This fseek() ought to work even on stdin as long as it's
188          not an interactive device (heck, is there anybody who would
189          want to type in a gmon.out at the terminal?).  */
190       if (fseek (ifp, 0, SEEK_SET) < 0)
191         {
192           perror (filename);
193           done (1);
194         }
195       
196       if (fread (&raw, 1, sizeof (struct raw_phdr), ifp)
197           != sizeof (struct raw_phdr))
198         {
199           fprintf (stderr, _("%s: file too short to be a gmon file\n"),
200                    filename);
201           done (1);
202         }
203       
204       tmp.low_pc = get_vma (core_bfd, (bfd_byte *) &raw.low_pc[0]);
205       tmp.high_pc = get_vma (core_bfd, (bfd_byte *) &raw.high_pc[0]);
206       tmp.ncnt = bfd_get_32 (core_bfd, (bfd_byte *) &raw.ncnt[0]);
207
208       if (bfd_get_32 (core_bfd, (bfd_byte *) &raw.version[0])
209           == GMONVERSION)
210         {
211           int profrate;
212
213           /* 4.4BSD format header.  */
214           profrate = bfd_get_32 (core_bfd, (bfd_byte *) &raw.profrate[0]);
215           
216           if (!s_highpc)
217             hz = profrate;
218           else if (hz != profrate)
219             {
220               fprintf (stderr,
221                        _("%s: profiling rate incompatible with first gmon file\n"),
222                        filename);
223               done (1);
224             }
225
226           header_size = sizeof (struct raw_phdr);
227         }
228       else
229         {
230           /* Old style BSD format.  */
231           if (file_format == FF_BSD44)
232             {
233               fprintf (stderr, _("%s: file `%s' has bad magic cookie\n"),
234                        whoami, filename);
235               done (1);
236             }
237
238           if (fseek (ifp, sizeof (struct old_raw_phdr), SEEK_SET) < 0)
239             {
240               perror (filename);
241               done (1);
242             }
243
244           header_size = sizeof (struct old_raw_phdr);
245         }
246
247       if (s_highpc && (tmp.low_pc != h.low_pc ||
248                        tmp.high_pc != h.high_pc || tmp.ncnt != h.ncnt))
249         {
250           fprintf (stderr, _("%s: incompatible with first gmon file\n"),
251                    filename);
252           done (1);
253         }
254       
255       h = tmp;
256       s_lowpc = (bfd_vma) h.low_pc;
257       s_highpc = (bfd_vma) h.high_pc;
258       lowpc = (bfd_vma) h.low_pc / sizeof (UNIT);
259       highpc = (bfd_vma) h.high_pc / sizeof (UNIT);
260       samp_bytes = h.ncnt - header_size;
261       hist_num_bins = samp_bytes / sizeof (UNIT);
262       
263       DBG (SAMPLEDEBUG,
264            printf ("[gmon_out_read] lowpc 0x%lx highpc 0x%lx ncnt %d\n",
265                    (unsigned long) h.low_pc, (unsigned long) h.high_pc,
266                    h.ncnt);
267            printf ("[gmon_out_read]   s_lowpc 0x%lx   s_highpc 0x%lx\n",
268                    (unsigned long) s_lowpc, (unsigned long) s_highpc);
269            printf ("[gmon_out_read]     lowpc 0x%lx     highpc 0x%lx\n",
270                    (unsigned long) lowpc, (unsigned long) highpc);
271            printf ("[gmon_out_read] samp_bytes %d hist_num_bins %d\n",
272                    samp_bytes, hist_num_bins));
273
274       /* Make sure that we have sensible values.  */
275       if (samp_bytes < 0 || lowpc > highpc)
276         {
277           fprintf (stderr, 
278             _("%s: file '%s' does not appear to be in gmon.out format\n"),
279             whoami, filename);
280           done (1);
281         }
282
283       if (hist_num_bins)
284         ++nhist;
285
286       if (!hist_sample)
287         {
288           hist_sample =
289             (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0]));
290           
291           memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0]));
292         }
293
294       for (i = 0; i < hist_num_bins; ++i)
295         {
296           if (fread (raw_bin_count, sizeof (raw_bin_count), 1, ifp) != 1)
297             {
298               fprintf (stderr,
299                        _("%s: unexpected EOF after reading %d/%d bins\n"),
300                        whoami, --i, hist_num_bins);
301               done (1);
302             }
303           
304           hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) raw_bin_count);
305         }
306
307       /* The rest of the file consists of a bunch of
308          <from,self,count> tuples.  */
309       while (fread (&raw_arc, sizeof (raw_arc), 1, ifp) == 1)
310         {
311           ++narcs;
312           from_pc = get_vma (core_bfd, (bfd_byte *) raw_arc.from_pc);
313           self_pc = get_vma (core_bfd, (bfd_byte *) raw_arc.self_pc);
314           count = bfd_get_32 (core_bfd, (bfd_byte *) raw_arc.count);
315           
316           DBG (SAMPLEDEBUG,
317              printf ("[gmon_out_read] frompc 0x%lx selfpc 0x%lx count %lu\n",
318                      (unsigned long) from_pc, (unsigned long) self_pc, count));
319           
320           /* Add this arc.  */
321           cg_tally (from_pc, self_pc, count);
322         }
323       
324       fclose (ifp);
325
326       if (hz == HZ_WRONG)
327         {
328           /* How many ticks per second?  If we can't tell, report
329              time in ticks.  */
330           hz = hertz ();
331           
332           if (hz == HZ_WRONG)
333             {
334               hz = 1;
335               fprintf (stderr, _("time is in ticks, not seconds\n"));
336             }
337         }
338     }
339   else
340     {
341       fprintf (stderr, _("%s: don't know how to deal with file format %d\n"),
342                whoami, file_format);
343       done (1);
344     }
345
346   if (output_style & STYLE_GMON_INFO)
347     {
348       printf (_("File `%s' (version %d) contains:\n"),
349               filename, gmon_file_version);
350       printf (_("\t%d histogram record%s\n"),
351               nhist, nhist == 1 ? "" : "s");
352       printf (_("\t%d call-graph record%s\n"),
353               narcs, narcs == 1 ? "" : "s");
354       printf (_("\t%d basic-block count record%s\n"),
355               nbbs, nbbs == 1 ? "" : "s");
356       first_output = FALSE;
357     }
358 }
359
360
361 void
362 DEFUN (gmon_out_write, (filename), const char *filename)
363 {
364   FILE *ofp;
365   struct gmon_hdr ghdr;
366
367   ofp = fopen (filename, FOPEN_WB);
368   if (!ofp)
369     {
370       perror (filename);
371       done (1);
372     }
373
374   if (file_format == FF_AUTO || file_format == FF_MAGIC)
375     {
376       /* Write gmon header.  */
377
378       memcpy (&ghdr.cookie[0], GMON_MAGIC, 4);
379       bfd_put_32 (core_bfd, GMON_VERSION, (bfd_byte *) ghdr.version);
380       
381       if (fwrite (&ghdr, sizeof (ghdr), 1, ofp) != 1)
382         {
383           perror (filename);
384           done (1);
385         }
386
387       /* Write execution time histogram if we have one.  */
388       if (gmon_input & INPUT_HISTOGRAM)
389         hist_write_hist (ofp, filename);
390
391       /* Write call graph arcs if we have any.  */
392       if (gmon_input & INPUT_CALL_GRAPH)
393         cg_write_arcs (ofp, filename);
394
395       /* Write basic-block info if we have it.  */
396       if (gmon_input & INPUT_BB_COUNTS)
397         bb_write_blocks (ofp, filename);
398     }
399   else if (file_format == FF_BSD || file_format == FF_BSD44)
400     {
401       struct raw_arc raw_arc;
402       UNIT raw_bin_count;
403       struct raw_phdr h;
404       int i;
405       Arc *arc;
406       Sym *sym;
407
408       memset (&h, 0, sizeof h);
409       put_vma (core_bfd, s_lowpc, (bfd_byte *) &h.low_pc);
410       put_vma (core_bfd, s_highpc, (bfd_byte *) &h.high_pc);
411       bfd_put_32 (core_bfd,
412                   hist_num_bins * sizeof (UNIT) + sizeof (struct raw_phdr),
413                   (bfd_byte *) &h.ncnt);
414
415       /* Write header.  Use new style BSD format is explicitly
416          specified, or if the profiling rate is non-standard;
417          otherwise, use the old BSD format.  */
418       if (file_format == FF_BSD44
419           || hz != hertz ())
420         {
421           bfd_put_32 (core_bfd, GMONVERSION, (bfd_byte *) &h.version);
422           bfd_put_32 (core_bfd, hz, (bfd_byte *) &h.profrate);
423           if (fwrite (&h, sizeof (struct raw_phdr), 1, ofp) != 1)
424             {
425               perror (filename);
426               done (1);
427             }
428         }
429       else
430         {
431           if (fwrite (&h, sizeof (struct old_raw_phdr), 1, ofp) != 1)
432             {
433               perror (filename);
434               done (1);
435             }
436         }
437
438       /* Dump the samples.  */
439       for (i = 0; i < hist_num_bins; ++i)
440         {
441           bfd_put_16 (core_bfd, hist_sample[i], (bfd_byte *) & raw_bin_count[0]);
442           if (fwrite (&raw_bin_count[0], sizeof (raw_bin_count), 1, ofp) != 1)
443             {
444               perror (filename);
445               done (1);
446             }
447         }
448
449       /* Dump the normalized raw arc information.  */
450       for (sym = symtab.base; sym < symtab.limit; ++sym)
451         {
452           for (arc = sym->cg.children; arc; arc = arc->next_child)
453             {
454               put_vma (core_bfd, arc->parent->addr,
455                        (bfd_byte *) raw_arc.from_pc);
456               put_vma (core_bfd, arc->child->addr,
457                        (bfd_byte *) raw_arc.self_pc);
458               bfd_put_32 (core_bfd, arc->count, (bfd_byte *) raw_arc.count);
459               if (fwrite (&raw_arc, sizeof (raw_arc), 1, ofp) != 1)
460                 {
461                   perror (filename);
462                   done (1);
463                 }
464               DBG (SAMPLEDEBUG,
465                    printf ("[dumpsum] frompc 0x%lx selfpc 0x%lx count %lu\n",
466                            (unsigned long) arc->parent->addr,
467                            (unsigned long) arc->child->addr, arc->count));
468             }
469         }
470       
471       fclose (ofp);
472     }
473   else
474     {
475       fprintf (stderr, _("%s: don't know how to deal with file format %d\n"),
476                whoami, file_format);
477       done (1);
478     }
479 }