在C语言中自定义调用栈

        对于规模大一些的 C 语言项目,往往会存在不好查的 bug,比如某些底层函数在检查参数时遇到不符合预期的情况,但由于调用路径很多,不能有效且快速的找出完整的调用链。此时,如果能在出问题的点打印出调用链,那将会有助于快速定位问题。
        之前在看 mariadb 的源码时,发现里面实现了自定义的调用栈跟踪功能,下面将其中的关键代码简化出来,以供大家参考。

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

#define DBUG_DUMP _db_dump_

#define DBUG_LEAVE do { \
        _db_stack_frame_.line = __LINE__; \
        _db_return_(&_db_stack_frame_); \
        _db_stack_frame_.line = 0; \
        } while (0)

#define DBUG_ENTER(a) struct _db_stack_frame_ _db_stack_frame_; \
        _db_enter_(a, __FILE__, __LINE__, &_db_stack_frame_)

#define DBUG_RETURN(a1) do {DBUG_LEAVE; return (a1);} while(0)
#define DBUG_VOID_RETURN do {DBUG_LEAVE; return;} while(0)

pthread_key_t db_key;

struct _db_stack_frame_ {
        const char *func;
        const char *file;
        int level;
        int line;
        struct _db_stack_frame_ *prev;
};

struct _db_code_state_ {
        struct _db_stack_frame_ *framep;
        int level;
        /* any other variables */
};

void _db_enter_(const char *_func_, const char *_file_,
                int _line_, struct _db_stack_frame_ *_stack_frame_)
{
        struct _db_code_state_ *cs;

        cs = pthread_getspecific(db_key);
        assert(cs != NULL);

        _stack_frame_->func = _func_;
        _stack_frame_->file = _file_;
        _stack_frame_->line = _line_;

        _stack_frame_->level = ++cs->level;
        _stack_frame_->prev = cs->framep;
        cs->framep = _stack_frame_;
}

void _db_return_(struct _db_stack_frame_ *_stack_frame_)
{
        struct _db_code_state_ *cs;

        cs = pthread_getspecific(db_key);
        assert(cs != NULL);

        assert(cs->framep != NULL);

        cs->framep = cs->framep->prev;
        if (cs->level > 0)
            --cs->level;
}

void _db_dump_(void)
{
        struct _db_stack_frame_ *_stack_frame_;
        struct _db_code_state_ *cs;

        cs = pthread_getspecific(db_key);
        assert(cs != NULL);

        _stack_frame_ = cs->framep;
        while (_stack_frame_) {
                printf("#%d %s() at %s:%d\n", _stack_frame_->level,
                                _stack_frame_->func, _stack_frame_->file,
                                _stack_frame_->line);
                _stack_frame_ = _stack_frame_->prev;
        }
}

int is_even(int num)
{
        return !(num & 0x1);
}

int fab(int num)
{
        int a, b, c, i;
        DBUG_ENTER(__func__);

        if (num <= 2)
                DBUG_RETURN(1);

        a = b = 1;
        for (i = 2; i < num; i++) {
                c = a + b;
                a = b;
                b = c;
        }

        if (!is_even(c)) {
                DBUG_DUMP();
                printf("Fatal error encountered!\n");
                exit(-1);
        }

        DBUG_RETURN(c);
}

int foo3(int num)
{
        int ret;
        DBUG_ENTER(__func__);

        ret = fab(num);

        DBUG_RETURN(ret);
}

int foo2(void)
{
        int ret;
        DBUG_ENTER(__func__);

        ret = foo3(5);

        DBUG_RETURN(ret);
}

void foo1(void)
{
        int ret;
        DBUG_ENTER(__func__);

        ret = foo2();
        printf("foo2 return: %d\n", ret);

        DBUG_VOID_RETURN;
}

void do_test(void)
{
        DBUG_ENTER(__func__);

        foo1();

        DBUG_VOID_RETURN;
}

int main(void)
{
        struct _db_code_state_ *cs;

        cs = (struct _db_code_state_ *)malloc(sizeof(struct _db_code_state_));
        cs->framep = NULL;
        cs->level = 0;

        pthread_key_create(&db_key, NULL);
        pthread_setspecific(db_key, cs);

        do_test();

        return 0;
}

        运行效果如下:

# gcc -o main main.c -lpthread
# ./main
#5 fab() at main.c:90
#4 foo3() at main.c:114
#3 foo2() at main.c:124
#2 foo1() at main.c:134
#1 do_test() at main.c:144
Fatal error encountered!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值