Friday February 27, 2009
Adding debugging functionality into your app!
In this blog I will just toss out some small ideas you may use if you would like to make your application a little bit more debug friendly while you are developing on it. The first little example is a function that you may call from your application to set a memory watchpoint on a memory area, so that you can trap "dangeling" pointers writing to your data structures. When do you want to use this? Well let's say that you when you develop your application it seems that someone change your data structure, and you cannot figure out how this is happening. One solution would be to run the program in the debugger and set a watchpoint on the memory area, but let's say that it happens only 1 out of a 1000 times. A better solution would probably be to set the watchpoint from your application code. The following little function does exactly that:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <procfs.h>
#include <sys/fault.h>
void memory_watchpoint(const void *ptr, size_t size, int mode)
{
int fd = open("/proc/self/ctl", O_WRONLY);
if (fd != -1) {
typedef struct {
long cmd;
prwatch_t prwatch;
} ctl_t;
ctl_t ctls = { .cmd = PCWATCH,
.prwatch.pr_vaddr = (uintptr_t)ptr,
.prwatch.pr_size = size };
if (mode & 1) {
ctls.prwatch.pr_wflags = WA_WRITE;
}
if (mode & 2) {
ctls.prwatch.pr_wflags |= WA_READ;
}
(void)write(fd, &ctls, sizeof(ctls));
(void)close(fd);
}
}
I would now turn on the watchpoint for the memory area when I'm done using it, and disable the watchpoint right before I intent to use it etc. Solaris would then trigger when someone tries to access the memory without disabling the watchpoint first.
Another thing you could do is to stop your binary when you detect a problem in your application instead of terminating the problem. Personally I hate to read log-files to try to figure out why the process exited (in 99,9% of the times you don't have all the information you want), and getting core-file is a big step in the right direction. My favorite is however when I can attach a debugger to the process instead:
#include <unistd.h>
#include <fcntl.h>
#include <procfs.h>
#include <unistd.h>
void stop_process(void)
{
int fd = open("/proc/self/ctl", O_WRONLY);
if (fd != -1) {
long cmds[2] = {PCSTOP, 0};
(void)write(fd, &cmds[0], sizeof(cmds));
(void)close(fd);
}
}
If you can't stop your server (let's say it's a database server), you could always call fork() first to let the parent continue and debug the child process ;-)
This is just two small examples you may use to make your application more debug friendly. You can take a look at proc(4) for more information on what you may use the /proc filesystem for. You will see that the /proc-filesystem on Solaris differs from the one on Linux in the way that the Linux /proc-filesystem is more optimized for the human eye (you may just cat the file, but Solaris provides a lot of tools to operate on the /proc-filesystem. See pmap, pstack, pfiles, pldd to mention a few (some of the tools works on core-files as well!! (and you may specify the thread you want to look at as well). I encourage you to look at the man pages.
Posted at 10:13PM Feb 27, 2009 by trond in OpenSolaris | Comments[0]