Stack traces: Difference between revisions
Content deleted Content added
→{{header|JavaScript}}: Never mind, apparently the program should be able to continue execution. |
|||
Line 149: | Line 149: | ||
=={{header|C}}== |
=={{header|C}}== |
||
=== Using GNU extensions === |
|||
{{works with|POSIX}} |
{{works with|POSIX}} |
||
Line 207: | Line 208: | ||
/lib/i686/libc.so.6(__libc_start_main+0xe5)[0xb7e045c5] |
/lib/i686/libc.so.6(__libc_start_main+0xe5)[0xb7e045c5] |
||
./pst[0x80485d1]</pre> |
./pst[0x80485d1]</pre> |
||
=== Using no extensions === |
|||
Sometimes microcomputers do not come with any kind of debug or stack tracing routines. Typically a program would "just hang" somewhere, or crash the gadget. This then requires manually "wolf fencing" of the bug with ''printf'' statements to identify the C source file, then the procedure, and then the line at the point of system crash. |
|||
The following macros and procedures provide an alternative way of doing this trouble shooting. |
|||
The steps are: |
|||
* #include "stack_trace.h" in the suspect C source code. |
|||
* Change the initial and last { and } of each procedure in the code to BEGIN(proc_name) and END. |
|||
* set both STACK_TRACE_ON and stack_trace_on to 1 (TRUE) |
|||
* Recompile and run.... |
|||
The resulting output can be used to locate offending procedure and - hopefully - give a hint to the location of the actual bug. |
|||
The key point is that the following can be done on systems that are equipped with only and editor and compiler, and no debugger or library extension. |
|||
'''==> stack_trace.h <==''' |
|||
<lang C> |
|||
/* stack_trace.c - macros for hinting/tracing where a program crashed |
|||
on a system _without_ any form of debugger. |
|||
Simple goodbye_cruel_world.c example: |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#define STACK_TRACE_ON // compile in these "stack_trace" routines |
|||
#include "stack_trace.h" |
|||
void goodbye_cruel_world() |
|||
BEGIN(goodbye_cruel_world) |
|||
print_stack_trace(); |
|||
for(;;){} |
|||
END |
|||
int main() |
|||
BEGIN(main) |
|||
stack_trace.on = TRUE; // turn on runtime tracing |
|||
goodbye_cruel_world(); |
|||
stack_trace.on = FALSE; |
|||
RETURN(EXIT_SUCCESS); |
|||
END |
|||
Output: |
|||
goodbye_cruel_world.c:8: BEGIN goodbye_cruel_world[0x80486a8], stack(depth:1, size:60) |
|||
goodbye_cruel_world.c:8: goodbye_cruel_world[0x80486a8] --- stack(depth:2, size:60) --- |
|||
goodbye_cruel_world.c:14: main[0x80486f4] --- stack(depth:1, size:0) --- |
|||
goodbye_cruel_world.c:8: --- (depth 2) --- |
|||
*/ |
|||
#ifndef _LINUX_STDDEF_H |
|||
#include <stddef.h> |
|||
#endif |
|||
typedef struct stack_trace_frame_s { |
|||
char *file_name; int file_line; char *proc_name; void *proc_addr; struct stack_trace_frame_s *down, *up; |
|||
} stack_trace_frame_t; |
|||
#define SKIP |
|||
typedef enum {TRUE=1, FALSE=0} bool_t; |
|||
typedef struct { |
|||
bool_t on; |
|||
struct { char *_begin, *_print, *_return, *_exit, *_end; } fmt; |
|||
struct { int depth; stack_trace_frame_t *lwb, *upb; } stack; |
|||
struct { int lwb, by, upb; char *prefix; } indent; |
|||
} stack_trace_t; |
|||
extern stack_trace_t stack_trace; |
|||
void stack_trace_begin(char *SKIP, stack_trace_frame_t *SKIP); |
|||
void stack_trace_end(char *SKIP, int SKIP); |
|||
void print_stack_trace(); |
|||
#ifdef STACK_TRACE_ON |
|||
/* Many ThanX to Steve R Bourne for inspiring the following macros ;-) */ |
|||
#define BEGIN(x) { auto stack_trace_frame_t this = {__FILE__, __LINE__, #x, &x, NULL, NULL}; \ |
|||
stack_trace_begin(stack_trace.fmt._begin, &this); { |
|||
#define RETURN(x) { stack_trace_end(stack_trace.fmt._return, __LINE__); return(x); } |
|||
#define EXIT(x) { stack_trace_end(stack_trace.fmt._exit, __LINE__); exit(x); } |
|||
#define END } stack_trace_end(stack_trace.fmt._end, __LINE__); } |
|||
#else |
|||
/* Apologies to Landon Curt Noll and Larry Bassel for the following macros :-) */ |
|||
#define BEGIN(x) { |
|||
#define RETURN(x) return(x) |
|||
#define EXIT(x) exit(x) |
|||
#define END } |
|||
#endif</lang> |
|||
'''==> stack_trace.c <==''' |
|||
<lang C>#include <stdio.h> |
|||
#include <stddef.h> |
|||
#define STACK_TRACE_ON |
|||
#include "stack_trace.h" |
|||
#define indent_fmt "%s" |
|||
#define std_cc_diag_fmt "%s:%d: " |
|||
#define stack_trace_diag_fmt " %s[0x%x], stack(depth:%d, size:%u)\n" |
|||
#define stack_trace_fmt "%s:%d:\t%s[0x%x]\t--- stack(depth:%d, size:%u) ---\n" |
|||
stack_trace_t stack_trace = { |
|||
FALSE, /* default: stack_trace.on == FALSE */ |
|||
{ std_cc_diag_fmt""indent_fmt"BEGIN"stack_trace_diag_fmt, |
|||
stack_trace_fmt, |
|||
std_cc_diag_fmt""indent_fmt"RETURN"stack_trace_diag_fmt, |
|||
std_cc_diag_fmt""indent_fmt"EXIT"stack_trace_diag_fmt, |
|||
std_cc_diag_fmt""indent_fmt"END"stack_trace_diag_fmt }, |
|||
{ 0, (stack_trace_frame_t*)NULL, (stack_trace_frame_t*)NULL }, /* stack */ |
|||
{ 19, 2, 20, " " } /* indent wrap */ |
|||
}; |
|||
void stack_trace_begin(char *fmt, stack_trace_frame_t *this){ |
|||
if(stack_trace.on){ |
|||
fprintf(stderr, fmt, |
|||
this->file_name, this->file_line, /* file details */ |
|||
stack_trace.indent.prefix+stack_trace.indent.lwb, |
|||
this->proc_name, this->proc_addr, /* procedure details */ |
|||
stack_trace.stack.depth, (unsigned)stack_trace.stack.lwb-(unsigned)this); |
|||
stack_trace.indent.lwb = ( stack_trace.indent.lwb - stack_trace.indent.by ) % stack_trace.indent.upb; |
|||
} |
|||
if(!stack_trace.stack.upb){ /* this IS the stack !! */ |
|||
stack_trace.stack.lwb = stack_trace.stack.upb = this; |
|||
} else { |
|||
this -> down = stack_trace.stack.upb; |
|||
stack_trace.stack.upb -> up = this; |
|||
stack_trace.stack.upb = this; |
|||
} |
|||
stack_trace.stack.depth++; |
|||
} |
|||
void stack_trace_end(char *fmt, int line){ |
|||
stack_trace.stack.depth--; |
|||
if(stack_trace.on){ |
|||
stack_trace.indent.lwb = ( stack_trace.indent.lwb + stack_trace.indent.by ) % stack_trace.indent.upb; |
|||
stack_trace_frame_t *this = stack_trace.stack.upb; |
|||
fprintf(stderr, fmt, |
|||
this->file_name, this->file_line, /* file details */ |
|||
stack_trace.indent.prefix+stack_trace.indent.lwb, |
|||
this->proc_name, this->proc_addr, /* procedure details */ |
|||
stack_trace.stack.depth, (unsigned)stack_trace.stack.lwb-(unsigned)this); |
|||
} |
|||
stack_trace.stack.upb = stack_trace.stack.upb -> down; |
|||
} |
|||
void print_indent(){ |
|||
if(!stack_trace.stack.upb){ |
|||
/* fprintf(stderr, "STACK_TRACE_ON not #defined during compilation\n"); */ |
|||
} else { |
|||
stack_trace_frame_t *this = stack_trace.stack.upb; |
|||
fprintf(stderr, std_cc_diag_fmt""indent_fmt, |
|||
this->file_name, this->file_line, /* file details */ |
|||
stack_trace.indent.prefix+stack_trace.indent.lwb |
|||
); |
|||
} |
|||
} |
|||
void print_stack_trace() { |
|||
if(!stack_trace.stack.upb){ |
|||
/* fprintf(stderr, "STACK_TRACE_ON not #defined during compilation\n"); */ |
|||
} else { |
|||
int depth = stack_trace.stack.depth; |
|||
stack_trace_frame_t *this = stack_trace.stack.upb; |
|||
for(this = stack_trace.stack.upb; this; this = this->down, depth--){ |
|||
fprintf(stderr, stack_trace.fmt._print, |
|||
this->file_name, this->file_line, /* file details */ |
|||
this->proc_name, this->proc_addr, /* procedure details */ |
|||
depth, (unsigned)stack_trace.stack.lwb-(unsigned)this); |
|||
} |
|||
print_indent(); fprintf(stderr, "--- (depth %d) ---\n", stack_trace.stack.depth); |
|||
} |
|||
}</lang> |
|||
'''==> stack_trace_test.c <==''' |
|||
The following code demonstrates the usage of the macros. Note that the initial and last curly brackets have been changed to BEGIN(procedure_name) and END. This is sometimes called '''macro magic''' and is unfashionable. |
|||
<lang C>#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#define STACK_TRACE_ON /* compile in these "stack_trace" routines */ |
|||
#include "stack_trace.h" |
|||
void inner(int k) |
|||
BEGIN(inner) |
|||
print_indent(); printf("*** Now dump the stack ***\n"); |
|||
print_stack_trace(); |
|||
END |
|||
void middle(int x, int y) |
|||
BEGIN(middle) |
|||
inner(x*y); |
|||
END |
|||
void outer(int a, int b, int c) |
|||
BEGIN(outer) |
|||
middle(a+b, b+c); |
|||
END |
|||
int main() |
|||
BEGIN(main) |
|||
stack_trace.on = TRUE; /* turn on runtime tracing */ |
|||
outer(2,3,5); |
|||
stack_trace.on = FALSE; |
|||
RETURN(EXIT_SUCCESS); |
|||
END</lang> |
|||
Output: |
|||
<pre> |
|||
stack_trace_test.c:19: BEGIN outer[0x80487b4], stack(depth:1, size:60) |
|||
stack_trace_test.c:14: BEGIN middle[0x8048749], stack(depth:2, size:108) |
|||
stack_trace_test.c:8: BEGIN inner[0x80486d8], stack(depth:3, size:156) |
|||
stack_trace_test.c:8: *** Now dump the stack *** |
|||
stack_trace_test.c:8: inner[0x80486d8] --- stack(depth:4, size:156) --- |
|||
stack_trace_test.c:14: middle[0x8048749] --- stack(depth:3, size:108) --- |
|||
stack_trace_test.c:19: outer[0x80487b4] --- stack(depth:2, size:60) --- |
|||
stack_trace_test.c:24: main[0x804882a] --- stack(depth:1, size:0) --- |
|||
stack_trace_test.c:8: --- (depth 4) --- |
|||
stack_trace_test.c:8: END inner[0x80486d8], stack(depth:3, size:156) |
|||
stack_trace_test.c:14: END middle[0x8048749], stack(depth:2, size:108) |
|||
stack_trace_test.c:19: END outer[0x80487b4], stack(depth:1, size:60) |
|||
</pre> |
|||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |