Apart from error codes, error strings and logs, are there any other features which can be incorporated in the code to increase getting debug / trace information during code runtime which can help debug issues (or let us know what is going on) at runtime?
Here's an example of the code that sends a stacktrace to a file upon a segmentation fault
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <stdarg.h> static void signal_handler(int); static void dumpstack(void); static void cleanup(void); void init_signals(void); void panic(const char *, ...); struct sigaction sigact; char *progname; int main(int argc, char **argv){ char *s; progname = *(argv); atexit(cleanup); init_signals(); printf("About to seg fault by assigning zero to *s\n"); *s = 0; sigemptyset(&sigact.sa_mask); return 0; } void init_signals(void){ sigact.sa_handler = signal_handler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGSEGV); sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGBUS); sigaction(SIGBUS, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGQUIT); sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGHUP); sigaction(SIGHUP, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGKILL); sigaction(SIGKILL, &sigact, (struct sigaction *)NULL); } static void signal_handler(int sig){ if (sig == SIGHUP) panic("FATAL: Program hanged up\n"); if (sig == SIGSEGV || sig == SIGBUS){ dumpstack(); panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown")); } if (sig == SIGQUIT) panic("QUIT signal ended program\n"); if (sig == SIGKILL) panic("KILL signal ended program\n"); if (sig == SIGINT) ; } void panic(const char *fmt, ...){ char buf[50]; va_list argptr; va_start(argptr, fmt); vsprintf(buf, fmt, argptr); va_end(argptr); fprintf(stderr, buf); exit(-1); } static void dumpstack(void){ /* Got this routine from http://www.whitefang.com/unix/faq_toc.html ** Section 6.5. Modified to redirect to file to prevent clutter */ char dbx[160]; sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname); system(dbx); return; } void cleanup(void){ sigemptyset(&sigact.sa_mask); /* Do any cleaning up chores here */ }
In the function dumpstack
, dbx
needs to be changed to suit your debugger, such as gdb
for the GNU Debugger, this code was used when I was programming on AIX box a few years ago. Notice how the signals are set up, and if a SIGSEGV fault occurs, the handler dumps the stack to a file with extension .dump
. The code demonstrates the segmentation fault and dumps the stacktrace.
That is my favourite code.
Hope this helps, Best regards, Tom.
- Build without optimization, to preserve as much of the "intent" of the code as possible
- Build in debug mode, to add symbol information
- Don't strip the executable (on Linux/Unix systems), to keep as much symbol information as possible for debuggers to use
Here's an example of the code that sends a stacktrace to a file upon a segmentation fault
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <stdarg.h> static void signal_handler(int); static void dumpstack(void); static void cleanup(void); void init_signals(void); void panic(const char *, ...); struct sigaction sigact; char *progname; int main(int argc, char **argv){ char *s; progname = *(argv); atexit(cleanup); init_signals(); printf("About to seg fault by assigning zero to *s\n"); *s = 0; sigemptyset(&sigact.sa_mask); return 0; } void init_signals(void){ sigact.sa_handler = signal_handler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGSEGV); sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGBUS); sigaction(SIGBUS, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGQUIT); sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGHUP); sigaction(SIGHUP, &sigact, (struct sigaction *)NULL); sigaddset(&sigact.sa_mask, SIGKILL); sigaction(SIGKILL, &sigact, (struct sigaction *)NULL); } static void signal_handler(int sig){ if (sig == SIGHUP) panic("FATAL: Program hanged up\n"); if (sig == SIGSEGV || sig == SIGBUS){ dumpstack(); panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown")); } if (sig == SIGQUIT) panic("QUIT signal ended program\n"); if (sig == SIGKILL) panic("KILL signal ended program\n"); if (sig == SIGINT) ; } void panic(const char *fmt, ...){ char buf[50]; va_list argptr; va_start(argptr, fmt); vsprintf(buf, fmt, argptr); va_end(argptr); fprintf(stderr, buf); exit(-1); } static void dumpstack(void){ /* Got this routine from http://www.whitefang.com/unix/faq_toc.html ** Section 6.5. Modified to redirect to file to prevent clutter */ char dbx[160]; sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname); system(dbx); return; } void cleanup(void){ sigemptyset(&sigact.sa_mask); /* Do any cleaning up chores here */ }
In the function dumpstack
, dbx
needs to be changed to suit your debugger, such as gdb
for the GNU Debugger, this code was used when I was programming on AIX box a few years ago. Notice how the signals are set up, and if a SIGSEGV fault occurs, the handler dumps the stack to a file with extension .dump
. The code demonstrates the segmentation fault and dumps the stacktrace.
That is my favourite code.
Hope this helps, Best regards, Tom.
When building for Linux, I like to be able to print a stack backtrace from a signal handler. This helps debug crashes (SIGSEGV
) or allows me to send a signal to the program to initiate a stack backtrace at runtime. Core dumps can also be useful in debugging crashes (again in Linux).
© 2022 - 2024 — McMap. All rights reserved.