mirror of
https://github.com/bminor/binutils-gdb.git
synced 2026-02-05 10:51:29 +00:00
Improve the linker's --stats option to record memory use information provided by mallinfo().
This commit is contained in:
@@ -215,6 +215,12 @@
|
||||
/* Define to 1 if you have the `waitpid' function. */
|
||||
#undef HAVE_WAITPID
|
||||
|
||||
/* Define to 1 if you have the `mallinfo' function. */
|
||||
#undef HAVE_MALLINFO
|
||||
|
||||
/* Define to 1 if you have the `mallinfo2' function. */
|
||||
#undef HAVE_MALLINFO2
|
||||
|
||||
/* Define to 1 if you have the <windows.h> header file. */
|
||||
#undef HAVE_WINDOWS_H
|
||||
|
||||
|
||||
2
ld/configure
vendored
2
ld/configure
vendored
@@ -18670,7 +18670,7 @@ fi
|
||||
|
||||
done
|
||||
|
||||
for ac_func in close getrusage glob lseek mkstemp open realpath waitpid
|
||||
for ac_func in close getrusage glob lseek mkstemp open realpath waitpid mallinfo mallinfo2
|
||||
do :
|
||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
||||
|
||||
@@ -447,7 +447,7 @@ AC_SUBST(NATIVE_LIB_DIRS)
|
||||
AC_CHECK_HEADERS(fcntl.h elf-hints.h limits.h inttypes.h stdint.h \
|
||||
sys/file.h sys/mman.h sys/param.h sys/stat.h sys/time.h \
|
||||
sys/types.h unistd.h)
|
||||
AC_CHECK_FUNCS(close getrusage glob lseek mkstemp open realpath waitpid)
|
||||
AC_CHECK_FUNCS(close getrusage glob lseek mkstemp open realpath waitpid mallinfo mallinfo2)
|
||||
|
||||
BFD_BINARY_FOPEN
|
||||
|
||||
|
||||
26
ld/ld.h
26
ld/ld.h
@@ -336,20 +336,38 @@ typedef struct
|
||||
|
||||
/* An enumeration of the linker phases for which resource usage information
|
||||
is recorded. PHASE_ALL is special as it covers the entire link process.
|
||||
|
||||
PHASE_DEBUG is special as it causes an instant resource report to be
|
||||
displayed each time ld_stop_phase(PHASE_DEBUG) is called. If there has
|
||||
been a previous ld_start_phase(PHASE_DEBUG) then the report just covers
|
||||
the resource usage between the two calls. Otherwise it reports the
|
||||
resource usage in total so far. Unfortunately the two types of use
|
||||
cannot be mixed.
|
||||
|
||||
Note: ld_set_phase_name() can be used to change the name displayed when
|
||||
PHASE_DEBUG is reported. Possibly helping to identify specific resource
|
||||
reports. Typical code usage would look like this:
|
||||
|
||||
ld_start_phase (PHASE_DEBUG);
|
||||
<code to be investigated>
|
||||
ld_set_phase_name (PHASE_DEBUG, "description of code");
|
||||
ld_stop_phase (PHASE_DEBUG);
|
||||
|
||||
Instructions for adding a new phase:
|
||||
1. Add an entry to this enumeration.
|
||||
2. Add an entry for the phase to the phase_data[] structure in ldmain.c.
|
||||
3. Add calls to ld_start_phase(PHASE_xxx) and ld_stop_phase(PHASE_xxx)
|
||||
at the appropriate place(s) in the code. It does not matter if the
|
||||
new phase overlaps with or is contained by any other phase.
|
||||
new phase overlaps with or is contained by other phases, but it must
|
||||
not overlap with or contain itself.
|
||||
|
||||
Instructions for adding a new resource:
|
||||
1. If necessary add a new field to the phase_data structure defined in
|
||||
ldmain.c.
|
||||
2. Add code to initialise the field in ld_main.c:ld_start_phase().
|
||||
3. Add code to finalise the field in ld_main.c:ld_stop_phase().
|
||||
4. Add code to report the field in ld_main.c:report_phases(). */
|
||||
4. Add code to report the field in ld_main.c:report_phases().
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
PHASE_ALL = 0,
|
||||
@@ -360,12 +378,16 @@ typedef enum
|
||||
PHASE_PROCESS,
|
||||
PHASE_WRITE,
|
||||
|
||||
PHASE_DEBUG, /* Not a real phase. Used to help debug linker resource usage. */
|
||||
|
||||
NUM_PHASES /* This must be the last entry. */
|
||||
}
|
||||
ld_phase;
|
||||
|
||||
extern void ld_start_phase (ld_phase);
|
||||
extern void ld_stop_phase (ld_phase);
|
||||
/* Change the name of a phase. Only really useful for PHASE_DEBUG. */
|
||||
extern void ld_set_phase_name (ld_phase, const char *);
|
||||
|
||||
extern ld_config_type config;
|
||||
|
||||
|
||||
44
ld/ld.texi
44
ld/ld.texi
@@ -2769,21 +2769,25 @@ linker's command line. Note: if both the environment variable and the
|
||||
takes precedence.
|
||||
|
||||
The extended information reported includes the cpu time used and, if
|
||||
the @var{getrusage()} system library call is available then memory use
|
||||
is recorded as well. This information is reported for individual
|
||||
parts of the linking process which are referred to as @emph{phases}.
|
||||
In addition the information is also reported for a special phase
|
||||
called @emph{ALL} which covers the entire linking process. Note that
|
||||
the @var{getrusage()} system library call is available then maximum
|
||||
set size and the user and system run times as well. In addition if
|
||||
the @var{mallinfo} or @var{mallinfo2} system library calls are
|
||||
available then the total memory usage is reported,
|
||||
|
||||
The information is displayed for individual parts of the linking
|
||||
process which are referred to as @emph{phases}. Note that
|
||||
individual phases can contain or overlap with each other so it should
|
||||
not be assumed that the overall resources used by the linker is the
|
||||
sum of the resources used by the individual phases.
|
||||
|
||||
In addition when extended information is being reported the linker
|
||||
version, command line arguments and linker start time are also
|
||||
included. This makes it easier to handle the situation where multiple
|
||||
links are being invoked by a build system and to indentify exactly
|
||||
which arguments were responsible for producing the statistics that are
|
||||
reported.
|
||||
In addition the information is also reported for a special phase
|
||||
called @emph{ALL} which covers the entire linking process.
|
||||
|
||||
When extended information is being reported the linker version,
|
||||
command line arguments and linker start time are also included. This
|
||||
makes it easier to handle the situation where multiple links are being
|
||||
invoked by a build system and to indentify exactly which arguments
|
||||
were responsible for producing the statistics that are reported.
|
||||
|
||||
The extended output looks something like this:
|
||||
|
||||
@@ -2792,15 +2796,15 @@ Stats: linker version: (GNU Binutils) 2.44.50.20250401
|
||||
Stats: linker started: Wed Apr 2 09:36:41 2025
|
||||
Stats: args: ld -z norelro -z nomemory-seal -z no-separate-code -o a.out [...]
|
||||
|
||||
Stats: phase cpu time memory user time system time
|
||||
Stats: name (microsec) (KiB) (seconds) (seconds)
|
||||
Stats: ALL 390082 217740 0 0
|
||||
Stats: ctf processing 12 0 0 0
|
||||
Stats: string merge 1324 0 0 0
|
||||
Stats: parsing 349 288 0 0
|
||||
Stats: plugins 1 0 0 0
|
||||
Stats: processing files 259616 214524 0 0
|
||||
Stats: write 116493 0 0 0
|
||||
Stats: phase cpu time rss user time system time memory
|
||||
Stats: name (microsec) (KiB) (seconds) (seconds) (KiB)
|
||||
Stats: ALL 390082 217740 0 0 25219440
|
||||
Stats: ctf processing 12 0 0 0 0
|
||||
Stats: string merge 1324 0 0 0 406544
|
||||
Stats: parsing 349 288 0 0 119792
|
||||
Stats: plugins 1 0 0 0 0
|
||||
Stats: processing files 259616 214524 0 0 31569120
|
||||
Stats: write 116493 0 0 0 48096
|
||||
@end smallexample
|
||||
|
||||
@kindex --no-stats
|
||||
|
||||
83
ld/ldmain.c
83
ld/ldmain.c
@@ -53,6 +53,10 @@
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_MALLINFO2) || defined (HAVE_MALLINFO)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifndef TARGET_SYSTEM_ROOT
|
||||
#define TARGET_SYSTEM_ROOT ""
|
||||
#endif
|
||||
@@ -289,6 +293,11 @@ struct ld_phase_data
|
||||
struct rusage begin;
|
||||
struct rusage use;
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_MALLINFO2) || defined (HAVE_MALLINFO)
|
||||
size_t begin_blks;
|
||||
size_t used_blks;
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ld_phase_data phase_data [NUM_PHASES] =
|
||||
@@ -300,8 +309,15 @@ static struct ld_phase_data phase_data [NUM_PHASES] =
|
||||
[PHASE_PLUGINS] = { .name = "plugins" },
|
||||
[PHASE_PROCESS] = { .name = "processing files" },
|
||||
[PHASE_WRITE] = { .name = "write" },
|
||||
[PHASE_DEBUG] = { .name = "debug" }
|
||||
};
|
||||
|
||||
void
|
||||
ld_set_phase_name (ld_phase phase, const char * name)
|
||||
{
|
||||
phase_data[phase].name = name ? name : "<unnamed>";
|
||||
}
|
||||
|
||||
void
|
||||
ld_start_phase (ld_phase phase)
|
||||
{
|
||||
@@ -345,6 +361,14 @@ ld_start_phase (ld_phase phase)
|
||||
|
||||
memcpy (& pd->begin, & usage, sizeof usage);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_MALLINFO2)
|
||||
struct mallinfo2 mi2 = mallinfo2 ();
|
||||
pd->begin_blks = mi2.uordblks;
|
||||
#elif defined (HAVE_MALLINFO)
|
||||
struct mallinfo mi = mallinfo ();
|
||||
pd->begin_blks = mi.uordblks;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -354,10 +378,14 @@ ld_stop_phase (ld_phase phase)
|
||||
|
||||
if (!pd->started)
|
||||
{
|
||||
/* We set the broken flag to indicate that the data
|
||||
recorded for this phase is inconsistent. */
|
||||
pd->broken = true;
|
||||
return;
|
||||
/* It does not matter if the debug phase has not been started. */
|
||||
if (phase != PHASE_DEBUG)
|
||||
{
|
||||
/* We set the broken flag to indicate that the data
|
||||
recorded for this phase is inconsistent. */
|
||||
pd->broken = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pd->duration += get_run_time () - pd->start;
|
||||
@@ -421,6 +449,33 @@ ld_stop_phase (ld_phase phase)
|
||||
pd->use.ru_maxrss += usage.ru_maxrss - pd->begin.ru_maxrss;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_MALLINFO2)
|
||||
/* FIXME: How do we know if mallinfo2() has failed ? */
|
||||
struct mallinfo2 mi2 = mallinfo2 ();
|
||||
pd->used_blks += mi2.uordblks - pd->begin_blks;
|
||||
#elif defined (HAVE_MALLINFO)
|
||||
struct mallinfo mi = mallinfo ();
|
||||
pd->used_blks += mi.uordblks - pd->begin_blks;
|
||||
#endif
|
||||
|
||||
if (phase == PHASE_DEBUG)
|
||||
{
|
||||
/* FIXME: Should we report other resources as well ? */
|
||||
/* FIXME: Can we integrate this code with report_phases() ? */
|
||||
|
||||
fprintf (stderr, "stats: %s: cpu time: %ld ", pd->name, pd->duration);
|
||||
#if defined (HAVE_GETRUSAGE)
|
||||
fprintf (stderr, "rss: %ld ", pd->use.ru_maxrss);
|
||||
#endif
|
||||
#if defined (HAVE_MALLINFO2) || defined (HAVE_MALLINFO)
|
||||
fprintf (stderr, "memory: %ld", (long) pd->used_blks);
|
||||
#endif
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
/* Reset the counters to zero. */
|
||||
memset (((char *) pd) + sizeof (pd->name), 0, (sizeof (* pd)) - sizeof (pd->name));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -473,9 +528,12 @@ report_phases (FILE * file, time_t * start, char ** argv)
|
||||
#if defined (HAVE_GETRUSAGE)
|
||||
/* Note: keep these columns in sync with the
|
||||
information recorded in ld_stop_phase(). */
|
||||
COLUMNS_FIELD ("memory", "(KiB)")
|
||||
COLUMNS_FIELD ("rss", "(KiB)")
|
||||
COLUMNS_FIELD ("user time", "(seconds)")
|
||||
COLUMNS_FIELD ("system time", "(seconds)")
|
||||
#endif
|
||||
#if defined (HAVE_MALLINFO2) || defined (HAVE_MALLINFO)
|
||||
COLUMNS_FIELD ("memory", "(KiB)")
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -485,7 +543,12 @@ report_phases (FILE * file, time_t * start, char ** argv)
|
||||
|
||||
size_t maxwidth = 1;
|
||||
for (i = 0; i < NUM_PHASES; i++)
|
||||
maxwidth = max (maxwidth, strlen (phase_data[i].name));
|
||||
{
|
||||
struct ld_phase_data * pd = phase_data + i;
|
||||
|
||||
if (pd->name != NULL)
|
||||
maxwidth = max (maxwidth, strlen (pd->name));
|
||||
}
|
||||
|
||||
fprintf (file, "%s", STATS_PREFIX);
|
||||
|
||||
@@ -543,6 +606,9 @@ report_phases (FILE * file, time_t * start, char ** argv)
|
||||
/* This should not be needed... */
|
||||
const char * name = pd->name ? pd->name : "<unnamed>";
|
||||
|
||||
if (i == PHASE_DEBUG)
|
||||
continue;
|
||||
|
||||
if (pd->broken)
|
||||
{
|
||||
fprintf (file, "%s %s: %s",
|
||||
@@ -552,7 +618,7 @@ report_phases (FILE * file, time_t * start, char ** argv)
|
||||
|
||||
fprintf (file, "%s", STATS_PREFIX);
|
||||
|
||||
/* Care must be taken to keep the lines below in sync with
|
||||
/* Care must be taken to keep the numbers below in sync with
|
||||
entries in the columns_info array.
|
||||
FIXME: There ought to be a better way to do this... */
|
||||
COLUMN_ENTRY (name, "s", 0);
|
||||
@@ -561,6 +627,9 @@ report_phases (FILE * file, time_t * start, char ** argv)
|
||||
COLUMN_ENTRY (pd->use.ru_maxrss, "ld", 2);
|
||||
COLUMN_ENTRY ((int64_t) pd->use.ru_utime.tv_sec, PRId64, 3);
|
||||
COLUMN_ENTRY ((int64_t) pd->use.ru_stime.tv_sec, PRId64, 4);
|
||||
#endif
|
||||
#if defined (HAVE_MALLINFO2) || defined (HAVE_MALLINFO)
|
||||
COLUMN_ENTRY ((int64_t) pd->used_blks / 1024, PRId64, 5);
|
||||
#endif
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user