This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch/rfc] Add a sentinel frame


(I hate jet lag, I'm never up at 7am, well ok ...)

I got back to the mainline/"sentinel" problem and have found that it happens when a program's function is called from a gdb prompt.

To reproduce create a very simple program:
int func(int arg) { return 2*arg; }
int main(int argc) { return func(argc); }

Then run mainline GDB on x86-64:
(gdb) break main
(gdb) run
(gdb) print func(1)
../../gdb-head/gdb/sentinel-frame.c:102: internal-error: Function sentinal_frame_pop called
A problem internal to GDB has been detected.  Further
debugging may prove unreliable.
Quit this debugging session? (y or n)

Attached is a backtrace of this failing GDB.
Any ideas?

Yes!



#6 0x00000000005446d1 in sentinel_frame_pop (frame=0x82c100, cache=0x82c130, regcache=0x850610) at ../../gdb-head/gdb/sentinel-frame.c:102

At this point GDB is hosed.


As I mentioned before, popping the sentinal frame is meaningless so the question is, where did that frame come from.

A wild guess is that it is trying to pop the dummy frame having finished the inferior function call. A confirmation is:

(gdb) break func
(gdb) print func(1)

It should manage to stop in func, the stack being something like (assuming bt doesn't also internal error :-):

(gdb) bt
.... func ...
.... <dummy-frame> ...
.... main ...

Returning from func(), causing the dummy-frame to be discarded should then trigger things:

(gdb) finish
... barf ...

Can you confirm that the PC for this frame is falling in the stack dummy?


#7  0x00000000004e6692 in frame_pop (frame=0x82c100)
    at ../../gdb-head/gdb/frame.c:164


#8 0x000000000048a3c4 in normal_stop () at ../../gdb-head/gdb/infrun.c:3113

The code reads:


  if (stop_stack_dummy)
    {
      /* Pop the empty frame that contains the stack dummy.  POP_FRAME
         ends with a setting of the current frame, so we can use that
         next. */
      frame_pop (get_current_frame ());
      /* Set stop_pc to what it was before we called the function.
         Can't rely on restore_inferior_status because that only gets
         called if we don't stop in the called function.  */
      stop_pc = read_pc ();
      select_frame (get_current_frame ());
    }

Hmm, not so good. I was expecting something involving sentinal frames. Anyway ...

get_current_frame() does the sequence:

  if (current_frame == NULL)
    {

This is where the sentinel frame comes from.

      struct frame_info *sentinel_frame =
        create_sentinel_frame (current_regcache);

This is where it tries to unwind it back to the current frame (a dummy frame in this case). Note that unwind_to_current_frame() calls get_prev_frame().

      if (catch_exceptions (uiout, unwind_to_current_frame, sentinel_frame,
                            NULL, RETURN_MASK_ERROR) != 0)
        {

And if it fails, it does this:

          /* Oops! Fake a current frame?  Is this useful?  It has a PC
             of zero, for instance.  */
          current_frame = sentinel_frame;
        }
    }

So lets assume that get_prev_frame() is failing. It can do it two ways:

- noisily via an error() call such as when a memory read fails
(I don't think it is this 'cos we'd see that error).
- silently because get_prev_frame() thinks that chaining is invalid
(more likely)

So I think it is one of these tests going awall:

  if (next_frame->level >= 0
      && !backtrace_below_main
      && inside_main_func (get_frame_pc (next_frame)))
    /* Don't unwind past main(), bug always unwind the sentinel frame.
       Note, this is done _before_ the frame has been marked as
       previously unwound.  That way if the user later decides to
       allow unwinds past main(), that just happens.  */
    return NULL;

  /* If we're inside the entry file, it isn't valid.  */
  /* NOTE: drow/2002-12-25: should there be a way to disable this
     check?  It assumes a single small entry file, and the way some
     debug readers (e.g.  dbxread) figure out which object is the
     entry file is somewhat hokey.  */
  /* NOTE: cagney/2003-01-10: If there is a way of disabling this test
     then it should probably be moved to before the ->prev_p test,
     above.  */
  if (inside_entry_file (get_frame_pc (next_frame)))
      return NULL;

The second looks worrying (the dummy frame breakpoint lives in the entry file ...). Perhaphs something like:

if (dummy_frame_p (get_frame_pc (next_frame) != NULL
    && inside_entry_file (get_frame_pc (next_frame))
  return NULL;

Andrew



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]