This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC][patch] Avoid repeated calls to solib_add on initial attach.


On Friday 15 July 2011 21:52:09, Paul Pluzhnikov wrote:
> Greetings,
> 
> Following up on my earlier "slow on high-latency links" message:
> http://sourceware.org/ml/gdb-patches/2011-07/msg00391.html ...
> 
> Attached patch avoids calling solib_add twice when initially attaching
> inferior.
> 
> I am not entirely happy about this patch, but don't have a better idea
> for a fix, and do want to avoid repeated rescans of the shared library list.

Me neigher, apart from doing as the comment says:

>   /* Sometimes the platform-specific hook loads initial shared
>	 libraries, and sometimes it doesn't.  If it doesn't FROM_TTY will be
>	 incorrectly 0 but such solib targets should be fixed anyway.  If we
>	 made all the inferior hook methods consistent, this call could be
>	 removed.  Call it only after the solib target has been initialized by
>	 solib_create_inferior_hook.  */

Or, making retrieving the dso list cheap enough to not care about the
multiple calls.  By e.g., caching more things, and making
target_read_string not super dumb (as you've yourself proposed).

Can you give a try on the attached patches and see how much
a difference they make for you?  We wrote these along
with yesterday's tracepoint's one, exactly to speed up
remote shared library loading/debugging on Android.  Patch 2
has the most dramatic affect; patches 1 and 3 make some
difference, though not as big.

Luis wrote at the time:

for patch 1:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This patch handles the caching of the AUXV block. Once loaded, GDB has 
access to the whole block of information. Queries can be made without 
the need to read the whole block from the remote target again). Some 
fields still require reading from the target (like the AT_PLATFORM 
field), but these tend to be smaller in size.

The cache is invalidated whenever any of these events occur: 
inferior_exit, inferior_appeared and executable_changed.

The data is kept in the following structure:

struct auxv_info
{
   LONGEST length;
   gdb_byte *data;
};

The inferior_data machinery is used to keep per-inferior caches of the AUXV.

Regressions:

I regtested this patch and found no regressions on x86-32/64.

Performance gains:

This patch reduces the overhead a bit since read_program_header queries 
the AUXV machinery three times. There are other calls as well. Though 
the performance gains are small, they're still noticeable.


for patch 2:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This patch (based on Pedro's patch) tries to optimize string reads from 
the target. Currently GDB reads 4 bytes at a time, and that's a big 
overhead if we're reading long path names. Experimenting with a few 
other block sizes, 64-bytes blocks seem to work great. This patch 
basically does the following:

- Stabilish bigger block sizes for the string reads.

- Replace target_read_memory with target_read_partial (Pedro's 
suggestion) to make sure we're testing for the terminating char at every 
tiny read (for targets that only support smaller packets).

Worth mentioning that i did try using "hints" for the sizes, so we could 
adjust the block size to be close to the estimated length of the path 
names. This works nicely for long path names, but if we have a scenario 
where there's a pattern like short/long/short/long, i'm inclined to say 
it would perform just the same as using a standard 64-bytes block.

Regressions:
---

None. Regtested on x86-32/64

Performance gains:
---

This is by far the patch that gives the most performance since it allows 
the reads to be performed in bigger blocks. This is specially 
interesting for remote targets, but also speeds up local targets a little.

for patch 3:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This patch caches the program header
information so we don't need to load it everytime. I've also used the 
inferior_data machinery to store such information. Additionaly, since 
it's a cache, i removed code that frees the allocated buffer after its 
use. That buffer is only deallocated inside the cleanup function instead 
of in functions that use this buffer.

Regressions:
---

Regtested the patch on x86-32/64 and found no additional errors.

Performance gains:
---

Reduces the overhead a little by caching the headers. I did some tests 
and noticed only a small improvement, but the function seems to be 
called a few times during the life of the process. It's worth just to 
reduce the number of unnecessary reads.

> 
> (Some of our executables use 4000+ shared libraries, and the time in
> solib_add does add up.)
> 
> Tested on Linux/x86_64, no regressions.
> 
> Comments?
> 
> Thanks,
> --
> Paul Pluzhnikov
> 
> 
> 
> 2011-07-15  Paul Pluzhnikov  <ppluzhnikov@google.com>
> 
> 	* inferior.h (struct inferior): Add solib_add_generation.
> 	* infcmd.c (post_create_inferior): Only call solib_add if not
> 	already done.
> 	* solib.c (solib_add): Increment solib_add_generation.
> 
> 
> 
> Index: inferior.h
> ===================================================================
> RCS file: /cvs/src/src/gdb/inferior.h,v
> retrieving revision 1.160
> diff -u -p -r1.160 inferior.h
> --- inferior.h	3 Jun 2011 15:32:44 -0000	1.160
> +++ inferior.h	15 Jul 2011 20:10:20 -0000
> @@ -536,6 +536,9 @@ struct inferior
>       if any catching is necessary.  */
>    int total_syscalls_count;
>  
> +  /* This counts the number of solib_add() calls performed.  */
> +  int solib_add_generation;
> +
>    /* Per inferior data-pointers required by other GDB modules.  */
>    void **data;
>    unsigned num_data;
> Index: solib.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/solib.c,v
> retrieving revision 1.149
> diff -u -p -r1.149 solib.c
> --- solib.c	30 Jun 2011 19:29:54 -0000	1.149
> +++ solib.c	15 Jul 2011 20:10:20 -0000
> @@ -914,6 +914,8 @@ solib_add (char *pattern, int from_tty,
>  {
>    struct so_list *gdb;
>  
> +  current_inferior ()->solib_add_generation++;
> +
>    if (pattern)
>      {
>        char *re_err = re_comp (pattern);
> Index: infcmd.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/infcmd.c,v
> retrieving revision 1.287
> diff -u -p -r1.287 infcmd.c
> --- infcmd.c	30 May 2011 18:04:32 -0000	1.287
> +++ infcmd.c	15 Jul 2011 20:50:56 -0000
> @@ -398,6 +398,7 @@ void
>  post_create_inferior (struct target_ops *target, int from_tty)
>  {
>    volatile struct gdb_exception ex;
> +  int solib_add_generation;
>  
>    /* Be sure we own the terminal in case write operations are performed.  */ 
>    target_terminal_ours ();
> @@ -419,6 +420,7 @@ post_create_inferior (struct target_ops 
>    if (ex.reason < 0 && ex.error != NOT_AVAILABLE_ERROR)
>      throw_exception (ex);
>  
> +  solib_add_generation = current_inferior ()->solib_add_generation;
>    if (exec_bfd)
>      {
>        /* Create the hooks to handle shared library load and unload
> @@ -432,14 +434,16 @@ post_create_inferior (struct target_ops 
>  
>    /* If the solist is global across processes, there's no need to
>       refetch it here.  */
> -  if (exec_bfd && !gdbarch_has_global_solist (target_gdbarch))
> +  if (exec_bfd && !gdbarch_has_global_solist (target_gdbarch)
> +      && current_inferior ()->solib_add_generation == solib_add_generation)
>      {
>        /* Sometimes the platform-specific hook loads initial shared
>  	 libraries, and sometimes it doesn't.  If it doesn't FROM_TTY will be
>  	 incorrectly 0 but such solib targets should be fixed anyway.  If we
>  	 made all the inferior hook methods consistent, this call could be
>  	 removed.  Call it only after the solib target has been initialized by
> -	 solib_create_inferior_hook.  */
> +	 solib_create_inferior_hook.  Only do this if not alreay done from
> +	 inside solib_create_inferior_hook.  */
>  
>  #ifdef SOLIB_ADD
>        SOLIB_ADD (NULL, 0, target, auto_solib_add);
> 

-- 
Pedro Alves
2011-07-20  Pedro Alves  <pedro@codesourcery.com>
	    Luis Machado  <lgustavo@codesourcery.com>

	gdb/
	* auxv.c: Include observer.h.
	(auxv_inferior_data_cleanup): New.
	(invalidate_auxv_cache_inf): New.
	(invalidate_auxv_cache): New.
	(get_auxv_inferior_data): New.
	(auxv_inferior_data): New static global.
	(auxv_info): New structure.
	(target_auxv_search): Use get_auxv_inferior_data instead of
	target_read_alloc and don't free cached buffers.
	(fprint_target_auxv): Likewise
	(_initialize_auxv): Register per-inferior auxv cache and register
	observers to invalidate auxv cache when needed.

---
 gdb/auxv.c |  116 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 100 insertions(+), 16 deletions(-)

Index: src/gdb/auxv.c
===================================================================
--- src.orig/gdb/auxv.c	2011-01-13 15:07:17.236075004 +0000
+++ src/gdb/auxv.c	2011-07-20 14:57:01.733657565 +0100
@@ -26,6 +26,7 @@
 #include "valprint.h"
 #include "gdb_assert.h"
 #include "gdbcore.h"
+#include "observer.h"
 
 #include "auxv.h"
 #include "elf/common.h"
@@ -277,6 +278,77 @@ target_auxv_parse (struct target_ops *op
   return default_auxv_parse (ops, readptr, endptr, typep, valp);
 }
 
+
+/* Per-inferior data key for auxv.  */
+static const struct inferior_data *auxv_inferior_data;
+
+/*  Auxiliary Vector information structure.  This is used by GDB
+    for caching purposes for each inferior.  This helps reduce the
+    overhead of transfering data from a remote target to the local host.  */
+struct auxv_info
+{
+  LONGEST length;
+  gdb_byte *data;
+};
+
+/* Handles the cleanup of the auxv cache for inferior INF.  ARG is ignored.
+   Frees whatever allocated space there is to be freed and sets INF's auxv cache
+   data pointer to NULL.
+
+   This function is called when the following events occur: inferior_appeared,
+   inferior_exit and executable_changed.  */
+
+static void
+auxv_inferior_data_cleanup (struct inferior *inf, void *arg)
+{
+  struct auxv_info *info;
+
+  info = inferior_data (inf, auxv_inferior_data);
+  if (info != NULL)
+    {
+      xfree (info->data);
+      xfree (info);
+      set_inferior_data (inf, auxv_inferior_data, NULL);
+    }
+}
+
+/* Invalidate INF's auxv cache.  */
+
+static void
+invalidate_auxv_cache_inf (struct inferior *inf)
+{
+  auxv_inferior_data_cleanup (inf, NULL);
+}
+
+/* Invalidate current inferior's auxv cache.  */
+
+static void
+invalidate_auxv_cache (void)
+{
+  invalidate_auxv_cache_inf (current_inferior ());
+}
+
+/* Fetch the auxv object from inferior INF.  If auxv is cached already,
+   return a pointer to the cache.  If not, fetch the auxv object from the
+   target and cache it.  This function always returns a valid INFO pointer.  */
+
+static struct auxv_info *
+get_auxv_inferior_data (struct inferior *inf, struct target_ops *ops)
+{
+  struct auxv_info *info;
+
+  info = inferior_data (inf, auxv_inferior_data);
+  if (info == NULL)
+    {
+      info = XZALLOC (struct auxv_info);
+      info->length = target_read_alloc (ops, TARGET_OBJECT_AUXV,
+					NULL, &info->data);
+      set_inferior_data (inf, auxv_inferior_data, info);
+    }
+
+  return info;
+}
+
 /* Extract the auxiliary vector entry with a_type matching MATCH.
    Return zero if no such entry was found, or -1 if there was
    an error getting the information.  On success, return 1 after
@@ -286,28 +358,30 @@ target_auxv_search (struct target_ops *o
 {
   CORE_ADDR type, val;
   gdb_byte *data;
-  LONGEST n = target_read_alloc (ops, TARGET_OBJECT_AUXV, NULL, &data);
-  gdb_byte *ptr = data;
+  gdb_byte *ptr;
+  struct auxv_info *info;
+
+  info = get_auxv_inferior_data (current_inferior (), ops);
 
-  if (n <= 0)
-    return n;
+  data = info->data;
+  ptr = data;
+
+  if (info->length <= 0)
+    return info->length;
 
   while (1)
-    switch (target_auxv_parse (ops, &ptr, data + n, &type, &val))
+    switch (target_auxv_parse (ops, &ptr, data + info->length, &type, &val))
       {
       case 1:			/* Here's an entry, check it.  */
 	if (type == match)
 	  {
-	    xfree (data);
 	    *valp = val;
 	    return 1;
 	  }
 	break;
       case 0:			/* End of the vector.  */
-	xfree (data);
 	return 0;
       default:			/* Bogosity.  */
-	xfree (data);
 	return -1;
       }
 
@@ -321,15 +395,18 @@ fprint_target_auxv (struct ui_file *file
 {
   CORE_ADDR type, val;
   gdb_byte *data;
-  LONGEST len = target_read_alloc (ops, TARGET_OBJECT_AUXV, NULL,
-				   &data);
-  gdb_byte *ptr = data;
+  gdb_byte *ptr;
+  struct auxv_info *info;
   int ents = 0;
 
-  if (len <= 0)
-    return len;
+  info = get_auxv_inferior_data (current_inferior (), ops);
+
+  data = info->data;
+  ptr = data;
+  if (info->length <= 0)
+    return info->length;
 
-  while (target_auxv_parse (ops, &ptr, data + len, &type, &val) > 0)
+  while (target_auxv_parse (ops, &ptr, data + info->length, &type, &val) > 0)
     {
       const char *name = "???";
       const char *description = "";
@@ -418,8 +495,6 @@ fprint_target_auxv (struct ui_file *file
 	break;
     }
 
-  xfree (data);
-
   return ents;
 }
 
@@ -448,4 +523,13 @@ _initialize_auxv (void)
   add_info ("auxv", info_auxv_command,
 	    _("Display the inferior's auxiliary vector.\n\
 This is information provided by the operating system at program startup."));
+
+  /* Set an auxv cache per-inferior.  */
+  auxv_inferior_data
+    = register_inferior_data_with_cleanup (auxv_inferior_data_cleanup);
+
+  /* Observers used to invalidate the auxv cache when needed.  */
+  observer_attach_inferior_exit (invalidate_auxv_cache_inf);
+  observer_attach_inferior_appeared (invalidate_auxv_cache_inf);
+  observer_attach_executable_changed (invalidate_auxv_cache);
 }
2011-07-20  Pedro Alves  <pedro@codesourcery.com>
	    Luis Machado  <lgustavo@codesourcery.com>

	gdb/
	* target.c (target_read_partial): Declare prototype.
	(target_read_string): Use 64-bytes blocks for reads and use
	target_read_partial instead of target_read_memory.

---
 gdb/target.c |   45 +++++++++++++++++++++++----------------------
 1 file changed, 23 insertions(+), 22 deletions(-)

Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c	2011-06-07 18:54:30.078164934 +0100
+++ src/gdb/target.c	2011-07-20 14:57:39.933657572 +0100
@@ -88,6 +88,11 @@ static LONGEST target_xfer_partial (stru
 				    void *readbuf, const void *writebuf,
 				    ULONGEST offset, LONGEST len);
 
+static LONGEST target_read_partial (struct target_ops *ops,
+				    enum target_object object,
+				    const char *annex, gdb_byte *buf,
+				    ULONGEST offset, LONGEST len);
+
 static struct gdbarch *default_thread_architecture (struct target_ops *ops,
 						    ptid_t ptid);
 
@@ -1185,55 +1190,51 @@ target_translate_tls_address (struct obj
 int
 target_read_string (CORE_ADDR memaddr, char **string, int len, int *errnop)
 {
-  int tlen, origlen, offset, i;
-  gdb_byte buf[4];
-  int errcode = 0;
-  char *buffer;
-  int buffer_allocated;
-  char *bufptr;
-  unsigned int nbytes_read = 0;
+  int tlen, i, errcode = 0, buffer_allocated;
+  gdb_byte buf[64];
+  char *buffer, *bufptr;
+  unsigned int nbytes_read = 0, raw_read = 0;
+  LONGEST xfered;
 
   gdb_assert (string);
 
   /* Small for testing.  */
-  buffer_allocated = 4;
+  buffer_allocated = 64;
   buffer = xmalloc (buffer_allocated);
   bufptr = buffer;
 
-  origlen = len;
-
   while (len > 0)
     {
-      tlen = MIN (len, 4 - (memaddr & 3));
-      offset = memaddr & 3;
+      tlen = MIN (len, 64);
 
-      errcode = target_read_memory (memaddr & ~3, buf, sizeof buf);
-      if (errcode != 0)
+      xfered = target_read_partial (current_target.beneath,
+				    TARGET_OBJECT_MEMORY,
+				    NULL, buf,
+				    memaddr, tlen);
+      if (xfered <= 0)
 	{
 	  /* The transfer request might have crossed the boundary to an
 	     unallocated region of memory.  Retry the transfer, requesting
 	     a single byte.  */
 	  tlen = 1;
-	  offset = 0;
 	  errcode = target_read_memory (memaddr, buf, 1);
 	  if (errcode != 0)
 	    goto done;
 	}
+      else
+	raw_read += tlen;
 
-      if (bufptr - buffer + tlen > buffer_allocated)
+      if (raw_read > buffer_allocated)
 	{
-	  unsigned int bytes;
-
-	  bytes = bufptr - buffer;
 	  buffer_allocated *= 2;
 	  buffer = xrealloc (buffer, buffer_allocated);
-	  bufptr = buffer + bytes;
+	  bufptr = buffer + raw_read - tlen;
 	}
 
       for (i = 0; i < tlen; i++)
 	{
-	  *bufptr++ = buf[i + offset];
-	  if (buf[i + offset] == '\000')
+	  *bufptr++ = buf[i];
+	  if (buf[i] == '\000')
 	    {
 	      nbytes_read += i + 1;
 	      goto done;
2011-07-20  Pedro Alves  <pedro@codesourcery.com>
	    Luis Machado  <lgustavo@codesourcery.com>

	gdb/
	* solib-svr4.c (pheaders_inferior_data): New static global.
	(pheader_info): New structure.
	(pheaders_info): Likewise.
	(pheaders_inferior_data_cleanup): New function.
	(invalidate_pheaders_cache_inf): Likewise.
	(invalidate_pheaders_cache): Likewise.
	(get_pheaders_inferior_data): Likewise.
	(read_program_header): Use cached program header information
	if available, otherwise load and cache it.
	(_initialize_svr4_solib): Register per-inferior program headers
	cache and register observers to invalidate it when needed.

---
 gdb/solib-svr4.c |  145 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 142 insertions(+), 3 deletions(-)

Index: src/gdb/solib-svr4.c
===================================================================
--- src.orig/gdb/solib-svr4.c	2011-07-20 09:33:50.833654174 +0100
+++ src/gdb/solib-svr4.c	2011-07-20 14:58:07.633657577 +0100
@@ -460,6 +460,99 @@ bfd_lookup_symbol (bfd *abfd, const char
   return symaddr;
 }
 
+/* Per-inferior program headers data key.  */
+static const struct inferior_data *pheaders_inferior_data;
+
+/* Cache structure for a single program header.  */
+struct pheader_info
+{
+  gdb_byte *data;
+  int sect_size;
+  int arch_size;
+};
+
+/* Cache structure for multiple program headers.  */
+struct pheaders_info
+{
+  struct pheader_info *header_minus_one;
+  struct pheader_info *header_two;
+};
+
+/* Handles the cleanup of program headers' cache for inferior INF.  ARG is ignored.
+   Frees whatever allocated space there is to be freed and sets INF's program header
+   cache data pointers to NULL.
+
+   This function is called when the following events occur: inferior_appeared and
+   executable_changed.  */
+
+static void
+pheaders_inferior_data_cleanup (struct inferior *inf, void *arg)
+{
+  struct pheaders_info *info;
+
+  info = inferior_data (inf, pheaders_inferior_data);
+  if (info != NULL)
+    {
+      if (info->header_minus_one != NULL)
+	{
+	  xfree (info->header_minus_one->data);
+	  xfree (info->header_minus_one);
+	  info->header_minus_one = NULL;
+	}
+
+      if (info->header_two != NULL)
+	{
+	  xfree (info->header_two->data);
+	  xfree (info->header_two);
+	  info->header_two = NULL;
+	}
+
+      xfree (info);
+      set_inferior_data (inf, pheaders_inferior_data, NULL);
+    }
+}
+
+/* Invalidate INF's program headers' cache.  */
+
+static void
+invalidate_pheaders_cache_inf (struct inferior *inf)
+{
+  pheaders_inferior_data_cleanup (inf, NULL);
+}
+
+/* Invalidate current inferior's program headers' cache.  */
+
+static void
+invalidate_pheaders_cache (void)
+{
+  invalidate_pheaders_cache_inf (current_inferior ());
+}
+
+/* Fetch a pointer to the cached program headers for INF.  If the program headers
+   are already cached, return a pointer to the cache.  If not, just allocate an empty
+   cache data structure and let the calling function handle updating the cache with
+   current data. This function always returns a valid INFO pointer.  */
+
+static struct pheaders_info *
+get_pheaders_inferior_data (struct inferior *inf)
+{
+  struct pheaders_info *info;
+
+  info = inferior_data (inf, pheaders_inferior_data);
+  if (info == NULL)
+    {
+      info = XZALLOC (struct pheaders_info);
+
+      /* Make sure these are empty so the calling function
+	 understands it and provides data for the cache.  */
+      info->header_minus_one = NULL;
+      info->header_two = NULL;
+
+      set_inferior_data (inf, pheaders_inferior_data, info);
+    }
+
+  return info;
+}
 
 /* Read program header TYPE from inferior memory.  The header is found
    by scanning the OS auxillary vector.
@@ -481,6 +574,31 @@ read_program_header (int type, int *p_se
   CORE_ADDR sect_addr;
   gdb_byte *buf;
 
+  struct pheader_info *header_minus_one = NULL;
+  struct pheader_info *header_two = NULL;
+  struct pheaders_info *all_pheaders =
+			get_pheaders_inferior_data (current_inferior ());
+
+  header_minus_one = all_pheaders->header_minus_one;
+
+  /* Check for cached information and return it if available.  Load
+  information from target otherwise.  */
+  if (type == -1 && header_minus_one != NULL)
+    {
+      *p_sect_size = header_minus_one->sect_size;
+      *p_arch_size = header_minus_one->arch_size;
+      return header_minus_one->data;
+    }
+
+  header_two = all_pheaders->header_two;
+
+  if (type == 2 && header_two != NULL)
+    {
+      *p_sect_size = header_two->sect_size;
+      *p_arch_size = header_two->arch_size;
+      return header_two->data;
+    }
+
   /* Get required auxv elements from target.  */
   if (target_auxv_search (&current_target, AT_PHDR, &at_phdr) <= 0)
     return 0;
@@ -571,6 +689,22 @@ read_program_header (int type, int *p_se
   if (p_sect_size)
     *p_sect_size = sect_size;
 
+  /* Update the cache with header information.  */
+  if (type == -1)
+    {
+      all_pheaders->header_minus_one = xmalloc (sizeof (*header_minus_one));
+      all_pheaders->header_minus_one->arch_size = arch_size;
+      all_pheaders->header_minus_one->sect_size = sect_size;
+      all_pheaders->header_minus_one->data = buf;
+    }
+  else if (type == 2)
+    {
+      all_pheaders->header_two = xmalloc (sizeof (*header_minus_one));
+      all_pheaders->header_two->arch_size = arch_size;
+      all_pheaders->header_two->sect_size = sect_size;
+      all_pheaders->header_two->data = buf;
+    }
+
   return buf;
 }
 
@@ -755,12 +889,10 @@ scan_dyntag_auxv (int dyntag, CORE_ADDR
 	if (ptr)
 	  *ptr = dyn_ptr;
 
-	xfree (bufstart);
 	return 1;
       }
   }
 
-  xfree (bufstart);
   return 0;
 }
 
@@ -2044,7 +2176,6 @@ svr4_exec_displacement (CORE_ADDR *displ
 	    ok = 0;
 	}
 
-      xfree (buf);
       xfree (buf2);
 
       if (!ok)
@@ -2470,4 +2601,12 @@ _initialize_svr4_solib (void)
   svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol;
   svr4_so_ops.same = svr4_same;
   svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core;
+
+  /* Set cache of program headers per-inferior.  */
+  pheaders_inferior_data
+    = register_inferior_data_with_cleanup (pheaders_inferior_data_cleanup);
+
+  /* Observers used to invalidate the program headers' cache when needed.  */
+  observer_attach_inferior_appeared (invalidate_pheaders_cache_inf);
+  observer_attach_executable_changed (invalidate_pheaders_cache);
 }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]