一直以来,Linus Torvalds对内核调试器都秉持着抵触态度,并且摆出了我是bastard我怕谁的姿态。他保持了一贯风格,言辞尖锐却直指本质。相信这是经验之谈。在调试内核时,最关键的问题是如何获取出错相关的信息,准确定位出错位置。获取信息有很多方法,其中内核调试器只能提供有限的帮助,而分析日志则是最基本也是最主要的方法。为内核层软件提供一种方便的日志工具,将大大简化其调试工作。在Linux社区中,对升级日志打印系统的讨论正进行得如火如荼。而Lustre文件系统则早已拥有了一个强大、高效的内核层跟踪调试系统。本章将分析这个系统。
Lustre的跟踪调试系统可以分成两个部分,一个是跟踪系统,负责内核日志的存储和管理,另一个是调试系统,它向上提供跟踪系统调用接口,并负责跟踪系统状态的管理。
1.1 日志的获得
Lustre的日志首先被缓存在内核一块有限的空间中。为了获得这些日志,需要使用Lustre给出的特定工具,即lctl debug_kernel。
除此之外,Lustre还将一部分重要信息输出到系统打印中,因此可以通过dmesg命令或/var/log/messages查看。然而,这些信息是经过删减的。与通过lctldebug_kernel命令获得的输出相比,不仅打印频率受到了限制,而且每条打印给出的信息项也经过了删节。
1.2 打印输出的释义
典型地,通过lctl debug_kernel命令获得的打印输出如下所示:
00000080:00000001:0.0:1339902577.938752:0:28824:0:(file.c:507:ll_file_open())Process entered
00000080:00000001:0.0:1339902577.938755:0:28824:0:(file.c:533:ll_file_open())Process leaving (rc=0 : 0 : 0)
其格式为:
子系统号:掩码号:CPU号.类型:秒.微秒:栈地址:PID:扩展PID:(文件名:行数:函数名()) 输出信息
- 子系统号。子系统号标识了日志输出时所处的功能模块,例如S_MDS对应元数据服务器,S_MDC对应元数据客户端。
- 掩码号。Lustre的日志区分了多种级别和用途。例如D_TRACE代表上面所述的函数跟踪信息,D_WARNING代表警告信息,而D_ERROR则代表出错信息。可以根据需要,通过sysctl接口更改日志系统的掩码号,从而使能或无效某些日志输出。例如,要打开D_TRACE类型的日志输出,则只需运行:echo “trace” > /proc/sys/lnet/debug
- CPU号。代表了当前进程运行所在的CPU ID。为了减少冲突,提高性能,Lustre在为每个CPU单独维护一套跟踪信息。
- 类型。针对Linux环境,Lustre区分了三种类型,分别对应处在硬件中断状态中、处在软中断状态中和正常状态。这三种类型也分别对应不同的跟踪信息。这些信息存储在struct cfs_trace_cpu_data结构体中。每个CPU和每种日志类型都对应着不同的structcfs_trace_cpu_data结构体实例,因此实例的数目为3*NR_CPUS,其中NR_CPUS是内核配置时就已经确定的CPU数目。
- 秒和微秒。这是日志在打印到内核空间时,通过do_gettimeofday()函数获得的时间。
- 栈地址。GCC给出了内嵌函数,可以用以获得函数所处的栈地址。不过这些内嵌函数是平台相关的,例如IA64平台是__builtin_dwarf_cfa()函数,