[Precord RFA/RFC] Check Linux sys_brk release memory in process record and replay.

Hui Zhu teawater@gmail.com
Tue May 5 13:09:00 GMT 2009


Sorry I forget the patch.

On Tue, May 5, 2009 at 21:07, Hui Zhu <teawater@gmail.com> wrote:
> Hi guys,
>
> This patch will make linux-record can check if the sys_brk will
> release the memory or not.  If memory will be released, gdb will query
> to user.
>
> For example:
> cat m.c
> #include <sys/types.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
> #include <errno.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <stdint.h>
>
> int
> main(int argc,char *argv[],char *envp[])
> {
>        sbrk (10);
>        sbrk (-10);
>
>        return (0);
> }
>
> gdb m
> GNU gdb (GDB) 6.8.50.20090505-cvs
> Copyright (C) 2009 Free Software Foundation, Inc.
> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
> This is free software: you are free to change and redistribute it.
> There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
> and "show warranty" for details.
> This GDB was configured as "i686-pc-linux-gnu".
> For bug reporting instructions, please see:
> <http://www.gnu.org/software/gdb/bugs/>...
> (gdb) start
> Temporary breakpoint 1 at 0x8048385: file m.c, line 14.
> Starting program: /home/teawater/gdb/m
>
> Temporary breakpoint 1, main (argc=<value optimized out>, argv=<value
> optimized out>,
>    envp=<value optimized out>) at m.c:14
> 14              sbrk (10);
> (gdb) record
> (gdb) n
> 15              sbrk (-10);
> (gdb)
> The next instruction is syscall brk.  It will release the memory that
> will cause process record target get error.  Do you want to stop the
> inferior?([y] or n)
> Process record: inferior program stopped.
>
> Program received signal SIGTRAP, Trace/breakpoint trap.
> 0xb7fe3405 in __kernel_vsyscall ()
>
>
> 2009-05-05  Hui Zhu  <teawater@gmail.com>
>
>        Add a architecture process record and replay reset interface
>        and i386 and i386-linux record and replay reset functions.
>
>        * gdbarch.sh (process_record_reset): This interface point to
>        the function that reset the architecture process record and
>        replay.
>        * record.c (record_open): Call process_record_reset.
>        * i386-tdep.c (i386_linux_record_reset): New function. Call
>        tdep interface "i386_record_reset".
>        (i386_gdbarch_init): Set "i386_linux_record_reset" to GDBARCH
>        "process_record_reset" interface.
>        * i386-tdep.h (gdbarch_tdep): New function pointer
>        i386_record_reset that point to the function that can reset
>        the process record.
>        * i386-linux-tdep.c (i386_linux_record_reset): New function.
>        Call record_linux_reset.
>        (i386_linux_init_abi): Set "i386_linux_record_reset" to
>        "i386_record_reset".
>
>        Check Linux sys_brk release memory in process record
>        and replay.
>
>        * linux-record.c (record_top_of_heap): New variable.
>        The current top of heap of inferior.
>        (record_linux_reset): New function.  The reset function of
>        Linux process record and replay.  It will reset the value
>        of record_top_of_heap.
>        (record_linux_system_call): Add the sys_brk check code.
>        If this sys_brk will release the memory, query to user.
>        * linux-record.h (record_linux_reset): New function extern.
>
>
> Thanks,
> Hui
>
-------------- next part --------------
---
 gdbarch.c         |   33 +++++++++++++++++
 gdbarch.h         |    8 ++++
 gdbarch.sh        |    3 +
 i386-linux-tdep.c |    7 +++
 i386-tdep.c       |    8 ++++
 i386-tdep.h       |    2 +
 linux-record.c    |  101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 linux-record.h    |    1 
 record.c          |    4 ++
 9 files changed, 167 insertions(+)

--- a/gdbarch.c
+++ b/gdbarch.c
@@ -240,6 +240,7 @@ struct gdbarch
   gdbarch_static_transform_name_ftype *static_transform_name;
   int sofun_address_maybe_missing;
   gdbarch_process_record_ftype *process_record;
+  gdbarch_process_record_reset_ftype *process_record_reset;
   gdbarch_target_signal_from_host_ftype *target_signal_from_host;
   gdbarch_target_signal_to_host_ftype *target_signal_to_host;
   gdbarch_get_siginfo_type_ftype *get_siginfo_type;
@@ -376,6 +377,7 @@ struct gdbarch startup_gdbarch =
   0,  /* static_transform_name */
   0,  /* sofun_address_maybe_missing */
   0,  /* process_record */
+  0,  /* process_record_reset */
   default_target_signal_from_host,  /* target_signal_from_host */
   default_target_signal_to_host,  /* target_signal_to_host */
   0,  /* get_siginfo_type */
@@ -633,6 +635,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of static_transform_name, has predicate */
   /* Skip verify of sofun_address_maybe_missing, invalid_p == 0 */
   /* Skip verify of process_record, has predicate */
