C 语言的函数装饰器-仿函数 设计与实现

程序在现场运行,发生了一系列的崩溃, 定位在 rte_mempool_get/rte_mempool_put和 enqueue_msg/dequeue_msg.

需要盘查 rte_mempool_get/rte_mempool_put 内存节点在各个线程之间来回穿梭,

当 rte_mempool_get 获取成功之后 enqueue入队列马上再 rte_mempool_put 释放,
一定会导致 dequeue 出队列的一方持有了一个野指针对象, 会发生一系列的莫名其妙的崩溃.
那么现在 盘查出 到底是哪里 发生了 上面这种场景.

低压力不能触发崩溃, 需要高压力才能触发.

整个项目中, 充斥着大量的, 诸如这类的代码,将近有70多个文件中
很明显, 直接改每个源文件,是最拙劣的方法!

1
2
3
4
5
6
7
8
9
1类:
if(rte_mempool_get(obj_mempool, (void**)&obj_new) < 0)
{
...
}

2类: if的语句没有花括号
if(enqueue_msg(obj_new) < 0)
rte_mempool_put(obj_mempool, (void *)obj_new);

提示:
如果我在 rte_mempool_put/rte_mempool_put 函数里面修改, 就得不到 调用者的文件名/行号信息

实现 仿函数

1
2
3
/usr/local/include/dpdk/rte_mempool.h
#define rte_mempool_get_debug(a,b) ({int rc=rte_mempool_get(a,b);printf("get obj %p %s:%u\n",*b,__FILE__,__LINE__);rc;})
#define rte_mempool_put_debug(a,b) ({rte_mempool_put(a,b);printf("put obj %p %s:%u\n",b,__FILE__,__LINE__);})

原始代码:获取

1
2
3
4
5
6
{
if (rte_mempool_get(tbl_log_mempool, (void **)&obj_new) < 0)
{
return PKT_OK;
}
}

原始代码:释放(注意if语句没有花括号)

1
2
3
4
5
{
if (write_tbl_log(obj_new) != 1)
rte_mempool_put(obj_mempool, (void *)obj_new);
return;
}

一顿操作:

1
2
sed -i 's@\brte_mempool_get\b@rte_mempool_get_debug@g' `grep -inrwl rte_mempool_get`
sed -i 's@\brte_mempool_put\b@rte_mempool_put_debug@g' `grep -inrwl rte_mempool_put`

恢复代码

1
2
sed -i 's@\brte_mempool_get_debug\b@rte_mempool_get@g' `grep -inrwl rte_mempool_get_debug`
sed -i 's@\brte_mempool_put_debug\b@rte_mempool_put@g' `grep -inrwl rte_mempool_put_debug`

新的代码:获取

1
2
3
4
5
6
{
if (({int rc=rte_mempool_get(tbl_log_mempool,(void **)&obj_new);printf("get obj %p %s:%u\n",*(void **)&obj_new,"xxx.c",283);rc;}) < 0)
{
return PKT_OK;
}
}

新的代码:释放(注意if语句没有花括号)

1
2
3
4
5
{
if (write_tbl_log(obj_new) != 1)
({rte_mempool_put(obj_mempool,(void *)obj_new); printf("put obj %p %s:%u\n", (void *)obj_new, "xxx.c", 433);});
return;
}

参考: list.h

1
#define container_of(ptr, type, member) ({const typeof(((type*)0)->member)*__mptr=(ptr);(type*)((char*)__mptr-offsetof(type,member));})