Bug 12940 - RFE: Step into the last function call on a line
Summary: RFE: Step into the last function call on a line
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: gdb (show other bugs)
Version: HEAD
: P2 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: archer
Depends on:
Blocks:
 
Reported: 2011-06-27 16:52 UTC by Jan Kratochvil
Modified: 2013-02-22 16:10 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jan Kratochvil 2011-06-27 16:52:10 UTC
int a (void) { return 1; }
int b (void) { return 2; }
void f (int x) {}
int main (void)
{
  f (a () + b ());
  return 0;
}
-------------------------------------------------------------------------------

Expected:
6	  f (a () + b ());
(gdb) stepfunc
f (x=3) at lastfunc.c:3
3	void f (int x) {}

Actual:
6	  f (a () + b ());
(gdb) step
a () at lastfunc.c:1
1	int a (void) { return 1; }

Now one has to (according to the number of called functions):
(gdb) step
(gdb) finish
(gdb) step
(gdb) finish
(gdb) step

or:
(gdb) record 
(gdb) next
(gdb) reverse-step
(gdb) reverse-finish 
(gdb) step

or to use:
(gdb) ignorefunc a
(gdb) ignorefunc b
(gdb) step
with:
define ignorefunc
	break $arg0
	commands
		up
		tbreak
		commands
			step
		end
		continue
	end
end

This feature should be doable the right way (to find the last call instruction during instruction stepping in a line) with the recent DW_TAG_GNU_call_site markers.
Comment 1 Lawrence Crowl 2012-06-27 23:29:41 UTC
This feature is highly valuable when debugging modern C++ code.  It is
common to have lines looking like: 

  a = b->f( c->g(), d->h()->x() );

It is often the case that g, h, and x are accessors of some form, and
are usually not the source of bugs.  The bug would typically be in
f.  As outlined above, the current commands needed to step into f are
tedious.  So, one wants to use one command to step from the start of
that statement directly into function f.

Siva Chandra <sivachandra@google.com> has written a Python script that
implements the core part of this feature.  It uses a recent patch to
expose find_pc_line to the Python API,
http://sourceware.org/ml/gdb-patches/2012-05/msg00257.html.

The simplest implementation of the features is to look at all
functions calls for a statement and break on the textually last one in
the assembly code.  I have used a debugger with this implementation,
and it alone is valuable.

There are situations, however, where the simple implementation of the
feature might yield unintended results.

(1) The compiler uses branching internal to a statement that results
in the textually last function in the assembly not being the last
function called in time.

This situation is mostly ignorable.  If not though, the user's
solution is to compile at a lower optimization level.  Otherwise, the
compiler and debugger would need to conspire to present the order
correctly.  That implementation can wait until demonstrated need.

(2) Two functions whose results are combined by non-function code.

  i = f() + g();
  b = f() && g();

In the first case, the compiler could generate the function calls in
either order.  In the second case, f might return false, in which case
g is never executed to step into.

This situation is hard to resolve, as the user's intent is hard to
discern.  Any solution can wait until demonstrated need.

(3) The statement might include implicit conversion functions that
happen as the last call.  In this case, the statement

  i = f(g());

is implemented as

  i = f(g()).operator int();

and the textually last call is to operator int.  The user might not
recognize the implicit conversion as being there, and the step would
go too far.

This situation can be resolved by providing a variant of the
function-step command that ignores implicit function calls when
choosing the last function.

(4) The statement might include a user-defined operator function that
the user does not recognize as a function.  The most common case here
is operator =. In this case, the statement

  t = f(g());

is implemented as

  t.operator=(f(g());

and the textually last call is ot operator =. The user might not
recognize the assignment operator as being a function call, and the
step would go too far.

This situation is by far the most common one that I encounted in 
actual use.  It can be resolved by providing a variant of the
function-step command that ignores assignment operator function calls
when choosing the last function.  Alternately, it could ignore all
operator function calls.

However, even without addressing the situations above, the feature
saves significant debugging time.
Comment 2 Jan Kratochvil 2012-10-25 16:39:58 UTC
Just if not clear there is DW_TAG_GNU_call_site now to find the function calls.