+  /* Skip verify of process_record_reset, has predicate */
   /* Skip verify of target_signal_from_host, invalid_p == 0 */
   /* Skip verify of target_signal_to_host, invalid_p == 0 */
   /* Skip verify of get_siginfo_type, has predicate */
@@ -955,6 +958,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                       "gdbarch_dump: process_record = <%s>\n",
                       host_address_to_string (gdbarch->process_record));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_process_record_reset_p() = %d\n",
+                      gdbarch_process_record_reset_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: process_record_reset = <%s>\n",
+                      host_address_to_string (gdbarch->process_record_reset));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: ps_regnum = %s\n",
                       plongest (gdbarch->ps_regnum));
   fprintf_unfiltered (file,
@@ -3283,6 +3292,30 @@ set_gdbarch_process_record (struct gdbar
   gdbarch->process_record = process_record;
 }
 
+int
+gdbarch_process_record_reset_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->process_record_reset != NULL;
+}
+
+void
+gdbarch_process_record_reset (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->process_record_reset != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_process_record_reset called\n");
+  gdbarch->process_record_reset (gdbarch);
+}
+
+void
+set_gdbarch_process_record_reset (struct gdbarch *gdbarch,
+                                  gdbarch_process_record_reset_ftype process_record_reset)
+{
+  gdbarch->process_record_reset = process_record_reset;
+}
+
 enum target_signal
 gdbarch_target_signal_from_host (struct gdbarch *gdbarch, int signo)
 {
--- a/gdbarch.h
+++ b/gdbarch.h
@@ -818,6 +818,14 @@ typedef int (gdbarch_process_record_ftyp
 extern int gdbarch_process_record (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr);
 extern void set_gdbarch_process_record (struct gdbarch *gdbarch, gdbarch_process_record_ftype *process_record);
 
+/* Reset the inside value of process record if need. */
+
+extern int gdbarch_process_record_reset_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_process_record_reset_ftype) (struct gdbarch *gdbarch);
+extern void gdbarch_process_record_reset (struct gdbarch *gdbarch);
+extern void set_gdbarch_process_record_reset (struct gdbarch *gdbarch, gdbarch_process_record_reset_ftype *process_record_reset);
+
 /* Signal translation: translate inferior's signal (host's) number into
    GDB's representation. */
 
--- a/gdbarch.sh
+++ b/gdbarch.sh
@@ -715,6 +715,9 @@ v:int:sofun_address_maybe_missing:::0:0:
 # Return -1 if something goes wrong, 0 otherwise.
 M:int:process_record:struct regcache *regcache, CORE_ADDR addr:regcache, addr
 
+# Reset the inside value of process record if need.
+M:void:process_record_reset:void
+
 # Signal translation: translate inferior's signal (host's) number into
 # GDB's representation.
 m:enum target_signal:target_signal_from_host:int signo:signo::default_target_signal_from_host::0
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -352,6 +352,12 @@ i386_linux_write_pc (struct regcache *re
   regcache_cooked_write_unsigned (regcache, I386_LINUX_ORIG_EAX_REGNUM, -1);
 }
 
+static void
+i386_linux_record_reset (struct gdbarch *gdbarch)
+{
+  record_linux_reset (gdbarch);
+}
+
 /* Parse the arguments of current system call instruction and record
    the values of the registers and memory that will be changed into
    "record_arch_list".  This instruction is "int 0x80" (Linux
@@ -787,6 +793,7 @@ i386_linux_init_abi (struct gdbarch_info
   i386_linux_record_tdep.arg4 = I386_ESI_REGNUM;
   i386_linux_record_tdep.arg5 = I386_EDI_REGNUM;
 
+  tdep->i386_record_reset = i386_linux_record_reset;
   tdep->i386_intx80_record = i386_linux_intx80_sysenter_record;
   tdep->i386_sysenter_record = i386_linux_intx80_sysenter_record;
 
--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -5086,6 +5086,13 @@ no_support:
   return -1;
 }
 
+static void
+i386_process_record_reset (struct gdbarch *gdbarch)
+{
+  if (gdbarch_tdep (gdbarch)->i386_record_reset)
+    gdbarch_tdep (gdbarch)->i386_record_reset (gdbarch);
+}
+
 

 static struct gdbarch *
 i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
@@ -5278,6 +5285,7 @@ i386_gdbarch_init (struct gdbarch_info i
 					 i386_skip_permanent_breakpoint);
 
   set_gdbarch_process_record (gdbarch, i386_process_record);
+  set_gdbarch_process_record_reset (gdbarch, i386_process_record_reset);
 
   return gdbarch;
 }
--- a/i386-tdep.h
+++ b/i386-tdep.h
@@ -108,6 +108,8 @@ struct gdbarch_tdep
   struct type *i386_sse_type;
 
   /* Process record/replay target.  */
+  /* Reset */
+  void (*i386_record_reset) (struct gdbarch *gdbarch);
   /* Parse intx80 args.  */
   int (*i386_intx80_record) (struct regcache *regcache);
   /* Parse sysenter args.  */
--- a/linux-record.c
+++ b/linux-record.c
@@ -20,6 +20,12 @@
 #include "defs.h"
 #include "target.h"
 #include "regcache.h"
+#include "infcall.h"
+#include "objfiles.h"
+#include "value.h"
+#include "breakpoint.h"
+#include "inferior.h"
+#include "gdbthread.h"
 #include "record.h"
 #include "linux-record.h"
 
@@ -80,6 +86,56 @@
 #define RECORD_Q_XGETQSTAT	(('5' << 8) + 5)
 #define RECORD_Q_XGETQUOTA	(('3' << 8) + 3)
 
+/* record_top_of_heap is the current top of heap of inferior.
+   When record deal with sys_brk, it will be used.
+   Get it need call call_function_by_hand, this function will reset a lot
+   of status of inferior.  It will make process record get error
+   if call it in record_linux_system_call.  So get record_top_of_heap in
+   record_linux_reset.  */
+
+static bfd_vma record_top_of_heap;
+
+void
+record_linux_reset (struct gdbarch *gdbarch)
+{
+  struct value *sbrk_fn;
+  struct objfile *sbrk_objf;
+
+  record_top_of_heap = 0;
+
+  /* Get sbrk_fn and zero.  Because all of them
+     will be free by free_all_values in each command begin.  */
+  sbrk_fn = NULL;
+  if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL)
+    sbrk_fn = find_function_in_inferior ("sbrk", &sbrk_objf);
+  if (!sbrk_fn)
+    {
+      if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL)
+	sbrk_fn = find_function_in_inferior ("_sbrk", &sbrk_objf);
+    }
+
+  if (sbrk_fn)
+    {
+      struct value *ret;
+      struct value *zero = value_from_longest
+	(builtin_type (get_objfile_arch (sbrk_objf))->builtin_int, 0);
+
+      remove_breakpoints ();
+      ret = call_function_by_hand (sbrk_fn, 1, &zero);
+      insert_breakpoints ();
+
+      if (ret)
+	{
+	  record_top_of_heap = value_as_long (ret);
+	}
+    }
+
+  if (!record_top_of_heap)
+    fprintf_unfiltered (gdb_stdlog,
+			_("Process record get current "
+			  "top of heap failied.\n"));
+}
+
 /* When the architecture process record get a Linux syscall
    instruction, it will get a Linux syscall number of this
    architecture and convert it to the Linux syscall number "num" which
@@ -246,8 +302,53 @@ record_linux_system_call (int num, struc
 
       /* sys_ni_syscall */
     case 44:
