ProFTPD Developer's Guide: Debugging the Code

ProFTPD Version 1.2

Table of Contents

When developing a new module, server debugging can help pinpoint what is happening in some cases; however, when tracking down such things as segfaults ("signal 11") or tracing the logic through a complex sequence of function calls, more complicated forms of debugging are needed.

The Developer configure Option
By default, proftpd compiles into an executable suitable for running on a live server. However, this means that the executable isn't as well suited for debugging (debug versions of programs often run slower). To generate a debug version of proftpd, use the --enable-devel configure option. By itself, this option has two side effects: first, it enables quite a few -W compiler switches, which make the compiler more verbose and more picky about shadowed variables, prototype mismatches, use of long double, etc; second, the generated proftpd binary will not be stripped of its symbols when make install is run. This means the binary will be larger, but it will also be easier to use debugging tools on the binary. Please compile your module using --enable-devel, and fixing any generated errors and warnings, before contributing it.

The --enable-devel option can also take parameters that alter proftpd's behavior. These parameters are: coredump, nodaemon, and nofork. The first parameter, coredump, enables proftpd to generate a core file under certain conditions (segfault, SIGABRT); unless this parameter is used, the code takes stringent steps explicitly in order to not generate coredumps, by design. The second parameter, nodaemon, makes proftpd not use any of the functions it would normally use when daemonizing. Finally, the nofork parameter means that proftpd will not fork() a process to handle a connecting client--all sessions will be handled by the same proftpd process. These parameters can be combined when using the --enable-devel option, like so:

  ./configure --enable-devel=nodaemon:nofork ...
or, to use them all:
  ./configure --enable-devel=coredump:nodaemon:nofork ...

Tracing Programs
Your operating system should provide some utilities to help with system call tracing (e.g. strace and ltrace on Linux, ktrace and kdump on FreeBSD, truss on Solaris, tusc on HP-UX); read their respective man pages for more information.

The GNU Debugger
The GNU debugger, gdb, is a superb tool for debugging programs. Like many such excellent tools, it can be intimidating for the uninitiated, but its use is highly recommended. There are even graphical frontends, such as ddd for those who prefer graphical environments.

gdb provides the most information when it is used to run a program that has been linked with the debug version of libc, and whose symbols have not been stripped. Thus, configure proftpd using the --enable-devel configure option. Then, run your compiled proftpd binary like so:

  # gdb ./proftpd
  Copyright 2001 Free Software Foundation, Inc.
  GDB is free software, covered by the GNU General Public License, and you are
  welcome to change it and/or distribute copies of it under certain conditions.
  Type "show copying" to see the conditions.
  There is absolutely no warranty for GDB.  Type "show warranty" for details.
  (no debugging symbols found)...
At the prompt, start running the program, and pass it any of the usual proftpd command-line options:
  (gdb) run -nd5
You can set breakpoints before running the program, step through certain functions, etc. One of the more useful uses of gdb is for determining where, and how, segfaults occur. You run proftpd under the debugger until you trigger the segfault. gdb will display a message about catching the SIGSEGV signal, and the name of the function, file, and line number at which it happened. Sometimes this segfault occurs within the C library, but is almost always due to the application code. When this happens, at the (gdb) prompt, generate a backtrace:
  (gdb) backtrace
A backtrace is a listing of the function call stack, from the start of the program to the point where the segfault occurs. One can then quickly narrow down where in the application code the bug is lurking.

C programs are notorious for memory leaks and other memory allocation related issues. Numerous articles and programs have been written to address this. I recommend using valgrind or GNU checker.

Profiling the code can be considered a form of debugging, once all other functionality-related bugs have been fixed. In order to compile a profile version of proftpd, you need the following:

  CFLAGS="-DPR_DEVEL_NO_DAEMON -DPR_DEVEL_NO_FORK -g -pg -a" LIBS=-pg ./configure --enable-devel ...
The resulting daemon will only handle one session, as is necessary (the daemonizing and fork() code interferes with the functioning of the profiling code added by the compiler). This profile version will generate two files, bb.out and gmon.out, in the RUN_DIR directory (typically /var/run/proftpd).

Then, to see the call graph info, change to the directory where the bb.out and gmon.out profiling files are, and invoke the following command:

  gprof /path/to/proftpd
The gprof program has quite a few options for generating call graphs and timing information; read the man page to find out more information.

Table of Contents

Author: $Author: castaglia $
Last Updated: $Date: 2003/07/23 15:36:36 $

© Copyright 2000-2003 TJ Saunders
All Rights Reserved