Stack traces: Difference between revisions

8,217 bytes added ,  14 years ago
(→‎{{header|JavaScript}}: Never mind, apparently the program should be able to continue execution.)
Line 149:
 
=={{header|C}}==
=== Using GNU extensions ===
{{works with|POSIX}}
 
Line 207 ⟶ 208:
/lib/i686/libc.so.6(__libc_start_main+0xe5)[0xb7e045c5]
./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}}==