]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gprofng: New tool for converting gmon.out file to experiments
authorClaudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
Thu, 20 Nov 2025 12:59:33 +0000 (14:59 +0200)
committerClaudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
Thu, 20 Nov 2025 12:59:33 +0000 (14:59 +0200)
This patch introduces the capability to convert a gmon.out file into a
gprofng experiment.

The gmon.out file format includes a histogram record, a call graph
record, and basic block execution count records. A gprofng experiment
folder consists of various files, of which four are particularly
important:

  - log.xml: Contains the profile timer setup and defines the profile
    data structure fields.

  - map.xml: Contains the memory map information of the profiled
    executable.

  - profile: Holds the time profile information.

  - data.frameinfo: Records the stack trace at a specific sampling
    moment, with each trace linked to the time profile information
    stored in the profile file.

This patch adds a new tool to gprofng that reads the gmon.out file and
the profiled executable. With the executable, we can reconstruct
information about the profiled symbols and the memory map layout. The
gmon.out file provides details about the execution duration for each
symbol, as well as data used to build the call graph. This new patch
is a subcommand for the display command.

New gmon subcommand options:

 --version           print the version number and exit.
 -h/--help           print usage information and exit.

 -o <exp_name>     specify the name for (and path to) the experiment directory; the
   the default path is the current directory.

 -O <exp_name>     the same as -o, but unlike the -o option, silently overwrite an
   existing experiment directory with the same name.

The new tool can be executed using the command line:
$ gprofng display gmon EXEC_FILE GMON_FILE

N.B. If EXEC_FILE or GMON_FILE is missing the default a.out and
gmon.out names are considered.

Upon successful execution, an experiment file is created that can be
processed with existing gprofng tools. For example:
$ gprofng display text -calltree EXPERIMENT

This command will display the call tree from the converted gmon file.

Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
23 files changed:
gprofng/src/Makefile.am
gprofng/src/Makefile.in
gprofng/src/basic_blocks.h [new file with mode: 0644]
gprofng/src/call_graph.h [new file with mode: 0644]
gprofng/src/cg_arcs.h [new file with mode: 0644]
gprofng/src/collctrl.h
gprofng/src/corefile.h [new file with mode: 0644]
gprofng/src/gmon.h [new file with mode: 0644]
gprofng/src/gmon_basic_blocks.cc [new file with mode: 0644]
gprofng/src/gmon_call_graph.cc [new file with mode: 0644]
gprofng/src/gmon_cg_arcs.cc [new file with mode: 0644]
gprofng/src/gmon_corefile.cc [new file with mode: 0644]
gprofng/src/gmon_hist.cc [new file with mode: 0644]
gprofng/src/gmon_io.cc [new file with mode: 0644]
gprofng/src/gmon_io.h [new file with mode: 0644]
gprofng/src/gmon_out.h [new file with mode: 0644]
gprofng/src/gmon_symtab.cc [new file with mode: 0644]
gprofng/src/gp-gmon.cc [new file with mode: 0644]
gprofng/src/gp-gmon.h [new file with mode: 0644]
gprofng/src/gprofng.cc
gprofng/src/hist.h [new file with mode: 0644]
gprofng/src/source.h [new file with mode: 0644]
gprofng/src/symtab.h [new file with mode: 0644]

index f6d3f5da7594e07f64343255b9f86ca3475c002c..b421fc4acf406dfecdc68b0a5469fb5c06fc0b60 100644 (file)
@@ -87,6 +87,13 @@ CCSOURCES = \
        UserLabel.cc \
        util.cc \
        Dbe.cc \
+       gmon_io.cc \
+       gmon_basic_blocks.cc \
+       gmon_call_graph.cc \
+       gmon_hist.cc \
+       gmon_symtab.cc \
+       gmon_cg_arcs.cc \
+       gmon_corefile.cc \
        $(NULL)
 
 CSOURCES = \
@@ -154,7 +161,7 @@ dbe_DATA = $(srcdir)/gprofng.rc
 
 
 bin_PROGRAMS = gprofng-archive gprofng-collect-app gprofng \
-       gprofng-display-text gprofng-display-src
+       gprofng-display-text gprofng-display-src gprofng-gmon
 
 gprofng_archive_SOURCES = gp-archive.cc ArchiveExp.cc
 gprofng_archive_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
@@ -171,6 +178,9 @@ gprofng_display_src_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
 gprofng_display_text_SOURCES = gp-display-text.cc ipc.cc ipcio.cc
 gprofng_display_text_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
 
+gprofng_gmon_SOURCES = gp-gmon.cc
+gprofng_gmon_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
+
 # Distribution involves building the binaries to generate the manpage,
 # so ensure that the necessary libraries are built at dist time.
 dist-hook: $(LIBGPROFNG)
index 0a1934e834421c0d7d80a20adc120cd8013e5767..3696c25f160be2c00968f9d0add078b7697876bd 100644 (file)
@@ -110,7 +110,7 @@ host_triplet = @host@
 target_triplet = @target@
 bin_PROGRAMS = gprofng-archive$(EXEEXT) gprofng-collect-app$(EXEEXT) \
        gprofng$(EXEEXT) gprofng-display-text$(EXEEXT) \
-       gprofng-display-src$(EXEEXT)
+       gprofng-display-src$(EXEEXT) gprofng-gmon$(EXEEXT)
 subdir = src
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/clang-plugin.m4 \
@@ -181,7 +181,9 @@ am__objects_1 = Application.lo BaseMetric.lo BaseMetricTreeNode.lo \
        SAXParserFactory.lo Sample.lo Settings.lo SourceFile.lo \
        Stabs.lo Stats_data.lo StringBuilder.lo Symbol.lo Table.lo \
        QLParser.tab.lo dbe_collctrl.lo i18n.lo parse.lo UserLabel.lo \
-       util.lo Dbe.lo
+       util.lo Dbe.lo gmon_io.lo gmon_basic_blocks.lo \
+       gmon_call_graph.lo gmon_hist.lo gmon_symtab.lo gmon_cg_arcs.lo \
+       gmon_corefile.lo
 am__objects_2 = dbe_hwcdrv.lo dbe_hwcfuncs.lo dbe_hwctable.lo \
        gethrtime.lo
 am_libgprofng_la_OBJECTS = $(am__objects_1) $(am__objects_2)
@@ -217,6 +219,10 @@ am_gprofng_display_text_OBJECTS = gp-display-text.$(OBJEXT) \
 gprofng_display_text_OBJECTS = $(am_gprofng_display_text_OBJECTS)
 gprofng_display_text_DEPENDENCIES = $(LIBGPROFNG) \
        $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_gprofng_gmon_OBJECTS = gp-gmon.$(OBJEXT)
+gprofng_gmon_OBJECTS = $(am_gprofng_gmon_OBJECTS)
+gprofng_gmon_DEPENDENCIES = $(LIBGPROFNG) $(am__DEPENDENCIES_1) \
+       $(am__DEPENDENCIES_1)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -271,10 +277,12 @@ am__v_CXXLD_0 = @echo "  CXXLD   " $@;
 am__v_CXXLD_1 = 
 SOURCES = $(libgprofng_la_SOURCES) $(gprofng_SOURCES) \
        $(gprofng_archive_SOURCES) $(gprofng_collect_app_SOURCES) \
-       $(gprofng_display_src_SOURCES) $(gprofng_display_text_SOURCES)
+       $(gprofng_display_src_SOURCES) $(gprofng_display_text_SOURCES) \
+       $(gprofng_gmon_SOURCES)
 DIST_SOURCES = $(libgprofng_la_SOURCES) $(gprofng_SOURCES) \
        $(gprofng_archive_SOURCES) $(gprofng_collect_app_SOURCES) \
-       $(gprofng_display_src_SOURCES) $(gprofng_display_text_SOURCES)
+       $(gprofng_display_src_SOURCES) $(gprofng_display_text_SOURCES) \
+       $(gprofng_gmon_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -435,6 +443,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -520,6 +529,13 @@ CCSOURCES = \
        UserLabel.cc \
        util.cc \
        Dbe.cc \
+       gmon_io.cc \
+       gmon_basic_blocks.cc \
+       gmon_call_graph.cc \
+       gmon_hist.cc \
+       gmon_symtab.cc \
+       gmon_cg_arcs.cc \
+       gmon_corefile.cc \
        $(NULL)
 
 CSOURCES = \
@@ -575,6 +591,8 @@ gprofng_display_src_SOURCES = gp-display-src.cc
 gprofng_display_src_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
 gprofng_display_text_SOURCES = gp-display-text.cc ipc.cc ipcio.cc
 gprofng_display_text_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
+gprofng_gmon_SOURCES = gp-gmon.cc
+gprofng_gmon_LDADD = $(LIBGPROFNG) $(CLOCK_GETTIME_LINK) $(ZLIB)
 all: $(BUILT_SOURCES)
        $(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -717,6 +735,10 @@ gprofng-display-text$(EXEEXT): $(gprofng_display_text_OBJECTS) $(gprofng_display
        @rm -f gprofng-display-text$(EXEEXT)
        $(AM_V_CXXLD)$(CXXLINK) $(gprofng_display_text_OBJECTS) $(gprofng_display_text_LDADD) $(LIBS)
 
+gprofng-gmon$(EXEEXT): $(gprofng_gmon_OBJECTS) $(gprofng_gmon_DEPENDENCIES) $(EXTRA_gprofng_gmon_DEPENDENCIES) 
+       @rm -f gprofng-gmon$(EXEEXT)
+       $(AM_V_CXXLD)$(CXXLINK) $(gprofng_gmon_OBJECTS) $(gprofng_gmon_LDADD) $(LIBS)
+
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
 
@@ -795,10 +817,18 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwctable.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/envsets.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gethrtime.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_basic_blocks.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_call_graph.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_cg_arcs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_corefile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_hist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmon_symtab.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-archive.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-collect-app.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-display-src.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-display-text.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-gmon.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprofng.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i18n.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc.Po@am__quote@
diff --git a/gprofng/src/basic_blocks.h b/gprofng/src/basic_blocks.h
new file mode 100644 (file)
index 0000000..97c5283
--- /dev/null
@@ -0,0 +1,30 @@
+/* basic_blocks.h
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef basic_blocks_h
+#define basic_blocks_h
+
+#include <stdio.h>
+
+/* Read a basic-block record from file IFP.  FILENAME is the name of
+   file IFP and is provided for formatting error-messages only.  */
+extern void bb_read_rec (FILE *, const char *, bool);
+
+#endif /* basic_blocks_h */
diff --git a/gprofng/src/call_graph.h b/gprofng/src/call_graph.h
new file mode 100644 (file)
index 0000000..5555bf3
--- /dev/null
@@ -0,0 +1,33 @@
+/* call_graph.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef call_graph_h
+#define call_graph_h
+
+extern void cg_tally (bfd_vma, bfd_vma, unsigned long);
+
+/* Read a record from file IFP describing an arc in the function
+   call-graph and the count of how many times the arc has been
+   traversed.  FILENAME is the name of file IFP and is provided for
+   formatting error-messages only.  */
+extern void cg_read_rec (FILE *, const char *);
+
+#endif /* call_graph_h */
diff --git a/gprofng/src/cg_arcs.h b/gprofng/src/cg_arcs.h
new file mode 100644 (file)
index 0000000..19a8b01
--- /dev/null
@@ -0,0 +1,51 @@
+/* Copyright (C) 2012-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef cg_arcs_h
+#define cg_arcs_h
+
+/*
+ * Arc structure for call-graph.
+ *
+ * With pointers to the symbols of the parent and the child, a count
+ * of how many times this arc was traversed, and pointers to the next
+ * parent of this child and the next child of this parent.
+ */
+typedef struct arc
+  {
+    Sym *parent;               /* source vertice of arc */
+    Sym *child;                        /* dest vertice of arc */
+    unsigned long count;       /* # of calls from parent to child */
+    double time;               /* time inherited along arc */
+    double child_time;         /* child-time inherited along arc */
+    struct arc *next_parent;   /* next parent of CHILD */
+    struct arc *next_child;    /* next child of PARENT */
+    int has_been_placed;       /* have this arc's functions been placed? */
+  }
+Arc;
+
+extern unsigned int num_cycles;        /* number of cycles discovered */
+extern Sym *cycle_header;      /* cycle headers */
+
+extern void arc_add (Sym * parent, Sym * child, unsigned long count);
+extern Arc *arc_lookup (Sym * parent, Sym * child);
+extern Arc **arcs;
+extern unsigned int numarcs;
+
+#endif /* cg_arcs_h */
index a21e9eadaa2b7547c68ec356d0999e903893f74f..79d2d87b71d6cb3e4b5a1c98401e5e1e62f223cf 100644 (file)
@@ -258,6 +258,7 @@ public:
   char *set (char *, const char *); /* set control's value */
   char *unset (char *); /* reset control's value to its default */
   void set_project_home (char *);
+  char *create_exp_dir ();
 
 private:
   int interactive;      /* 1 - dbx, 0 - collect */
@@ -380,7 +381,6 @@ private:
   void determine_profile_params ();
   char *preprocess_names ();
   char *get_exp_name (const char *);
-  char *create_exp_dir ();
   void build_data_desc ();
   char *check_group ();
   char *join_group ();
diff --git a/gprofng/src/corefile.h b/gprofng/src/corefile.h
new file mode 100644 (file)
index 0000000..3db1a40
--- /dev/null
@@ -0,0 +1,41 @@
+/* corefile.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef corefile_h
+#define corefile_h
+
+struct function_map
+{
+  char *         function_name;
+  char *         file_name;
+  unsigned int   is_first:1;  /* Is this the first symbol in an object file?  */
+};
+
+extern bfd * core_bfd;            /* BFD for core-file.  */
+extern asection * core_text_sect; /* Core text section.  */
+extern void * core_text_space;    /* Text space of a.out in core.  */
+extern int offset_to_code;        /* Offset (in bytes) of code from entry
+                                     address of routine.  */
+
+extern int core_init (const char *);
+extern void symtab_init (void);
+
+#endif /* corefile_h */
diff --git a/gprofng/src/gmon.h b/gprofng/src/gmon.h
new file mode 100644 (file)
index 0000000..762f970
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1983, 1991, 1993, 2001
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef gmon_h
+#define gmon_h
+
+/* Size of the 4.4BSD gmon header */
+#define GMON_HDRSIZE_BSD44_32 (4 + 4 + 4 + 4 + 4 + (3 * 4))
+#define GMON_HDRSIZE_BSD44_64 (8 + 8 + 4 + 4 + 4 + (3 * 4))
+
+/* *INDENT-OFF* */
+/* For documentation purposes only.
+
+   struct raw_phdr
+    {
+      char low_pc[sizeof(void *)];   -- base pc address of sample buffer
+      char high_pc[sizeof(void *)];  -- max pc address of sampled buffer
+      char ncnt[4];                 -- size of sample buffer (plus this
+                                       header)
+
+      char version[4];              -- version number
+      char profrate[4];                     -- profiling clock rate
+      char spare[3*4];              -- reserved
+    };
+*/
+/* *INDENT-ON* */
+
+#define GMONVERSION     0x00051879
+
+/* Size of the old BSD gmon header */
+#define GMON_HDRSIZE_OLDBSD_32 (4 + 4 + 4)
+
+/* FIXME: Checking host compiler defines here means that we can't
+   use a cross gprof alpha OSF.  */
+#if defined(__alpha__) && defined (__osf__)
+#define GMON_HDRSIZE_OLDBSD_64 (8 + 8 + 4 + 4)
+#else
+#define GMON_HDRSIZE_OLDBSD_64 (8 + 8 + 4)
+#endif
+
+/* *INDENT-OFF* */
+/* For documentation purposes only.
+
+  struct old_raw_phdr
+    {
+      char low_pc[sizeof(void *)];  -- base pc address of sample buffer
+      char high_pc[sizeof(void *)]  -- max pc address of sampled buffer
+      char ncnt[4];                -- size of sample buffer (plus this
+                                      header)
+
+      if defined (__alpha__) && defined (__osf__)
+      char pad[4];                 -- DEC's OSF v3.0 uses 4 bytes of padding
+                                   -- to bring the header to a size that is a
+                                   -- multiple of 8.
+      endif
+    };
+*/
+/* *INDENT-ON* */
+
+/*
+ * Histogram counters are unsigned shorts:
+ */
+#define        HISTCOUNTER unsigned short
+
+/*
+ * Fraction of text space to allocate for histogram counters here, 1/2:
+ */
+#define        HISTFRACTION    2
+
+/*
+ * Fraction of text space to allocate for from hash buckets.  The
+ * value of HASHFRACTION is based on the minimum number of bytes of
+ * separation between two subroutine call points in the object code.
+ * Given MIN_SUBR_SEPARATION bytes of separation the value of
+ * HASHFRACTION is calculated as:
+ *
+ *      HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
+ *
+ * For the VAX, the shortest two call sequence is:
+ *
+ *      calls   $0,(r0)
+ *      calls   $0,(r0)
+ *
+ * which is separated by only three bytes, thus HASHFRACTION is
+ * calculated as:
+ *
+ *      HASHFRACTION = 3 / (2 * 2 - 1) = 1
+ *
+ * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
+ * is less than three, this algorithm will not work!
+ */
+#define        HASHFRACTION 1
+
+/*
+ * Percent of text space to allocate for tostructs with a minimum:
+ */
+#define ARCDENSITY     2
+#define MINARCS                50
+
+struct tostruct
+  {
+    char *selfpc;
+    int count;
+    unsigned short link;
+  };
+
+/*
+ * A raw arc, with pointers to the calling site and the called site
+ * and a count.  Everything is defined in terms of characters so
+ * as to get a packed representation (otherwise, different compilers
+ * might introduce different padding):
+ */
+
+/* *INDENT-OFF* */
+/* For documentation purposes only.
+
+  struct raw_arc
+    {
+      char from_pc[sizeof(void *)];
+      char self_pc[sizeof(void *)];
+      char count[sizeof(long)];
+    };
+*/
+/* *INDENT-ON* */
+
+/*
+ * General rounding functions:
+ */
+#define ROUNDDOWN(x,y) (((x)/(y))*(y))
+#define ROUNDUP(x,y)   ((((x)+(y)-1)/(y))*(y))
+
+#endif /* gmon_h */
diff --git a/gprofng/src/gmon_basic_blocks.cc b/gprofng/src/gmon_basic_blocks.cc
new file mode 100644 (file)
index 0000000..cc37b27
--- /dev/null
@@ -0,0 +1,139 @@
+/* basic_blocks.c  -  Basic-block level related code: reading/writing
+   of basic-block info to/from gmon.out; computing and formatting of
+   basic-block related statistics.
+
+   Copyright (C) 1999-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+
+#include "config.h"
+#include "util.h"
+#include "Elf.h" /* link to bfd.h fixes bfd* */
+
+#include "gp-gmon.h"
+
+#include "basic_blocks.h"
+#include "gmon_io.h"
+#include "gmon_out.h"
+#include "symtab.h"
+
+/* Skip over variable length string.  */
+
+static void
+fskip_string (FILE *fp)
+{
+  int ch;
+
+  while ((ch = fgetc (fp)) != EOF)
+    {
+      if (ch == '\0')
+       break;
+    }
+}
+
+/* Read a basic-block record from file IFP.  FILENAME is the name
+   of file IFP and is provided for formatting error-messages only.  */
+
+void
+bb_read_rec (FILE *ifp, const char *filename, bool line_granularity)
+{
+  unsigned int nblocks, b;
+  bfd_vma addr, ncalls;
+  Sym *sym;
+  Sym_Table *symtab;
+
+  if (gmon_io_read_32 (ifp, &nblocks))
+    {
+      fprintf (stderr, "%s: %s: unexpected end of file\n",
+              whoami, filename);
+      done (1);
+    }
+
+  symtab = get_symtab ();
+
+  nblocks = bfd_get_32 (core_bfd, (bfd_byte *) & nblocks);
+  if (gmon_file_version == 0)
+    fskip_string (ifp);
+
+  for (b = 0; b < nblocks; ++b)
+    {
+      if (gmon_file_version == 0)
+       {
+         int line_num;
+
+         /* Version 0 had lots of extra stuff that we don't
+            care about anymore.  */
+         if ((fread (&ncalls, sizeof (ncalls), 1, ifp) != 1)
+             || (fread (&addr, sizeof (addr), 1, ifp) != 1)
+             || (fskip_string (ifp), false)
+             || (fskip_string (ifp), false)
+             || (fread (&line_num, sizeof (line_num), 1, ifp) != 1))
+           {
+             perror (filename);
+             done (1);
+           }
+       }
+      else if (gmon_io_read_vma (ifp, &addr)
+              || gmon_io_read_vma (ifp, &ncalls))
+       {
+         perror (filename);
+         done (1);
+       }
+
+      /* Basic-block execution counts are meaningful only if we're
+        profiling at the line-by-line level:  */
+      if (line_granularity)
+       {
+         sym = sym_lookup (symtab, addr);
+
+         if (sym)
+           {
+             int i;
+
+             DBG (BBDEBUG,
+                  printf ("[bb_read_rec] 0x%lx->0x%lx (%s:%d) cnt=%lu\n",
+                          (unsigned long) addr, (unsigned long) sym->addr,
+                          sym->name, sym->line_num, (unsigned long) ncalls));
+
+             for (i = 0; i < NBBS; i++)
+               {
+                 if (! sym->bb_addr[i] || sym->bb_addr[i] == addr)
+                   {
+                     sym->bb_addr[i] = addr;
+                     sym->bb_calls[i] += ncalls;
+                     break;
+                   }
+               }
+           }
+       }
+      else
+       {
+         static bool user_warned = false;
+
+         if (!user_warned)
+           {
+             user_warned = true;
+             fprintf (stderr,
+                      "%s: warning: ignoring basic-block exec counts (use -l or --line)\n",
+                      whoami);
+           }
+       }
+    }
+  return;
+}
diff --git a/gprofng/src/gmon_call_graph.cc b/gprofng/src/gmon_call_graph.cc
new file mode 100644 (file)
index 0000000..433c3a9
--- /dev/null
@@ -0,0 +1,91 @@
+/* call_graph.c  -  Create call graphs.
+
+   Copyright (C) 1999-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "config.h"
+#include "util.h"
+#include "Elf.h" /* link to bfd.h */
+#include "gp-gmon.h"
+
+#include "symtab.h"
+#include "cg_arcs.h"
+#include "call_graph.h"
+#include "gmon_io.h"
+#include "gmon_out.h"
+
+void
+cg_tally (bfd_vma from_pc, bfd_vma self_pc, unsigned long count)
+{
+  Sym *parent;
+  Sym *child;
+  Sym_Table *symtab = get_symtab ();
+
+  parent = sym_lookup (symtab, from_pc);
+  child = sym_lookup (symtab, self_pc);
+
+  if (child == NULL || parent == NULL)
+    return;
+
+  /* If we're doing line-by-line profiling, both the parent and the
+     child will probably point to line symbols instead of function
+     symbols.  For the parent this is fine, since this identifies the
+     line number in the calling routing, but the child should always
+     point to a function entry point, so we back up in the symbol
+     table until we find it.
+
+     For normal profiling, is_func will be set on all symbols, so this
+     code will do nothing.  */
+  while (child >= symtab->base && ! child->is_func)
+    --child;
+
+  if (child < symtab->base)
+    return;
+
+  child->ncalls += count;
+  arc_add (parent, child, count);
+}
+
+/* Read a record from file IFP describing an arc in the function
+   call-graph and the count of how many times the arc has been
+   traversed.  FILENAME is the name of file IFP and is provided for
+   formatting error-messages only.  */
+
+void
+cg_read_rec (FILE *ifp, const char *filename)
+{
+  bfd_vma from_pc, self_pc;
+  unsigned int count;
+
+  if (gmon_io_read_vma (ifp, &from_pc)
+      || gmon_io_read_vma (ifp, &self_pc)
+      || gmon_io_read_32 (ifp, &count))
+    {
+      fprintf (stderr, "%s: %s: unexpected end of file\n",
+              whoami, filename);
+      done (1);
+    }
+
+  DBG (SAMPLEDEBUG,
+       printf ("[cg_read_rec] frompc 0x%lx selfpc 0x%lx count %lu\n",
+              (unsigned long) from_pc, (unsigned long) self_pc,
+              (unsigned long) count));
+  /* Add this arc:  */
+  cg_tally (from_pc, self_pc, count);
+}
diff --git a/gprofng/src/gmon_cg_arcs.cc b/gprofng/src/gmon_cg_arcs.cc
new file mode 100644 (file)
index 0000000..e7f58e5
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1983, 1993, 2001
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "util.h"
+#include "bfd.h"
+#include "gp-gmon.h"
+
+#include "symtab.h"
+#include "cg_arcs.h"
+
+Sym *cycle_header;
+unsigned int num_cycles;
+Arc **arcs;
+unsigned int numarcs;
+
+/*
+ * Return TRUE iff PARENT has an arc to covers the address
+ * range covered by CHILD.
+ */
+Arc *
+arc_lookup (Sym *parent, Sym *child)
+{
+  Arc *arc;
+
+  if (!parent || !child)
+    {
+      printf ("[arc_lookup] parent == 0 || child == 0\n");
+      return 0;
+    }
+  DBG (LOOKUPDEBUG, printf ("[arc_lookup] parent %s child %s\n",
+                           parent->name, child->name));
+  for (arc = parent->cg.children; arc; arc = arc->next_child)
+    {
+      DBG (LOOKUPDEBUG, printf ("[arc_lookup]\t parent %s child %s\n",
+                               arc->parent->name, arc->child->name));
+      if (child->addr >= arc->child->addr
+         && child->end_addr <= arc->child->end_addr)
+       {
+         return arc;
+       }
+    }
+  return 0;
+}
+
+
+/*
+ * Add (or just increment) an arc:
+ */
+void
+arc_add (Sym *parent, Sym *child, unsigned long count)
+{
+  static unsigned int maxarcs = 0;
+  Arc *arc;
+
+  DBG (TALLYDEBUG, printf ("[arc_add] %lu arcs from %s to %s\n",
+                          count, parent->name, child->name));
+  arc = arc_lookup (parent, child);
+  if (arc)
+    {
+      /*
+       * A hit: just increment the count.
+       */
+      DBG (TALLYDEBUG, printf ("[tally] hit %lu += %lu\n",
+                              arc->count, count));
+      arc->count += count;
+      return;
+    }
+  arc = (Arc *) xmalloc (sizeof (*arc));
+  memset (arc, 0, sizeof (*arc));
+  arc->parent = parent;
+  arc->child = child;
+  arc->count = count;
+
+  /* If this isn't an arc for a recursive call to parent, then add it
+     to the array of arcs.  */
+  if (parent != child)
+    {
+      /* If we've exhausted space in our current array, get a new one
+        and copy the contents.   We might want to throttle the doubling
+        factor one day.  */
+      if (numarcs == maxarcs)
+       {
+         /* Determine how much space we want to allocate.  */
+         if (maxarcs == 0)
+           maxarcs = 1;
+         maxarcs *= 2;
+
+         arcs = (Arc**) xrealloc (arcs, sizeof (*arcs) * maxarcs);
+       }
+
+      /* Place this arc in the arc array.  */
+      arcs[numarcs++] = arc;
+    }
+
+  /* prepend this child to the children of this parent: */
+  arc->next_child = parent->cg.children;
+  parent->cg.children = arc;
+
+  /* prepend this parent to the parents of this child: */
+  arc->next_parent = child->cg.parents;
+  child->cg.parents = arc;
+}
diff --git a/gprofng/src/gmon_corefile.cc b/gprofng/src/gmon_corefile.cc
new file mode 100644 (file)
index 0000000..93abb91
--- /dev/null
@@ -0,0 +1,492 @@
+/* corefile.c
+
+   Copyright (C) 1999-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "config.h"
+#include "util.h"
+#include "bfd.h"
+#include "gp-gmon.h"
+
+#include "source.h"
+#include "symtab.h"
+
+#include "safe-ctype.h"
+#include <limits.h>    /* For UINT_MAX.  */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include "gp-experiment.h"
+#include <filenames.h>
+
+#define _(String) (String)
+
+bfd *core_bfd;
+static int core_num_syms;
+static asymbol **core_syms;
+asection *core_text_sect;
+void * core_text_space;
+
+/* Greatest common divisor of instruction sizes and alignments.  */
+static int insn_boundary;
+int offset_to_code;
+
+/* For mapping symbols to specific .o files during file ordering.  */
+static struct function_map * symbol_map;
+static unsigned int symbol_map_count;
+
+static int core_sym_class (asymbol *);
+static bool get_src_info
+  (bfd_vma, const char **, const char **, int *);
+
+
+#define BUFSIZE      (1024)
+/* This is BUFSIZE - 1 as a string.  Suitable for use in fprintf/sscanf format strings.  */
+#define STR_BUFSIZE  "1023"
+
+
+int
+core_init (const char * aout_name)
+{
+  int core_sym_bytes;
+  asymbol *synthsyms;
+  long synth_count;
+
+  core_bfd = bfd_openr (aout_name, 0);
+
+  if (!core_bfd)
+    {
+      perror (aout_name);
+      return -1;
+    }
+
+  core_bfd->flags |= BFD_DECOMPRESS;
+
+  if (!bfd_check_format (core_bfd, bfd_object))
+    {
+      fprintf (stderr, _("%s: %s: not in executable format\n"), whoami, aout_name);
+      return -1;
+    }
+
+  /* Get core's text section.  */
+  core_text_sect = bfd_get_section_by_name (core_bfd, ".text");
+  if (!core_text_sect)
+    {
+      core_text_sect = bfd_get_section_by_name (core_bfd, "$CODE$");
+      if (!core_text_sect)
+       {
+         fprintf (stderr, _("%s: can't find .text section in %s\n"),
+                  whoami, aout_name);
+         return -1;
+       }
+    }
+
+  /* Read core's symbol table.  */
+
+  /* This will probably give us more than we need, but that's ok.  */
+  core_sym_bytes = bfd_get_symtab_upper_bound (core_bfd);
+  if (core_sym_bytes < 0)
+    {
+      fprintf (stderr, "%s: %s: %s\n", whoami, aout_name,
+              bfd_errmsg (bfd_get_error ()));
+      return -1;
+    }
+
+  core_syms = (asymbol **) xmalloc (core_sym_bytes);
+  core_num_syms = bfd_canonicalize_symtab (core_bfd, core_syms);
+
+  if (core_num_syms < 0)
+    {
+      fprintf (stderr, "%s: %s: %s\n", whoami, aout_name,
+              bfd_errmsg (bfd_get_error ()));
+      return -1;
+    }
+
+  synth_count = bfd_get_synthetic_symtab (core_bfd, core_num_syms, core_syms,
+                                         0, NULL, &synthsyms);
+  if (synth_count > 0)
+    {
+      asymbol **symp;
+      long new_size;
+      long i;
+
+      new_size = (core_num_syms + synth_count + 1) * sizeof (*core_syms);
+      core_syms = (asymbol **) xrealloc (core_syms, new_size);
+      symp = core_syms + core_num_syms;
+      core_num_syms += synth_count;
+      for (i = 0; i < synth_count; i++)
+       *symp++ = synthsyms + i;
+      *symp = 0;
+    }
+
+  insn_boundary = 1;
+  offset_to_code = 0;
+
+  switch (bfd_get_arch (core_bfd))
+    {
+    case bfd_arch_vax:
+      offset_to_code = 2;
+      break;
+
+    case bfd_arch_mips:/* and microMIPS */
+    case bfd_arch_powerpc:/* and VLE */
+    case bfd_arch_riscv:/* and RVC */
+    case bfd_arch_sh:
+      insn_boundary = 2;
+      break;
+
+    case bfd_arch_alpha:
+      insn_boundary = 4;
+      break;
+
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+
+/* Return class of symbol SYM.  The returned class can be any of:
+       0   -> symbol is not interesting to us
+       'T' -> symbol is a global name
+       't' -> symbol is a local (static) name.  */
+
+static int
+core_sym_class (asymbol *sym)
+{
+  symbol_info syminfo;
+  const char *name;
+  char sym_prefix;
+  int i;
+
+  if (sym->section == NULL || (sym->flags & BSF_DEBUGGING) != 0)
+    return 0;
+
+  bfd_get_symbol_info (core_bfd, sym, &syminfo);
+  i = syminfo.type;
+
+  if (i == 'T')
+    return i;                  /* It's a global symbol.  */
+
+  if (i == 'W')
+    /* Treat weak symbols as text symbols.  FIXME: a weak symbol may
+       also be a data symbol.  */
+    return 'T';
+
+  if (i != 't')
+    {
+      /* Not a static text symbol.  */
+      DBG (AOUTDEBUG, printf ("[core_sym_class] %s is of class %c\n",
+                             sym->name, i));
+      return 0;
+    }
+
+
+  /* Can't zero-length name or funny characters in name, where
+     `funny' includes: `.' (.o file names) and `$' (Pascal labels).  */
+  if (!sym->name || sym->name[0] == '\0')
+    return 0;
+
+  for (name = sym->name; *name; ++name)
+    {
+      if (*name == '$')
+        return 0;
+
+      while (*name == '.')
+       {
+         /* Allow both nested subprograms (which end with ".NNN", where N is
+            a digit) and GCC cloned functions (which contain ".clone").
+            Allow for multiple iterations of both - apparently GCC can clone
+            clones and subprograms.  */
+         int digit_seen = 0;
+#define CLONE_NAME         ".clone."
+#define CLONE_NAME_LEN     strlen (CLONE_NAME)
+#define CONSTPROP_NAME     ".constprop."
+#define CONSTPROP_NAME_LEN  strlen (CONSTPROP_NAME)
+
+         if (strlen (name) > CLONE_NAME_LEN
+             && strncmp (name, CLONE_NAME, CLONE_NAME_LEN) == 0)
+           name += CLONE_NAME_LEN - 1;
+
+         else if (strlen (name) > CONSTPROP_NAME_LEN
+             && strncmp (name, CONSTPROP_NAME, CONSTPROP_NAME_LEN) == 0)
+           name += CONSTPROP_NAME_LEN - 1;
+
+         for (name++; *name; name++)
+           if (digit_seen && *name == '.')
+             break;
+           else if (ISDIGIT (*name))
+             digit_seen = 1;
+           else
+             return 0;
+       }
+    }
+
+  /* On systems where the C compiler adds an underscore to all
+     names, static names without underscores seem usually to be
+     labels in hand written assembler in the library.  We don't want
+     these names.  This is certainly necessary on a Sparc running
+     SunOS 4.1 (try profiling a program that does a lot of
+     division). I don't know whether it has harmful side effects on
+     other systems.  Perhaps it should be made configurable.  */
+  sym_prefix = bfd_get_symbol_leading_char (core_bfd);
+
+  if ((sym_prefix && sym_prefix != sym->name[0])
+      /* GCC may add special symbols to help gdb figure out the file
+       language.  We want to ignore these, since sometimes they mask
+       the real function.  (dj@ctron)  */
+      || !strncmp (sym->name, "__gnu_compiled", 14)
+      || !strncmp (sym->name, "___gnu_compiled", 15))
+    {
+      return 0;
+    }
+
+  return 't';                  /* It's a static text symbol.  */
+}
+
+/* Get whatever source info we can get regarding address ADDR.  */
+
+static bool
+get_src_info (bfd_vma addr, const char **filename, const char **name,
+             int *line_num)
+{
+  const char *fname = 0, *func_name = 0;
+  int l = 0;
+
+  if (bfd_find_nearest_line (core_bfd, core_text_sect, core_syms,
+                            addr - core_text_sect->vma,
+                            &fname, &func_name, (unsigned int *) &l)
+      && fname && func_name && l)
+    {
+      DBG (AOUTDEBUG, printf ("[get_src_info] 0x%lx -> %s:%d (%s)\n",
+                             (unsigned long) addr, fname, l, func_name));
+      *filename = fname;
+      *name = func_name;
+      *line_num = l;
+      return true;
+    }
+  else
+    {
+      DBG (AOUTDEBUG, printf ("[get_src_info] no info for 0x%lx (%s:%d,%s)\n",
+                             (unsigned long) addr,
+                             fname ? fname : "<unknown>", l,
+                             func_name ? func_name : "<unknown>"));
+      return false;
+    }
+}
+
+static int
+search_mapped_symbol (const void * l, const void * r)
+{
+    return strcmp ((const char *) l, ((const struct function_map *) r)->function_name);
+}
+
+Source_File *first_src_file = 0;
+
+Source_File *
+source_file_lookup_path (const char *path)
+{
+  Source_File *sf;
+
+  for (sf = first_src_file; sf; sf = sf->next)
+    {
+      if (FILENAME_CMP (path, sf->name) == 0)
+       break;
+    }
+
+  if (!sf)
+    {
+      /* Create a new source file descriptor.  */
+      sf = (Source_File *) xmalloc (sizeof (*sf));
+
+      memset (sf, 0, sizeof (*sf));
+
+      sf->name = xstrdup (path);
+      sf->next = first_src_file;
+      first_src_file = sf;
+    }
+
+  return sf;
+}
+
+/* Read in symbol table from core.
+   One symbol per function is entered.  */
+
+static void
+core_create_function_syms (void)
+{
+  int cxxclass;
+  long i;
+  struct function_map * found = NULL;
+  int core_has_func_syms = 0;
+  Sym_Table *symtab = get_symtab_direct ();
+
+  switch (core_bfd->xvec->flavour)
+    {
+    default:
+      break;
+    case bfd_target_coff_flavour:
+    case bfd_target_ecoff_flavour:
+    case bfd_target_xcoff_flavour:
+    case bfd_target_elf_flavour:
+    case bfd_target_som_flavour:
+      core_has_func_syms = 1;
+    }
+
+  /* Pass 1 - determine upper bound on number of function names.  */
+  symtab->len = 0;
+
+  for (i = 0; i < core_num_syms; ++i)
+    {
+      if (!core_sym_class (core_syms[i]))
+       continue;
+
+      /* Don't create a symtab entry for a function that has
+        a mapping to a file, unless it's the first function
+        in the file.  */
+      if (symbol_map_count != 0)
+       {
+         /* Note: some systems (SunOS 5.8) crash if bsearch base argument
+            is NULL.  */
+         found = (struct function_map *) bsearch
+           (core_syms[i]->name, symbol_map, symbol_map_count,
+            sizeof (struct function_map), search_mapped_symbol);
+       }
+      if (found == NULL || found->is_first)
+       ++symtab->len;
+    }
+
+  if (symtab->len == 0)
+    {
+      fprintf (stderr, _("%s: file has no symbols\n"), whoami);
+      done (1);
+    }
+
+  symtab->base = (Sym *) xmalloc (symtab->len * sizeof (Sym));
+
+  /* Pass 2 - create symbols.  */
+  symtab->limit = symtab->base;
+
+  for (i = 0; i < core_num_syms; ++i)
+    {
+      asection *sym_sec;
+
+      cxxclass = core_sym_class (core_syms[i]);
+
+      if (!cxxclass)
+       {
+         DBG (AOUTDEBUG,
+              printf ("[core_create_function_syms] rejecting: 0x%lx %s\n",
+                      (unsigned long) core_syms[i]->value,
+                      core_syms[i]->name));
+         continue;
+       }
+
+      if (symbol_map_count != 0)
+       {
+         /* Note: some systems (SunOS 5.8) crash if bsearch base argument
+            is NULL.  */
+         found = (struct function_map *) bsearch
+           (core_syms[i]->name, symbol_map, symbol_map_count,
+            sizeof (struct function_map), search_mapped_symbol);
+       }
+      if (found && ! found->is_first)
+       continue;
+
+      sym_init (symtab->limit);
+
+      /* Symbol offsets are always section-relative.  */
+      sym_sec = core_syms[i]->section;
+      symtab->limit->addr = core_syms[i]->value;
+      if (sym_sec)
+       symtab->limit->addr += bfd_section_vma (sym_sec);
+
+      if (found)
+       {
+         symtab->limit->name = found->file_name;
+         symtab->limit->mapped = 1;
+       }
+      else
+       {
+         symtab->limit->name = core_syms[i]->name;
+         symtab->limit->mapped = 0;
+       }
+
+      /* Lookup filename and line number, if we can.  */
+      {
+       const char * filename;
+       const char * func_name;
+
+       if (get_src_info (symtab->limit->addr, & filename, & func_name,
+                         & symtab->limit->line_num))
+         {
+           symtab->limit->file = source_file_lookup_path (filename);
+
+           /* FIXME: Checking __osf__ here does not work with a cross
+              gprof.  */
+#ifdef __osf__
+           /* Suppress symbols that are not function names.  This is
+              useful to suppress code-labels and aliases.
+
+              This is known to be useful under DEC's OSF/1.  Under SunOS 4.x,
+              labels do not appear in the symbol table info, so this isn't
+              necessary.  */
+
+           if (strcmp (symtab->limit->name, func_name) != 0)
+             {
+               /* The symbol's address maps to a different name, so
+                  it can't be a function-entry point.  This happens
+                  for labels, for example.  */
+               DBG (AOUTDEBUG,
+                    printf ("[core_create_function_syms: rej %s (maps to %s)\n",
+                            symtab->limit->name, func_name));
+               continue;
+             }
+#endif
+         }
+      }
+
+      symtab->limit->is_func = (!core_has_func_syms
+                              || (core_syms[i]->flags & BSF_FUNCTION) != 0);
+      symtab->limit->is_bb_head = true;
+
+      if (cxxclass == 't')
+       symtab->limit->is_static = true;
+
+      DBG (AOUTDEBUG, printf ("[core_create_function_syms] %ld %s 0x%lx\n",
+                             (long) (symtab->limit - symtab->base),
+                             symtab->limit->name,
+                             (unsigned long) symtab->limit->addr));
+      ++symtab->limit;
+    }
+
+  symtab->len = symtab->limit - symtab->base;
+  symtab_finalize (symtab);
+}
+
+
+/* Initialize the symbol table.  */
+
+void
+symtab_init (void)
+{
+  core_create_function_syms ();
+}
diff --git a/gprofng/src/gmon_hist.cc b/gprofng/src/gmon_hist.cc
new file mode 100644 (file)
index 0000000..e3c7fd8
--- /dev/null
@@ -0,0 +1,490 @@
+/* hist.c  -  Histogram related operations.
+
+   Copyright (C) 1999-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "config.h"
+#include "util.h"
+#include "bfd.h"
+#include "gp-gmon.h"
+
+#include "symtab.h"
+#include "gmon_io.h"
+#include "gmon_out.h"
+#include "hist.h"
+
+extern int print_name_only (Sym * self);
+extern void print_name (Sym * self);
+
+#include <math.h>
+#define _(String) (String)
+
+typedef unsigned char UNIT[2]; /* unit of profiling */
+#define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
+
+static void scale_and_align_entries (void);
+
+/* Given a range of addresses for a symbol, find a histogram record
+   that intersects with this range, and clips the range to that
+   histogram record, modifying *P_LOWPC and *P_HIGHPC.
+
+   If no intersection is found, *P_LOWPC and *P_HIGHPC will be set to
+   one unspecified value.  If more that one intersection is found,
+   an error is emitted.  */
+static void hist_clip_symbol_address (bfd_vma *p_lowpc, bfd_vma *p_highpc);
+
+
+/* Declarations of automatically generated functions to output blurbs.  */
+extern void flat_blurb (FILE * fp);
+
+static histogram *find_histogram (bfd_vma lowpc, bfd_vma highpc);
+static histogram *find_histogram_for_pc (bfd_vma pc);
+
+static histogram * histograms;
+static unsigned num_histograms;
+
+/* Scale factor converting samples to pc values:
+   each sample covers HIST_SCALE bytes.  */
+static double hist_scale;
+static char hist_dimension[16] = "seconds";
+static char hist_dimension_abbrev = 's';
+
+static double total_time;      /* Total time for all routines.  */
+
+/* Table of SI prefixes for powers of 10 (used to automatically
+   scale some of the values in the flat profile).  */
+const struct
+  {
+    char prefix;
+    double scale;
+  }
+SItab[] =
+{
+  { 'T', 1e-12 },                              /* tera */
+  { 'G', 1e-09 },                              /* giga */
+  { 'M', 1e-06 },                              /* mega */
+  { 'K', 1e-03 },                              /* kilo */
+  { ' ', 1e-00 },
+  { 'm', 1e+03 },                              /* milli */
+  { 'u', 1e+06 },                              /* micro */
+  { 'n', 1e+09 },                              /* nano */
+  { 'p', 1e+12 },                              /* pico */
+  { 'f', 1e+15 },                              /* femto */
+  { 'a', 1e+18 }                               /* ato */
+};
+
+/* Reads just the header part of histogram record into
+   *RECORD from IFP.  FILENAME is the name of IFP and
+   is provided for formatting error messages only.
+
+   If FIRST is non-zero, sets global variables HZ, HIST_DIMENSION,
+   HIST_DIMENSION_ABBREV, HIST_SCALE.  If FIRST is zero, checks
+   that the new histogram is compatible with already-set values
+   of those variables and emits an error if that's not so.  */
+static void
+read_histogram_header (histogram *record,
+                      FILE *ifp, const char *filename,
+                      int first)
+{
+  unsigned int profrate;
+  char n_hist_dimension[15];
+  char n_hist_dimension_abbrev;
+  double n_hist_scale;
+
+  if (gmon_io_read_vma (ifp, &record->lowpc)
+      || gmon_io_read_vma (ifp, &record->highpc)
+      || gmon_io_read_32 (ifp, &record->num_bins)
+      || gmon_io_read_32 (ifp, &profrate)
+      || gmon_io_read (ifp, n_hist_dimension, 15)
+      || gmon_io_read (ifp, &n_hist_dimension_abbrev, 1))
+    {
+      fprintf (stderr, "%s: %s: unexpected end of file\n",
+              whoami, filename);
+
+      done (1);
+    }
+
+  n_hist_scale = (double)(record->highpc - record->lowpc) / sizeof (UNIT)
+    / record->num_bins;
+
+  if (first)
+    {
+      /* We don't try to verify profrate is the same for all histogram
+        records.  If we have two histogram records for the same
+        address range and profiling samples is done as often
+        as possible as opposed on timer, then the actual profrate will
+        be slightly different.  Most of the time the difference does not
+        matter and insisting that profiling rate is exactly the same
+        will only create inconvenient.  */
+      hz = profrate;
+      memcpy (hist_dimension, n_hist_dimension, 15);
+      hist_dimension_abbrev = n_hist_dimension_abbrev;
+      hist_scale = n_hist_scale;
+    }
+  else
+    {
+      if (strncmp (n_hist_dimension, hist_dimension, 15) != 0)
+       {
+         fprintf (stderr,
+                  "%s: dimension unit changed between histogram records\n"
+                    "%s: from '%s'\n"
+                    "%s: to '%s'\n",
+                  whoami, whoami, hist_dimension, whoami, n_hist_dimension);
+         done (1);
+       }
+
+      if (n_hist_dimension_abbrev != hist_dimension_abbrev)
+       {
+         fprintf (stderr,
+                  _("%s: dimension abbreviation changed between histogram records\n"
+                    "%s: from '%c'\n"
+                    "%s: to '%c'\n"),
+                  whoami, whoami, hist_dimension_abbrev, whoami, n_hist_dimension_abbrev);
+         done (1);
+       }
+
+      /* The only reason we require the same scale for histograms is that
+        there's code (notably printing code), that prints units,
+        and it would be very confusing to have one unit mean different
+        things for different functions.  */
+      if (fabs (hist_scale - n_hist_scale) > 0.000001)
+       {
+         fprintf (stderr,
+                  _("%s: different scales in histogram records: %f != %f\n"),
+                  whoami, hist_scale, n_hist_scale);
+         done (1);
+       }
+    }
+}
+
+/* Read the histogram from file IFP.  FILENAME is the name of IFP and
+   is provided for formatting error messages only.  */
+
+void
+hist_read_rec (FILE * ifp, const char *filename)
+{
+  bfd_vma lowpc, highpc;
+  histogram n_record;
+  histogram *record, *existing_record;
+  unsigned i;
+
+  /* 1. Read the header and see if there's existing record for the
+     same address range and that there are no overlapping records.  */
+  read_histogram_header (&n_record, ifp, filename, num_histograms == 0);
+
+  existing_record = find_histogram (n_record.lowpc, n_record.highpc);
+  if (existing_record)
+    {
+      record = existing_record;
+    }
+  else
+    {
+      /* If this record overlaps, but does not completely match an existing
+        record, it's an error.  */
+      lowpc = n_record.lowpc;
+      highpc = n_record.highpc;
+      hist_clip_symbol_address (&lowpc, &highpc);
+      if (lowpc != highpc)
+       {
+         fprintf (stderr,
+                  _("%s: overlapping histogram records\n"),
+                  whoami);
+         done (1);
+       }
+
+      /* This is new record.  Add it to global array and allocate space for
+        the samples.  */
+      histograms = (struct histogram *)
+          xrealloc (histograms, sizeof (histogram) * (num_histograms + 1));
+      memcpy (histograms + num_histograms,
+             &n_record, sizeof (histogram));
+      record = &histograms[num_histograms];
+      ++num_histograms;
+
+      record->sample = (int *) xmalloc (record->num_bins
+                                       * sizeof (record->sample[0]));
+      memset (record->sample, 0, record->num_bins * sizeof (record->sample[0]));
+    }
+
+  /* 2. We have either a new record (with zeroed histogram data), or an existing
+     record with some data in the histogram already.  Read new data into the
+     record, adding hit counts.  */
+
+  DBG (SAMPLEDEBUG,
+       printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %u\n",
+              (unsigned long) record->lowpc, (unsigned long) record->highpc,
+               record->num_bins));
+
+  for (i = 0; i < record->num_bins; ++i)
+    {
+      UNIT count;
+      if (fread (&count[0], sizeof (count), 1, ifp) != 1)
+       {
+         fprintf (stderr,
+                 _("%s: %s: unexpected EOF after reading %u of %u samples\n"),
+                  whoami, filename, i, record->num_bins);
+         done (1);
+       }
+      record->sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
+      DBG (SAMPLEDEBUG,
+          printf ("[hist_read_rec] 0x%lx: %u\n",
+                  (unsigned long) (record->lowpc
+                                    + i * (record->highpc - record->lowpc)
+                                    / record->num_bins),
+                  record->sample[i]));
+    }
+}
+
+/* Calculate scaled entry point addresses (to save time in
+   hist_assign_samples), and, on architectures that have procedure
+   entry masks at the start of a function, possibly push the scaled
+   entry points over the procedure entry mask, if it turns out that
+   the entry point is in one bin and the code for a routine is in the
+   next bin.  */
+
+static void
+scale_and_align_entries (void)
+{
+  Sym *sym;
+  bfd_vma bin_of_entry;
+  bfd_vma bin_of_code;
+  Sym_Table *symtab = get_symtab ();
+
+  for (sym = symtab->base; sym < symtab->limit; sym++)
+    {
+      histogram *r = find_histogram_for_pc (sym->addr);
+
+      sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
+
+      if (r)
+       {
+         bin_of_entry = (sym->hist.scaled_addr - r->lowpc) / hist_scale;
+         bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - r->lowpc)
+                    / hist_scale);
+         if (bin_of_entry < bin_of_code)
+           {
+             DBG (SAMPLEDEBUG,
+                  printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
+                          (unsigned long) sym->hist.scaled_addr,
+                          (unsigned long) (sym->hist.scaled_addr
+                                           + UNITS_TO_CODE)));
+             sym->hist.scaled_addr += UNITS_TO_CODE;
+           }
+       }
+    }
+}
+
+
+/* Assign samples to the symbol to which they belong.
+
+   Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
+   which may overlap one more symbol address ranges.  If a symbol
+   overlaps with the bin's address range by O percent, then O percent
+   of the bin's count is credited to that symbol.
+
+   There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
+   with respect to the symbol's address range [SYM_LOW_PC,
+   SYM_HIGH_PC) as shown in the following diagram.  OVERLAP computes
+   the distance (in UNITs) between the arrows, the fraction of the
+   sample that is to be credited to the symbol which starts at
+   SYM_LOW_PC.
+
+         sym_low_pc                                      sym_high_pc
+              |                                               |
+              v                                               v
+
+              +-----------------------------------------------+
+              |                                               |
+         |  ->|    |<-         ->|         |<-         ->|    |<-  |
+         |         |             |         |             |         |
+         +---------+             +---------+             +---------+
+
+         ^         ^             ^         ^             ^         ^
+         |         |             |         |             |         |
+     bin_low_pc bin_high_pc  bin_low_pc bin_high_pc  bin_low_pc bin_high_pc
+
+   For the VAX we assert that samples will never fall in the first two
+   bytes of any routine, since that is the entry mask, thus we call
+   scale_and_align_entries() to adjust the entry points if the entry
+   mask falls in one bin but the code for the routine doesn't start
+   until the next bin.  In conjunction with the alignment of routine
+   addresses, this should allow us to have only one sample for every
+   four bytes of text space and never have any overlap (the two end
+   cases, above).  */
+
+static void
+hist_assign_samples_1 (histogram *r)
+{
+  bfd_vma bin_low_pc, bin_high_pc;
+  bfd_vma sym_low_pc, sym_high_pc;
+  bfd_vma overlap;
+  unsigned int bin_count;
+  unsigned int i, j, k;
+  double count_time, credit;
+  Sym_Table *symtab = get_symtab ();
+
+  bfd_vma lowpc = r->lowpc / sizeof (UNIT);
+
+  /* Iterate over all sample bins.  */
+  for (i = 0, k = 1; i < r->num_bins; ++i)
+    {
+      bin_count = r->sample[i];
+      if (! bin_count)
+       continue;
+
+      bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
+      bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
+      count_time = bin_count;
+
+      DBG (SAMPLEDEBUG,
+          printf (
+      "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%u\n",
+                   (unsigned long) (sizeof (UNIT) * bin_low_pc),
+                   (unsigned long) (sizeof (UNIT) * bin_high_pc),
+                   bin_count));
+      total_time += count_time;
+
+      /* Credit all symbols that are covered by bin I.
+
+         PR gprof/13325: Make sure that K does not get decremented
+        and J will never be less than 0.  */
+      for (j = k - 1; j < symtab->len; k = ++j)
+       {
+         sym_low_pc = symtab->base[j].hist.scaled_addr;
+         sym_high_pc = symtab->base[j + 1].hist.scaled_addr;
+
+         /* If high end of bin is below entry address,
+            go for next bin.  */
+         if (bin_high_pc < sym_low_pc)
+           break;
+
+         /* If low end of bin is above high end of symbol,
+            go for next symbol.  */
+         if (bin_low_pc >= sym_high_pc)
+           continue;
+
+         overlap =
+           MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
+         if (overlap > 0)
+           {
+             DBG (SAMPLEDEBUG,
+                  printf (
+              "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
+                          (unsigned long) symtab->base[j].addr,
+                          (unsigned long) (sizeof (UNIT) * sym_high_pc),
+                          symtab->base[j].name, overlap * count_time / hist_scale,
+                          (long) overlap));
+
+             credit = overlap * count_time / hist_scale;
+             symtab->base[j].hist.time += credit;
+           }
+       }
+    }
+
+  DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
+                           total_time));
+}
+
+/* Calls 'hist_assign_samples_1' for all histogram records read so far. */
+void
+hist_assign_samples (void)
+{
+  unsigned i;
+
+  scale_and_align_entries ();
+
+  for (i = 0; i < num_histograms; ++i)
+    hist_assign_samples_1 (&histograms[i]);
+
+}
+
+#if ! defined(min)
+#define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+#if ! defined(max)
+#define max(a,b) (((a)>(b)) ? (a) : (b))
+#endif
+
+void
+hist_clip_symbol_address (bfd_vma *p_lowpc, bfd_vma *p_highpc)
+{
+  unsigned i;
+  int found = 0;
+
+  if (num_histograms == 0)
+    {
+      *p_highpc = *p_lowpc;
+      return;
+    }
+
+  for (i = 0; i < num_histograms; ++i)
+    {
+      bfd_vma common_low, common_high;
+      common_low = max (histograms[i].lowpc, *p_lowpc);
+      common_high = min (histograms[i].highpc, *p_highpc);
+
+      if (common_low < common_high)
+       {
+         if (found)
+           {
+             fprintf (stderr,
+                      _("%s: found a symbol that covers "
+                        "several histogram records"),
+                        whoami);
+             done (1);
+           }
+
+         found = 1;
+         *p_lowpc = common_low;
+         *p_highpc = common_high;
+       }
+    }
+
+  if (!found)
+    *p_highpc = *p_lowpc;
+}
+
+/* Find and return existing histogram record having the same lowpc and
+   highpc as passed via the parameters.  Return NULL if nothing is found.
+   The return value is valid until any new histogram is read.  */
+static histogram *
+find_histogram (bfd_vma lowpc, bfd_vma highpc)
+{
+  unsigned i;
+  for (i = 0; i < num_histograms; ++i)
+    {
+      if (histograms[i].lowpc == lowpc && histograms[i].highpc == highpc)
+       return &histograms[i];
+    }
+  return 0;
+}
+
+/* Given a PC, return histogram record which address range include this PC.
+   Return NULL if there's no such record.  */
+static histogram *
+find_histogram_for_pc (bfd_vma pc)
+{
+  unsigned i;
+  for (i = 0; i < num_histograms; ++i)
+    {
+      if (histograms[i].lowpc <= pc && pc < histograms[i].highpc)
+       return &histograms[i];
+    }
+  return 0;
+}
diff --git a/gprofng/src/gmon_io.cc b/gprofng/src/gmon_io.cc
new file mode 100644 (file)
index 0000000..6860d2e
--- /dev/null
@@ -0,0 +1,257 @@
+/* gmon_io.c - Input and output from/to gmon.out files.
+
+   Copyright (C) 1999-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "util.h"
+#include "bfd.h"
+
+#include "fopen-bin.h" /* maybe better sysdep.h ? */
+
+#include "gp-gmon.h"
+
+#include "binary-io.h"
+#include "symtab.h"
+#include "cg_arcs.h"
+#include "basic_blocks.h"
+#include "call_graph.h"
+#include "gmon_io.h"
+#include "gmon_out.h"
+#include "gmon.h"              /* Fetch header for old format.  */
+#include "hist.h"
+
+enum gmon_ptr_size {
+  ptr_32bit,
+  ptr_64bit,
+  ptr_unknown
+};
+
+enum gmon_ptr_signedness {
+  ptr_signed,
+  ptr_unsigned
+};
+
+static enum gmon_ptr_size gmon_get_ptr_size (void);
+static enum gmon_ptr_signedness gmon_get_ptr_signedness (void);
+
+static int gmon_io_read_64 (FILE *, uint64_t *);
+
+int gmon_input = 0;
+int gmon_file_version = 0;     /* 0 == old (non-versioned) file format.  */
+
+static enum gmon_ptr_size
+gmon_get_ptr_size (void)
+{
+  int size;
+
+  /* Pick best size for pointers.  Start with the ELF size, and if not
+     elf go with the architecture's address size.  */
+  size = bfd_get_arch_size (core_bfd);
+  if (size == -1)
+    size = bfd_arch_bits_per_address (core_bfd);
+
+  switch (size)
+    {
+    case 32:
+      return ptr_32bit;
+
+    case 64:
+      return ptr_64bit;
+
+    default:
+      fprintf (stderr, "%s: address size has unexpected value of %u\n",
+              whoami, size);
+      done (1);
+    }
+  return ptr_unknown;
+}
+
+static enum gmon_ptr_signedness
+gmon_get_ptr_signedness (void)
+{
+  int sext;
+
+  /* Figure out whether to sign extend.  If BFD doesn't know, assume no.  */
+  sext = bfd_get_sign_extend_vma (core_bfd);
+  if (sext == -1)
+    return ptr_unsigned;
+  return (sext ? ptr_signed : ptr_unsigned);
+}
+
+int
+gmon_io_read_32 (FILE *ifp, unsigned int *valp)
+{
+  char buf[4];
+
+  if (fread (buf, 1, 4, ifp) != 4)
+    return 1;
+  *valp = bfd_get_32 (core_bfd, buf);
+  return 0;
+}
+
+static int
+gmon_io_read_64 (FILE *ifp, uint64_t *valp)
+{
+  char buf[8];
+
+  if (fread (buf, 1, 8, ifp) != 8)
+    return 1;
+  *valp = bfd_get_64 (core_bfd, buf);
+  return 0;
+}
+
+int
+gmon_io_read_vma (FILE *ifp, bfd_vma *valp)
+{
+  unsigned int val32;
+  uint64_t val64;
+
+  switch (gmon_get_ptr_size ())
+    {
+    case ptr_32bit:
+      if (gmon_io_read_32 (ifp, &val32))
+       return 1;
+      if (gmon_get_ptr_signedness () == ptr_signed)
+       *valp = (int) val32;
+      else
+       *valp = val32;
+      break;
+
+    case ptr_64bit:
+      if (gmon_io_read_64 (ifp, &val64))
+       return 1;
+      if (gmon_get_ptr_signedness () == ptr_signed)
+       *valp = (int64_t) val64;
+      else
+       *valp = val64;
+      break;
+    }
+  return 0;
+}
+
+int
+gmon_io_read (FILE *ifp, char *buf, size_t n)
+{
+  if (fread (buf, 1, n, ifp) != n)
+    return 1;
+  return 0;
+}
+
+int
+gmon_out_read (const char *filename, File_Format file_format)
+{
+  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;
+      SET_BINARY (fileno (stdin));
+    }
+  else
+    {
+      ifp = fopen (filename, FOPEN_RB);
+
+      if (!ifp)
+       {
+         perror (filename);
+         return -1;
+       }
+    }
+
+  if (fread (&ghdr, sizeof (struct gmon_hdr), 1, ifp) != 1)
+    {
+      fprintf (stderr, "%s: file too short to be a gmon file\n",
+              filename);
+      return -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);
+         return -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);
+         return -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, false);
+             break;
+
+           default:
+             fprintf (stderr,
+                      "%s: %s: found bad tag %d (file corrupted?)\n",
+                      whoami, filename, tag);
+             return -1;
+           }
+       }
+    }
+  else
+    {
+      fprintf (stderr, "%s: don't know how to deal with file format %d\n",
+              whoami, file_format);
+      return -1;
+    }
+
+  if (ifp != stdin)
+    fclose (ifp);
+  return 0;
+}
diff --git a/gprofng/src/gmon_io.h b/gprofng/src/gmon_io.h
new file mode 100644 (file)
index 0000000..d2fe854
--- /dev/null
@@ -0,0 +1,46 @@
+/* gmon_io.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef gmon_io_h
+#define gmon_io_h
+
+#include <stdio.h>
+
+#define INPUT_HISTOGRAM        (1 << 0)
+#define INPUT_CALL_GRAPH       (1 << 1)
+#define INPUT_BB_COUNTS        (1 << 2)
+
+typedef enum
+  {
+    FF_AUTO = 0, FF_MAGIC, FF_BSD, FF_BSD44, FF_PROF
+  }
+File_Format;
+
+extern int gmon_input;          /* What input did we see?  */
+extern int gmon_file_version;   /* File version are we dealing with.  */
+
+extern int gmon_io_read_vma (FILE *ifp, bfd_vma *valp);
+extern int gmon_io_read_32 (FILE *ifp, unsigned int *valp);
+extern int gmon_io_read (FILE *ifp, char *buf, size_t n);
+
+extern int gmon_out_read   (const char *, File_Format);
+
+#endif /* gmon_io_h */
diff --git a/gprofng/src/gmon_out.h b/gprofng/src/gmon_out.h
new file mode 100644 (file)
index 0000000..95cedf8
--- /dev/null
@@ -0,0 +1,46 @@
+/* gmon_out.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+/* A gmon.out file consists of a header (defined by gmon_hdr) followed
+   by a sequence of records.  Each record starts with a one-byte tag
+   identifying the type of records, followed by records specific data.  */
+#ifndef gmon_out_h
+#define gmon_out_h
+
+#define        GMON_MAGIC      "gmon"  /* magic cookie */
+#define GMON_VERSION   1       /* version number */
+
+/* Raw header as it appears on file (without padding).  */
+struct gmon_hdr
+  {
+    char cookie[4];
+    char version[4];
+    char spare[3 * 4];
+  };
+
+/* Types of records in this file.  */
+typedef enum
+  {
+    GMON_TAG_TIME_HIST = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2
+  }
+GMON_Record_Tag;
+
+#endif /* gmon_out_h */
diff --git a/gprofng/src/gmon_symtab.cc b/gprofng/src/gmon_symtab.cc
new file mode 100644 (file)
index 0000000..edf82de
--- /dev/null
@@ -0,0 +1,259 @@
+/* symtab.c
+
+   Copyright (C) 1999-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "config.h"
+#include "util.h"
+#include "bfd.h"
+#include "gp-gmon.h"
+
+#include "symtab.h"
+#include "cg_arcs.h"
+
+static int cmp_addr (const void *, const void *);
+
+/* The symbol table.  */
+static Sym_Table symtab;
+
+/* Return the pointer to the symbol table.  */
+
+Sym_Table *
+get_symtab_direct (void)
+{
+  return &symtab;
+}
+
+/* Return the pointer to the symbol table and initialize it if it isn't
+   initialized yet.  */
+
+Sym_Table *
+get_symtab (void)
+{
+  static Sym_Table *symtab_p;
+  if (!symtab_p)
+    {
+      symtab_init ();
+
+      symtab_p = &symtab;
+    }
+  return symtab_p;
+}
+
+/* Initialize a symbol (so it's empty).  */
+
+void
+sym_init (Sym *sym)
+{
+  memset (sym, 0, sizeof (*sym));
+
+  /* It is not safe to assume that a binary zero corresponds
+     to a floating-point 0.0, so initialize floats explicitly.  */
+  sym->hist.time = 0.0;
+  sym->cg.child_time = 0.0;
+  sym->cg.prop.fract = 0.0;
+  sym->cg.prop.self = 0.0;
+  sym->cg.prop.child = 0.0;
+}
+
+
+/* Compare the function entry-point of two symbols and return <0, =0,
+   or >0 depending on whether the left value is smaller than, equal
+   to, or greater than the right value.  If two symbols are equal
+   but one has is_func set and the other doesn't, we make the
+   non-function symbol one "bigger" so that the function symbol will
+   survive duplicate removal.  Finally, if both symbols have the
+   same is_func value, we discriminate against is_static such that
+   the global symbol survives.  */
+
+static int
+cmp_addr (const void *lp, const void *rp)
+{
+  const Sym *left = (const Sym *) lp;
+  const Sym *right = (const Sym *) rp;
+
+  if (left->addr > right->addr)
+    return 1;
+  else if (left->addr < right->addr)
+    return -1;
+
+  if (left->is_func != right->is_func)
+    return right->is_func - left->is_func;
+
+  return left->is_static - right->is_static;
+}
+
+
+void
+symtab_finalize (Sym_Table *tab)
+{
+  Sym *src, *dst;
+  bfd_vma prev_addr;
+
+  if (!tab->len)
+    return;
+
+  /* Sort symbol table in order of increasing function addresses.  */
+  qsort (tab->base, tab->len, sizeof (Sym), cmp_addr);
+
+  /* Remove duplicate entries to speed-up later processing and
+     set end_addr if its not set yet.  */
+  prev_addr = tab->base[0].addr - 1;
+
+  for (src = dst = tab->base; src < tab->limit; ++src)
+    {
+      if (src->addr == prev_addr)
+       {
+         /* If same address, favor global symbol over static one,
+            then function over line number.  If both symbols are
+            either static or global and either function or line, check
+            whether one has name beginning with underscore while
+            the other doesn't.  In such cases, keep sym without
+            underscore.  This takes cares of compiler generated
+            symbols (such as __gnu_compiled, __c89_used, etc.).  */
+         if ((!src->is_static && dst[-1].is_static)
+             || ((src->is_static == dst[-1].is_static)
+                 && ((src->is_func && !dst[-1].is_func)
+                     || ((src->is_func == dst[-1].is_func)
+                         && ((src->name[0] != '_' && dst[-1].name[0] == '_')
+                             || (src->name[0] == '_' && dst[-1].name[0] == '_'
+                                 && src->name[1] != '_'
+                                 && dst[-1].name[1] == '_'))))))
+           {
+             DBG (AOUTDEBUG | IDDEBUG,
+                  printf ("[symtab_finalize] favor %s@%c%c over %s@%c%c",
+                          src->name, src->is_static ? 't' : 'T',
+                          src->is_func ? 'F' : 'f',
+                          dst[-1].name, dst[-1].is_static ? 't' : 'T',
+                          dst[-1].is_func ? 'F' : 'f');
+                  printf (" (addr=%lx)\n", (unsigned long) src->addr));
+
+             dst[-1] = *src;
+           }
+         else
+           {
+             DBG (AOUTDEBUG | IDDEBUG,
+                  printf ("[symtab_finalize] favor %s@%c%c over %s@%c%c",
+                          dst[-1].name, dst[-1].is_static ? 't' : 'T',
+                          dst[-1].is_func ? 'F' : 'f',
+                          src->name, src->is_static ? 't' : 'T',
+                          src->is_func ? 'F' : 'f');
+                  printf (" (addr=%lx)\n", (unsigned long) src->addr));
+           }
+       }
+      else
+       {
+         if (dst > tab->base && dst[-1].end_addr == 0)
+           dst[-1].end_addr = src->addr - 1;
+
+         /* Retain sym only if it has a non-empty address range.  */
+         if (!src->end_addr || src->addr <= src->end_addr)
+           {
+             *dst = *src;
+             dst++;
+             prev_addr = src->addr;
+           }
+       }
+    }
+
+  if (tab->len > 0 && dst > tab->base && dst[-1].end_addr == 0)
+    dst[-1].end_addr
+      = core_text_sect->vma + bfd_section_size (core_text_sect) - 1;
+
+  DBG (AOUTDEBUG | IDDEBUG,
+       printf ("[symtab_finalize]: removed %d duplicate entries\n",
+              tab->len - (int) (dst - tab->base)));
+
+  tab->limit = dst;
+  tab->len = tab->limit - tab->base;
+
+  DBG (AOUTDEBUG | IDDEBUG,
+       unsigned int j;
+
+       for (j = 0; j < tab->len; ++j)
+        {
+          printf ("[symtab_finalize] 0x%lx-0x%lx\t%s\n",
+                  (unsigned long) tab->base[j].addr,
+                  (unsigned long) tab->base[j].end_addr,
+                  tab->base[j].name);
+        }
+  );
+}
+
+/* Look up an address in the symbol-table that is sorted by address.
+   If address does not hit any symbol, 0 is returned.  */
+Sym *
+sym_lookup (Sym_Table *sym_tab, bfd_vma address)
+{
+  long low, high;
+  long mid = -1;
+  Sym *sym;
+#ifdef DEBUG
+  int probes = 0;
+#endif /* DEBUG */
+
+  if (!sym_tab->len)
+    return 0;
+
+  sym = sym_tab->base;
+  for (low = 0, high = sym_tab->len - 1; low != high;)
+    {
+      DBG (LOOKUPDEBUG, ++probes);
+      mid = (high + low) / 2;
+
+      if (sym[mid].addr <= address && sym[mid + 1].addr > address)
+       {
+         if (address > sym[mid].end_addr)
+           {
+             /* Address falls into gap between
+                sym[mid] and sym[mid + 1].  */
+             return 0;
+           }
+         else
+           {
+             DBG (LOOKUPDEBUG,
+                  printf ("[sym_lookup] %d probes (symtab->len=%u)\n",
+                          probes, sym_tab->len - 1));
+             return &sym[mid];
+           }
+       }
+
+      if (sym[mid].addr > address)
+       high = mid;
+      else
+       low = mid + 1;
+    }
+
+  if (sym[mid + 1].addr <= address)
+    {
+      if (address > sym[mid + 1].end_addr)
+       {
+         /* Address is beyond end of sym[mid + 1].  */
+         return 0;
+       }
+      else
+       {
+         DBG (LOOKUPDEBUG, printf ("[sym_lookup] %d (%u) probes, fall off\n",
+                                   probes, sym_tab->len - 1));
+         return &sym[mid + 1];
+       }
+    }
+
+  return 0;
+}
diff --git a/gprofng/src/gp-gmon.cc b/gprofng/src/gp-gmon.cc
new file mode 100644 (file)
index 0000000..07739b7
--- /dev/null
@@ -0,0 +1,772 @@
+/* Copyright (C) 2025 Free Software Foundation, Inc.
+   Contributed by Oracle.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include "config.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include <fstream>
+#include <libgen.h>
+#include <sys/mman.h>
+
+#include "util.h"
+#include "DbeApplication.h"
+
+#include "bfd.h"
+#include "gmon_io.h"
+
+#include "gp-gmon.h"
+#include "corefile.h"
+#include "data_pckts.h"
+#include "call_graph.h"
+#include "hist.h"
+
+#include "symtab.h"
+#include "cg_arcs.h"
+
+#include "gp-experiment.h"
+#include "collctrl.h"
+#include "util.h"
+#include "gp-defs.h"
+
+class er_gmon : public DbeApplication
+{
+public:
+  er_gmon (int argc, char *argv[]);
+  virtual ~er_gmon ();
+  void start (int argc, char *argv[]);
+
+private:
+  // override methods in base class
+  void usage ();
+  int check_mods (int argc, char *argv[], bool check);
+
+  bool overwrite = false;
+  Coll_Ctrl *cc;
+};
+
+/*
+ * Default options values:
+ */
+#define HZ_WRONG 0
+#define INC_DEPTH (128)
+#define DFN_BUSY -1
+#define DFN_NAN   0
+#define CLOCK_TYPE OPROF_PCKT
+
+/****/
+#define ROOT_UID       801425552975190205ULL
+#define ROOT_UID_INV   92251691606677ULL
+#define ROOT_IDX       13907816567264074199ULL
+#define ROOT_IDX_INV   2075111ULL
+#define UIDTableSize   1048576
+
+#define NATIVE_FRAME_BYTES(nframes) ( ((nframes)+1) * sizeof(long) )
+#define OVERHEAD_BYTES ( 2 * sizeof(long) + 2 * sizeof(Stack_info) )
+#define DEFAULT_MAX_NFRAMES 256
+
+typedef struct
+{
+  Sym *sym;
+} DFN_Stack;
+
+typedef struct CM_Array
+{
+  unsigned int length;          /* in bytes, not including length */
+  void *bytes;
+} CM_Array;
+
+typedef struct ClockPacket
+{ /* clock profiling packet */
+  CM_Packet comm;
+  pthread_t lwp_id;
+  pthread_t thr_id;
+  uint32_t  cpu_id;
+  hrtime_t  tstamp __attribute__ ((packed));
+  uint64_t  frinfo __attribute__ ((packed));
+  int       mstate;     /* kernel microstate */
+  int       nticks;     /* number of ticks in that state */
+} ClockPacket;
+
+const char *whoami;
+long hz = HZ_WRONG;
+
+static unsigned long *pc_array = NULL;
+static char *base_folder;
+static DFN_Stack *dfn_stack = NULL;
+static int dfn_maxdepth = 0;
+static int dfn_depth = 0;
+static int pc_size = 0;
+static int pc_maxdepth = 0;
+static hrtime_t mytime = 0;
+static uint64_t *UIDTable;
+
+// forward declarations
+static uint64_t get_frame_info (const CM_Array *array);
+static uint64_t compute_uid (Frame_packet *frp);
+static void writeBufferToFile (const CM_Packet *pckt,
+                              const char *filename);
+
+static int
+real_main (int argc, char *argv[])
+{
+  er_gmon *src = new er_gmon (argc, argv);
+  src->start (argc, argv);
+  delete src;
+  return 0;
+}
+
+/* Push a symbol into stack, increase size stack if too low.  */
+static void
+pre_visit (Sym *sym)
+{
+  ++dfn_depth;
+
+  if (dfn_depth >= dfn_maxdepth)
+    {
+      dfn_maxdepth += INC_DEPTH;
+      dfn_stack = (DFN_Stack *) xrealloc (dfn_stack,
+                                         dfn_maxdepth * sizeof (*dfn_stack));
+    }
+  dfn_stack[dfn_depth - 1].sym = sym;
+  // Mark the input sym as busy
+  sym->cg.top_order = DFN_BUSY;
+
+  /* Mimic time.  */
+  DBG (LOOKUPDEBUG,
+       printf (">>> Run for:%s ticks:%lf\n", sym->name, sym->hist.time));
+  DBG (LOOKUPDEBUG, printf (">>> Call chain:"));
+  pc_size = 0;
+  for (int i = (dfn_depth - 1); i >= 0; i--)
+    {
+      Sym *t = dfn_stack[i].sym;
+      ++pc_size;
+      if (pc_size >= pc_maxdepth)
+       {
+         pc_maxdepth += INC_DEPTH;
+         pc_array = (unsigned long *)
+           xrealloc (pc_array, pc_maxdepth * sizeof (unsigned long));
+       }
+      pc_array[pc_size - 1] = t->addr;
+      DBG (LOOKUPDEBUG, printf ("----> %s: 0x%lx (%lf)\n", t->name,
+                               t->addr, t->hist.time));
+    }
+  DBG (LOOKUPDEBUG, printf ("\n"));
+  ClockPacket pckt;
+  memset (&pckt, 0, sizeof (ClockPacket));
+  pckt.comm.type = CLOCK_TYPE;
+  pckt.comm.tsize = sizeof (ClockPacket);
+  pckt.lwp_id = 0xdeadbeef;
+  pckt.thr_id = 0xcafecafe;
+  pckt.cpu_id = 0;
+  pckt.tstamp = mytime++;
+  // Output `data.frameinfo` packet
+  CM_Array array;
+  array.length = pc_size * sizeof (unsigned long);
+  array.bytes = (void *) pc_array;
+
+  pckt.frinfo = get_frame_info (&array);
+
+  pckt.mstate = LMS_LINUX_CPU;
+  int calls = sym->ncalls > 0 ? sym->ncalls : 1;
+  double time = sym->hist.time > 0 ? sym->hist.time : 1;
+  uint64_t nticks = (uint64_t)(time / calls);
+  pckt.nticks = nticks > 0 ? nticks : 1;
+
+  // Output `profile` packet
+  writeBufferToFile ((CM_Packet*) &pckt, SP_PROFILE_FILE);
+}
+
+/* Take the last symbol out of the stack.  */
+static void
+post_visit (Sym *sym)
+{
+  sym->cg.top_order = DFN_NAN;
+  if (dfn_depth)
+    --dfn_depth;
+}
+
+static void
+dfn (Sym *parent)
+{
+  /* Detect cycles.  */
+  if (parent->cg.top_order == DFN_BUSY)
+    {
+      parent->ncalls = 1;
+      pre_visit (parent);
+      return;
+    }
+
+  pre_visit (parent);
+  for (Arc *arc = parent->cg.children; arc; arc = arc->next_child)
+    {
+      dfn (arc->child);
+    }
+  post_visit(parent);
+}
+
+/* Go through all the symbols, start from the top of a call chain.
+   Mimimc a stack trace using a DF algorithm.  */
+
+static int
+cg_traverse_arcs (void)
+{
+  Sym *sym;
+  Sym_Table *symtab = get_symtab ();
+
+  for (sym = symtab->base; sym < symtab->limit; sym++)
+    {
+      /* Start DF algorithm from the top symbol.  */
+      if (sym->cg.parents)
+       continue;
+      /* Skip unused symbols.  */
+      if (!sym->cg.children)
+       continue;
+      dfn (sym);
+    }
+  return pc_size;
+}
+
+static void
+writeBufferToFile (const CM_Packet *pckt,
+                  const char *filename)
+{
+  size_t size = pckt->tsize;
+  size_t asz;
+  char *buffer = (char *) pckt;
+  char *new_file_path;
+  FILE *outFile;
+
+  asz = strlen (base_folder) + strlen (filename) + 1 + 1;
+  new_file_path = (char *) alloca (asz);
+  snprintf (new_file_path, asz, "%s/%s", base_folder, filename);
+
+  // Open the file in binary mode
+  outFile = fopen (new_file_path, "ab");
+  if (!outFile)
+    {
+      perror ("Fopen failed");
+      exit (1);
+    }
+
+  // Write the buffer to the file
+  fwrite (buffer, size, 1, outFile);
+
+  fclose (outFile);
+}
+
+static uint64_t
+compute_uid (Frame_packet *frp)
+{
+  uint64_t idxs[LAST_INFO];
+  uint64_t uid = ROOT_UID;
+  uint64_t idx = ROOT_IDX;
+
+  Common_info *cinfo = (Common_info*) ((char*) frp + frp->hsize);
+  char *end = (char*) frp + frp->tsize;
+  for (;;)
+    {
+      if ((char*) cinfo >= end || cinfo->hsize == 0 ||
+         (char*) cinfo + cinfo->hsize > end)
+       break;
+
+      /* Start with a different value to avoid matching with uid */
+      uint64_t uidt = 1;
+      uint64_t idxt = 1;
+      long *ptr = (long*) ((char*) cinfo + cinfo->hsize);
+      long *bnd = (long*) ((char*) cinfo + sizeof (Common_info));
+      DBG (SAMPLEDEBUG,
+          printf( "compute_uid: Cnt=%ld: ", (long) cinfo->hsize));
+      while (ptr > bnd)
+       {
+         long val = *(--ptr);
+         DBG (SAMPLEDEBUG,
+              printf ("0x%8.8llx ", (unsigned long long) val));
+         uidt = (uidt + val) * ROOT_UID;
+         idxt = (idxt + val) * ROOT_IDX;
+         uid = (uid + val) * ROOT_UID;
+         idx = (idx + val) * ROOT_IDX;
+       }
+      if (cinfo->kind == STACK_INFO)
+       {
+         cinfo->uid = uidt;
+         idxs[cinfo->kind] = idxt;
+       }
+      cinfo = (Common_info*) ((char*) cinfo + cinfo->hsize);
+    }
+  DBG (SAMPLEDEBUG, printf ("\n"));
+
+  /* Check if we have already recorded that uid.
+   * The following fragment contains benign data races.
+   * It's important, though, that all reads from UIDTable
+   * happen before writes.
+   */
+  int found1 = 0;
+  int idx1 = (int) ((idx >> 44) % UIDTableSize);
+  if (UIDTable[idx1] == uid)
+    found1 = 1;
+  int found2 = 0;
+  int idx2 = (int) ((idx >> 24) % UIDTableSize);
+  if (UIDTable[idx2] == uid)
+    found2 = 1;
+  int found3 = 0;
+  int idx3 = (int) ((idx >> 4) % UIDTableSize);
+  if (UIDTable[idx3] == uid)
+    found3 = 1;
+  if (!found1)
+    UIDTable[idx1] = uid;
+  if (!found2)
+    UIDTable[idx2] = uid;
+  if (!found3)
+    UIDTable[idx3] = uid;
+
+  if (found1 || found2 || found3)
+    {
+      return uid;
+    }
+  frp->uid = uid;
+
+  /* Compress info's */
+  cinfo = (Common_info*) ((char*) frp + frp->hsize);
+  for (;;)
+    {
+      if ((char*) cinfo >= end || cinfo->hsize == 0 ||
+         (char*) cinfo + cinfo->hsize > end)
+       break;
+      if (cinfo->kind == STACK_INFO || cinfo->kind == JAVA_INFO)
+       {
+         long *ptr = (long*) ((char*) cinfo + sizeof (Common_info));
+         long *bnd = (long*) ((char*) cinfo + cinfo->hsize);
+         uint64_t uidt = cinfo->uid;
+         uint64_t idxt = idxs[cinfo->kind];
+         int found = 0;
+         int first = 1;
+         while (ptr < bnd - 1)
+           {
+             int idx1 = (int) ((idxt >> 44) % UIDTableSize);
+             if (UIDTable[idx1] == uidt)
+               {
+                 found = 1;
+                 break;
+               }
+             else if (first)
+               {
+                 first = 0;
+                 UIDTable[idx1] = uidt;
+               }
+             long val = *ptr++;
+             uidt = uidt * ROOT_UID_INV - val;
+             idxt = idxt * ROOT_IDX_INV - val;
+           }
+         if (found)
+           {
+             char *d = (char*) ptr;
+             char *s = (char*) bnd;
+             if (!first)
+               {
+                 int i;
+                 for (i = 0; i < (int) sizeof (uidt); i++)
+                   {
+                     *d++ = (char) uidt;
+                     uidt = uidt >> 8;
+                   }
+               }
+             int delta = s - d;
+             while (s < end)
+               *d++ = *s++;
+             cinfo->kind |= COMPRESSED_INFO;
+             cinfo->hsize -= delta;
+             frp->tsize -= delta;
+             end -= delta;
+           }
+       }
+      cinfo = (Common_info*) ((char*) cinfo + cinfo->hsize);
+    }
+  writeBufferToFile ((CM_Packet*) frp, "data.frameinfo");
+  return uid;
+}
+
+static uint64_t
+get_frame_info (const CM_Array *array)
+{
+  int max_native_nframes = DEFAULT_MAX_NFRAMES;
+
+  if (array == NULL || array->length <= 0)
+    return 0;
+
+  int max_frame_size = OVERHEAD_BYTES + NATIVE_FRAME_BYTES (max_native_nframes);
+  Frame_packet *frpckt = (Frame_packet *) alloca (sizeof (Frame_packet) + max_frame_size);
+  frpckt->type = FRAME_PCKT;
+  frpckt->hsize = sizeof (Frame_packet);
+
+  char *d = (char*) (frpckt + 1);
+  int size = max_frame_size;
+
+  /* create a stack image from user data */
+  Stack_info *sinfo = (Stack_info*) d;
+  int sz = sizeof (Stack_info);
+  d += sz;
+  size -= sz;
+  sz = array->length;
+  if (sz > size)
+    sz = size;  // YXXX should we mark this with truncation frame?
+  memcpy (d, array->bytes, sz);
+  d += sz;
+  size -= sz;
+  sinfo->kind = STACK_INFO;
+  sinfo->hsize = (d - (char*) sinfo);
+
+  /* Compute the total size */
+  frpckt->tsize = d - (char*) frpckt;
+  return compute_uid (frpckt);
+}
+
+/* Generate the log.xml file.  */
+static void
+gen_gmon_log (void)
+{
+  FILE *logx;
+  char *new_file_path;
+  size_t asz = strlen (base_folder) + strlen (SP_LOG_FILE) + 1 + 1;
+
+  new_file_path = (char *) alloca (asz);
+  snprintf (new_file_path, asz, "%s/%s", base_folder, SP_LOG_FILE);
+
+  logx = fopen (new_file_path, "w");
+
+  int gmon_interval = (hz == HZ_WRONG ? 1000 : hz * 100);
+
+  fprintf (logx, "<profile name=\"%s\" ptimer=\"%d\" numstates=\"%d\">\n",
+          SP_JCMD_PROFILE, gmon_interval, LMS_MAGIC_ID_LINUX);
+  fprintf (logx, "  <profdata fname=\"profile\"/>\n");
+
+  /* Record Profile packet description */
+  fprintf (logx, "  <profpckt kind=\"%d\" uname=\"Clock profiling data\">\n",
+          CLOCK_TYPE);
+  fprintf (logx, "    <field name=\"LWPID\" uname=\"Lightweight process id\" \
+offset=\"%d\" type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, lwp_id),
+          fld_sizeof (ClockPacket, lwp_id) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "    <field name=\"THRID\" uname=\"Thread number\" \
+offset=\"%d\" type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, thr_id),
+          fld_sizeof (ClockPacket, thr_id) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "    <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" \
+type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, cpu_id),
+          fld_sizeof (ClockPacket, cpu_id) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "    <field name=\"TSTAMP\" uname=\"High resolution \
+timestamp\" offset=\"%d\" type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, tstamp),
+          fld_sizeof (ClockPacket, tstamp) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "    <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, frinfo),
+          fld_sizeof (ClockPacket, frinfo) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "    <field name=\"MSTATE\" uname=\"Thread state\" \
+offset=\"%d\" type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, mstate),
+          fld_sizeof (ClockPacket, mstate) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "    <field name=\"NTICK\" uname=\"Duration\" offset=\"%d\" \
+type=\"%s\"/>\n",
+          (int) offsetof (ClockPacket, nticks),
+          fld_sizeof (ClockPacket, nticks) == 4 ? "INT32" : "INT64");
+  fprintf (logx, "  </profpckt>\n");
+  fprintf (logx,"</profile>\n");
+  fprintf (logx, "<event kind=\"%s\" tstamp=\"%u.%09u\" time=\"%lld\" \
+tm_zone=\"%lld\"/>\n",
+          SP_JCMD_RUN, 0, 0, 0LL, 0LL);
+
+  fclose (logx);
+}
+
+/* This will not work for PIE execs.  */
+
+static void
+gen_gmon_map (char *name)
+{
+  bfd_vma mpage, page_size = sysconf(_SC_PAGESIZE);
+  bfd_vma loadaddr, vaddr = core_text_sect->vma; //lma?
+  bfd_size_type msize = core_text_sect->size;
+  int timestamp = 1;
+  int offset = 0;
+  int check = -1;
+  int modeflags = PROT_READ | PROT_EXEC; //0x05
+  char *new_file_path;
+  size_t asz = strlen (base_folder) + strlen (SP_MAP_FILE) + 1 + 1;
+
+  new_file_path = (char *) alloca (asz);
+  snprintf (new_file_path, asz, "%s/%s", base_folder, SP_MAP_FILE);
+
+  FILE *mapx = fopen (new_file_path, "w");
+
+  if (mapx == NULL) {
+    return;
+  }
+  /* alignment mask.  */
+  mpage = ~(page_size - 1);
+  // Round down to page size alignment
+  loadaddr = vaddr & mpage;
+  // Compensate for the alignment gain
+  msize += vaddr - loadaddr;
+  // Round up to a multiple of page size;
+  msize = (msize + page_size - 1) & mpage;
+
+  fprintf (mapx, "<event kind=\"map\" object=\"segment\" tstamp=\"%u.%09u\" "
+          "vaddr=\"0x%016llX\" size=\"%lu\" pagesz=\"%d\" foffset=\"%c0x%08llX\" "
+          "modes=\"0x%03X\" chksum=\"0x%0X\" name=\"%s\"/>\n",
+          (unsigned) (timestamp / NANOSEC),
+          (unsigned) (timestamp % NANOSEC),
+          (long long unsigned) loadaddr, msize, (int) page_size,
+          offset < 0 ? '-' : '+',
+          (long long unsigned) (offset < 0 ? -offset : offset),
+          modeflags, check, name);
+
+  fclose (mapx);
+}
+
+static int
+checkflagterm (const char *c)
+{
+  if (c[2] != 0)
+    {
+      dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), c);
+      return -1;
+    }
+  return 0;
+}
+
+static void
+writeStr (int f, const char *buf)
+{
+  if (buf != NULL)
+    write (f, buf, strlen (buf));
+}
+
+/* main entry point. It calls catch_out_of_memory which will call
+   real_main() function.  */
+int
+main (int argc, char *argv[])
+{
+  xmalloc_set_program_name (argv[0]);
+  return catch_out_of_memory (real_main, argc, argv);
+}
+
+er_gmon::er_gmon (int argc, char *argv[]) : DbeApplication (argc, argv)
+{
+  int sz = UIDTableSize * sizeof (*UIDTable);
+  UIDTable = (uint64_t *) xmalloc (sz);
+  if (!UIDTable)
+    return;
+  memset (UIDTable, 0, sz);
+  overwrite = false;
+  cc = NULL;
+}
+
+void
+er_gmon::start (int argc, char *argv[])
+{
+  char *gmon_name;
+  char *a_out_name;
+  // Create a collector control structure, disabling aggressive
+  // warning.
+  cc = new Coll_Ctrl (0, false, false);
+  cc->set_default_stem ("gmon");
+
+  whoami = argv[0];
+  if (argc > 1 && strncmp (argv[1], NTXT ("--whoami="), 9) == 0)
+    {
+      whoami = argv[1] + 9;
+      argc--;
+      argv++;
+    }
+  if (argc == 2 && strcmp (argv[1], NTXT ("-h")) == 0)
+    {
+      /* only one argument, -h */
+      usage ();
+      exit (0);
+    }
+  else if (argc == 2 && (strcmp (argv[1], NTXT ("-help")) == 0 ||
+                        strcmp (argv[1], NTXT ("--help")) == 0))
+    {
+      /* only one argument, -help or --help */
+      usage ();
+      exit (0);
+    }
+  else if ((argc == 2) &&
+          (strcmp (argv[1], NTXT ("--version")) == 0))
+    {
+      /* only one argument, --version */
+
+      /* print the version info */
+      Application::print_version_info ();
+      exit (0);
+    }
+
+  check_mods (argc, argv, true);
+  int adj = check_mods (argc, argv, false);
+  if (adj < 0)
+    {
+      usage ();
+      exit (0);
+    }
+
+  char *ret = cc->create_exp_dir ();
+  if (ret != NULL)
+    {
+      dbe_write (2, NTXT ("%s\n"), ret);
+      free (ret);
+      exit (1);
+    }
+
+  // Get the file path.
+  base_folder = cc->get_experiment ();
+
+  // Default names
+  a_out_name =  xstrdup ("a.out");
+  gmon_name =  xstrdup ("gmon.out");
+  // Get the ELF and the GMON.OUT file if they exist.
+  if (argc == (adj + 1))
+    {
+      a_out_name = argv[adj];
+    }
+  else if (argc == (adj + 2))
+    {
+      a_out_name = argv[adj++];
+      gmon_name = argv[adj];
+    }
+  else if (argc != adj)
+    {
+      usage ();
+      exit (0);
+    }
+
+  /* Read the elf syms and the gmon file.  */
+  if (core_init (a_out_name) < 0)
+    {
+      cc->remove_exp_dir ();
+      exit (1);
+    }
+  if (gmon_out_read (gmon_name, FF_AUTO) < 0)
+    {
+      cc->remove_exp_dir ();
+      exit (1);
+    }
+
+  /* Process the gmon file, and output the gprofng project files.  */
+  hist_assign_samples ();
+  cg_traverse_arcs ();
+  gen_gmon_map (a_out_name);
+  gen_gmon_log ();
+}
+
+/* Get the args and search for modifiers.  */
+int
+er_gmon::check_mods (int argc, char *argv[], bool check)
+{
+  char *expName = NULL;
+  int i = -1;
+  for (i = 1; i < argc; i++)
+    {
+      if (argv[i] == NULL)
+       break;
+      if (argv[i][0] != '-')
+       break;
+      switch (argv[i][1])
+       {
+       case 'O':
+         overwrite = true;
+         //FALLTHROU
+       case 'o':
+         if (check)
+           return i;
+         if (checkflagterm (argv[i]) == -1)
+           return -1;
+         if (argv[i + 1] == NULL)
+           {
+             dbe_write (2,
+                        GTXT ("Argument %s must be followed by a file name\n"),
+                        argv[i]);
+             return -1;
+           }
+         if (expName != NULL)
+           {
+             dbe_write (2, GTXT ("Only one -o or -O argument may be used\n"));
+             return -1;
+           }
+         expName = argv[i + 1];
+         i++;
+         break;
+
+       default:
+         dbe_write (2, GTXT ("gmon: unrecognized argument `%s'\n"),
+                    argv[i]);
+         return -1;
+       }
+    }
+  if (check)
+    return i;
+  if (expName)
+    {
+      char *ccret;
+      char *ccwarn = NULL;
+      ccret = cc->set_expt (expName, &ccwarn, overwrite);
+      if (ccwarn)
+       {
+         writeStr (2, ccwarn);
+         free (ccwarn);
+       }
+      if (ccret)
+       {
+         writeStr (2, ccret);
+         return -1;
+       }
+    }
+  return (check ? -1 : i);
+}
+
+void
+er_gmon::usage ()
+{
+  printf ( GTXT (
+    "Usage: gprofng display gmon [OPTION(S)] [TARGET-OBJECT [GMON-FILE]]\n"));
+  printf ( GTXT (
+    "\n"
+    "Convert an gmon.out file to a gprofng experiment.\n"
+    "\n"
+    "Options:\n"
+    "\n"
+    " --version           print the version number and exit.\n"
+    " -h/--help           print usage information and exit.\n"
+    "\n"
+    " -o <exp_name>     specify the name for (and path to) the experiment directory; the\n"
+    "                    the default path is the current directory.\n"
+    "\n"
+    " -O <exp_name>     the same as -o, but unlike the -o option, silently overwrite an\n"
+    "                    existing experiment directory with the same name.\n"
+    "\n"));
+
+    exit (0);
+}
+
+er_gmon::~er_gmon ()
+{
+  free (UIDTable);
+  delete cc;
+}
diff --git a/gprofng/src/gp-gmon.h b/gprofng/src/gp-gmon.h
new file mode 100644 (file)
index 0000000..bb8697c
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef gp_gmon_h
+#define gp_gmon_h
+
+#include "corefile.h"
+
+#ifdef DEBUG
+#define DBG(l,s) {s;}
+#else
+#define DBG(l,s)
+#endif
+
+#define done(INT) exit(INT)
+
+#ifndef MIN
+#define MIN(a,b)        ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b)        ((a) > (b) ? (a) : (b))
+#endif
+
+extern const char *whoami;
+#endif /* gp_gmon_h */
index 4364d971d213a65f37d05d00429a37c18b15b57f..5a3045bd2cd4e725c98274e92c2e0d832eec3f3b 100644 (file)
@@ -110,6 +110,7 @@ Gprofng::usage ()
     " gprofng display gui     invoke the GUI to graphically analyze the results.\n"
 */
     " gprofng display src     display source or disassembly with compiler annotations.\n"
