Sample Debugger Sessions

An Extended Debug Session

In this session we will go into the debugger initially using the --debugger or -X option. We’ll use the Makefile from the source code from cd-paranoia

Basic Information when stopped inside Debugger

$ remake -X
Reading makefiles...
Updating makefiles...
-> (/tmp/libcdio-paranoia/Makefile:428)
Makefile: Makefile.in config.status
remake<0>

The line immediately before the prompt remake<0>, we show the the target name, Makefile and its dependencies: Makefile.in and config.status.

The line before that has position information (/tmp/libcdio-paranoia/Makefile:428). But at the beginning of the line is and arrow made up of two characters, ->. This indicates that we have not done prerequisite checking for this target yet. Later we will come across other two-character icons like ++. See icons for a complete list.

The zero in the prompt remake<0> is the command history number. If GNU Readline history support has it increments as we enter commands, otherwise it stays zero.

For each recursive call to remake, we’ll add another pair of angle brackets <> around the number.

Some of the information is given in more verbose format using info program:

remake<0> info program
Starting directory `/tmp/libcdio-paranoia'
Program invocation:
    remake/make  -X
Recursion level: 0
Line 428 of "/tmp/libcdio-paranoia/Makefile"
Program stopped before rule-prequisite checking.
remake<1>

Notice that the prompt has incremented to 1 after entering the a command.

Stepping

We can use the step, command to progress a little in the interpretation or execution of the makefile:

remake<1> step
-> (/tmp/libcdio-paranoia/Makefile:415)
Makefile.in: Makefile.am m4/ld-version-script.m4 ...
remake<2> step
-> (/src/external-vcs/github/rocky/libcdio-paranoia/Makefile:443)
aclocal.m4: m4/ld-version-script.m4 ...
remake<3>

I have elided the list of dependencies listed above and substituted ellipses (...).

There is a slight difference between what you will find in the Makefile and the target output seen above. Below I’ll list the what is in the Makefile versus what is line as shown above.

For line 415:

$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
Makefile.in: Makefile.am m4/ld-version-script.m4 ...

while line 443:

$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
aclocal.m4: m4/ld-version-script.m4 ...

In the debugger, variables have been expanded and file paths have been canonicalized. Therefore you see:

Makefile remake output
$(srcdir)/Makefile.in Makefile.in
$(ACLOCAL_M4) aclocal.m4 …

Let’s recap where remake is in the process of running the Makefile. The first thing that seems to be done is that the Makefile dependencies need to checked. A dependency of Makefile is Makefile.in and that in turn depends on target aclocal.m4. We have now stepped into and stopped at that target. At the remake<3> prompt then before checking for the dependencies of aclocal.m4.

You can see this dependency nesting that got us to this state using the backtrace command:

remake<3> backtrace
=>#0  aclocal.m4 at /tmp/libcdio-paranoia/Makefile:443
  #1  Makefile.in at /tmp/libcdio-paranoia/Makefile:415
  #2  Makefile at /tmp/libcdio-paranoia/Makefile:428
remake<4>

Stepping through the program can be illuminating as far as what is going on, especially when the Makefile has been derived in some way, as is the case here. This Makefile was created via autotools.

I had assumed that when I run make it looks for a default target and runs that. But as we see here, the first thing that goes on is to check to see if the Makefile is being used is itself out of date. If that is the situation, then the Makefile will get recreated and you start again.

However while all of this may be interesting, stepping can be a bit tedious.

In the next section, we talk about breakpoints which can get you to where you want to debug faster. To finish this session though use the quit command.

remake<4> quit
remake: That's all, folks...

Stopping with Continue

Let’s say I am interested in what goes on when make dist is run. Again, I’ll invoke the debugger initially.

$ remake -X
Reading makefiles...
Updating makefiles...
-> (/tmp/libcdio-paranoia/Makefile:428)
Makefile: Makefile.in config.status
remake<0>

Instead of stepping we can set a breakpoint on the dist target and continue running to that point in one command, using continue.

remake<0> continue dist
Breakpoint 1 on target `dist', mask 0x0f: file Makefile, line 703.
Updating goal targets...
-> (/src/external-vcs/github/rocky/libcdio-paranoia/Makefile:703)
dist:
remake<1>

Now when I issue a step, I will step into the commands associated with the dist target:

remake<1> step
File 'dist' does not exist.
Must remake target 'dist'.
Makefile:704: target 'dist' does not exist
##>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
remake  dist-bzip2 dist-gzip am__post_remove_distdir='@:'
##<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
++ (/src/external-vcs/github/rocky/libcdio-paranoia/Makefile:703)
dist
remake<2>

Notice that the event icon above is ++ which means I am stepping shell commands, here those associated with the Make target dist. Above the line with the event icon in between the two chevrons is the command that is about to be run.

To see the entire build commands, there is the list command. Here is that:

remake<2> list
/tmp/libcdio-paranoia/Makefile:705
dist:
#  recipe to execute (from 'Makefile', line 706):
    $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
    $(am__post_remove_distdir)

A form of the target command, target @ command does about the same thing. Note that in both cases variables are not expanded as the are in the trace output shown above between chevrons.

