Associate dummy_frame with ptid

This patch is to add ptid into dummy_frame and extend frame_id to
dummy_frame_id (which has a ptid field).  With this change, GDB uses
dummy_frame_id (thread ptid and frame_id) to find the dummy frames.

Currently, dummy frames are looked up by frame_id, which isn't
accurate in non-stop or multi-process mode.  The test case
gdb.multi/dummy-frame-restore.exp shows the problem and this patch can
fix it.

Test dummy-frame-restore.exp makes two inferiors stop at
different functions, say, inferior 1 stops at f1 while inferior 2
stops at f2.  Set a breakpoint to a function, do the inferior call
in two inferiors, and GDB has two dummy frames of the same frame_id.
When the inferior call is finished, GDB will look up a dummy frame
from its stack/list and restore the inferior's regcache.  Two
inferiors are finished in different orders, the inferiors' states are
restored differently, which is wrong.  Running dummy-frame-restore.exp
under un-patched GDB, we'll get two fails:

FAIL: gdb.multi/dummy-frame-restore.exp: inf 2 first: after infcall: bt in inferior 2
FAIL: gdb.multi/dummy-frame-restore.exp: inf 2 first: after infcall: bt in inferior 1

With this patch applied, GDB will choose the correct dummy_frame to
restore for a given inferior, because ptid is considered when looking up
dummy frames.  Two fails above are fixed.

Regression tested on x86_64-linux, both native and gdbserver.

gdb:

2014-06-27  Yao Qi  <yao@codesourcery.com>

	* breakpoint.c (check_longjmp_breakpoint_for_call_dummy):
	Change parameter type to 'struct thread_info *'.  Caller
	updated.
	* breakpoint.h (check_longjmp_breakpoint_for_call_dummy):
	Update declaration.
	* dummy-frame.c (struct dummy_frame_id): New.
	(dummy_frame_id_eq): New function.
	(struct dummy_frame) <id>: Change its type to 'struct
	dummy_frame_id'.
	(dummy_frame_push): Add parameter ptid and save it in
	dummy_frame_id.
	(pop_dummy_frame_bpt): Use ptid of dummy_frame instead of
	inferior_ptid.
	(pop_dummy_frame): Assert that the ptid of dummy_frame equals
	to inferior_ptid.
	(lookup_dummy_frame): Change parameter type to 'struct
	dummy_frame_id *'.  Callers updated.  Call dummy_frame_id_eq
	instead of frame_id_eq.
	(dummy_frame_pop): Add parameter ptid.  Callers updated.
	Update comments.  Compose dummy_frame_id and pass it to
	lookup_dummy_frame.
	(dummy_frame_discard): Add parameter ptid.
	(dummy_frame_sniffer): Compose dummy_frame_id and call
	dummy_frame_id_eq instead of frame_id_eq.
	(fprint_dummy_frames): Print ptid.
	* dummy-frame.h: Remove comments.
	(dummy_frame_push): Add ptid in declaration.
	(dummy_frame_pop, dummy_frame_discard): Likewise.

gdb/testsuite:

2014-06-27  Yao Qi  <yao@codesourcery.com>

	* gdb.multi/dummy-frame-restore.exp: New.
	* gdb.multi/dummy-frame-restore.c: New.

gdb/doc:

2014-06-27  Yao Qi  <yao@codesourcery.com>

	* gdb.texinfo (Maintenance Commands): Update the output of
	'maint print dummy-frames' command.
This commit is contained in:
Yao Qi
2014-06-25 11:52:52 +08:00
parent 9393ab1c00
commit b67a2c6fd4
13 changed files with 236 additions and 45 deletions

View File