+    " gprofng display gmon    convert a gmon file into a gprofng experiment.\n"
     "\n"
     "Miscellaneous commands\n"
     "\n"
@@ -190,6 +191,7 @@ Gprofng::exec_cmd (char *tool_name, int argc, char **argv)
     { "display", "gui", "gprofng-display-gui"},
     { "display", "html", "gprofng-display-html"},
     { "display", "src", "gprofng-display-src"},
+    { "display", "gmon", "gprofng-gmon"},
     { NULL, NULL}
   };
 
diff --git a/gprofng/src/hist.h b/gprofng/src/hist.h
new file mode 100644 (file)
index 0000000..8844388
--- /dev/null
@@ -0,0 +1,38 @@
+/* hist.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef hist_h
+#define hist_h
+
+typedef struct histogram
+{
+  bfd_vma lowpc;
+  bfd_vma highpc;
+  unsigned int num_bins;
+  int *sample;           /* Histogram samples (shorts in the file!).  */
+} histogram;
+
+extern long hz;
+
+extern void hist_read_rec        (FILE *, const char *);
+extern void hist_assign_samples  (void);
+
+#endif /* hist_h */
diff --git a/gprofng/src/source.h b/gprofng/src/source.h
new file mode 100644 (file)
index 0000000..e6bae7d
--- /dev/null
@@ -0,0 +1,35 @@
+/* source.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef source_h
+#define source_h
+
+typedef struct source_file
+  {
+    struct source_file *next;
+    const char *name;          /* Name of source file.  */
+    unsigned long ncalls;      /* # of "calls" to this file.  */
+    int num_lines;             /* # of lines in file.  */
+    int nalloced;              /* Number of lines allocated.  */
+    void **line;               /* Usage-dependent per-line data.  */
+  }
+Source_File;
+#endif /* source_h */
diff --git a/gprofng/src/symtab.h b/gprofng/src/symtab.h
new file mode 100644 (file)
index 0000000..f7b612d
--- /dev/null
@@ -0,0 +1,122 @@
+/* symtab.h
+
+   Copyright (C) 2000-2025 Free Software Foundation, Inc.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef symtab_h
+#define symtab_h
+
+#include "source.h"
+
+/* For a profile to be intelligible to a human user, it is necessary
+   to map code-addresses into source-code information.  Source-code
+   information can be any combination of: (i) function-name, (ii)
+   source file-name, and (iii) source line number.
+
+   The symbol table is used to map addresses into source-code
+   information.  */
+
+#define NBBS 10
+
+/* Symbol-entry.  For each external in the specified file we gather
+   its address, the number of calls and compute its share of cpu time.  */
+typedef struct sym
+  {
+    /* Common information:
+
+       In the symbol-table, fields ADDR and FUNC_NAME are guaranteed
+       to contain valid information.  FILE may be 0, if unknown and
+       LINE_NUM maybe 0 if unknown.  */
+
+    bfd_vma addr;              /* Address of entry point.  */
+    bfd_vma end_addr;          /* End-address.  */
+    const char *name;          /* Name of function this sym is from.  */
+    Source_File *file;         /* Source file symbol comes from.  */
+    int line_num;              /* Source line number.  */
+    unsigned int               /* Boolean fields:  */
+      is_func:1,               /*  Is this a function entry point?  */
+      is_static:1,             /*  Is this a local (static) symbol?  */
+      is_bb_head:1,            /*  Is this the head of a basic-blk?  */
+      mapped:1,                        /*  This symbol was mapped to another name.  */
+      has_been_placed:1;       /*  Have we placed this symbol?  */
+    unsigned long ncalls;      /* How many times executed  */
+    int nuses;                 /* How many times this symbol appears in
+                                  a particular context.  */
+    bfd_vma bb_addr[NBBS];     /* Address of basic-block start.  */
+    unsigned long bb_calls[NBBS];/* How many times basic-block was called.  */
+    struct sym *next;          /* For building chains of syms.  */
+    struct sym *prev;          /* For building chains of syms.  */
+
+    /* Profile specific information:  */
+
+    /* Histogram specific information:  */
+    struct
+      {
+       double time;            /* (Weighted) ticks in this routine.  */
+       bfd_vma scaled_addr;    /* Scaled entry point.  */
+      }
+    hist;
+
+    /* Call-graph specific information:  */
+    struct
+      {
+       unsigned long self_calls; /* How many calls to self.  */
+       double child_time;      /* Cumulative ticks in children.  */
+       int index;              /* Index in the graph list.  */
+       int top_order;          /* Graph call chain top-sort order.  */
+       bool print_flag;        /* Should this be printed?  */
+       struct
+         {
+           double fract;       /* What % of time propagates.  */
+           double self;        /* How much self time propagates.  */
+           double child;       /* How much child time propagates.  */
+         }
+       prop;
+       struct
+         {
+           int num;            /* Internal number of cycle on.  */
+           struct sym *head;   /* Head of cycle.  */
+           struct sym *next;   /* Next member of cycle.  */
+         }
+       cyc;
+       struct arc *parents;    /* List of caller arcs.  */
+       struct arc *children;   /* List of callee arcs.  */
+      }
+    cg;
+  }
+Sym;
+
+/* Symbol-tables are always assumed to be sorted
+   in increasing order of addresses.  */
+typedef struct
+  {
+    unsigned int len;          /* # of symbols in this table.  */
+    Sym *base;                 /* First element in symbol table.  */
+    Sym *limit;                        /* Limit = base + len.  */
+  }
+Sym_Table;
+
+extern Sym_Table *get_symtab (void);
+extern Sym_Table *get_symtab_direct (void);
+
+extern void sym_init        (Sym *);
+extern void symtab_finalize (Sym_Table *);
+extern Sym *sym_lookup      (Sym_Table *, bfd_vma);
+
+#endif /* symtab_h */