Debugging Make Variables

In the above session we have seen that output has variables expanded, while in the list and target commands variables were not expanded.

You can query any GNU Make variable that has been set in the program without variables inside expanded using the print command.

remake<2> print MAKE
(origin default) MAKE = $(MAKE_COMMAND)

The (origin default) means this is a built-in definition. Many variables that you will be interested in though, are set somewhere, and the variable is not a default it’s location is also shown:

remake<3> print DATA
Makefile:168 (origin: makefile) DATA := libcdio_paranoia.pc libcdio_cdda.pc

The other kind of print which does full expansion of the variables is called expand or x. Here is an example

remake<4> expand MAKE
(origin default) MAKE := remake

Note that in printing expanded values we use := while non-expanded values we use = This output matches the semantics of these assignment operators.

In fact, expand doesn’t need a variable name, it will work with a string. For example:

remake<5> x $(MAKE) $(DIST_TARGETS)
remake dist-bzip2 dist-gzip

No location identification is given here since what I put in isn’t a variable. Also note that for expand I add the dollar sign and parenthesis when there is other stuff. If you just want information about the variable you can leave that off.

However for print you never add the dollar sign; printing only prints variables not strings.

You can change values too using either the set, set or setqx commands. Let’s see the difference between set and setq:

remake<6> set MAKE $(MAKE_COMMAND)
Variable MAKE now has value 'remake'
remake<7>  setq MAKE $(MAKE_COMMAND)
Variable MAKE now has value '$(MAKE_COMMAND)'

So with set, the value in the expression $(MAKE_COMMAND) is expanded before the variable definition is assigned. With setq the internal variables are kept unexpanded. Which you use or want is up to you.

Note the irregular syntax of set and setq. Don’t put an equal sign between the variable and the expression. That is, set MAKE = $(MAKE_COMMAND) gives:

remake<8> set MAKE = $(MAKE_COMMAND)
Variable MAKE now has value '= remake'

which is probably not what you want. You can optionally put in the word “variable” when using set and “variable” is ignored. But it won’t be if you use setq.

Debugging POSIX Shell Commands

Now consider the following sample Makefile test2.mk:

PACKAGE=make

all: $(PACKAGE).txt

$(PACKAGE).txt: ../doc/remake.texi
    makeinfo --no-headers $< > $@

Running this entering the debugger initially:

$ remake -X -f test2.mk
...
Reading makefiles...
updating makefiles....
Updating goal targets....
  /tmp/remake/src/test2.mk:3        File `all' does not exist.

-> (/tmp/test2.mk:5)
make.txt: ../doc/remake.texi

We could use the target command to show information about the current target, but that returns lots if information. So let us instead narrow the information to just the automatic variables that get set. The following commands do this are all mean the same thing: target make.txt variables, target @ variables, and info locals.

remake<1> target @ variables
@ := all
% :=
* :=
+ := make.txt
| :=
< := all
^ := make.txt
? :=

There is a target option to list just the shell commands of the target:

remake<2> target @ commands

make.txt:
#  commands to execute (from `test2.mk', line 6):
    makeinfo --no-headers $< > $@

We can see a full expansion of the command that is about to be run:

remake<5> target @ expand

#  commands to execute (from `test2.mk', line 6):
    makeinfo --no-headers $< > $@

#  commands to execute (from `test2.mk', line 6):
    -makeinfo --no-headers ../doc/remake.texi > make.txt

Now if we want to write out commands as a shell script which we might want to execute, we can use the write command:

(/tmp/remake/src/test2.mk:6): make.txt
remake<6> write
File "/tmp/make.txt.sh" written.

We can issue a shell command cat -n /tmp/make.txt.sh to see what was written. See shell.

remake<7> shell cat -n /tmp/make.txt.sh
#!/bin/sh
# cd /tmp/remake/src/
#/tmp/remake/src/test2.mk:5
makeinfo --no-headers ../doc/remake.texi > make.txt

If you issue step commands, the debugger runs the each command and stops. In this way, you can inspect the result of running that particular shell command and decide to continue or not.

remake<8> step

  Must remake target `make.txt'.
Invoking recipe from test2.mk:6 to update target `make.txt'.
##>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
makeinfo --no-headers ../doc/remake.texi > make.txt
##<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

++ (/tmp/test2.mk:5)

Notice that we’ve shown the expansion automatically. One subtle difference in the above output, is that we only show the single shell command that is about to be run when there are several commands. In our example though, there is only one command; so there is no a difference.

The ++ icon means that we are about to run that code.

make.txt
remake<9> @b{step}
  Successfully remade target file `make.txt'.

<- (/tmp/test2.mk:5)
make.txt
remake<10>

We ran the code, and are still at target make.txt. The <- icon means that have finished with this target and are about to return.

If you are at a target and want to continue to the end of the target you can use the command finish which is the same as finish 0.

Post-Mortem Debug Session

In this session we’ll go into the debugger on encountering an error. For this the --post-mortem or -! option is used. We’ll use the Makefile from the source code of this distribution.

to be continued…