Debugging SWIG Code

SWIG (Simplified Wrapper and Interface Generator) is a popular open source tool for interfacing Java, Perl, Python, and a number of other interpreted languages with C/C++. It is included with most Linux distributions and can also be downloaded from the Web. It allows you to write most of an application's code in an interpreted language and incorporate specific sections that you have written in C/C++, for example, to enhance performance.

The question arises of how to run GDB/DDD on such code. Here we will present a small example using Python and C. The C code will manage a first in, first out (FIFO) queue:

 1  // fifo.c, SWIG example; manages a FIFO queue of characters
 2
 3  char *fifo; // the queue
 4
 5  int nfifo = 0, // current length of the queue
 6      maxfifo; // max length of the queue
 7
 8  int fifoinit(int spfifo) // allocate space for a max of spfifo elements
 9  { fifo = malloc(spfifo);
10    if (fifo == 0) return 0; // failure
11    else {
12       maxfifo = spfifo;
13       return 1; // success
14    }
15  }
16
17  int fifoput(char c) // append c to queue
18  { if (nfifo < maxfifo) {
19       fifo[nfifo] = c;
20       return 1; // success
21    }
22    else return 0; // failure
23  }
24
25  char fifoget() // delete head of queue and return
26  { char c;
27     if (nfifo > 0) {
28        c = fifo[0];
29        memmove(fifo,fifo+1,--nfifo);
30        return c;
31     }
32     else return 0; // assume no null characters ever in queue
33  }

Besides the .c file, SWIG also requires an interface file, in this case fifo.i. It consists of the global symbols, listed once in SWIG style and once in C style:

%module fifo

%{extern char *fifo;
extern int nfifo,
           maxfifo;
extern int fifoinit(int);
extern int fifoput(char);
extern char fifoget(); %}

extern char *fifo;
extern int nfifo,
           maxfifo;
extern int fifoinit(int);
extern int fifoput(char);
extern char fifoget();

To compile the code, first run swig, which generates an extra .c file and a Python file. Then use GCC and ld to produce a .so shared object dynamic library. Here is the Makefile:

_fifo.so:  fifo.o fifo_wrap.o
        gcc -shared fifo.o fifo_wrap.o -o _fifo.so

fifo.o fifo_wrap.o:  fifo.c fifo_wrap.c
        gcc -fPIC -g -c fifo.c fifo_wrap.c -I/usr/include/python2.4

fifo.py fifo_wrap.c:  fifo.i
        swig -python fifo.i

The library is imported into Python as a module, as shown in the test program:

# testfifo.py

import fifo

def main():
   fifo.fifoinit(100)
   fifo.fifoput('x')
   fifo.fifoput('c')
   c = fifo.fifoget()
   print c
   c = fifo.fifoget()
   print c

if __name__ == '__main__': main()

The output of this program should be "x" and "c," but we come up empty:

$ python testfifo.py



$

To use GDB, remember that the actual program you are running is the Python interpreter, python. So run GDB on the interpreter:

$ gdb python

Now, we'd like to set a breakpoint within the attached FIFO library, but the library has not been loaded yet. The loading won't occur until the import line

import fifo

is executed by the Python interpreter. Fortunately, we can ask GDB to stop at a function in the library anyway:

(gdb) b fifoput
Function "fifoput" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (fifoput) pending.

Now run the interpreter, whose argument is the test program, testfifo.py:

(gdb) r testfifo.py
Starting program: /usr/bin/python testfifo.py
Reading symbols from shared object read from target memory...(no debugging
symbols found)...done.
Loaded system supplied DSO at 0x164000
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
[New Thread -1208383808 (LWP 15912)]
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Breakpoint 2 at 0x3b25f8: file fifo.c, line 18.
Pending breakpoint "fifoput" resolved
[Switching to Thread -1208383808 (LWP 15912)]

Breakpoint 2, fifoput (c=120 'x') at fifo.c:18
18      {  if (nfifo < maxfifo)  {

You can now do what you already know so well:

(gdb) n
19            fifo[nfifo] = c;
(gdb) p nfifo
$1 = 0
(gdb) c
Continuing.

Breakpoint 2, fifoput (c=99 'c') at fifo.c:18
18      {  if (nfifo < maxfifo)  {
(gdb) n
19            fifo[nfifo] = c;
(gdb) p nfifo
$2 = 0

Well, there's the problem. Each time you attempt to add a character to the queue, you simply overwrite the previously added character. Line 19 should be

fifo[nfifo++] = c;

Once you make that change, the code works fine.