2008-05-17 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 is no longer needed. (currently_stepping): Return true if stepping through longjmp. * 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/gdbthread.h | 14 +++++++- gdb/infrun.c | 95 +++++++++++++++++++++++++++++++++++++++++--------------- gdb/thread.c | 12 +++++-- 3 files changed, 93 insertions(+), 28 deletions(-) Index: src/gdb/infrun.c =================================================================== --- src.orig/gdb/infrun.c 2008-05-17 02:38:39.000000000 +0100 +++ src/gdb/infrun.c 2008-05-17 02:49:21.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,7 @@ 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; } /* Return the cached copy of the last pid/waitstatus returned by @@ -1605,7 +1613,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 +1625,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 +2461,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); @@ -2575,35 +2588,24 @@ 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); + /* Why do I need to call this? */ + get_frame_type (get_current_frame ()); - /* Insert a breakpoint at resume address. */ - insert_longjmp_resume_breakpoint (jmp_buf_pc); + /* Store the frame of the caller. This is used to compare + with any step-resume step set. */ + 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 +2821,50 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( return; } + if (ecs->stepping_through_longjmp) + { + struct frame_info *frame = get_current_frame (); + struct frame_id current_frame = get_frame_id (frame); + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: stepping through longjmp\n"); + if (frame_id_inner (get_frame_arch (frame), + current_frame, ecs->longjmp_caller_frame)) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: still in longjmp\n"); + keep_going (ecs); + return; + } + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: got out of longjmp\n"); + ecs->stepping_through_longjmp = 0; + + /* Now decide if we should proceed to the step-resume + breakpoint, or if we already went passed it. */ + + if (step_resume_breakpoint) + { + if (frame_id_inner (get_frame_arch (frame), + current_frame, step_resume_breakpoint->frame_id)) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: longjmp-resume is inner than step-resume\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 +3229,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-17 02:39:51.000000000 +0100 +++ src/gdb/gdbthread.h 2008-05-17 02:45:44.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-17 02:39:54.000000000 +0100 +++ src/gdb/thread.c 2008-05-17 02:47:18.000000000 +0100 @@ -323,7 +323,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; @@ -346,6 +348,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. */ @@ -362,7 +366,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; @@ -383,6 +389,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. */