@ -31,6 +31,25 @@
#include "observer.h"
#include "gdbthread.h"
struct dummy_frame_id
{
/* This frame's ID. Must match the value returned by
gdbarch_dummy_id. */
struct frame_id id;
/* The thread this dummy_frame relates to. */
ptid_t ptid;
};
/* Return whether dummy_frame_id *ID1 and *ID2 are equal. */
static int
dummy_frame_id_eq (struct dummy_frame_id *id1,
struct dummy_frame_id *id2)
{
return frame_id_eq (id1->id, id2->id) && ptid_equal (id1->ptid, id2->ptid);
}
/* Dummy frame. This saves the processor state just prior to setting
up the inferior function call. Older targets save the registers
on the target stack (but that really slows down function calls). */
@ -38,9 +57,10 @@
struct dummy_frame
{
struct dummy_frame *next;
/* This frame's ID. Must match the value returned by
gdbarch_dummy_id. */
struct frame_id id;
/* An id represents a dummy frame. */
struct dummy_frame_id id;
/* The caller's state prior to the call. */
struct infcall_suspend_state *caller_state;
};
@ -52,13 +72,14 @@ static struct dummy_frame *dummy_frame_stack = NULL;
void
dummy_frame_push (struct infcall_suspend_state *caller_state,
const struct frame_id *dummy_id)
const struct frame_id *dummy_id, ptid_t ptid)
{
struct dummy_frame *dummy_frame;
dummy_frame = XCNEW (struct dummy_frame);
dummy_frame->caller_state = caller_state;
dummy_frame->id = (*dummy_id);
dummy_frame->id.id = (*dummy_id);
dummy_frame->id.ptid = ptid;
dummy_frame->next = dummy_frame_stack;
dummy_frame_stack = dummy_frame;
}
@ -83,8 +104,8 @@ 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))
if (b->thread == pid_to_thread_id (dummy->id.ptid)
&& b->disposition == disp_del && frame_id_eq (b->frame_id, dummy->id.id))
{
while (b->related_breakpoint != b)
delete_breakpoint (b->related_breakpoint);
@ -107,6 +128,7 @@ pop_dummy_frame (struct dummy_frame **dummy_ptr)
{
struct dummy_frame *dummy = *dummy_ptr;
gdb_assert (ptid_equal (dummy->id.ptid, inferior_ptid));
restore_infcall_suspend_state (dummy->caller_state);
iterate_over_breakpoints (pop_dummy_frame_bpt, dummy);
@ -125,48 +147,47 @@ pop_dummy_frame (struct dummy_frame **dummy_ptr)
Return NULL if not found. */
static struct dummy_frame **
lookup_dummy_frame (struct frame_id dummy_id)
lookup_dummy_frame (struct dummy_frame_id *dummy_id)
{
struct dummy_frame **dp;
for (dp = &dummy_frame_stack; *dp != NULL; dp = &(*dp)->next)
{
if (frame_id_eq ((*dp)->id, dummy_id))
if (dummy_frame_id_eq (&(*dp)->id, dummy_id))
return dp;
}
return NULL;
}
/* Pop the dummy frame DUMMY_ID, restoring program state to that before the
frame was created.
/* Find the dummy frame by DUMMY_ID and PTID, and pop it, restoring
program state to that before the frame was created.
On return reinit_frame_cache has been called.
If the frame isn't found, flag an internal error.
NOTE: This can only pop the one frame, even if it is in the middle of the
stack, because the other frames may be for different threads, and there's
currently no way to tell which stack frame is for which thread. */
If the frame isn't found, flag an internal error. */
void
dummy_frame_pop (struct frame_id dummy_id)
dummy_frame_pop (struct frame_id dummy_id, ptid_t ptid)
{
struct dummy_frame **dp;
struct dummy_frame_id id = { dummy_id, ptid };
dp = lookup_dummy_frame (dummy_id);
dp = lookup_dummy_frame (&id);
gdb_assert (dp != NULL);
pop_dummy_frame (dp);
}
/* Drop dummy frame DUMMY_ID. Do nothing if it is not found. Do not restore
its state into inferior, just free its memory. */
/* Find the dummy frame by DUMMY_ID and PTID and drop it. 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)
dummy_frame_discard (struct frame_id dummy_id, ptid_t ptid)
{
struct dummy_frame **dp;
struct dummy_frame_id id = { dummy_id, ptid };
dp = lookup_dummy_frame (dummy_id);
dp = lookup_dummy_frame (&id);
if (dp)
remove_dummy_frame (dp);
}
@ -211,13 +232,14 @@ dummy_frame_sniffer (const struct frame_unwind *self,
dummy ID, assuming it is a dummy frame. */
struct frame_id this_id
= gdbarch_dummy_id (get_frame_arch (this_frame), this_frame);
struct dummy_frame_id dummy_id = { this_id, inferior_ptid };
/* Use that ID to find the corresponding cache entry. */
for (dummyframe = dummy_frame_stack;
dummyframe != NULL;
dummyframe = dummyframe->next)
{
if (frame_id_eq (dummyframe->id, this_id))
if (dummy_frame_id_eq (&dummyframe->id, &dummy_id))
{
struct dummy_frame_cache *cache;
@ -297,7 +319,9 @@ fprint_dummy_frames (struct ui_file *file)
gdb_print_host_address (s, file);
fprintf_unfiltered (file, ":");
fprintf_unfiltered (file, " id=");
fprint_frame_id (file, s->id);
fprint_frame_id (file, s->id.id);
fprintf_unfiltered (file, ", ptid=%s",
target_pid_to_str (s->id.ptid));
fprintf_unfiltered (file, "\n");
}
}