+      break;
+
       /* sys_brk */
     case 45:
+      {
+	int q;
+	bfd_vma end_data_segment;
+
+	regcache_raw_read (regcache, tdep->arg1,
+			   (gdb_byte *) & end_data_segment);
+
+	if (record_top_of_heap)
+	  {
+	    if (end_data_segment && record_top_of_heap > end_data_segment)
+	      {
+		target_terminal_ours ();
+		q =
+		  yquery (_("The next instruction is syscall brk.  "
+			    "It will release the memory that will cause "
+			    "process record target get error.  Do "
+			    "you want to stop the inferior?"));
+		target_terminal_inferior ();
+		if (q)
+		  return 1;
+	      }
+	  }
+	else
+	  {
+
+	    target_terminal_ours ();
+	    q =
+	      yquery (_("The next instruction is syscall brk.  "
+			"Process record cannot make sure it will "
+			"release memory or not.  "
+			"It may cause process record target get error.  "
+			"Do you want to stop the inferior?"));
+	    target_terminal_inferior ();
+	    if (q)
+	      return 1;
+	  }
+
+	/* If syscall brk execute, end_data_segment will be
+	   the top of heap.  */
+	record_top_of_heap = end_data_segment;
+      }
+      break;
+
       /* sys_setgid16 */
     case 46:
       /* sys_getgid16 */
--- a/linux-record.h
+++ b/linux-record.h
@@ -167,6 +167,7 @@ struct linux_record_tdep
   int arg5;
 };
 
+extern void record_linux_reset (struct gdbarch *gdbarch);
 extern int record_linux_system_call (int num, struct regcache *regcache,
 				     struct linux_record_tdep *tdep);
 
--- a/record.c
+++ b/record.c
@@ -440,6 +440,10 @@ record_open (char *name, int from_tty)
 	return;
     }
 
+  /*Reset the gdbarch part of process record.  */
+  if (gdbarch_process_record_reset_p (current_gdbarch))
+    gdbarch_process_record_reset (current_gdbarch);
+
   /*Reset the beneath function pointers.  */
   record_beneath_to_resume = NULL;
   record_beneath_to_wait = NULL;


More information about the Gdb-patches mailing list