| Title: Apache HTTPD Debugging Guide |
| license: https://www.apache.org/licenses/LICENSE-2.0 |
| |
| # Apache Debugging Guide |
| |
| This document is a collection of notes regarding tools and techniques for |
| debugging Apache httpd and its modules. |
| |
| Got more tips? Send 'em to |
| [docs@httpd.apache.org](mailto:docs@httpd.apache.org). Thanks! |
| |
| 1. [Using gdb](#gdb) |
| |
| 1. [Getting a live backtrace on unix](#backtrace) |
| |
| 1. [Getting a live backtrace on Windows](#backtrace-win) |
| |
| 1. [Debugging intermittent crashes](#crashes) |
| |
| 1. [Using truss](#truss) |
| |
| 1. [Getting the server to dump core](#gcore) |
| |
| 1. [Solaris and coredumps](#sol27) |
| |
| 1. [Getting and analyzing a TCP packet trace](#tcpdump) |
| |
| # Using gdb {#gdb} |
| |
| If you use the gcc compiler, it is likely that the best debugger for your |
| system is gdb. This is only a brief summary of how to run gdb on Apache -- |
| you should look at the info and man files for gdb to get more information |
| on gdb commands and common debugging techniques. Before running gdb, be |
| sure that the server is compiled with the `-g` option in `CFLAGS` to |
| include the symbol information in the object files. |
| |
| The only tricky part of running gdb on Apache is forcing the server into a |
| single-process mode so that the parent process being debugged does the |
| request-handling work instead of forking child processes. We have provided |
| the `-X` option for that purpose, which will work fine for most cases. |
| However, some modules don't like starting up with `-X` , but are happy if |
| you force only one child to run (using " `MaxClients 1` "); you can then |
| use gdb's attach command to debug the child server. |
| |
| The following example, with <font color="green">user input in green</font>, |
| shows the output of gdb run on a server executable (httpd) in the current |
| working directory and using the server root of `/usr/local/apache` : |
| |
| <pre> |
| % <font color="green">gdb httpd</font> |
| GDB is free software and you are welcome to 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. |
| GDB 4.16.gnat.1.13 (sparc-sun-solaris2.5), |
| Copyright 1996 Free Software Foundation, Inc... |
| (gdb) <font color="green">b ap_process_request</font> |
| Breakpoint 1 at 0x49fb4: file http_request.c, line 1164. |
| (gdb) <font color="green">run -X -d /usr/local/apache</font> |
| Starting program: /usr/local/apache/src/httpd -X -d /usr/local/apache |
| |
| [ at this point I make a request from another window ] |
| |
| Breakpoint 1, ap_process_request (r=0x95250) at http_request.c:1164 |
| 1164 if (ap_extended_status) |
| (gdb) <font color="green">s</font> |
| 1165 |
| ap_time_process_request(r->connection->child_num,... |
| (gdb) <font color="green">n</font> |
| 1167 process_request_internal(r); |
| (gdb) <font color="green">s</font> |
| process_request_internal (r=0x95250) at http_request.c:1028 |
| 1028 if (!r->proxyreq && r->parsed_uri.path) { |
| (gdb) <font color="green">s</font> |
| 1029 access_status = ap_unescape_url(r->parsed_uri.path); |
| (gdb) <font color="green">n</font> |
| 1030 if (access_status) { |
| (gdb) <font color="green">s</font> |
| 1036 ap_getparents(r->uri); /* OK... |
| (gdb) <font color="green">n</font> |
| 1038 if ((access_status = location_walk(r))) { |
| (gdb) <font color="green">n</font> |
| 1043 if ((access_status = ap_translate_name(r))) { |
| (gdb) <font color="green">n</font> |
| 1048 if (!r->proxyreq) { |
| (gdb) <font color="green">n</font> |
| 1053 if (r->method_number == M_TRACE) { |
| (gdb) <font color="green">n</font> |
| 1062 if (r->proto_num > HTTP_VERSION(1,0) && |
| ap_... |
| (gdb) <font color="green">n</font> |
| 1071 if ((access_status = directory_walk(r))) { |
| (gdb) <font color="green">s</font> |
| directory_walk (r=0x95250) at http_request.c:288 |
| 288 core_server_config *sconf = ap_get_module_... |
| (gdb) <font color="green">b ap_send_error_response</font> |
| Breakpoint 2 at 0x47dcc: file http_protocol.c, line 2090. |
| (gdb) <font color="green">c</font> |
| Continuing. |
| |
| Breakpoint 2, ap_send_error_response (r=0x95250, recursive_error=0) |
| at http_protocol.c:2090 |
| 2090 BUFF *fd = r->connection->client; |
| (gdb) <font color="green">where</font> |
| #0 ap_send_error_response (r=0x95250, recursive_error=0) |
| at http_protocol.c:2090 |
| #1 0x49b10 in ap_die (type=403, r=0x95250) at http_request.c:989 |
| #2 0x49b60 in decl_die (status=403, phase=0x62db8 "check access", |
| r=0x95250) |
| at http_request.c:1000 |
| #3 0x49f68 in process_request_internal (r=0x95250) at |
| http_request.c:1141 |
| #4 0x49fe0 in ap_process_request (r=0x95250) at http_request.c:1167 |
| #5 0x439d8 in child_main (child_num_arg=550608) at http_main.c:3826 |
| #6 0x43b5c in make_child (s=0x7c3e8, slot=0, now=907958743) |
| at http_main.c:3898 |
| #7 0x43ca8 in startup_children (number_to_start=6) at http_main.c:3972 |
| #8 0x44260 in standalone_main (argc=392552, argv=0x75800) at |
| http_main.c:4250 |
| #9 0x449fc in main (argc=4, argv=0xefffee8c) at http_main.c:4534 |
| (gdb) <font color="green">s</font> |
| 2091 int status = r->status; |
| (gdb) <font color="green">p status</font> |
| $1 = 403 |
| (gdb) |
| </pre> |
| |
| There are a few things to note about the above example: |
| |
| 1. the " `gdb httpd` " command does not include any command-line options |
| for httpd: those are provided when the " `run` " command is done within |
| gdb; |
| |
| 1. I set a breakpoint before starting the run so that execution would stop |
| at the top of ap_process_request(); |
| |
| 1. the " `s` " command steps through the code and into called procedures, |
| whereas the " `n` " (next) command steps through the code but not into |
| called procedures. |
| |
| 1. additional breakpoints can be set with the " `b` " command, and the run |
| continued with the " `c` " command. |
| |
| 1. use the " `where` " command (a.k.a. " `bt` ") to see a stack backtrace |
| that shows the order of called procedures and their parameter values. |
| |
| 1. use the " `p` " command to print the value of a variable. |
| |
| A file in the the root directory called `.gdbinit` provides useful macros |
| for printing out various internal structures of httpd like tables ( |
| `dump_table` ), brigades ( `dump_brigade` ) and filter chains ( |
| `dump_filters` ). |
| |
| If you are debugging a repeatable crash, simply run gdb as above and make |
| the request -- gdb should capture the crash and provide a prompt where it |
| occurs. |
| |
| If you are debugging an apparent infinite loop, simply run gdb as above and |
| type a Control-C -- gdb will interrupt the process and provide a prompt |
| where it was stopped. |
| |
| If you are debugging a system crash and you have a core file from the |
| crash, then do the following: |
| |
| ``` |
| % gdb httpd -c core |
| (gdb) where |
| ``` |
| |
| and it will (hopefully) print a stack backtrace of where the core dump |
| occurred during processing. |
| |
| # Getting a live backtrace on unix {#backtrace} |
| |
| A backtrace will let you know the hierarchy of procedures that were called |
| to get to a particular point in the process. On some platforms you can get |
| a live backtrace of any process. |
| |
| For SVR4-based variants of Unix, the `pstack` command for proc can be used |
| to display a a live backtrace. For example, on Solaris it looks like |
| |
| ``` |
| % /usr/proc/bin/pstack 10623 |
| 10623: httpd -d /usr/local/apache |
| ef5b68d8 poll (efffcd08, 0, 3e8) |
| ef5d21e0 select (0, ef612c28, 0, 0, 3e8, efffcd08) + 288 |
| 00042574 wait_or_timeout (0, 75000, 75000, 7c3e8, 60f40, 52c00) + 78 |
| 00044310 standalone_main (5fd68, 75800, 75c00, 75000, 2, 64) + 240 |
| 000449f4 main (3, efffeee4, efffeef4, 75fe4, 1, 0) + 374 |
| 000162fc _start (0, 0, 0, 0, 0, 0) + 5c |
| ``` |
| |
| Another technique is to use gdb to attach to the running process and then |
| using "thread apply all bt" to print the backtrace, as in: |
| |
| ``` |
| sudo gdb /path/to/bin/httpd 10623 --batch --eval-command "where" --eval-command "thread apply all bt" --eval-command "detach" --eval-command "quit" | tee gdb-output.txt |
| ``` |
| |
| Or interactively: |
| |
| ``` |
| % gdb httpd 10623 |
| GDB is free software and you are welcome to 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. |
| GDB 4.16.gnat.1.13 (sparc-sun-solaris2.5), |
| Copyright 1996 Free Software Foundation, Inc... |
| |
| /usr/local/apache/src/10623: No such file or directory. |
| Attaching to program `/usr/local/apache/src/httpd', process 10623 |
| Reading symbols from /usr/lib/libsocket.so.1...done. |
| ... |
| 0xef5b68d8 in () |
| (gdb) where |
| #0 0xef5b68d8 in () |
| #1 0xef5d21e8 in select () |
| #2 0x4257c in wait_or_timeout (status=0x0) at http_main.c:2357 |
| #3 0x44318 in standalone_main (argc=392552, argv=0x75800) at... |
| #4 0x449fc in main (argc=3, argv=0xefffeee4) at http_main.c:4534 |
| (gdb) thread apply all bt |
| (gdb) detach |
| (gdb> quit |
| ``` |
| |
| # Getting a live backtrace on Windows {#backtrace-win} |
| |
| 1. Unzip the `-symbols.zip` files (obtained from the Apache download site) |
| in the root Apache2 directory tree (where bin\, htdocs\, modules\ etc. are |
| usually found.) These.pdb files should unpack alongside the.exe,.dll,.so |
| binary files they represent, e.g., mod_usertrack.pdb will unpack alongside |
| mod_usertrack.so. |
| |
| 1. Optional: Obtain `procdump` from Sysinternals. |
| |
| 1. Invoke `drwtsn32` and ensure you are creating a crash dump file, you are |
| dumping all thread contexts, your log and crash dump paths make sense, and |
| (depending on the nature of the bug) you pick an appropriate crash dump |
| type. (Full is quite large, but necessary sometimes for a programmer-type |
| to load your crash dump into a debugger and begin unwinding exactly what |
| has happened. Mini is sufficient for your first pass through the process.) |
| |
| You can use `procdump` or `procdump -ma` at this stage instead. |
| |
| 1. Note that if you previously installed and then uninstalled other |
| debugging software, you may need to invoke `drwtsn32 -i` in order to make |
| Dr Watson your default crash dump tool. This will replace the 'report |
| problem to MS' dialogs. (Don't do this if you have a full debugger such as |
| Visual Studio or windbg installed on the machine, unless you back up the |
| registry value for `Debugger` under the `HKLM\SOFTWARE\Microsoft\Windows |
| NT\CurrentVersion\AeDebug` registry tree. Developers using multiple tools |
| might want to keep copies of their different tools Debugger entries there, |
| for fast switching.) |
| |
| 1. Invoke the Task Manager, Choose 'show processes from all users', and |
| modify the `View -> Select Columns` to include **at least** the `PID` |
| and `Thread |
| Count`. You can change this just once and Task Manager should keep your |
| preference. |
| |
| 1. Now, track down the errant Apache that is hanging. The parent process |
| has about three threads, we don't care about that one. The child worker |
| process we want has many more threads (a few more than you configured with |
| the ThreadsPerChild directive.) The process name is Apache (for 1.3 and |
| 2.0) or httpd (for 2.2 and 2.4). Make note of the child worker's PID. |
| |
| 1. Using the {pid} number you noted above, invoke the command |
| > `drwtsn32 -p {pid}` |
| or |
| > `procdump {pid}` |
| |
| |
| Voila, you will find in your 'log file path' a `drwtsn32.log` file, and if |
| you choose to 'append to existing log file', jump through the 'App:' |
| sections until you find the one for the process you just killed. Now you |
| can identify about where 'Stack Back Trace' points to help identify what |
| the server is doing. |
| |
| You will note that many threads look identical, almost all of them polling |
| for the next connection, and you don't care about those. You will want to |
| see the ones that are deep inside of a request at the time you kill them, |
| and only the stack back trace entries for those. This can give folks a clue |
| of where that request is hanging, which handler module picked up the |
| request, and what filter it might be stuck in. |
| |
| # Debugging intermittent crashes {#crashes} |
| |
| For situations where a child process is crashing intermittently, the server |
| must be configured and started such that it produces core dumps which can |
| be analyzed further. |
| |
| To ensure that a core dump is written to a directory which is writable by |
| the user which child processes run as (such as `apache` ), the |
| [](https://httpd.apache.org/docs/current/mod/mpm_common.html#coredumpdirectory) |
| directive must be added to `httpd.conf` ; for example: |
| ``` |
| CoreDumpDirectory /tmp |
| ``` |
| Before starting up the server, any process limits on core dump file size |
| must be lifted; for example: |
| ``` |
| # ulimit -c unlimited |
| # apachectl start |
| ``` |
| On some platforms, further steps might be needed to enable core dumps - see |
| [Solaris and coredumps](#sol27) below. |
| |
| When a child process crashes, a message like the following will be logged |
| to the error_log: |
| |
| ``` |
| [Mon Sep 05 13:35:39 2005] [notice] child pid 2027 exit signal Segmentation |
| fault (11), possible coredump in /tmp |
| ``` |
| |
| If the text "possible coredump in /tmp" does not appear in the error line, |
| check that the ulimit was set correctly, that the permissions on the |
| configured `CoreDumpDirectory` are suitable and that platform specific |
| steps ( [Solaris and coredumps](#sol27) ) have been done if needed. |
| |
| To analyse the core dump, pass the core dump filename on the gdb |
| command-line, and enter the command `thread apply all bt full` at the gdb prompt: |
| |
| ``` |
| % gdb /usr/local/apache2/bin/httpd /tmp/core.2027 |
| ... |
| Core was generated by `/usr/local/apache2/bin/httpd -k start' |
| ... |
| (gdb) thread apply all bt full |
| ``` |
| |
| # Using 'truss/trace/strace' to trace system calls and signals {#truss} |
| |
| Most Unix-based systems have at least one command for displaying a trace of |
| system calls and signals as they are accessed by a running process. This |
| command is called `truss` on most SVR4-based systems and either `trace` or |
| `strace` on many other systems. |
| |
| A useful tip for using the `truss` command on Solaris is the `-f` option |
| (often also works with `strace` ); it tells truss to follow and continue |
| tracing any child processes forked by the main process. The easiest way to |
| get a full trace of a server is to do something like: |
| |
| <pre> |
| % truss -f httpd -d /usr/local/apache >& outfile |
| % egrep '^10698:' outfile |
| </pre> |
| |
| to view just the trace of the process id 10698. |
| |
| If attempting to truss a threaded server, for example when using the |
| `worker` MPM, the `truss` option `-l` is very useful as it prints also the |
| LWP id after the process id. You can use something like |
| |
| ``` |
| % egrep '^10698/1:' outfile |
| ``` |
| |
| to view just the trace of the process id 10698 and LWP id 1. |
| |
| Other useful options for truss are |
| |
| - `-a` to print all command line parameters used for this executable. |
| |
| - `-e` to print all environment variables used for this executable. |
| |
| - `-d` to print timestamps. |
| |
| # Getting the server to dump core {#gcore} |
| |
| Strangely enough, sometimes you actually want to force the server to crash |
| so that you can get a look at some nutty behavior. Normally this can be |
| done simply by using the `gcore` command. However, for security reasons, |
| most Unix systems do not allow a setuid process to dump core, since the |
| file contents might reveal something that is supposed to be protected in |
| memory. |
| |
| Here is one way to get a core file from a setuid Apache httpd process on |
| Solaris, without knowing which httpd child might be the one to die [note: |
| it is probably easier to use the MaxClients trick in the first section |
| above]. |
| |
| <pre> |
| # for pid in `ps -eaf | fgrep httpd | cut -d' ' -f4` |
| do |
| truss -f -l -t\!all -S SIGSEGV -p $pid 2>&1 | egrep SIGSEGV |
| & |
| done |
| </pre> |
| |
| The [undocumented '-S' |
| flag](http://www.dejanews.com/=zzz_maf/getdoc.xp?AN=352257973) to truss |
| will halt the process in place upon receipt of a given signal (SIGSEGV in |
| this case). At this point you can use: |
| |
| ``` |
| # gcore PID |
| ``` |
| |
| and then look at the backtrace as discussed above for [gdb](#gdb). |
| |
| # Solaris and coredumps {#sol27} |
| |
| On Solaris use [](http://docs.sun.com/app/docs/doc/816-5166/coreadm-1m) to |
| make `setuid()` processes actually dump core. By default a `setuid()` process |
| does not dump core. This is the reason why httpd servers started as root |
| with child processes running as a different user (such as `apache` ) do not |
| coredump even when the |
| [CoreDumpDirectory](https://httpd.apache.org/docs/current/mod/mpm_common.html#coredumpdirectory) |
| directive had been set to an appropriate and writable directory and **`ulimit -c`** has a sufficient size. |
| See also [Debugging intermittent crashes](#crashes) above. |
| |
| Example: |
| |
| ``` |
| -bash-3.00# coreadm |
| global core file pattern: /var/core/core.%f.%p.u%u |
| global core file content: default |
| init core file pattern: core |
| init core file content: default |
| global core dumps: disabled |
| per-process core dumps: enabled |
| global setid core dumps: enabled |
| per-process setid core dumps: enabled |
| global core dump logging: disabled |
| ``` |
| |
| # Getting and analyzing a TCP packet trace {#tcpdump} |
| |
| This is too deep a subject to fully describe in this documentation. Here |
| are some pointers to useful discussions and tools: |
| |
| - `snoop` is a packet sniffer that is part of Solaris. |
| |
| - [tcpdump](http://www.tcpdump.org/) is a packet sniffer that is available |
| for Unix-based systems and Windows ( |
| [windump](http://www.winpcap.org/windump/) ). It is part of many free |
| Unix-based distributions. |
| |
| - [Wireshark](http://www.wireshark.org/) is another packet sniffer that is |
| available for Unix-based systems and Windows. It has a nice GUI and allows |
| the analysis of the sniffed data. |
| |
| - [tcptrace](http://jarok.cs.ohiou.edu/software/tcptrace/) is a TCP dump |
| file analysis tool. |
| |
| - [tcpshow](http://www.tcpshow.org/) is another one. |