Red Antigua Logo
Yet another piece of web.
Ads by Goooooogle
Search this site (by Google)
Tools    (top)
Your IP
Check a site for broken links
(W3C)

Perl modules    (top)
Tree::Numbered::Tools
(CPAN)
Perl tutorials    (top)
Perl modules
HTML::Template
CGI::Application
Mail::POP3Client
Mail::Send
MIME::Tools
Cookies with CGI::Application
Upload files with CGI::Application
Download files with CGI::Application
Redirect with CGI::Application
CPAN shell
Install DBD::mysql from the CPAN shell
Perl trim function
Validate an IP with Perl
Run suid Perl scripts under Apache
Perl taint mode
Perl date functions with Date::Calc

In Spanish
Curso de Perl

C tutorials    (top)
C - Introduction
C - Absolute beginner's Emacs
C - Examples for beginners
C - Makefile examples
C - Autotools examples
Server configurations    (top)
DNS
Apache
Apache Authentication and Access Control
mod_perl on FreeBSD
MySQL
MySQL add account
phpMyAdmin
Squid
DHCP

UNIX on Windows    (top)
Apache setup on Windows
MySQL setup on Windows
PHP setup on Windows
Perl setup on Windows
Emacs setup on Windows
UnxUtils
PuTTY
WinSCP
GIMP on Windows
MinGW - gcc on Windows
MSYS - UNIX-styled shell on Windows
msysDTK - autotools on Windows
GDB for MinGW on Windows

Misc. FreeBSD/UNIX    (top)
CD and DVD creation on FreeBSD using 'k3b' on FreeBSD
'portupgrade' on FreeBSD
'ipf' on FreeBSD
'pf' on FreeBSD
'su' on FreeBSD
Mount an ISO image under FreeBSD
Load the correct sound driver under FreeBSD without knowing what sound card you are using
Simultaneous sound channels on FreeBSD
FreeBSD network stuff
DOS-to-UNIX file conversion
favicon.ico on UNIX
Emacs tips
Sendmail tips
GKrellm
Command Line Calculator
Save multimedia streams with 'mplayer'
xargs - solution to 'Argument list too long'
Process multiple images from the command line using 'ImageMagick'
Turn the system bell off under X Windows
Process each line in an input file from the command line (or in a shell script)
How to keep a program running in the background using 'nohup'
How to remove symbolic links in the current directory using 'find' and 'rm'
How to remove Emacs backup files in the current directory and all subdirectories using 'find' and 'rm'
How to execute .profile without logging in
Configure X to handle non-English characters
How to move /var to /usr/var

Redirect a web page    (top)
Redirect to another web page
Apache redirect
C redirect
Perl redirect
PHP redirect
HTML redirect
JavaScript redirect

Javascript    (top)
Trim function
Login form
Register form
Popup window

Virus list    (top)
Latest 10 viruses - Sophos

Off topic    (top)
Personal home links

C - Debugging, security check, memory leak check, profiling
C tutorials » Debugging, security check, memory leak check, profiling (work in progress)
  • Introduction
  • Tools for debugging
    • gdb (from the command line)
      • Simple debugging example
      • Debugging a core file
      • Debugging a running program (client/server example)
    • gdb (inside Emacs)
    • ddd (GUI for gdb)
  • Tools for security & memory leak check
    • splint (security)
    • Valgrind (memory leak)
  • Read more
Introduction (top)
Consider the following program:
%%%SOURCE CODE GOES HERE%%%
It's ok to debug your program myprog.c once in a while by adding lines such as
printf("DEBUG: The variable i=d%\n", i);
but you should get used to something more sophisticated (it is not necessarily more complicated).
This tutorial shows some examples how to use gdb from inside Emacs, but it also applies to the debugger in general.
For example, you can start debugging with gdb myprog.c., step to the line you want to debug, then inside gdb type exactly the same as you would in your source code:
printf "DEBUG: The variable i=d%\n", i
No more reasons for not using gdb!
Other important tools to help you produce better code are ddd (GUI for gdb), splint (security check a.o.) and Valgrind (memory leak check a.o.).

Tools for debugging (top)

gdb (command line) (top)

Simple debugging (top)

Debugging a core file (top)

