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 = \
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)
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)
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 \
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)
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
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;; \
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
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 = \
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
@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)
@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@
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
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 */
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 ();
--- /dev/null
+/* 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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 */
" 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"
{ "display", "gui", "gprofng-display-gui"},
{ "display", "html", "gprofng-display-html"},
{ "display", "src", "gprofng-display-src"},
+ { "display", "gmon", "gprofng-gmon"},
{ NULL, NULL}
};
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */