views:

277

answers:

4

GNU libc's backtrace and In-circuit emulators/debuggers are not always available when porting code to a new platform, especially when the target is a micro C compiler such as for the Z80. (Typically a program bug would "just hang" somewhere, or crash the gadget.)

Is there an alternative to the classic "wolf fencing" method of manually inserting printf? Something simple and portable (using no C extensions) that a coder can do while developing a program that includes tracing and backtracing into a C program?

BTW: Here are a couple of other question on stackoverflow that are related, but these both use GNU GLIBC's backtrace and backtrace is often compiler/implementation specific:

A: 

on Symbian there were some scripts made to go over the registers and stack looking for things that looked like code addresses.

This is not portable, but it doesn't depend on decorating the code either. This was a necessary tradeoff on a platform where byte counts mattered... and it wasn't nearly as limited as Z80! But limited enough to compile without frame-pointers and such.

To calculate a backtrace from a stack without frame-pointers you have to work up the stack not down it.

Will
Given a choice of decorating C code or using another language: decorating code seems more portable. Especially if the code decoration is tidy and can be inserted with a script.
NevilleDNZ
+3  A: 

Here is the kernel of the kernel of my answer: write some code.

The kernel of my answer is: If your compiler allocates locals on the stack always, then...

Add blobs to the stack at every function entry that record the name of the function, throw in some magic numbers to maybe catch stack smashes.

typedef struct stack_debug_blob_ {
    int magic1;
    const char * function_name;
    int magic2;
    struct stack_debug_blob_ * called_by;
    int magic3;
} stack_debug_blob;

stack_debug_blob * top_of_stack_debug_blobs = 0;

Create a macro ENTER(f) taking the name of the function. The macro should be about the first line of code in every function after the opening {. It adds a struct with a pointer to the (const) char * function name, a pointer to the previous struct on the stack, and maybe some magic numbers to check sanity. Make the top of blob stack pointer point at this new struct.

#define ENTER(f)                                                \
stack_debug_blob new_stack_debug_blob = {                       \
    MAGIC1, (f), MAGIC2, top_of_stack_debug_blobs, MAGIC3};     \
stack_debug_blob * evil_hack = (top_of_stack_debug_blobs = (&new_stack_debug_blob))

To keep things as portable as possible, all ENTER can do is declare and initialize variables. Hence the evil_hack to do a little extra computation than just initializing a variable.

Create a function to walk down the list of blobs checking pointers and magic numbers. It should signal an error (maybe print to stderr, maybe lockup the cpu with while (1) { /* nada */ }, maybe enter the debugger... depends on your hardware) if it finds things messed up.

Create a macro EXIT() that checks your stack of blobs, then de-links the topmost from the linked list. It needs to be put at the exit points of all your functions.

#define EXIT() do {                                            \
    check_debug_blobs();                                       \
    top_of_stack_debug_blobs = new_stack_debug_blob.called_by; \
    new_stack_debug_blob.magic1 -= 1; /* paranoia */           \
} while (0)

Probably will also need to replace all return's with RETURN macro calls, the RETURN macro is just like EXIT, but has a return before the } while (0).

Create a function to walk down the list of blobs printing out the function names, call it something like stacktrace or backtrace maybe.

Write a program to instrument your C code with calls to ENTER(f) and EXIT() and RETURN(x).

Left out a few details to let you have fun with it...

See also http://stackoverflow.com/questions/2536021/any-porting-available-of-backtrace-for-uclibc

jsl4tv
magic1, magic2 and magic3 look useful.
NevilleDNZ
you could rename `ENTER` as `ENTER_` and `#define ENTER() ENTER_(__FUNCTION__)`.
John Ledbetter
+2  A: 

There is an implementation at RosettaCode.org which uses the same basic idea as @jsl4tv's suggestion.

Example, given the following classic C code with built in "hang":

#include <stdio.h>
#include <stdlib.h>

void inner(int k)
{
   for(;;){} /* hang */
}

void middle(int x, int y)
{
  inner(x*y);
}

void outer(int a, int b, int c)
{
  middle(a+b, b+c);
}

int main()
{
  outer(2,3,5);
  return(EXIT_SUCCESS);
}

#define STACK_TRACE_ON and #include "stack_trace.h" from RosettaCode.org then insert BEGIN(f)/ENDs where required:

#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();
   for(;;){} /* hang */
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

Produces:

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) ---

A well polished [open source] version of this BEGIN ~ END method would be perfect. (Esp if it has a "FINALLY" clause for exception handling).

Hints/URLs appreciated.

NevilleDNZ
Nice! That goes in my list of bookmarks.
jsl4tv
NevilleDNZ
The only thing I see missing from the rosetta code, well a major thing missing, is code to instrument/decorate your source automagically. Course then my next feature request would be the hard one "...and the parameters too". I *think* you could get that in a portable way by making all functions varargs and using the polite/standard access to the args. Big todo item!
jsl4tv
@jsl4tv - 1) a retooled version of indent(1L) could be made to insert BEGIN/ENDs, but a simplistic tool can be done in sed||awk||python etc; 2) re: args, maybe arguments can be handled like printf with a funny format string, eg BEGIN(outer,"%d,%d,%d"). These would be tricky, but nice. However my typical scenario is that "somewhere" - usually during porting - the code hangs, and you know not where!!! A trace is a really good start.
NevilleDNZ
I'm thinking about accepting a self answer... complete with the bounty. The rosettacode soultion [http://rosettacode.org/wiki/Stack_traces#Using_no_extensions] appears to be the most complete so far.
NevilleDNZ
However @Jonathan_Fischoff suggestion is useful: basically use msvc's _penter and _pexit [http://www.drdobbs.com/184403601] or gcc's option -finstrument-functions c.f. http://stackoverflow.com/questions/3315248/c-c-need-a-clever-way-to-track-function-calls/3315327#3315327
NevilleDNZ