Debugging a running program (client/server example) (top)
Client/server debugging is a bit more tricky than debugging "simple executables".
Normally we are interested in debugging the server's child process created by fork(), not the parent, as the parent normally terminates after creating the child process.
To do this, we need to startup the server, get the child process ID, and attach it to gdb.
Here follows an example debug session for a buggy HTTP server.
(Check out the complete source code at http://www.redantigua.com/src/c-more-examples/basichttpd/.)
Input is shown in red, output is shown in black, and hints are shown in green.
We will need 2 windows for this session.

Window 1:
Server - startup.
./basichttpd 1024
ps axww|grep basichttpd
22862  p1  I      0:00.00 ./basichttpd 1024
gdb
(gdb) attach 22862
The following warning messages may or may not be displayed: A problem internal to GDB has been detected, further debugging may prove unreliable. Quit this debugging session? (y or n) n A problem internal to GDB has been detected, further debugging may prove unreliable. Create a core file of GDB? (y or n) n
Reading symbols from /home/redantig/public_html/src/c-more-examples/basichttpd/basichttpd...done. Reading symbols from /lib/libc.so.5...done. Loaded symbols for /lib/libc.so.5 Reading symbols from /libexec/ld-elf.so.1...done. Loaded symbols for /libexec/ld-elf.so.1 0x280c42b7 in accept () from /lib/libc.so.5 (gdb) s Single stepping until exit from function accept, which has no line number information.
Window 2:
Client - connect.
telnet 0 1024
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
Window 1:
Server - accepts connection and waits for a HTTP Request from the client with recvfrom().
Let's set a breakpoint to examine what the server actually receives.
main (argc=1024, argv=0xbfbfecac) at basichttpd.c:194
194             if ((nbytes = recvfrom(fd_connection, request_header, MAXREQUESTLEN - 1 , 0, (struct sockaddr *)&client_addr, &addr_len)) < 0)
(gdb) b 203
Breakpoint 1 at 0x804a130: file basichttpd.c, line 203.
(gdb)
Window 2:
Client - send the HTTP Request.
GET /index.html HTTP/1.0
Host: localhost:1024
(RETURN)
Window 1:
Server - debug step, check how many bytes we received, and the HTTP Request itself.
(gdb) s

Breakpoint 1, main (argc=1024, argv=0xbfbfecac) at basichttpd.c:203
203             request_header[nbytes] = '\0';
(gdb) print nbytes
$2 = 28
(gdb) print request_header
$4 = "GET /index.html HTTP/1.0\r\n\r\n\000\000\000\000\000\020\000\000ò<\005
(\037¹\b(\037¹\b(|ê¿¿v<\005(è\000\000\000xw\b(\000Ð\023\000س\006( 0\a(
\000\000\000\000¬ê¿¿!<\005(\037¹\b(+2¦\t\000\"\a(\000\000\000\000Õw\b(\000\"\a
(¼ê¿\000س\006(\000\000\000\000xw\b(,ë¿¿m:\005(\037¹\b(+2¦\t
\020 \006(\000ë¿¿\000\000\000\000\004ë¿¿ ·\006(\000\000\000
\000ô¹\006(@\020\a(üê¿¿\000 \a(\000\"\a(\003\000\000\000\000\200\023(\2229\005("...
(gdb) printf "%s", request_header
GET /index.html HTTP/1.0

(gdb) s
httprequestline (request_header=0xbfbfea2c "GET /index.html HTTP/1.0\r\n\r\n") at http.c:225
225         return strtoken(request_header, CRLF, 0);
(gdb) s
strtoken (str=0x1 <Error reading address 0x1: Bad address>, sep=0x4 <Error reading address 0x4: Bad address>, n=-1077941716)
    at string.c:223
223     {
(gdb)
As we see, print displays the complete request_header char array (255 chars in this example), while printf just displays the NULL-terminated string we want to investigate.
The HTTP Request seems to be received correctly by the server.
Anyhow, when we want to split the separate tokens GET, /index.html, and HTTP/1.0 with strtoken(), something goes wrong.

Window 1:
We continue the debug session, just to exit.
(dbg)  c
Continuing.
[error] 2007-05-25 17:14:47 [127.0.0.1] "Unknown error: 0"
At least a server error message is generated (although buggy itself), and it is also sent to the client.

Window 2:
Client - receives buggy HTTP Response.
(null) 500 Internal Server Error
 Date: 2007-05-24 15:48:43
Server: basicHTTPd/0.1
Last-Modified: 2007-05-24 15:48:43
Content-Type: (null)
Content-Length: 0

<html><head><title>basicHTTPd error: Internal Server Error (500)</title></head>
<body><h1>Internal Server Error (500)</h1>
<pre>[error] 2007-05-24 15:48:43 [127.0.0.1] "Unknown error: 0"</pre></body></html>
Window 1:
Server - type CTRL-C to exit if connection hangs.
Program received signal SIGINT, Interrupt.
0x280c42b7 in accept () from /lib/libc.so.5
(gdb) quit
The program is running.  Quit anyway (and detach it)? (y or n) y
Detaching from program: /home/redantig/public_html/src/c-more-examples/basichttpd/basichttpd, process 22862

kill 22862
Window 2:
Client - hangup.
Connection closed by foreign host.
To resume, we isolated the problem to httprequestline():
(We ignore the buggy HTTP Response at the moment.)
/* Extract the Request Line (the first line) from a Request Header. */
char *
httprequestline(char *request_header)
{
    return strtoken(request_header, CRLF, 0);
}
There is no error check for strtoken(). Bad!
We suppose incorrectly that strtoken always returns with success.
A modified version of strtoken() may look like:
/* httprequestline() - extract the Request Line (the first line) from a Request Header. */
char *
httprequestline(char *request_header, char **err_msg)
{
    char *request_line = NULL;
    if ((request_line = strtoken(request_header, CRLF, 0)) == NULL)
    {
        asprintf(err_msg, "Error at file %s, function %s(), line %d: Couldn't retreive the HTTP Request-Line\n", __FILE__, __FUNCTION__, __LINE__);
    }
    else
    {
        *err_msg = NULL;
    }
    return request_line;
}
Note the extensive error message, including the use of __FILE__, __FUNCTION__, and __LINE__.
If you get used to use them at every check, you will save yourself a lot of time when debugging.

In this kind of application, maybe we don't want to be too detailed in our error messages sent to the client, but the server should log an error message as detailed as possible.

Further on, we try to split the the HTTP Request-Line GET /index.html HTTP/1.0 into 3 words using the strsplit() function.
However, there is a bug in strsplit(), which in this case should return the words GET, /index.html, and HTTP/1.0, and set ntokens to 3.
Anyhow, even if strsplit() seems to split the line correctly into words, something goes wrong, as we see in the following debug session:
Breakpoint 5, strsplit (str=0x804e0a0 "GET /index.html HTTP/1.0", sep=0x804b365 " ", n=0xbfbfe738) at string.c:221
221         return tokens;
(gdb) print tokens[0]
$6 = 0x8050050 "GET"
(gdb) print tokens[1]
$7 = 0x8050060 "/index.html"
(gdb) print tokens[2]
$8 = 0x8050070 "HTTP/1.0"
(gdb) print *n
$9 = 4
(gdb) print tokens[3]
$10 = 0x0
(gdb) print tokens[4]
$11 = 0x544547 >Error reading address 0x544547: Bad address>
(gdb)
We see that ntokens (called *n locally) is set to 4 instead of 3, and the last token is not HTTP/1.0, as expected, but NULL (0x0).
(Anyhow, tokens[4] is correctly returning an error when trying to access the non-existing token.)
Time to investigate the source code...

strsplit() (BUGGY!):
char **
strsplit(char *str, char *sep, int *n)
{
    char **tokens = NULL;
    char *str_tmp = NULL;
    char *word = NULL;
    int i = 0;

    /* No tokens for null strings. */
    if (str == NULL)
    {
        n = 0;
        return NULL;
    }

    /* Allocate memory for the temporary string. */
    if ((str_tmp = (char *)malloc(sizeof(char *) * strlen(str) + 1)) == NULL)
    {
        /* errmsg? */
        /* fprintf(stderr, "%s: realloc() error: %s", pgm, strerror(errno)); */
        *n = -1;
        return NULL;
    }
    strcpy(str_tmp, str);

    /* Calculate number of tokens. */
    for (word = strtok(str_tmp, sep); 
         word; 
         word = strtok(NULL, sep))
    {
        (*n)++;
    }

    /* Allocate memory for the pointer to each token. */
    if ((tokens = (char **)malloc(sizeof(char *) * *n)) == NULL)
    {
        /* errmsg? */
        /* fprintf(stderr, "%s: realloc() error: %s", pgm, strerror(errno)); */
        *n = -1;
        return NULL;
    }

    strcpy(str_tmp, str);

    /* Allocate memory for each token. */
    for (word = strtok(str_tmp, sep); 
         word; 
         word = strtok(NULL, sep))
    {
        if ((tokens[i] = (char *)malloc(sizeof(char) * (strlen(word) + 1))) == NULL)
        {
            /* errmsg? */
            /* free() pointer? */
            /* fprintf(stderr, "%s: realloc() error: %s", pgm, strerror(errno)); */
            *n = -1;
            return NULL;
        }
        strcpy(tokens[i++], word);
    }

    return tokens;
}
There seems to be unpredictable results using strtok() in strsplit().
That is, unpredictable due to my way to use strtok(), which obviously isn't correct.
Of course there are other solutions, some mentioned at comp.lang.c FAQ list · Question 13.6.
The FreeBSD strsep() man page recommends to replace strtok() with strsep() (even if there are platform compatibility problems):
The strsep() function is intended as a replacement for the strtok() function.
While the strtok() function should be preferred for portability
reasons (it conforms to ISO/IEC 9899:1990 (``ISO C90'')) it is unable to
handle empty fields, i.e., detect fields delimited by two adjacent delimiter
characters, or to be used for more than a single string at a time.

Googling around, we soon find that strsep() are missing on Solaris and on HP-UX.
This isn't the only platform compatibility problem - the function asprintf() is missing on AIX,
so to make our application platform independent, we have to include our own version of asprintf() and strsep().
The ideal situation is of course to try making better source code (in this case, using strsep() instead of strtok()), but at the same time make the source code as platform independent as possible (in this case, make it compatible with Solaris and HP-UX).
The solution should be to write two different versions of strsplit(), one using strsep() (used when available), and another using strtok().
But for now, just to fix our bug, we will rewrite strsplit(), still using strtok(), and put "strsplit-using-strsep" on our TODO-list.
The problem when using strtok() is keeping record of its internal state, that may confuse the programmer (that is, me).
This new version of strsplit() avoids using strtok() to count the tokens.

strsplit() (FIXED):
char **
strsplit(char *str, char *sep, int *n)
{
    char **tokens = NULL;
    char *str_tmp = NULL;
    char *word = NULL;
    int i = 0;

    /* No tokens for null strings. */
    if (str == NULL)
    {
        *n = 0;
        return NULL;
    }

    /* Allocate memory for the temporary string. */
    if ((str_tmp = (char *)malloc(sizeof(char *) * strlen(str) + 1)) == NULL)
    {
        /* errmsg? */
        /* fprintf(stderr, "%s: realloc() error: %s", pgm, strerror(errno)); */
        *n = -1;
        return NULL;
    }

    strcpy(str_tmp, str);

    /* Allocate memory for the pointer to the tokens. */
    if ((tokens = (char **)malloc(sizeof(char *))) == NULL)
    {
        /* errmsg? */
        /* fprintf(stderr, "%s: realloc() error: %s", pgm, strerror(errno)); */
        *n = -1;
        return NULL;
    }

    /* Allocate memory for each token as we parse it. */
    for (word = strtok(str_tmp, sep); 
         word; 
         word = strtok(NULL, sep))
    {
        if ((tokens[i] = (char *)malloc(sizeof(char) * (strlen(word) + 1))) == NULL)
        {
            /* errmsg? */
            /* free() pointer? */
            /* fprintf(stderr, "%s: realloc() error: %s", pgm, strerror(errno)); */
            *n = -1;
            return NULL;
        }
        strcpy(tokens[i++], word);
    }
    (*n) = i;

    return tokens;
}

As we can see, at least we get another error message when fixing strsplit():
Window 2:
Client - receives buggy HTTP Response.
(null) 500 Internal Server Error
 Date: 2007-06-04 12:08:28
Server: basicHTTPd/0.1
Last-Modified: 2007-06-04 12:08:28
Content-Type: (null)
Content-Length: 0

<html><head><title>basicHTTPd error: Internal Server Error (500)</title></head><body><h1>Internal Server
Error (500)</h1><pre>[error at basichttpd.c -> main() -> line 222]
2007-06-04 12:08:28 [127.0.0.1] "./basichttpd: httpstatuscode(): Client HTTP Request-Line error:
When using HTTP/1.0, the client must send the server host name, either as part of an
absolute URI in the Request-Line, or as a separate "Host" header field.<br>
Now the problem is the "Host" header field.
We are not going into details here how to solve this problem, but the method is the same as above:
Debug using gdb, isolate the problem, make a small test program if necessary to isolate the problem even more.
That is, have patience, and keep on debugging.

gdb (inside emacs) (top)

ddd (GUI for gdb) (top)

Tools for security & memory leak check (top)

splint (security check) (top)

Valgrind (memory leak check) (top)

Read more (top)
Guide to Faster, Less Frustrating Debugging
The Student's Guide to the Secret Art of Debugging (pdf)
An Introduction To Using GDB Under Emacs
Debugging (on FreeBSD)

Last modified: Tue Jan 22 17:52:38 Romance Standard Time 2008