2008-05-21 Pedro Alves * infrun.c (struct execution_control_state): Add stepping_through_longjmp and longjmp_caller_frame. (init_execution_control_state): Clear them. (context_switch): Context switch them. (handle_inferior_event): If stepping through longjmp, a SIGTRAP is not random. When a longjmp breakpoint is hit, prepare to step until the other end. Keep stepping while in an inner frame relative to the longjmp caller. When coming out on the other end, check if the step-resume breakpoint frame is not needed anymore. (currently_stepping): Return true if stepping through longjmp. * breakpoint.c (set_longjmp_breakpoint): Always set longjmp breakpoints. * gdbthread.h (struct thread_info): Add stepping_through_longjmp and longjmp_caller_frame. (save_infrun_state, load_infrun_state): Context switch them. * thread.c (save_infrun_state, load_infrun_state): Ditto. --- gdb/breakpoint.c | 13 ++---- gdb/gdbthread.h | 14 ++++++- gdb/infrun.c | 106 +++++++++++++++++++++++++++++++++++++++++-------------- gdb/thread.c | 12 +++++- 4 files changed, 106 insertions(+), 39 deletions(-) Index: src/gdb/infrun.c =================================================================== --- src.orig/gdb/infrun.c 2008-05-21 20:27:11.000000000 +0100 +++ src/gdb/infrun.c 2008-05-21 22:31:15.000000000 +0100 @@ -1386,6 +1386,13 @@ struct execution_control_state int step_after_step_resume_breakpoint; int stepping_through_solib_after_catch; bpstat stepping_through_solib_catchpoints; + + /* True if a longjmp call was detected while stepping, and we're + single-stepping until the other end. */ + int stepping_through_longjmp; + /* The frame that called longjmp. */ + struct frame_id longjmp_caller_frame; + int new_thread_event; struct target_waitstatus tmpstatus; enum infwait_states infwait_state; @@ -1556,6 +1563,8 @@ init_execution_control_state (struct exe ecs->infwait_state = infwait_normal_state; ecs->waiton_ptid = pid_to_ptid (-1); ecs->wp = &(ecs->ws); + ecs->stepping_through_longjmp = 0; + ecs->longjmp_caller_frame = null_frame_id; } /* Return the cached copy of the last pid/waitstatus returned by @@ -1605,7 +1614,9 @@ context_switch (struct execution_control ecs->stepping_over_breakpoint, ecs->stepping_through_solib_after_catch, ecs->stepping_through_solib_catchpoints, - ecs->current_line, ecs->current_symtab); + ecs->current_line, ecs->current_symtab, + ecs->stepping_through_longjmp, + ecs->longjmp_caller_frame); /* Load infrun state for the new thread. */ load_infrun_state (ecs->ptid, &prev_pc, @@ -1615,7 +1626,9 @@ context_switch (struct execution_control &ecs->stepping_over_breakpoint, &ecs->stepping_through_solib_after_catch, &ecs->stepping_through_solib_catchpoints, - &ecs->current_line, &ecs->current_symtab); + &ecs->current_line, &ecs->current_symtab, + &ecs->stepping_through_longjmp, + &ecs->longjmp_caller_frame); } switch_to_thread (ecs->ptid); @@ -2449,7 +2462,8 @@ handle_inferior_event (struct execution_ ecs->random_signal = !(bpstat_explains_signal (stop_bpstat) || stepping_over_breakpoint - || (step_range_end && step_resume_breakpoint == NULL)); + || (step_range_end && step_resume_breakpoint == NULL) + || ecs->stepping_through_longjmp); else { ecs->random_signal = !bpstat_explains_signal (stop_bpstat); @@ -2562,7 +2576,6 @@ process_event_stop_test: /* Handle cases caused by hitting a breakpoint. */ { - CORE_ADDR jmp_buf_pc; struct bpstat_what what; what = bpstat_what (stop_bpstat); @@ -2575,35 +2588,21 @@ process_event_stop_test: switch (what.main_action) { case BPSTAT_WHAT_SET_LONGJMP_RESUME: - /* If we hit the breakpoint at longjmp while stepping, we - install a momentary breakpoint at the target of the - jmp_buf. */ - + /* If we hit the breakpoint at longjmp while stepping, prepare + to step all the way through it. */ if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n"); + /* Step over this longjmp breakpoint. */ ecs->stepping_over_breakpoint = 1; - if (!gdbarch_get_longjmp_target_p (current_gdbarch) - || !gdbarch_get_longjmp_target (current_gdbarch, - get_current_frame (), &jmp_buf_pc)) - { - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "\ -infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n"); - keep_going (ecs); - return; - } - - /* We're going to replace the current step-resume breakpoint - with a longjmp-resume breakpoint. */ - if (step_resume_breakpoint != NULL) - delete_step_resume_breakpoint (&step_resume_breakpoint); - - /* Insert a breakpoint at resume address. */ - insert_longjmp_resume_breakpoint (jmp_buf_pc); + /* Store the frame id of the caller. We'll single-step until + we detect an outer frame becoming current. */ + ecs->longjmp_caller_frame = frame_unwind_id (get_current_frame ()); + /* See you on the other side! */ + ecs->stepping_through_longjmp = 1; keep_going (ecs); return; @@ -2819,6 +2818,60 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( return; } + if (ecs->stepping_through_longjmp) + { + struct frame_info *frame = get_current_frame (); + struct frame_id frame_id = get_frame_id (frame); + struct gdbarch *gdbarch = get_frame_arch (frame); + + /* If the current frame is the longjmp caller's, then we're + done. We're also done if the longjmp caller's frame is not + found on the frame stack. */ + if (!frame_id_eq (frame_id, ecs->longjmp_caller_frame) + && frame_find_by_id (ecs->longjmp_caller_frame) != NULL) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: stepping through longjmp\n"); +#if 0 + /* REMOVE ME: For debugging purposes only. */ + print_stack_frame (frame, 0, SRC_AND_LOC); +#endif + /* Still not there. */ + keep_going (ecs); + return; + } + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: got out of longjmp\n"); + + /* We made it. */ + ecs->stepping_through_longjmp = 0; + + /* If there's a step-resume breakpoint set, decide if we should + keep stepping to the step-resume breakpoint, or if the + longjmp took us outermost already, hence the step-resume + breakpoint will never be hit, and we should stop now. */ + if (step_resume_breakpoint) + { + if (!frame_id_eq (frame_id, step_resume_breakpoint->frame_id) + && frame_find_by_id (step_resume_breakpoint->frame_id) != NULL) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "\ +infrun: step-resume frame found on the stack\n"); + } + else + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: step-resume overran by longjmp\n"); + delete_step_resume_breakpoint (&step_resume_breakpoint); + } + } + } + if (step_resume_breakpoint) { if (debug_infrun) @@ -3183,6 +3236,7 @@ currently_stepping (struct execution_con { return (((step_range_end && step_resume_breakpoint == NULL) || stepping_over_breakpoint) + || ecs->stepping_through_longjmp || ecs->stepping_through_solib_after_catch || bpstat_should_step ()); } Index: src/gdb/gdbthread.h =================================================================== --- src.orig/gdb/gdbthread.h 2008-05-21 20:27:11.000000000 +0100 +++ src/gdb/gdbthread.h 2008-05-21 20:42:06.000000000 +0100 @@ -63,6 +63,12 @@ struct thread_info when we finally do stop stepping. */ bpstat stepping_through_solib_catchpoints; + /* True if a longjmp call was detected while stepping, and we're + single-stepping until the other end. */ + int stepping_through_longjmp; + /* The frame that called longjmp. */ + struct frame_id longjmp_caller_frame; + /* Private data used by the target vector implementation. */ struct private_thread_info *private; }; @@ -126,7 +132,9 @@ extern void save_infrun_state (ptid_t pt int stepping_through_solib_after_catch, bpstat stepping_through_solib_catchpoints, int current_line, - struct symtab *current_symtab); + struct symtab *current_symtab, + int stepping_through_longjmp, + struct frame_id longjmp_caller_frame); /* infrun context switch: load the debugger state previously saved for the given thread. */ @@ -141,7 +149,9 @@ extern void load_infrun_state (ptid_t pt int *stepping_through_solib_affter_catch, bpstat *stepping_through_solib_catchpoints, int *current_line, - struct symtab **current_symtab); + struct symtab **current_symtab, + int *stepping_through_longjmp, + struct frame_id *longjmp_caller_frame); /* Switch from one thread to another. */ extern void switch_to_thread (ptid_t ptid); Index: src/gdb/thread.c =================================================================== --- src.orig/gdb/thread.c 2008-05-21 20:27:11.000000000 +0100 +++ src/gdb/thread.c 2008-05-21 20:42:06.000000000 +0100 @@ -325,7 +325,9 @@ load_infrun_state (ptid_t ptid, int *stepping_through_solib_after_catch, bpstat *stepping_through_solib_catchpoints, int *current_line, - struct symtab **current_symtab) + struct symtab **current_symtab, + int *stepping_through_longjmp, + struct frame_id *longjmp_caller_frame) { struct thread_info *tp; @@ -348,6 +350,8 @@ load_infrun_state (ptid_t ptid, tp->stepping_through_solib_catchpoints; *current_line = tp->current_line; *current_symtab = tp->current_symtab; + *stepping_through_longjmp = tp->stepping_through_longjmp; + *longjmp_caller_frame = tp->longjmp_caller_frame; } /* Save infrun state for the thread PID. */ @@ -364,7 +368,9 @@ save_infrun_state (ptid_t ptid, int stepping_through_solib_after_catch, bpstat stepping_through_solib_catchpoints, int current_line, - struct symtab *current_symtab) + struct symtab *current_symtab, + int stepping_through_longjmp, + struct frame_id longjmp_caller_frame) { struct thread_info *tp; @@ -385,6 +391,8 @@ save_infrun_state (ptid_t ptid, tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints; tp->current_line = current_line; tp->current_symtab = current_symtab; + tp->stepping_through_longjmp = stepping_through_longjmp; + tp->longjmp_caller_frame = longjmp_caller_frame; } /* Return true if TP is an active thread. */ Index: src/gdb/breakpoint.c =================================================================== --- src.orig/gdb/breakpoint.c 2008-05-21 20:27:11.000000000 +0100 +++ src/gdb/breakpoint.c 2008-05-21 20:42:06.000000000 +0100 @@ -4470,15 +4470,10 @@ create_longjmp_breakpoint (char *func_na void set_longjmp_breakpoint (void) { - struct breakpoint *b; - - if (gdbarch_get_longjmp_target_p (current_gdbarch)) - { - create_longjmp_breakpoint ("longjmp"); - create_longjmp_breakpoint ("_longjmp"); - create_longjmp_breakpoint ("siglongjmp"); - create_longjmp_breakpoint ("_siglongjmp"); - } + create_longjmp_breakpoint ("longjmp"); + create_longjmp_breakpoint ("_longjmp"); + create_longjmp_breakpoint ("siglongjmp"); + create_longjmp_breakpoint ("_siglongjmp"); } /* Delete all longjmp breakpoints from THREAD. */