[TOC]

debug_locks

  • 各种锁的常用调试工具的通用位置: 自旋锁、rwlocks、mutex 和 rwsems。

debug_locks 的介绍

debug_locks 是 Linux 内核中用于调试各种锁(如自旋锁、自读写锁、互斥锁和读写信号量)的工具。它提供了一种机制,用于检测锁的使用错误,并在发现问题时记录或报告相关信息。

特点

  • 全局控制:通过全局标志 debug_locks,可以一次性启用或禁用所有锁的调试功能。
  • 静默模式:支持通过 debug_locks_silent 实现“静默失败”,即在检测到锁错误时不打印任何信息到控制台。
  • 原子操作:使用 xchg 等原子操作确保在多线程环境中安全地切换调试状态。
  • 减少噪声:在检测到第一个锁错误后,可以关闭后续的调试输出,避免日志被大量错误信息淹没。

使用场景

  1. 锁错误检测

    • 在开发和调试阶段,用于检测锁的使用错误,例如死锁、锁未正确释放等问题。
    • 适用于调试自旋锁(spinlock)、读写锁(rwlock)、互斥锁(mutex)和读写信号量(rwsem)等。
  2. 内核模块开发

    • 在开发内核模块时,debug_locks 可以帮助开发者快速发现锁的使用问题,避免潜在的竞争条件或死锁。
  3. 内核稳定性测试

    • 在运行内核测试套件时,debug_locks 可以捕获锁相关的错误,确保内核的稳定性和可靠性。
  4. 生产环境问题排查

    • 在生产环境中,可以通过静默模式(debug_locks_silent)记录锁错误,而不会干扰系统的正常运行。

工作原理

1. 全局标志控制

  • debug_locks
    • 全局变量,初始值为 1,表示锁调试功能启用。
    • 当检测到锁错误时,可以通过 debug_locks_off() 将其设置为 0,关闭后续的调试功能。
  • debug_locks_silent
    • 全局变量,表示是否启用静默模式。如果启用,锁错误不会打印到控制台。

2. 锁调试关闭函数

1
2
3
4
static __always_inline int __debug_locks_off(void)
{
return xchg(&debug_locks, 0);
}
  • 使用 xchg 原子操作将 debug_locks 设置为 0,确保在多线程环境中安全地关闭调试功能。
  • 返回原来的值,用于判断调试功能是否已经关闭。

3. 锁错误检测

1
2
3
4
5
6
7
8
9
10
11
12
13
#define DEBUG_LOCKS_WARN_ON(c)						\
({ \
int __ret = 0; \
\
if (!oops_in_progress && unlikely(c)) { \
instrumentation_begin(); \
if (debug_locks_off() && !debug_locks_silent) \
WARN(1, "DEBUG_LOCKS_WARN_ON(%s)", #c); \
instrumentation_end(); \
__ret = 1; \
} \
__ret; \
})
  • 条件检测
    • 如果全局变量 oops_in_progress0(表示当前没有内核错误正在处理),并且条件 c 为真,则触发锁错误检测。
  • 关闭调试功能
    • 调用 debug_locks_off() 关闭调试功能,避免后续错误产生大量日志。
  • 打印警告
    • 如果未启用静默模式(debug_locks_silent0),通过 WARN 宏打印锁错误信息。
  • 插桩支持
    • 使用 instrumentation_begin()instrumentation_end() 包裹调试代码,支持性能分析和跟踪。

4. 锁调试关闭的通用函数

1
2
3
4
5
6
7
8
9
10
int debug_locks_off(void)
{
if (debug_locks && __debug_locks_off()) {
if (!debug_locks_silent) {
console_verbose();
return 1;
}
}
return 0;
}
  • 检查 debug_locks 是否启用,如果启用则关闭调试功能。
  • 如果未启用静默模式,调用 console_verbose() 打印详细信息。

总结

  • debug_locks 是 Linux 内核中用于调试锁使用问题的重要工具,适用于开发、测试和生产环境。
  • 全局控制静默模式 提供了灵活的调试选项,既可以捕获锁错误,又能避免过多的日志输出。
  • 原子操作插桩支持 确保了调试功能的安全性和性能。
  • 通过 DEBUG_LOCKS_WARN_ON 等宏,可以快速检测锁的使用错误,并在必要时关闭调试功能,避免日志噪声。

lib/debug_locks.c

  • xchg: xchg 的主要功能是以原子方式交换两个变量的值。原子操作意味着该操作在执行过程中不会被中断,因此在多线程环境中是线程安全的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static __always_inline int __debug_locks_off(void)
{
return xchg(&debug_locks, 0);
}
/*
* 我们希望通过一个全局标志一次性打开/关闭所有锁调试工具。
* 原因在于,一旦发现并报告了一个bug,就可能会有一连串的后续bug,这只会把日志弄得一团糟。所以我们报了第一个,之后就闭嘴。
*/
int debug_locks __read_mostly = 1;
EXPORT_SYMBOL_GPL(debug_locks);

/*
* lock -testsuite使用<debug_locks_silent>来得到一个“静默失败”:当检测到锁定错误时,没有任何东西打印到控制台。
*/
int debug_locks_silent __read_mostly;
EXPORT_SYMBOL_GPL(debug_locks_silent);

/*
* Generic 'turn off all lock debugging' function:
*/
int debug_locks_off(void)
{
if (debug_locks && __debug_locks_off()) {
if (!debug_locks_silent) {
console_verbose();
return 1;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(debug_locks_off);

include/linux/debug_locks.h

DEBUG_LOCKS_WARN_ON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define DEBUG_LOCKS_WARN_ON(c)						\
({ \
int __ret = 0; \
\
if (!oops_in_progress //全局变量.用于指示当前是否正在处理内核 "oops"(内核错误)。
&& unlikely(c)) { \
instrumentation_begin(); \ //插桩开始
if (debug_locks_off() && !debug_locks_silent) \
WARN(1, "DEBUG_LOCKS_WARN_ON(%s)", #c); \
instrumentation_end(); \ //插桩结束
__ret = 1; \
} \
__ret; \
})