Testing &debugging Perl CGI script

Debugging CGI programs is sometimes a difficult task because they rely on different information from several different sources. There are several different ways you can test your CGI programs, both interactively over the Web and stand-alone using a debugger. Both of these approaches have different advantages and disadvantages.
In this chapter, you learn some common debugging techniques using CGI scripts and common debuggers as tools. You then learn some very common CGI errors and solutions.

Debugging Techniques

There are two different approaches to testing and debugging CGI programs: testing the program over the Web server as a CGI program and testing it as a stand-alone program. Although you can open HTML and other files directly from a Web browser, you need to have a Web server running in order to test the results of a CGI program from a Web browser. If you already have a server from which you can test your CGI programs or if you set up a personal or experimental server for testing purposes, how can you debug your CGI programs?
There are several steps you can take. First, see if your program works. If it doesn't and if you receive a server error message, your program did not execute correctly. If you do not receive a server error message but your output is incorrect, then there is most likely a problem either with one of your algorithms or with the expected data.
There are several potential server error messages, the simplest being ones such as "file not found" (404). One of the most common server error messages when your CGI program is not working properly is "server error" (500), which means that your CGI program did not send an appropriate response to the server. The server always expects CGI headers (such as Content-Type) and usually some data; if the appropriate headers are not sent, then the server will return a 500 error.
Tip
Many servers redirect stderr to a file. The NCSA and Apache servers, for example, log error messages and stderr to the file logs/error_log by default. This is an invaluable resource for debugging CGI programs, because you can often determine the exact nature of the problem by looking at this log file. You can also log certain information to this file from within your CGI program by printing messages to stderr.
For example, the following program returns the error 500 because the header is invalid:
#include <stdio.h>

int main()
{
  printf("Cotnent-Tpye: txet/plain\r\n\r\n");
  printf("Hello, World!\n");
}
If you check your server error logs, you are likely to find a message that says the headers are invalid.
If you know your program should return the appropriate headers (that is, you have the proper print statements in the proper places), then your program has failed somewhere before the headers are sent. For example, the following C code seems to be a valid CGI program:
#include <stdio.h>
#include <string.h>

int main()
{
  char *name;

  strcpy(name,NULL);
  printf("Content-Type: text/plain\r\n\r\n");
  printf("Hello, world!\n");
}
This program will compile fine and the headers it prints are valid, but when you try to run it from the Web server, the server returns an error 500. The reason is clear in this contrived example:strcpy() produces a segmentation fault when you try to copy a NULL value to a string. Because the program crashes before the header is sent, the server never receives valid information and so must return an error 500. Removing the strcpy() line from the program fixes the problem.
Another common browser message is Document contains no data. This message appears when a successful status code (200) and Content-Type are sent but no data is. If you know your program should print data following the header, you can infer that the problem lies between the header and body output. Consider the modified code:
#include <stdio.h>
#include <string.h>

int main()
{
  char *name;

  printf("Content-Type: text/plain\r\n\r\n");
  strcpy(name,NULL);
  printf("Hello, world!\n");
}
If you compile and run this program as a CGI, you will receive a Document contains no data message but no error. However, there is supposed to be data: "Hello, world!". Again, the error is clear: You cannot copy a NULL string to a variable. Because the program crashes after the header is printed, the body is never sent, and consequently, the browser thinks the document has no data. The error message helps you narrow down the location of the error and quickly identify the problem.
With a compiled language such as C, server error 500 generally means that the program has crashed before the header has been sent. Any syntax errors in the code are caught at compile-time. However, because scripting languages such as Perl are compiled languages, you don't know whether there are syntax errors until you actually run the program. If there are syntax errors, then the program will crash immediately and once again, you will see the familiar error 500. For example:
#!/usr/local/bin/perl

pirnt "Content-Type: text/plain\n\n";
print "Hello, World!\n";
There is a typo in the first print statement, so the program will not run, and consequently, the server receives no headers and sends an error 500. If your server logs stderr to an error file, you can find exactly where the syntax errors are by checking the log.
How can you debug your program if it runs correctly, does not crash, but returns the incorrect output? Normally, you could run your program through a debugger and watch the important variables to see exactly where your program is flawed. However, you cannot run the CGI program through a debugger if it is being run by the server. If you are testing your CGI program in this manner, you want to take advantage of the server and the browser to locate the error.
The poor man's method of debugging is to include a lot of print statements throughout the code. Because everything printed to the stdout is sent to the browser, you can look at the values of various variables from your Web browser. For example, the following code is supposed to output the numbers 1 factorial (1), 2 factorial (2), and 3 factorial (6):
#include <stdio.h>

int main()
{
  int product = 1;
  int i;

  printf("Content-Type: text/html\r\n\r\n");
  printf("<html><head>\n");
  printf("<title>1, 2, and 6</title>\n");
  printf("</head>\n\n");
  printf("<body>\n");

  for (i=1; i<=3; i++)
  printf("<p>%d</p>\n",product*i);

  printf("</body></html>\n");
}
When you compile and run this program as a CGI, you get 12, and 3 as shown in Figure 7.1. Suppose for the moment that this is a vastly complex program and that you cannot for the life of you figure out why this code is not working properly. To give you more information and help you trace the problem, you could print the values of product and i at each stage of the loop. Adding the appropriate lines of code produces the output in Figure 7.2.
Figure 7.1 : Output of buggy factorial program.
Figure 7.2 : Output of buggy factorial program with debugging information.
#include <stdio.h>

int main()
{
  int product = 1;
  int i;

  printf("Content-Type: text/html\r\n\r\n");
  printf("<html><head>\n");
  printf("<title>1, 2, and 6</title>\n");
  printf("</head>\n\n");
  printf("<body>\n");

  for (i=1; i<=3; i++) {
    /* print product and i */
    printf("<p>product = %d i = %d<br>\n",product,i);
    printf("%d</p>\n",product*i);
  }

  printf("</body></html>\n");
}

No comments:

Post a Comment