首先在 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 在初始化过程中产生的大量日志了。