gdb/
Remove stale dummy frames. * breakpoint.c: Include dummy-frame.h. (longjmp_breakpoint_ops): New variable. (update_breakpoints_after_exec, breakpoint_init_inferior): Delete also bp_longjmp_call_dummy. (bpstat_what, bptype_string, print_one_breakpoint_location) (init_bp_location): Support bp_longjmp_call_dummy. (set_longjmp_breakpoint): Use longjmp_breakpoint_ops. Comment why. (set_longjmp_breakpoint_for_call_dummy) (check_longjmp_breakpoint_for_call_dummy, longjmp_bkpt_dtor): New functions. (initialize_breakpoint_ops): Initialize longjmp_breakpoint_ops. * breakpoint.h (enum bptype): New item bp_longjmp_call_dummy. Delete FIXME comment and extend the other comment for bp_call_dummy. (set_longjmp_breakpoint_for_call_dummy) (check_longjmp_breakpoint_for_call_dummy): New declarations. * dummy-frame.c: Include gdbthread.h. (pop_dummy_frame_bpt): New function. (pop_dummy_frame): Call pop_dummy_frame_bpt. (dummy_frame_discard): New function. (cleanup_dummy_frames): Update the comment about longjmps. * dummy-frame.h (dummy_frame_discard): New declaration. * gdbthread.h (struct thread_info): Extend initiating_frame comment. * infcall.c (call_function_by_hand): New variable longjmp_b. Call set_longjmp_breakpoint_for_call_dummy. Chain its breakpoints with BPT. * infrun.c (handle_inferior_event) <BPSTAT_WHAT_CLEAR_LONGJMP_RESUME>: Add case 4 comment. Call check_longjmp_breakpoint_for_call_dummy and keep_going if IS_LONGJMP and there is no other reason to stop. gdb/testsuite/ Remove stale dummy frames. * gdb.base/call-signal-resume.exp (maintenance print dummy-frames) (maintenance info breakpoints): New tests. * gdb.base/stale-infcall.c: New file. * gdb.base/stale-infcall.exp: New file.
This commit is contained in:
parent
09d5912c6f
commit
e2e4d78b22
12 changed files with 367 additions and 21 deletions
|
@ -1,3 +1,34 @@
|
|||
2012-06-18 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
|
||||
Remove stale dummy frames.
|
||||
* breakpoint.c: Include dummy-frame.h.
|
||||
(longjmp_breakpoint_ops): New variable.
|
||||
(update_breakpoints_after_exec, breakpoint_init_inferior): Delete also
|
||||
bp_longjmp_call_dummy.
|
||||
(bpstat_what, bptype_string, print_one_breakpoint_location)
|
||||
(init_bp_location): Support bp_longjmp_call_dummy.
|
||||
(set_longjmp_breakpoint): Use longjmp_breakpoint_ops. Comment why.
|
||||
(set_longjmp_breakpoint_for_call_dummy)
|
||||
(check_longjmp_breakpoint_for_call_dummy, longjmp_bkpt_dtor): New
|
||||
functions.
|
||||
(initialize_breakpoint_ops): Initialize longjmp_breakpoint_ops.
|
||||
* breakpoint.h (enum bptype): New item bp_longjmp_call_dummy. Delete
|
||||
FIXME comment and extend the other comment for bp_call_dummy.
|
||||
(set_longjmp_breakpoint_for_call_dummy)
|
||||
(check_longjmp_breakpoint_for_call_dummy): New declarations.
|
||||
* dummy-frame.c: Include gdbthread.h.
|
||||
(pop_dummy_frame_bpt): New function.
|
||||
(pop_dummy_frame): Call pop_dummy_frame_bpt.
|
||||
(dummy_frame_discard): New function.
|
||||
(cleanup_dummy_frames): Update the comment about longjmps.
|
||||
* dummy-frame.h (dummy_frame_discard): New declaration.
|
||||
* gdbthread.h (struct thread_info): Extend initiating_frame comment.
|
||||
* infcall.c (call_function_by_hand): New variable longjmp_b. Call
|
||||
set_longjmp_breakpoint_for_call_dummy. Chain its breakpoints with BPT.
|
||||
* infrun.c (handle_inferior_event) <BPSTAT_WHAT_CLEAR_LONGJMP_RESUME>:
|
||||
Add case 4 comment. Call check_longjmp_breakpoint_for_call_dummy and
|
||||
keep_going if IS_LONGJMP and there is no other reason to stop.
|
||||
|
||||
2012-06-18 Greta Yorsh <Greta.Yorsh@arm.com>
|
||||
|
||||
* remote-sim.c (sim_command_completer): Initialize
|
||||
|
|
106
gdb/breakpoint.c
106
gdb/breakpoint.c
|
@ -68,6 +68,7 @@
|
|||
#include "skip.h"
|
||||
#include "gdb_regex.h"
|
||||
#include "ax-gdb.h"
|
||||
#include "dummy-frame.h"
|
||||
|
||||
/* readline include files */
|
||||
#include "readline/readline.h"
|
||||
|
@ -287,6 +288,9 @@ static struct breakpoint_ops internal_breakpoint_ops;
|
|||
/* Momentary breakpoints class type. */
|
||||
static struct breakpoint_ops momentary_breakpoint_ops;
|
||||
|
||||
/* Momentary breakpoints for bp_longjmp and bp_exception class type. */
|
||||
static struct breakpoint_ops longjmp_breakpoint_ops;
|
||||
|
||||
/* The breakpoint_ops structure to be used in regular user created
|
||||
breakpoints. */
|
||||
struct breakpoint_ops bkpt_breakpoint_ops;
|
||||
|
@ -3204,6 +3208,7 @@ update_breakpoints_after_exec (void)
|
|||
/* Longjmp and longjmp-resume breakpoints are also meaningless
|
||||
after an exec. */
|
||||
if (b->type == bp_longjmp || b->type == bp_longjmp_resume
|
||||
|| b->type == bp_longjmp_call_dummy
|
||||
|| b->type == bp_exception || b->type == bp_exception_resume)
|
||||
{
|
||||
delete_breakpoint (b);
|
||||
|
@ -3495,6 +3500,7 @@ breakpoint_init_inferior (enum inf_context context)
|
|||
switch (b->type)
|
||||
{
|
||||
case bp_call_dummy:
|
||||
case bp_longjmp_call_dummy:
|
||||
|
||||
/* If the call dummy breakpoint is at the entry point it will
|
||||
cause problems when the inferior is rerun, so we better get
|
||||
|
@ -5154,9 +5160,10 @@ bpstat_what (bpstat bs_head)
|
|||
}
|
||||
break;
|
||||
case bp_longjmp:
|
||||
case bp_longjmp_call_dummy:
|
||||
case bp_exception:
|
||||
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
|
||||
retval.is_longjmp = bptype == bp_longjmp;
|
||||
retval.is_longjmp = bptype != bp_exception;
|
||||
break;
|
||||
case bp_longjmp_resume:
|
||||
case bp_exception_resume:
|
||||
|
@ -5489,6 +5496,7 @@ bptype_string (enum bptype type)
|
|||
{bp_access_watchpoint, "acc watchpoint"},
|
||||
{bp_longjmp, "longjmp"},
|
||||
{bp_longjmp_resume, "longjmp resume"},
|
||||
{bp_longjmp_call_dummy, "longjmp for call dummy"},
|
||||
{bp_exception, "exception"},
|
||||
{bp_exception_resume, "exception resume"},
|
||||
{bp_step_resume, "step resume"},
|
||||
|
@ -5631,6 +5639,7 @@ print_one_breakpoint_location (struct breakpoint *b,
|
|||
case bp_finish:
|
||||
case bp_longjmp:
|
||||
case bp_longjmp_resume:
|
||||
case bp_longjmp_call_dummy:
|
||||
case bp_exception:
|
||||
case bp_exception_resume:
|
||||
case bp_step_resume:
|
||||
|
@ -6494,6 +6503,7 @@ init_bp_location (struct bp_location *loc, const struct bp_location_ops *ops,
|
|||
case bp_finish:
|
||||
case bp_longjmp:
|
||||
case bp_longjmp_resume:
|
||||
case bp_longjmp_call_dummy:
|
||||
case bp_exception:
|
||||
case bp_exception_resume:
|
||||
case bp_step_resume:
|
||||
|
@ -6797,8 +6807,10 @@ set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
|
|||
enum bptype type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
|
||||
struct breakpoint *clone;
|
||||
|
||||
/* longjmp_breakpoint_ops ensures INITIATING_FRAME is cleared again
|
||||
after their removal. */
|
||||
clone = momentary_breakpoint_from_master (b, type,
|
||||
&momentary_breakpoint_ops);
|
||||
&longjmp_breakpoint_ops);
|
||||
clone->thread = thread;
|
||||
}
|
||||
|
||||
|
@ -6832,6 +6844,75 @@ delete_longjmp_breakpoint_at_next_stop (int thread)
|
|||
}
|
||||
}
|
||||
|
||||
/* Place breakpoints of type bp_longjmp_call_dummy to catch longjmp for
|
||||
INFERIOR_PTID thread. Chain them all by RELATED_BREAKPOINT and return
|
||||
pointer to any of them. Return NULL if this system cannot place longjmp
|
||||
breakpoints. */
|
||||
|
||||
struct breakpoint *
|
||||
set_longjmp_breakpoint_for_call_dummy (void)
|
||||
{
|
||||
struct breakpoint *b, *retval = NULL;
|
||||
|
||||
ALL_BREAKPOINTS (b)
|
||||
if (b->pspace == current_program_space && b->type == bp_longjmp_master)
|
||||
{
|
||||
struct breakpoint *new_b;
|
||||
|
||||
new_b = momentary_breakpoint_from_master (b, bp_longjmp_call_dummy,
|
||||
&momentary_breakpoint_ops);
|
||||
new_b->thread = pid_to_thread_id (inferior_ptid);
|
||||
|
||||
/* Link NEW_B into the chain of RETVAL breakpoints. */
|
||||
|
||||
gdb_assert (new_b->related_breakpoint == new_b);
|
||||
if (retval == NULL)
|
||||
retval = new_b;
|
||||
new_b->related_breakpoint = retval;
|
||||
while (retval->related_breakpoint != new_b->related_breakpoint)
|
||||
retval = retval->related_breakpoint;
|
||||
retval->related_breakpoint = new_b;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Verify all existing dummy frames and their associated breakpoints for
|
||||
THREAD. Remove those which can no longer be found in the current frame
|
||||
stack.
|
||||
|
||||
You should call this function only at places where it is safe to currently
|
||||
unwind the whole stack. Failed stack unwind would discard live dummy
|
||||
frames. */
|
||||
|
||||
void
|
||||
check_longjmp_breakpoint_for_call_dummy (int thread)
|
||||
{
|
||||
struct breakpoint *b, *b_tmp;
|
||||
|
||||
ALL_BREAKPOINTS_SAFE (b, b_tmp)
|
||||
if (b->type == bp_longjmp_call_dummy && b->thread == thread)
|
||||
{
|
||||
struct breakpoint *dummy_b = b->related_breakpoint;
|
||||
|
||||
while (dummy_b != b && dummy_b->type != bp_call_dummy)
|
||||
dummy_b = dummy_b->related_breakpoint;
|
||||
if (dummy_b->type != bp_call_dummy
|
||||
|| frame_find_by_id (dummy_b->frame_id) != NULL)
|
||||
continue;
|
||||
|
||||
dummy_frame_discard (dummy_b->frame_id);
|
||||
|
||||
while (b->related_breakpoint != b)
|
||||
{
|
||||
if (b_tmp == b->related_breakpoint)
|
||||
b_tmp = b->related_breakpoint->next;
|
||||
delete_breakpoint (b->related_breakpoint);
|
||||
}
|
||||
delete_breakpoint (b);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
enable_overlay_breakpoints (void)
|
||||
{
|
||||
|
@ -12821,6 +12902,22 @@ momentary_bkpt_print_mention (struct breakpoint *b)
|
|||
/* Nothing to mention. These breakpoints are internal. */
|
||||
}
|
||||
|
||||
/* Ensure INITIATING_FRAME is cleared when no such breakpoint exists.
|
||||
|
||||
It gets cleared already on the removal of the first one of such placed
|
||||
breakpoints. This is OK as they get all removed altogether. */
|
||||
|
||||
static void
|
||||
longjmp_bkpt_dtor (struct breakpoint *self)
|
||||
{
|
||||
struct thread_info *tp = find_thread_id (self->thread);
|
||||
|
||||
if (tp)
|
||||
tp->initiating_frame = null_frame_id;
|
||||
|
||||
momentary_breakpoint_ops.dtor (self);
|
||||
}
|
||||
|
||||
/* Specific methods for probe breakpoints. */
|
||||
|
||||
static int
|
||||
|
@ -15409,6 +15506,11 @@ initialize_breakpoint_ops (void)
|
|||
ops->print_it = momentary_bkpt_print_it;
|
||||
ops->print_mention = momentary_bkpt_print_mention;
|
||||
|
||||
/* Momentary breakpoints for bp_longjmp and bp_exception. */
|
||||
ops = &longjmp_breakpoint_ops;
|
||||
*ops = momentary_breakpoint_ops;
|
||||
ops->dtor = longjmp_bkpt_dtor;
|
||||
|
||||
/* Probe breakpoints. */
|
||||
ops = &bkpt_probe_breakpoint_ops;
|
||||
*ops = bkpt_breakpoint_ops;
|
||||
|
|
|
@ -65,6 +65,12 @@ enum bptype
|
|||
bp_longjmp, /* secret breakpoint to find longjmp() */
|
||||
bp_longjmp_resume, /* secret breakpoint to escape longjmp() */
|
||||
|
||||
/* Breakpoint placed to the same location(s) like bp_longjmp but used to
|
||||
protect against stale DUMMY_FRAME. Multiple bp_longjmp_call_dummy and
|
||||
one bp_call_dummy are chained together by related_breakpoint for each
|
||||
DUMMY_FRAME. */
|
||||
bp_longjmp_call_dummy,
|
||||
|
||||
/* An internal breakpoint that is installed on the unwinder's
|
||||
debug hook. */
|
||||
bp_exception,
|
||||
|
@ -94,14 +100,8 @@ enum bptype
|
|||
3) It can never be disabled. */
|
||||
bp_watchpoint_scope,
|
||||
|
||||
/* The breakpoint at the end of a call dummy. */
|
||||
/* FIXME: What if the function we are calling longjmp()s out of
|
||||
the call, or the user gets out with the "return" command? We
|
||||
currently have no way of cleaning up the breakpoint in these
|
||||
(obscure) situations. (Probably can solve this by noticing
|
||||
longjmp, "return", etc., it's similar to noticing when a
|
||||
watchpoint on a local variable goes out of scope (with hardware
|
||||
support for watchpoints)). */
|
||||
/* The breakpoint at the end of a call dummy. See bp_longjmp_call_dummy it
|
||||
is chained with by related_breakpoint. */
|
||||
bp_call_dummy,
|
||||
|
||||
/* A breakpoint set on std::terminate, that is used to catch
|
||||
|
@ -1287,6 +1287,9 @@ extern void delete_longjmp_breakpoint (int thread);
|
|||
/* Mark all longjmp breakpoints from THREAD for later deletion. */
|
||||
extern void delete_longjmp_breakpoint_at_next_stop (int thread);
|
||||
|
||||
extern struct breakpoint *set_longjmp_breakpoint_for_call_dummy (void);
|
||||
extern void check_longjmp_breakpoint_for_call_dummy (int thread);
|
||||
|
||||
extern void enable_overlay_breakpoints (void);
|
||||
extern void disable_overlay_breakpoints (void);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "gdbcmd.h"
|
||||
#include "gdb_string.h"
|
||||
#include "observer.h"
|
||||
#include "gdbthread.h"
|
||||
|
||||
/* Dummy frame. This saves the processor state just prior to setting
|
||||
up the inferior function call. Older targets save the registers
|
||||
|
@ -108,19 +109,44 @@ remove_dummy_frame (struct dummy_frame **dummy_ptr)
|
|||
xfree (dummy);
|
||||
}
|
||||
|
||||
/* Delete any breakpoint B which is a momentary breakpoint for return from
|
||||
inferior call matching DUMMY_VOIDP. */
|
||||
|
||||
static int
|
||||
pop_dummy_frame_bpt (struct breakpoint *b, void *dummy_voidp)
|
||||
{
|
||||
struct dummy_frame *dummy = dummy_voidp;
|
||||
|
||||
if (b->thread == pid_to_thread_id (inferior_ptid)
|
||||
&& b->disposition == disp_del && frame_id_eq (b->frame_id, dummy->id))
|
||||
{
|
||||
while (b->related_breakpoint != b)
|
||||
delete_breakpoint (b->related_breakpoint);
|
||||
|
||||
delete_breakpoint (b);
|
||||
|
||||
/* Stop the traversal. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Continue the traversal. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pop *DUMMY_PTR, restoring program state to that before the
|
||||
frame was created. */
|
||||
|
||||
static void
|
||||
pop_dummy_frame (struct dummy_frame **dummy_ptr)
|
||||
{
|
||||
struct dummy_frame *dummy;
|
||||
struct dummy_frame *dummy = *dummy_ptr;
|
||||
|
||||
restore_infcall_suspend_state ((*dummy_ptr)->caller_state);
|
||||
restore_infcall_suspend_state (dummy->caller_state);
|
||||
|
||||
iterate_over_breakpoints (pop_dummy_frame_bpt, dummy);
|
||||
|
||||
/* restore_infcall_control_state frees inf_state,
|
||||
all that remains is to pop *dummy_ptr. */
|
||||
dummy = *dummy_ptr;
|
||||
*dummy_ptr = dummy->next;
|
||||
xfree (dummy);
|
||||
|
||||
|
@ -166,9 +192,22 @@ dummy_frame_pop (struct frame_id dummy_id)
|
|||
pop_dummy_frame (dp);
|
||||
}
|
||||
|
||||
/* There may be stale dummy frames, perhaps left over from when a longjump took
|
||||
us out of a function that was called by the debugger. Clean them up at
|
||||
least once whenever we start a new inferior. */
|
||||
/* Drop dummy frame DUMMY_ID. Do nothing if it is not found. Do not restore
|
||||
its state into inferior, just free its memory. */
|
||||
|
||||
void
|
||||
dummy_frame_discard (struct frame_id dummy_id)
|
||||
{
|
||||
struct dummy_frame **dp;
|
||||
|
||||
dp = lookup_dummy_frame (dummy_id);
|
||||
if (dp)
|
||||
remove_dummy_frame (dp);
|
||||
}
|
||||
|
||||
/* There may be stale dummy frames, perhaps left over from when an uncaught
|
||||
longjmp took us out of a function that was called by the debugger. Clean
|
||||
them up at least once whenever we start a new inferior. */
|
||||
|
||||
static void
|
||||
cleanup_dummy_frames (struct target_ops *target, int from_tty)
|
||||
|
|
|
@ -52,6 +52,8 @@ extern void dummy_frame_push (struct infcall_suspend_state *caller_state,
|
|||
|
||||
extern void dummy_frame_pop (struct frame_id dummy_id);
|
||||
|
||||
extern void dummy_frame_discard (struct frame_id dummy_id);
|
||||
|
||||
/* If the PC falls in a dummy frame, return a dummy frame
|
||||
unwinder. */
|
||||
|
||||
|
|
|
@ -216,7 +216,9 @@ struct thread_info
|
|||
int stop_requested;
|
||||
|
||||
/* The initiating frame of a nexting operation, used for deciding
|
||||
which exceptions to intercept. */
|
||||
which exceptions to intercept. If it is null_frame_id no
|
||||
bp_longjmp or bp_exception but longjmp has been caught just for
|
||||
bp_longjmp_call_dummy. */
|
||||
struct frame_id initiating_frame;
|
||||
|
||||
/* Private data used by the target vector implementation. */
|
||||
|
|
|
@ -744,7 +744,7 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
|
|||
inferior. That way it breaks when it returns. */
|
||||
|
||||
{
|
||||
struct breakpoint *bpt;
|
||||
struct breakpoint *bpt, *longjmp_b;
|
||||
struct symtab_and_line sal;
|
||||
|
||||
init_sal (&sal); /* initialize to zeroes */
|
||||
|
@ -760,6 +760,17 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
|
|||
frame = NULL;
|
||||
|
||||
bpt->disposition = disp_del;
|
||||
gdb_assert (bpt->related_breakpoint == bpt);
|
||||
|
||||
longjmp_b = set_longjmp_breakpoint_for_call_dummy ();
|
||||
if (longjmp_b)
|
||||
{
|
||||
/* Link BPT into the chain of LONGJMP_B. */
|
||||
bpt->related_breakpoint = longjmp_b;
|
||||
while (longjmp_b->related_breakpoint != bpt->related_breakpoint)
|
||||
longjmp_b = longjmp_b->related_breakpoint;
|
||||
longjmp_b->related_breakpoint = bpt;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a breakpoint in std::terminate.
|
||||
|
|
22
gdb/infrun.c
22
gdb/infrun.c
|
@ -4445,18 +4445,34 @@ process_event_stop_test:
|
|||
3. The initiating frame exists and is different from the
|
||||
current frame. This means the exception or longjmp has
|
||||
been caught beneath the initiating frame, so keep
|
||||
going. */
|
||||
going.
|
||||
|
||||
4. longjmp breakpoint has been placed just to protect
|
||||
against stale dummy frames and user is not interested in
|
||||
stopping around longjmps. */
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
|
||||
|
||||
init_frame = frame_find_by_id (ecs->event_thread->initiating_frame);
|
||||
|
||||
gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
|
||||
!= NULL);
|
||||
delete_exception_resume_breakpoint (ecs->event_thread);
|
||||
|
||||
if (what.is_longjmp)
|
||||
{
|
||||
check_longjmp_breakpoint_for_call_dummy (ecs->event_thread->num);
|
||||
|
||||
if (!frame_id_p (ecs->event_thread->initiating_frame))
|
||||
{
|
||||
/* Case 4. */
|
||||
keep_going (ecs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
init_frame = frame_find_by_id (ecs->event_thread->initiating_frame);
|
||||
|
||||
if (init_frame)
|
||||
{
|
||||
struct frame_id current_id
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2012-06-18 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
|
||||
Remove stale dummy frames.
|
||||
* gdb.base/call-signal-resume.exp (maintenance print dummy-frames)
|
||||
(maintenance info breakpoints): New tests.
|
||||
* gdb.base/stale-infcall.c: New file.
|
||||
* gdb.base/stale-infcall.exp: New file.
|
||||
|
||||
2012-06-17 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
|
||||
* gdb.arch/amd64-entry-value-param.S: New file.
|
||||
|
|
|
@ -101,6 +101,18 @@ gdb_test "frame $frame_number" ".*"
|
|||
gdb_test_no_output "set confirm off"
|
||||
gdb_test "return" ""
|
||||
|
||||
# Verify there are no remains of the dummy frame.
|
||||
gdb_test_no_output "maintenance print dummy-frames"
|
||||
set test "maintenance info breakpoints"
|
||||
gdb_test_multiple $test $test {
|
||||
-re " call dummy .*\r\n$gdb_prompt $" {
|
||||
fail $test
|
||||
}
|
||||
-re "\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
# Resume execution, the program should continue without any signal.
|
||||
|
||||
gdb_test "break stop_two" "Breakpoint \[0-9\]* at .*"
|
||||
|
|
63
gdb/testsuite/gdb.base/stale-infcall.c
Normal file
63
gdb/testsuite/gdb.base/stale-infcall.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define BUFSIZE 0x1000
|
||||
|
||||
static jmp_buf jmp;
|
||||
|
||||
void
|
||||
infcall (void)
|
||||
{
|
||||
longjmp (jmp, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
run1 (void)
|
||||
{
|
||||
char buf[BUFSIZE / 2];
|
||||
int dummy = 0;
|
||||
|
||||
dummy++; /* break-run1 */
|
||||
}
|
||||
|
||||
static char buf_zero[BUFSIZE];
|
||||
|
||||
static void
|
||||
run2 (void)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
memset (buf, 0, sizeof (buf));
|
||||
|
||||
if (memcmp (buf, buf_zero, sizeof (buf)) != 0) /* break-run2 */
|
||||
abort (); /* break-fail */
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
if (setjmp (jmp) == 0)
|
||||
run1 ();
|
||||
else
|
||||
run2 ();
|
||||
|
||||
return 0; /* break-exit */
|
||||
}
|
57
gdb/testsuite/gdb.base/stale-infcall.exp
Normal file
57
gdb/testsuite/gdb.base/stale-infcall.exp
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Copyright (C) 2012 Free Software Foundation # Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set testfile stale-infcall
|
||||
set srcfile ${testfile}.c
|
||||
if { [prepare_for_testing $testfile.exp $testfile $srcfile] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
if ![runto_main] {
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_breakpoint [gdb_get_line_number "break-run1"]
|
||||
gdb_breakpoint [gdb_get_line_number "break-run2"]
|
||||
gdb_breakpoint [gdb_get_line_number "break-exit"]
|
||||
gdb_breakpoint [gdb_get_line_number "break-fail"]
|
||||
|
||||
gdb_continue_to_breakpoint "break-run1" ".* break-run1 .*"
|
||||
|
||||
gdb_test "print infcall ()" " break-run2 .*The program being debugged stopped while in a function called from GDB\\..*When the function is done executing, GDB will silently stop\\."
|
||||
|
||||
set test "stack corrupted"
|
||||
gdb_test_multiple "continue" $test {
|
||||
-re " break-exit .*\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
-re " break-fail .*\r\n$gdb_prompt $" {
|
||||
fail $test
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test "bt" "#0 \[^\r\n\]* main \[^\r\n\]*"
|
||||
|
||||
# Verify there are no remains of the dummy frame.
|
||||
gdb_test_no_output "maintenance print dummy-frames"
|
||||
set test "maintenance info breakpoints"
|
||||
gdb_test_multiple $test $test {
|
||||
-re " call dummy .*\r\n$gdb_prompt $" {
|
||||
fail $test
|
||||
}
|
||||
-re "\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue