--- 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;