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

Hui Zhu
2009-05-05T13:09:09Z

[-- Attachment #1: Type: text/plain, Size: 3426 bytes --]

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
>

[-- Attachment #2: check-brk.txt --]
[-- Type: text/plain, Size: 10478 bytes --]

---
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);
+}
+
\f
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;