smb调试方法

点击这里查看配套的教学视频

点击跳转到smb课程所有目录

1 打印

1.1 内核态server打印

smb server打印函数是ksmbd_debug(),相关代码如下:

ksmbd_debug
  // 拼接成 KSMBD_DEBUG_ALL 等宏定义
  if (ksmbd_debug_types & KSMBD_DEBUG_##type)

// 使用宏拼接
CLASS_ATTR_RW(debug)
  struct class_attribute class_attr_debug = __ATTR_RW(debug)
    __ATTR(debug, 0644, debug_show, debug_store)
      .attr = {.name = __stringify(debug),
        __stringify_1(x)
          #debug ==> /sys/class/ksmbd-control/debug文件
      .show   = debug_show,
      .store  = debug_store,

// 还是宏拼接
ATTRIBUTE_GROUPS(ksmbd_control_class)
  ksmbd_control_class_group
  .attrs = ksmbd_control_class_attrs
  __ATTRIBUTE_GROUPS(ksmbd_control_class)
    ksmbd_control_class_groups[] // 引用这个变量的是ksmbd_control_class
    &ksmbd_control_class_group,

static struct class ksmbd_control_class = {
        .name           = "ksmbd-control", ==> /sys/class/ksmbd-control/目录
        .class_groups   = ksmbd_control_class_groups,
};

通过读写/sys/class/ksmbd-control/debug文件控制,但我们一般不直接操作这个文件,而是用以下命令控制打印的开关:

ksmbd.control --help # 查看帮助
# COMPONENT的值有: `all', `smb', `auth', `vfs', `oplock', `ipc', `conn', or `rdma'
ksmbd.control --debug=vfs
ksmbd.control --debug= # 不加COMPONENT可以查看当前的状态

在熟悉代码阶段,可以默认把日志开关全部打开:

--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -22,7 +22,7 @@
 #include "crypto_ctx.h"
 #include "auth.h"
 
-int ksmbd_debug_types;
+int ksmbd_debug_types = KSMBD_DEBUG_ALL;
 
 struct ksmbd_server_config server_conf;

1.2 用户态server打印

用户态server仓库

修改配置文件/etc/samba/smb.conf:

[global]
log level = 4
# 日志文件路径
log file = /usr/local/samba/var/log.%m

常用的几个log level有以下几个:

#define DEBUG_ERR     DBGLVL_ERR     // 0      /* error conditions */
#define DEBUG_WARNING DBGLVL_WARNING // 1      /* warning conditions */
#define DEBUG_NOTICE  DBGLVL_NOTICE  // 3      /* normal, but significant, condition */
#define DEBUG_INFO    DBGLVL_INFO    // 5      /* informational message */
#define DEBUG_DEBUG   DBGLVL_DEBUG   // 10     /* debug-level message */

当然,代码中还有使用DEBUG(11, ...)DEBUG(15, ...)DEBUG(18, ...)DEBUG(19, ...)等等,最大可用的log level为:

#define MAX_DEBUG_LEVEL 1000

log level的解析在debug_parse_param()函数中。

打印函数堆栈用log_stack_trace(),比如打印smbd_parent_loop()的调用栈的补丁0001-dump-stack-of-smbd_parent_loop.patch

/usr/local/samba/var/log.smbd日志文件中的打印结果如下:

[2025/05/27 22:06:24.110919,  0] ../../lib/util/fault.c:261(log_stack_trace)
  BACKTRACE:
   #0 log_stack_trace + 0x28 [ip=0x7f61f4a4098a] [sp=0x7ffc42cae650]
   #1 smbd_parent_loop + 0x93 [ip=0x409d78] [sp=0x7ffc42caef50]
   #2 main + 0x1a27 [ip=0x40ce2c] [sp=0x7ffc42caef80]
   #3 __libc_start_call_main + 0x78 [ip=0x7f61f480f088] [sp=0x7ffc42caf320]
   #4 __libc_start_main + 0x8b [ip=0x7f61f480f14b] [sp=0x7ffc42caf3c0]
   #5 _start + 0x25 [ip=0x405e95] [sp=0x7ffc42caf420]

另外log_stack_trace()中的backtrace_symbols()没有和free()配套使用,注释中说是free()可能产生问题。

1.3 client打印

smb client打印函数有cifs_dbg()cifs_server_dbg()cifs_tcon_dbg()cifs_info(),要打开配置CONFIG_CIFS_DEBUG才有效,打开CONFIG_CIFS_DEBUG2CONFIG_CIFS_DEBUG_DUMP_KEYS能打印更多信息,以cifs_dbg()为例代码如下:

cifs_dbg
  cifs_dbg_func(once, ...)
    pr_debug_once / pr_err_once
      printk_once(KERN_DEBUG / printk_once(KERN_ERR // 只打印一次
  cifs_dbg_func(ratelimited, ...)
    pr_debug_ratelimited
      __dynamic_pr_debug // 打开配置 CONFIG_DYNAMIC_DEBUG
    pr_err_ratelimited
      printk_ratelimited
        printk

动态打印相关的内容请查看《内核调试方法》

通过以下命令控制开关:

cd /sys/kernel/debug/dynamic_debug/
cat control | less # 查看所有的动态打印
echo 'file fs/smb/client/cifsfs.c +p' > control # 打开文件中所有的动态打印
echo 'module cifs -p' > control # 关闭cifs模块所有动态打印
echo 'func cifs_copy_file_range +p' > control # 打开某个函数的打印
echo -n '*cifs_smb3* -p' > control # 关闭文件路径中包含cifs_smb3的打印
echo -n '+p' > control # 所有打印

1.3.1 mydebug_print()

但在熟悉代码阶段,一个调试打印这么折腾还限制打印次数,对熟悉代码肯定不友好,所以我在熟悉代码阶段用的是mydebug模块, 打上mydebug模块的补丁后, 再打上补丁0001-smb-client-use-mydebug_print.patch

另外可能还会有一些新增的动态打印(使用pr_debug()),可以用以下命令查看并打开动态打印:

cd /sys/kernel/debug/dynamic_debug/
echo 'module cifs +p' > control # 打开cifs模块所有动态打印
cat control | grep cifs

2 tracepoint

除了日志,还可以打开tracepoint。tracepoint的使用请查看《内核调试方法》

cd /sys/kernel/debug/tracing/
echo nop > current_tracer
echo 1 > tracing_on
cat available_events  | grep cifs
cat available_events  | grep smb
ls events/*cifs* -d
ls events/*smb* -d # 没有
echo cifs:nfsd_cb_recall_done > set_event # 打开某个tracepoint
echo cifs:* > set_event # 打开所有的cifs跟踪点
# echo 1 > events/cifs/smb3_close_enter/enable # 打开某个tracepoint
# echo 1 > events/cifs/enable # 打开所有的cifs跟踪点
echo 0 > trace # 清除trace信息
cat trace_pipe

注意目前(2025.05.21)smb server的代码还没有使用任何的tracepoint,但以后可能会用,你可以使用命令grep -r trace_ fs/smb/server/在内核仓库下确认。

3 tcpdump抓包

请查看《nfs调试方法》