| /* |
| * Input and output from/to gmon.out files. |
| */ |
| #include "cg_arcs.h" |
| #include "basic_blocks.h" |
| #include "bfd.h" |
| #include "corefile.h" |
| #include "call_graph.h" |
| #include "gmon_io.h" |
| #include "gmon_out.h" |
| #include "gmon.h" /* fetch header for old format */ |
| #include "gprof.h" |
| #include "hertz.h" |
| #include "hist.h" |
| #include "libiberty.h" |
| |
| int gmon_input = 0; |
| int gmon_file_version = 0; /* 0 == old (non-versioned) file format */ |
| |
| /* |
| * This probably ought to be in libbfd. |
| */ |
| bfd_vma |
| DEFUN (get_vma, (abfd, addr), bfd * abfd AND bfd_byte * addr) |
| { |
| switch (sizeof (char*)) |
| { |
| case 4: |
| return bfd_get_32 (abfd, addr); |
| case 8: |
| return bfd_get_64 (abfd, addr); |
| default: |
| fprintf (stderr, _("%s: bfd_vma has unexpected size of %ld bytes\n"), |
| whoami, (long) sizeof (char*)); |
| done (1); |
| } |
| } |
| |
| |
| /* |
| * This probably ought to be in libbfd. |
| */ |
| void |
| DEFUN (put_vma, (abfd, val, addr), bfd * abfd AND bfd_vma val AND bfd_byte * addr) |
| { |
| switch (sizeof (char*)) |
| { |
| case 4: |
| bfd_put_32 (abfd, val, addr); |
| break; |
| case 8: |
| bfd_put_64 (abfd, val, addr); |
| break; |
| default: |
| fprintf (stderr, _("%s: bfd_vma has unexpected size of %ld bytes\n"), |
| whoami, (long) sizeof (char*)); |
| done (1); |
| } |
| } |
| |
| |
| void |
| DEFUN (gmon_out_read, (filename), const char *filename) |
| { |
| FILE *ifp; |
| struct gmon_hdr ghdr; |
| unsigned char tag; |
| int nhist = 0, narcs = 0, nbbs = 0; |
| |
| /* open gmon.out file: */ |
| |
| if (strcmp (filename, "-") == 0) |
| { |
| ifp = stdin; |
| } |
| else |
| { |
| ifp = fopen (filename, FOPEN_RB); |
| if (!ifp) |
| { |
| perror (filename); |
| done (1); |
| } |
| } |
| if (fread (&ghdr, sizeof (struct gmon_hdr), 1, ifp) != 1) |
| { |
| fprintf (stderr, _("%s: file too short to be a gmon file\n"), |
| filename); |
| done (1); |
| } |
| |
| if ((file_format == FF_MAGIC) || |
| (file_format == FF_AUTO && !strncmp (&ghdr.cookie[0], GMON_MAGIC, 4))) |
| { |
| if (file_format == FF_MAGIC && strncmp (&ghdr.cookie[0], GMON_MAGIC, 4)) |
| { |
| fprintf (stderr, _("%s: file `%s' has bad magic cookie\n"), |
| whoami, filename); |
| done (1); |
| } |
| |
| /* right magic, so it's probably really a new gmon.out file */ |
| |
| gmon_file_version = bfd_get_32 (core_bfd, (bfd_byte *) ghdr.version); |
| if (gmon_file_version != GMON_VERSION && gmon_file_version != 0) |
| { |
| fprintf (stderr, |
| _("%s: file `%s' has unsupported version %d\n"), |
| whoami, filename, gmon_file_version); |
| done (1); |
| } |
| |
| /* read in all the records: */ |
| while (fread (&tag, sizeof (tag), 1, ifp) == 1) |
| { |
| switch (tag) |
| { |
| case GMON_TAG_TIME_HIST: |
| ++nhist; |
| gmon_input |= INPUT_HISTOGRAM; |
| hist_read_rec (ifp, filename); |
| break; |
| |
| case GMON_TAG_CG_ARC: |
| ++narcs; |
| gmon_input |= INPUT_CALL_GRAPH; |
| cg_read_rec (ifp, filename); |
| break; |
| |
| case GMON_TAG_BB_COUNT: |
| ++nbbs; |
| gmon_input |= INPUT_BB_COUNTS; |
| bb_read_rec (ifp, filename); |
| break; |
| |
| default: |
| fprintf (stderr, |
| _("%s: %s: found bad tag %d (file corrupted?)\n"), |
| whoami, filename, tag); |
| done (1); |
| } |
| } |
| } |
| else if (file_format == FF_AUTO |
| || file_format == FF_BSD |
| || file_format == FF_BSD44) |
| { |
| struct hdr |
| { |
| bfd_vma low_pc; |
| bfd_vma high_pc; |
| int ncnt; |
| }; |
| int i, samp_bytes, header_size; |
| unsigned long count; |
| bfd_vma from_pc, self_pc; |
| struct raw_arc raw_arc; |
| struct raw_phdr raw; |
| static struct hdr h; |
| UNIT raw_bin_count; |
| struct hdr tmp; |
| |
| /* |
| * Information from a gmon.out file is in two parts: an array of |
| * sampling hits within pc ranges, and the arcs. |
| */ |
| gmon_input = INPUT_HISTOGRAM | INPUT_CALL_GRAPH; |
| |
| /* |
| * This fseek() ought to work even on stdin as long as it's |
| * not an interactive device (heck, is there anybody who would |
| * want to type in a gmon.out at the terminal?). |
| */ |
| if (fseek (ifp, 0, SEEK_SET) < 0) |
| { |
| perror (filename); |
| done (1); |
| } |
| if (fread (&raw, 1, sizeof (struct raw_phdr), ifp) |
| != sizeof (struct raw_phdr)) |
| { |
| fprintf (stderr, _("%s: file too short to be a gmon file\n"), |
| filename); |
| done (1); |
| } |
| tmp.low_pc = get_vma (core_bfd, (bfd_byte *) &raw.low_pc[0]); |
| tmp.high_pc = get_vma (core_bfd, (bfd_byte *) &raw.high_pc[0]); |
| tmp.ncnt = bfd_get_32 (core_bfd, (bfd_byte *) &raw.ncnt[0]); |
| |
| if (bfd_get_32 (core_bfd, (bfd_byte *) &raw.version[0]) |
| == GMONVERSION) |
| { |
| int profrate; |
| |
| /* 4.4BSD format header. */ |
| |
| profrate = bfd_get_32 (core_bfd, (bfd_byte *) &raw.profrate[0]); |
| if (!s_highpc) |
| hz = profrate; |
| else if (hz != profrate) |
| { |
| fprintf (stderr, |
| _("%s: profiling rate incompatible with first gmon file\n"), |
| filename); |
| done (1); |
| } |
| |
| header_size = sizeof (struct raw_phdr); |
| } |
| else |
| { |
| /* old style BSD format. */ |
| if (file_format == FF_BSD44) |
| { |
| fprintf (stderr, _("%s: file `%s' has bad magic cookie\n"), |
| whoami, filename); |
| done (1); |
| } |
| |
| if (fseek (ifp, sizeof (struct old_raw_phdr), SEEK_SET) < 0) |
| { |
| perror (filename); |
| done (1); |
| } |
| |
| header_size = sizeof (struct old_raw_phdr); |
| } |
| |
| if (s_highpc && (tmp.low_pc != h.low_pc || |
| tmp.high_pc != h.high_pc || tmp.ncnt != h.ncnt)) |
| { |
| fprintf (stderr, _("%s: incompatible with first gmon file\n"), |
| filename); |
| done (1); |
| } |
| h = tmp; |
| s_lowpc = (bfd_vma) h.low_pc; |
| s_highpc = (bfd_vma) h.high_pc; |
| lowpc = (bfd_vma) h.low_pc / sizeof (UNIT); |
| highpc = (bfd_vma) h.high_pc / sizeof (UNIT); |
| samp_bytes = h.ncnt - header_size; |
| hist_num_bins = samp_bytes / sizeof (UNIT); |
| DBG (SAMPLEDEBUG, |
| printf ("[gmon_out_read] lowpc 0x%lx highpc 0x%lx ncnt %d\n", |
| h.low_pc, h.high_pc, h.ncnt); |
| printf ("[gmon_out_read] s_lowpc 0x%lx s_highpc 0x%lx\n", |
| s_lowpc, s_highpc); |
| printf ("[gmon_out_read] lowpc 0x%lx highpc 0x%lx\n", |
| lowpc, highpc); |
| printf ("[gmon_out_read] samp_bytes %d hist_num_bins %d\n", |
| samp_bytes, hist_num_bins)); |
| |
| if (hist_num_bins) |
| { |
| ++nhist; |
| } |
| |
| if (!hist_sample) |
| { |
| hist_sample = |
| (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0])); |
| memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0])); |
| } |
| |
| for (i = 0; i < hist_num_bins; ++i) |
| { |
| if (fread (raw_bin_count, sizeof (raw_bin_count), 1, ifp) != 1) |
| { |
| fprintf (stderr, |
| _("%s: unexpected EOF after reading %d/%d bins\n"), |
| whoami, --i, hist_num_bins); |
| done (1); |
| } |
| hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) raw_bin_count); |
| } |
| |
| /* |
| * The rest of the file consists of a bunch of <from,self,count> |
| * tuples: |
| */ |
| while (fread (&raw_arc, sizeof (raw_arc), 1, ifp) == 1) |
| { |
| ++narcs; |
| from_pc = get_vma (core_bfd, (bfd_byte *) raw_arc.from_pc); |
| self_pc = get_vma (core_bfd, (bfd_byte *) raw_arc.self_pc); |
| count = bfd_get_32 (core_bfd, (bfd_byte *) raw_arc.count); |
| DBG (SAMPLEDEBUG, |
| printf ("[gmon_out_read] frompc 0x%lx selfpc 0x%lx count %lu\n", |
| from_pc, self_pc, count)); |
| /* add this arc: */ |
| cg_tally (from_pc, self_pc, count); |
| } |
| fclose (ifp); |
| |
| if (hz == HZ_WRONG) |
| { |
| /* |
| * How many ticks per second? If we can't tell, report |
| * time in ticks. |
| */ |
| hz = hertz (); |
| if (hz == HZ_WRONG) |
| { |
| hz = 1; |
| fprintf (stderr, _("time is in ticks, not seconds\n")); |
| } |
| } |
| } |
| else |
| { |
| fprintf (stderr, _("%s: don't know how to deal with file format %d\n"), |
| whoami, file_format); |
| done (1); |
| } |
| |
| if (output_style & STYLE_GMON_INFO) |
| { |
| printf (_("File `%s' (version %d) contains:\n"), |
| filename, gmon_file_version); |
| printf (_("\t%d histogram record%s\n"), |
| nhist, nhist == 1 ? "" : "s"); |
| printf (_("\t%d call-graph record%s\n"), |
| narcs, narcs == 1 ? "" : "s"); |
| printf (_("\t%d basic-block count record%s\n"), |
| nbbs, nbbs == 1 ? "" : "s"); |
| first_output = FALSE; |
| } |
| } |
| |
| |
| void |
| DEFUN (gmon_out_write, (filename), const char *filename) |
| { |
| FILE *ofp; |
| struct gmon_hdr ghdr; |
| |
| ofp = fopen (filename, FOPEN_WB); |
| if (!ofp) |
| { |
| perror (filename); |
| done (1); |
| } |
| |
| if (file_format == FF_AUTO || file_format == FF_MAGIC) |
| { |
| /* write gmon header: */ |
| |
| memcpy (&ghdr.cookie[0], GMON_MAGIC, 4); |
| bfd_put_32 (core_bfd, GMON_VERSION, (bfd_byte *) ghdr.version); |
| if (fwrite (&ghdr, sizeof (ghdr), 1, ofp) != 1) |
| { |
| perror (filename); |
| done (1); |
| } |
| |
| /* write execution time histogram if we have one: */ |
| if (gmon_input & INPUT_HISTOGRAM) |
| { |
| hist_write_hist (ofp, filename); |
| } |
| |
| /* write call graph arcs if we have any: */ |
| if (gmon_input & INPUT_CALL_GRAPH) |
| { |
| cg_write_arcs (ofp, filename); |
| } |
| |
| /* write basic-block info if we have it: */ |
| if (gmon_input & INPUT_BB_COUNTS) |
| { |
| bb_write_blocks (ofp, filename); |
| } |
| } |
| else if (file_format == FF_BSD || file_format == FF_BSD44) |
| { |
| struct raw_arc raw_arc; |
| UNIT raw_bin_count; |
| struct raw_phdr h; |
| int i; |
| Arc *arc; |
| Sym *sym; |
| |
| memset (&h, 0, sizeof h); |
| put_vma (core_bfd, s_lowpc, (bfd_byte *) &h.low_pc); |
| put_vma (core_bfd, s_highpc, (bfd_byte *) &h.high_pc); |
| bfd_put_32 (core_bfd, |
| hist_num_bins * sizeof (UNIT) + sizeof (struct raw_phdr), |
| (bfd_byte *) &h.ncnt); |
| |
| /* Write header. Use new style BSD format is explicitly |
| specified, or if the profiling rate is non-standard; |
| otherwise, use the old BSD format. */ |
| if (file_format == FF_BSD44 |
| || hz != hertz ()) |
| { |
| bfd_put_32 (core_bfd, GMONVERSION, (bfd_byte *) &h.version); |
| bfd_put_32 (core_bfd, hz, (bfd_byte *) &h.profrate); |
| if (fwrite (&h, sizeof (struct raw_phdr), 1, ofp) != 1) |
| { |
| perror (filename); |
| done (1); |
| } |
| } |
| else |
| { |
| if (fwrite (&h, sizeof (struct old_raw_phdr), 1, ofp) != 1) |
| { |
| perror (filename); |
| done (1); |
| } |
| } |
| |
| /* dump the samples: */ |
| |
| for (i = 0; i < hist_num_bins; ++i) |
| { |
| bfd_put_16 (core_bfd, hist_sample[i], (bfd_byte *) & raw_bin_count[0]); |
| if (fwrite (&raw_bin_count[0], sizeof (raw_bin_count), 1, ofp) != 1) |
| { |
| perror (filename); |
| done (1); |
| } |
| } |
| |
| /* dump the normalized raw arc information: */ |
| |
| for (sym = symtab.base; sym < symtab.limit; ++sym) |
| { |
| for (arc = sym->cg.children; arc; arc = arc->next_child) |
| { |
| put_vma (core_bfd, arc->parent->addr, |
| (bfd_byte *) raw_arc.from_pc); |
| put_vma (core_bfd, arc->child->addr, |
| (bfd_byte *) raw_arc.self_pc); |
| bfd_put_32 (core_bfd, arc->count, (bfd_byte *) raw_arc.count); |
| if (fwrite (&raw_arc, sizeof (raw_arc), 1, ofp) != 1) |
| { |
| perror (filename); |
| done (1); |
| } |
| DBG (SAMPLEDEBUG, |
| printf ("[dumpsum] frompc 0x%lx selfpc 0x%lx count %lu\n", |
| arc->parent->addr, arc->child->addr, arc->count)); |
| } |
| } |
| fclose (ofp); |
| } |
| else |
| { |
| fprintf (stderr, _("%s: don't know how to deal with file format %d\n"), |
| whoami, file_format); |
| done (1); |
| } |
| } |