一种简单,统一且可移植的方式,包括跟踪和回溯到C程序中

时间:2010-07-17 23:46:43

标签: c embedded debugging portability backtrace

将代码移植到新平台时,

GNU libc's backtraceIn-circuit emulators/debuggers并不总是可用,尤其是当目标是微C编译器时,例如Z80。 (通常程序错误会“暂停”某个地方,或者崩溃小工具。)

是否有替代手动插入printf的经典“wolf fencing”方法?编程人员在开发包含跟踪和回溯到C程序的程序时可以做的简单和便携(不使用C扩展)的东西?

顺便说一句:以下是关于stackoverflow的其他一些相关问题,但这两个问题都使用GNU GLIBC's backtrace,而backtrace通常是编译器/实现特定的:

3 个答案:

答案 0 :(得分:4)

这是我的答案内核的核心:编写一些代码。

我的答案的核心是:如果您的编译器总是在堆栈上分配本地,那么......

在记录函数名称的每个函数条目中将blob添加到堆栈中,抛出一些幻数以捕获堆栈碎片。

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;

创建一个宏ENTER(f)获取该函数的名称。宏应该是关于开放后每个函数中的第一行代码{。它添加了一个结构,其中包含指向(const)char *函数名称的指针,指向堆栈上前一个结构的指针,以及一些用于检查健全性的魔术数字。使blob堆栈指针的顶部指向这个新结构。

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

为了尽可能保持可移植性,所有ENTER都能做的是声明和初始化变量。因此,evil_hack除了初始化变量之外还要做一些额外的计算。

创建一个函数来查看检查指针和幻数的blob列表。它应该发出一个错误信号(可能是打印到stderr,可能用while(1){/ * nada * /}锁定cpu,也许进入调试器......取决于你的硬件)如果发现事情搞砸了。

创建一个宏EXIT(),用于检查您的blob堆栈,然后从链接列表中取消最顶层的链接。它需要放在所有功能的退出点。

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

可能还需要用RETURN宏调用替换所有返回值,RETURN宏就像EXIT一样,但在} while(0)之前返回。

创建一个函数来向下遍历打印出函数名称的blob列表,可以将其称为stacktrace或backtrace。

通过调用ENTER(f)和EXIT()和RETURN(x)编写程序来检测C代码。

遗漏了一些细节,让你玩得开心......

另见Any porting available of backtrace for uclibc?

答案 1 :(得分:2)

RosettaCode.org有一个实现,它使用与@ jsl4tv建议相同的基本思想。

示例,给出以下内置“ hang ”的经典C代码:

#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);
}

RosettaCode.org #define STACK_TRACE_ON和#include“stack_trace.h”然后在需要时插入BEGIN(f)/ ENDs:

#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

产地:

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

这个BEGIN~END方法的完美抛光[开源]版本将是完美的。 (如果它有异常处理的“FINALLY”子句,则为Esp)。

提示/网址赞赏。

答案 2 :(得分:0)

在Symbian上有一些脚本用于遍历寄存器和堆栈,寻找看起来像代码地址的东西。

这不是便携式的,但它也不依赖于装饰代码。这是字节计数重要的平台上的必要权衡......并且它几乎不像Z80那样有限!但有限,足以编译没有帧指针等。

要计算没有帧指针的堆栈的回溯,你必须处理堆栈而不是它。