vpp中dpdk日志的配置和查看

        首先在 vpp 配置文件(/etc/vpp/startup.conf) 的 dpdk section 添加 log-level debug 配置项,其中 debug 是日志级别。在 vpp 的 dpdk_config 函数中将 log-level 解析到了整型变量 log_level 中:

else if (unformat (input, "log-level %U", unformat_dpdk_log_level, &x))
        log_level = x;

        然后,通过 dpdk 提供的 rte_log_set_global_level (log_level) 函数,将 log_level 设置到了全局结构体变量 rte_logs 中:

/* Set global log level */
void
rte_log_set_global_level(uint32_t level)
{ 
        rte_logs.level = (uint32_t)level;
}   

        接着,dpdk 在运行过程中产生的日志通过 pipe 方式发送 vpp:

  int log_fds[2] = { 0 };
  if (pipe (log_fds) == 0)
    {
      FILE *f = fdopen (log_fds[1], "a");
      if (f && rte_openlog_stream (f) == 0)
        {
          clib_file_t t = { 0 };
          t.read_function = dpdk_log_read_ready;
          t.file_descriptor = log_fds[0];
          t.description = format (0, "DPDK logging pipe");
          clib_file_add (&file_main, &t);
        }
    }

        然后,vpp 会定期去轮询 dpdk 的日志信息,用户在 vpp 命令行里面可以通过 show log 来查看 dpdk 的日志信息。

        上述方式查看 dpdk 日志的前提是 vpp 启动成功,如果 vpp 在启动过程中因 dpdk 报错而启动失败,那么就无法看到 dpdk 的日志信息。

        解决方法:
        使用 gdb 对 vpp 进行调试,将断点打在 src/plugins/dpdk/device/init.c:1351 行附近,然后当 vpp 执行完第 1354 行的代码:FILE *f = fdopen(log_fds[1], "a"); 后,在 gdb 中通过 "set var f = 0",将 f 强制赋值为空指针,以便跳过对 rte_openlog_stream (f) 的调用,这样 dpdk 在初始化过程中产生的日志就不会写入 pipe 中,而是写入 stdout 和 syslog 中,或是 stderr 中。

        经过上述修改后,依然发现 rte_eal_init() 执行过程中的某些日志没有被打印,这是因为在 dpdk 初始化时 EAL 模块默认的日志级别是:INFO,所以 DEBUG 级别的日志没有被输出,如下代码所示:

/* Logging should be first initializer (before drivers and bus) */
RTE_INIT_PRIO(rte_log_init, LOG);
static void
rte_log_init(void)
{
        uint32_t i;

        rte_log_set_global_level(RTE_LOG_DEBUG);

        rte_logs.dynamic_types = calloc(RTE_LOGTYPE_FIRST_EXT_ID,
                sizeof(struct rte_log_dynamic_type));
        if (rte_logs.dynamic_types == NULL)
                return;

        /* register legacy log types */
        for (i = 0; i < RTE_DIM(logtype_strings); i++)
                __rte_log_register(logtype_strings[i].logtype,
                                logtype_strings[i].log_id);

        rte_logs.dynamic_types_len = RTE_LOGTYPE_FIRST_EXT_ID;
}

        上述代码通过 __rte_log_register() 注册了 EAL 模块的日志级别:

static int
__rte_log_register(const char *name, int id)
{
        char *dup_name = strdup(name);

        if (dup_name == NULL)
                return -ENOMEM;

        rte_logs.dynamic_types[id].name = dup_name;
        rte_logs.dynamic_types[id].loglevel = RTE_LOG_INFO;

        return id;
}

        可以看到默认的日志级别是: RTE_LOG_INFO。

        这个有一个快捷的修改方法,思路和上面的一样,当 gdb 命中上述断点后,手动修改 EAL 模块的日志级别,步骤如下:
        1)修改前确认:

(gdb) p rte_logs.dynamic_types[0]
$4 = {name = 0xaaaab9d3d0 "lib.eal", loglevel = 7}

        从上面的输出可以看到,修改前 EAL 模块的日志信息已经注册成功,该模块对应的下标为 0,loglevel = 7,为 INFO 级别。
        2)进行修改:

(gdb) set rte_logs.dynamic_types[0].loglevel=8
(gdb) p rte_logs.dynamic_types[0]
$5 = {name = 0xaaaab9d3d0 "lib.eal", loglevel = 8}

        确认已经修改成功。依次类推,还可以手动修改其他模块的日志级别。
        最后,在 gdb 中让程序 continue,接下来就能看到 dpdk 在初始化过程中产生的大量日志了。

上一篇:Python基础--函数的定义和调用


下一篇:2021.12.1 Definitions