调试 dpdk 应用程序的coredump整理

目录

稳定复现的 coredump

打开 dpdk 相关的 debug开关,重新编译

mbuf debug

malloc debug 

mempool debug

给 dpdk 源码加检查或日志,重新编译

无法稳定复现的 coredump

打开大页内存的 coredump

案例

字节序导致取地址不对,从而踩内存

多线程竞争资源导致


稳定复现的 coredump

打开 dpdk 相关的 debug开关,重新编译

# grep -i "DEBUG" common_base  | grep -Ei "malloc|mbuf|mempool"
CONFIG_RTE_MALLOC_DEBUG=n
CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL_DEBUG=n
CONFIG_RTE_LIBRTE_MBUF_DEBUG=n

mbuf debug

malloc debug 

mempool debug

当开启RTE_LIBRTE_MEMPOOL_DEBUG时,申请mempool中的一个元素时,比如mbuf, 在mbuf的申请和释放出都会调用rte_mempool_check_cookies这个函数,对mbuf的cookie值进行校验。

其校验原理可以理解为:

1)在申请mbuf时,校验是否为cookie2,是,则对其打上cookie1的标记,不是,则panic;

2)对释放mbuf是对其校验是否为cookie1,是,则打上cookie2,不是则 panic;

简单的理解即mbuf的申请和释放处于两种状态,当任何一种动作(申请或释放)做了两次,都会产生panic;

这个开关的存在,正好可以解决我们这个多线程重复free mbuf的问题。

mempool中存储的每一个对象的结构分3部分,首部,数据和尾部。每一部分都填充以做到字节对齐,在调试模式首部和尾部还可以加上cookie。

#define RTE_MEMPOOL_HEADER_COOKIE2  0xf2eef2eedadd2e55ULL /**< Header cookie. */

static void
mempool_add_elem(struct rte_mempool *mp, void *obj, phys_addr_t physaddr)
{
    struct rte_mempool_objhdr *hdr;
    struct rte_mempool_objtlr *tlr __rte_unused;

    /* set mempool ptr in header */
    hdr = RTE_PTR_SUB(obj, sizeof(*hdr));
    hdr->mp = mp;
    hdr->physaddr = physaddr;
    STAILQ_INSERT_TAIL(&mp->elt_list, hdr, next);
    mp->populated_size++;

#ifdef RTE_LIBRTE_MEMPOOL_DEBUG
    hdr->cookie = RTE_MEMPOOL_HEADER_COOKIE2;
    tlr = __mempool_get_trailer(obj);
    tlr->cookie = RTE_MEMPOOL_TRAILER_COOKIE;
#endif

    /* enqueue in ring */
    rte_mempool_ops_enqueue_bulk(mp, &obj, 1);
}

给 dpdk 源码加检查或日志,重新编译

比如 对于 dpdk 从 mempool 中申请 的  mbuf 的相关的 core;

(1)有可能是一个 mbuf 被多个线程给释放了?

针对这种情况,可以自己封装 dpdk 的 struct rte_mbuf,添加一个 core_id,core_id 是申请 mbuf 时的线程的 core_id;

释放的时候,比较下释放的 core_Id 和 申请的 core_id 是否是同一个;

(2)一个mbuf 被一个线程的多处给释放了?

可以自己封装  dpdk 的 struct rte_mbuf 结构;仿造上面的例子,申请的时候记录状态1;

释放的时候,检查处于状态1,则设置状态2,记录释放的函数(通过函数宏的方式进行释放,可以记录上层函数),进行释放;

如果存在再次释放,则已经处于状态2,检查失败;

无法稳定复现的 coredump

打开大页内存的 coredump

设置 /proc/PID/coredump_filter 的参数;

程序崩溃 或 使用gcore工具 生成进程的core文件时,可以通过设定内核转储掩码来筛选需要dump的部分内存。

可以设置为 0xff 来打印大页内存相关

echo 0xff > coredump_filter 
不要通过vim更改,也不要写为 ​echo 000000ff > coredump_filter; 否则会失败;

#man core

 

/proc/<pid>/coredump_filter中的值为16进制,默认值是0x33,二进制为 0011 0011;
也即发生coredump时会将所有anonymous内存、ELF头页面、hugetlb private memory内容保存。

案例

字节序导致取地址不对,从而踩内存

多线程竞争资源导致

kni的共享临界区的未加锁问题,导致多线程抢占kni发包接口,从而出现两次free的情况。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用C++编程语言编写DPDK应用程序与使用C语言类似,只是在代码的组织和语法上有一些差异。下面是一个简单的示例,展示了如何使用C++编写一个DPDK应用程序来接收和处理数据包: ```cpp #include <iostream> #include <rte_eal.h> #include <rte_ethdev.h> #include <rte_mbuf.h> #define RX_RING_SIZE 128 #define NUM_MBUFS 8191 #define MBUF_CACHE_SIZE 250 #define BURST_SIZE 32 int main(int argc, char *argv[]) { int ret; // 初始化DPDK环境 ret = rte_eal_init(argc, argv); if (ret < 0) { rte_exit(EXIT_FAILURE, "Failed to initialize DPDK environment\n"); } // 获取可用的网卡数量 int num_ports = rte_eth_dev_count_avail(); if (num_ports == 0) { rte_exit(EXIT_FAILURE, "No available Ethernet ports\n"); } // 配置第一个网卡 ret = rte_eth_dev_configure(0, 1, 1, NULL); if (ret < 0) { rte_exit(EXIT_FAILURE, "Failed to configure Ethernet port\n"); } // 分配和初始化内存池 struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); if (mbuf_pool == NULL) { rte_exit(EXIT_FAILURE, "Failed to create memory pool\n"); } // 启动第一个网卡 ret = rte_eth_dev_start(0); if (ret < 0) { rte_exit(EXIT_FAILURE, "Failed to start Ethernet port\n"); } // 设置接收队列 ret = rte_eth_rx_queue_setup(0, 0, RX_RING_SIZE, rte_eth_dev_socket_id(0), NULL, mbuf_pool); if (ret < 0) { rte_exit(EXIT_FAILURE, "Failed to setup RX queue\n"); } // 接收和处理数据包 while (true) { struct rte_mbuf *bufs[BURST_SIZE]; const uint16_t nb_rx = rte_eth_rx_burst(0, 0, bufs, BURST_SIZE); for (uint16_t i = 0; i < nb_rx; ++i) { // 处理接收到的数据包 // ... // 释放缓冲区 rte_pktmbuf_free(bufs[i]); } } return 0; } ``` 上述示例中,我们使用了C++的标准库头文件,并使用了DPDK提供的C API函数和数据结构。在编写DPDK应用程序时,需要包含适当的DPDK头文件,并按照DPDK的编程模型进行操作。此外,还需要注意资源的分配和释放,如内存池和缓冲区的创建和释放。 需要注意的是,上述示例仅用于演示目的,实际的DPDK应用程序可能需要更复杂的逻辑和功能,如数据包解析、协议处理、流量控制等。开发人员可以根据自己的需求在此基础上进行扩展和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值