Re: Understanding GDB frames

Jim Blandy
2007-05-22T18:37:30Z

Maxim Grigoriev <maxim@tensilica.com> writes:
> QUESTION
> ========
>
> The program (frame.c) is
>
> #include <stdio.h>
>
> int frame_number = 1;
>
> int f11(int b)
> {
> int a;
>
> printf("f11() frame number %d\n", frame_number++);
>
> a = b + 1;
> b--;
> if (b != 0)
> {
> printf("f11() will be called recursively\n");
> f11(b);
> }
> return a; /* <-- BP set here. */
> }
>
> int f1(int a)
> {
> return f11(a);
> }
>
> int main()
> {
> int a = 1;
> int i;
>
> for (i = 0; i <2; i++)
> a = f11(a);
>
> a = f11(1);
> a = f1(1);
>
> return 0;
> }
>
> The gdb command file "CMD":
>
> break frame.c:18
> run
> continue
> continue
> continue
> continue
> kill
> quit
>
> was used to run a gdb session like this :
> gdb <executable> --command=CMD
>
> Let's define that "frames A and B are the same" if
>
> frame_id_eq ( A->this_id, B->this_id ) == true
>
> The breakpoit has been set at line 18 and hit 5 times.
> Execution control has been subsequently taken by
> gdb in five frames numbered 1, 2, 3, 4, and 5.
>
> According to the definition of GDB frames,
> which statement is correct ?
>
> ANSWERS
> =======
>
> 1) All frames 1, 2, 3, 4, and 5 are the same;
> 2) All frames 1, 2, 3, 4, and 5 are different from each other;
> 3) Frames 1,2, and 4 are the same. Frames 3 and 5 are
> different from 1, 2, 4 and from each other;
> 4) It's implementation-dependent. While doing architecture ports,
> people decide how to implement frame_id-related functions
> to compare frames;
> 5) Other ( explanation would be appreciated ).

All those frames ought to be different, but GDB will treat some of
them as the same.

Ideally, a frame is allocated each time a function is entered, and
destroyed when a function returns, or when we longjmp out of it, or
throw an exception out of it, or when the thread exits or is
cancelled. So every one of those frames is distinct. This is the
programmer's view of frames.

The first source of difficulty is optimization. The compiler may
choose to inline a function call, and thus not allocate a distinct
frame for it. Or, the compiler may choose to implement the final call
in a function as a jump, effectively freeing the caller's frame before
allocating its callee's frame. So optimization can affect which
frames actually exist in memory. GDB could 'fabricate' frames for
inlined functions, but for tail calls there's no easy solution --- the
state which would describe the caller's frame is just gone.

The second source of difficulty is that GDB doesn't get any
notification when a frame is popped. (It could arrange to get this
notification, but as Daniel says, that would slow things down.) Thus,
if a function returns, and then is called again with exactly the same
incoming stack pointer --- which is quite possible if it's the same
frame underneath making both calls --- then it's impossible for GDB to
look in on the first call, and then look in on the second call, and
know that the two frames are distinct. The PC may be the same; the SP
may be the same; the values of the arguments might be the same. If
the calls are in a loop, the PC in the caller might well be the same.

So GDB's frame ID's are a best-effort, but still faulty, approximation
to what we really want. If two ID's are *unequal*, then you can trust
that result, as far as I know. But if they're *equal*, you can't
trust that.

Joel is right when he says:

> If my understanding of the frame code is correct, then the only
> thing that is really guaranteed is that the frame ID remains
> constant throughout the lifetime of its associated frame, or
> function call. The rest is implementation-dependent.

It's worth pointing out that the 'PC' in a frame ID isn't the current
PC within the function. It's always the PC of the function's entry
point, so that stepping within a function doesn't cause the frame ID
to change. Usually it's a PC value returned by 'frame_func_unwind',
which takes care of calling get_pc_function_start for you.