요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
재사용 가능한 custom tracepoint
driver_development_debugging_guide.rst:101-118빈도가 높거나 state correlation이 필요한 path는 TRACE_EVENT 기반 tracepoint로 field를 구조화한다. Tracepoint는 enable되지 않았을 때 overhead를 최소화하고 perf, trace-cmd와 BPF consumer가 같은 event를 사용할 수 있다.
DebugFS state 노출
driver_development_debugging_guide.rst:119-161Debugfs는 driver 내부 state와 실험 control을 개발자에게 노출할 수 있지만 stable userspace ABI가 아니다. Production application dependency를 만들지 않고 lifetime과 removal을 device unbind 경로에 맞춘다. Sensitive register와 kernel address를 무분별하게 노출하지 않는다.
KASAN, UBSAN과 lockdep
driver_development_debugging_guide.rst:162-206| 도구 | 찾는 문제 |
|---|---|
| KASAN | Out-of-bounds, use-after-free와 잘못된 memory access |
| UBSAN | C undefined behavior, overflow와 잘못된 shift·type operation |
| lockdep | Lock ordering cycle, recursive acquire와 잘못된 context 사용 |
| PSI | CPU, memory와 I/O pressure 때문에 task가 실제로 stall된 시간 |
Sanitizer와 lockdep는 report 첫 fault에서 stack과 allocation·lock history를 읽고 후속 cascade message보다 최초 원인을 우선한다. Instrumentation이 memory와 timing을 바꾸므로 reproduction 차이도 기록한다.
Device firmware coredump
driver_development_debugging_guide.rst:207-236Device가 firmware crash state나 on-device memory를 제공할 수 있으면 devcoredump interface로 userspace가 binary dump를 회수하게 한다. Buffer ownership은 devcoredump API로 넘긴 뒤 driver가 다시 free하지 않으며 timeout·read 완료 때 release callback이 처리한다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
========================================
Debugging advice for driver development
========================================
This document serves as a general starting point and lookup for debugging
device drivers.
While this guide focuses on debugging that requires re-compiling the
module/kernel, the :doc:`userspace debugging guide
</process/debugging/userspace_debugging_guide>` will guide
you through tools like dynamic debug, ftrace and other tools useful for
debugging issues and behavior.
For general debugging advice, see the :doc:`general advice document
</process/debugging/index>`.
.. contents::
:depth: 3
The following sections show you the available tools.
printk() & friends
------------------
These are derivatives of printf() with varying destinations and support for
being dynamically turned on or off, or lack thereof.
Simple printk()
~~~~~~~~~~~~~~~
The classic, can be used to great effect for quick and dirty development
of new modules or to extract arbitrary necessary data for troubleshooting.
Prerequisite: ``CONFIG_PRINTK`` (usually enabled by default)
**Pros**:
- No need to learn anything, simple to use
- Easy to modify exactly to your needs (formatting of the data (See:
:doc:`/core-api/printk-formats`), visibility in the log)
- Can cause delays in the execution of the code (beneficial to confirm whether
timing is a factor)
**Cons**:
- Requires rebuilding the kernel/module
- Can cause delays in the execution of the code (which can cause issues to be
not reproducible)
For the full documentation see :doc:`/core-api/printk-basics`
Trace_printk
~~~~~~~~~~~~
Prerequisite: ``CONFIG_DYNAMIC_FTRACE`` & ``#include <linux/ftrace.h>``
It is a tiny bit less comfortable to use than printk(), because you will have
to read the messages from the trace file (See: :ref:`read_ftrace_log`
instead of from the kernel log, but very useful when printk() adds unwanted
delays into the code execution, causing issues to be flaky or hidden.)
If the processing of this still causes timing issues then you can try
trace_puts().
For the full Documentation see trace_printk()
dev_dbg
~~~~~~~
Print statement, which can be targeted by
:ref:`process/debugging/userspace_debugging_guide:dynamic debug` that contains
additional information about the device used within the context.
**When is it appropriate to leave a debug print in the code?**
Permanent debug statements have to be useful for a developer to troubleshoot
driver misbehavior. Judging that is a bit more of an art than a science, but
some guidelines are in the :ref:`Coding style guidelines
<process/coding-style:13) printing kernel messages>`. In almost all cases the
debug statements shouldn't be upstreamed, as a working driver is supposed to be
silent.
Custom printk
~~~~~~~~~~~~~
Example::
#define core_dbg(fmt, arg...) do { \
if (core_debug) \
printk(KERN_DEBUG pr_fmt("core: " fmt), ## arg); \
} while (0)
**When should you do this?**
It is better to just use a pr_debug(), which can later be turned on/off with
dynamic debug. Additionally, a lot of drivers activate these prints via a
variable like ``core_debug`` set by a module parameter. However, Module
parameters `are not recommended anymore
<https://lore.kernel.org/all/2024032757-surcharge-grime-d3dd@gregkh>`_.
Ftrace
------
Creating a custom Ftrace tracepoint
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A tracepoint adds a hook into your code that will be called and logged when the
tracepoint is enabled. This can be used, for example, to trace hitting a
conditional branch or to dump the internal state at specific points of the code
flow during a debugging session.
Here is a basic description of :ref:`how to implement new tracepoints
<trace/tracepoints:usage>`.
For the full event tracing documentation see :doc:`/trace/events`
For the full Ftrace documentation see :doc:`/trace/ftrace`
DebugFS
-------
Prerequisite: ``CONFIG_DEBUG_FS` & `#include <linux/debugfs.h>``
DebugFS differs from the other approaches of debugging, as it doesn't write
messages to the kernel log nor add traces to the code. Instead it allows the
developer to handle a set of files.
With these files you can either store values of variables or make
register/memory dumps or you can make these files writable and modify
values/settings in the driver.
Possible use-cases among others:
- Store register values
- Keep track of variables
- Store errors
- Store settings
- Toggle a setting like debug on/off
- Error injection
This is especially useful, when the size of a data dump would be hard to digest
as part of the general kernel log (for example when dumping raw bitstream data)
or when you are not interested in all the values all the time, but with the
possibility to inspect them.
The general idea is:
- Create a directory during probe (``struct dentry *parent =
debugfs_create_dir("my_driver", NULL);``)
- Create a file (``debugfs_create_u32("my_value", 444, parent, &my_variable);``)
- In this example the file is found in
``/sys/kernel/debug/my_driver/my_value`` (with read permissions for
user/group/all)
- any read of the file will return the current contents of the variable
``my_variable``
- Clean up the directory when removing the device
(``debugfs_remove(parent);``)
For the full documentation see :doc:`/filesystems/debugfs`.
KASAN, UBSAN, lockdep and other error checkers
----------------------------------------------
KASAN (Kernel Address Sanitizer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prerequisite: ``CONFIG_KASAN``
KASAN is a dynamic memory error detector that helps to find use-after-free and
out-of-bounds bugs. It uses compile-time instrumentation to check every memory
access.
For the full documentation see :doc:`/dev-tools/kasan`.
UBSAN (Undefined Behavior Sanitizer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prerequisite: ``CONFIG_UBSAN``
UBSAN relies on compiler instrumentation and runtime checks to detect undefined
behavior. It is designed to find a variety of issues, including signed integer
overflow, array index out of bounds, and more.
For the full documentation see :doc:`/dev-tools/ubsan`
lockdep (Lock Dependency Validator)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prerequisite: ``CONFIG_DEBUG_LOCKDEP``
lockdep is a runtime lock dependency validator that detects potential deadlocks
and other locking-related issues in the kernel.
It tracks lock acquisitions and releases, building a dependency graph that is
analyzed for potential deadlocks.
lockdep is especially useful for validating the correctness of lock ordering in
the kernel.
PSI (Pressure stall information tracking)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prerequisite: ``CONFIG_PSI``
PSI is a measurement tool to identify excessive overcommits on hardware
resources, that can cause performance disruptions or even OOM kills.
device coredump
---------------
Prerequisite: ``CONFIG_DEV_COREDUMP`` & ``#include <linux/devcoredump.h>``
Provides the infrastructure for a driver to provide arbitrary data to userland.
It is most often used in conjunction with udev or similar userland application
to listen for kernel uevents, which indicate that the dump is ready. Udev has
rules to copy that file somewhere for long-term storage and analysis, as by
default, the data for the dump is automatically cleaned up after a default
5 minutes. That data is analyzed with driver-specific tools or GDB.
A device coredump can be created with a vmalloc area, with read/free
methods, or as a scatter/gather list.
You can find an example implementation at:
`drivers/media/platform/qcom/venus/core.c
<https://elixir.bootlin.com/linux/v6.11.6/source/drivers/media/platform/qcom/venus/core.c#L30>`__,
in the Bluetooth HCI layer, in several wireless drivers, and in several
DRM drivers.
devcoredump interfaces
~~~~~~~~~~~~~~~~~~~~~~
.. kernel-doc:: include/linux/devcoredump.h
.. kernel-doc:: drivers/base/devcoredump.c
**Copyright** ©2024 : Collabora
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Driver debugging 도구의 범위
1-20이 문서는 device driver debugging의 일반 출발점이다. Module 또는 kernel rebuild가 필요한 기법을 중심으로 하고 dynamic debug, ftrace 같은 runtime 도구는 userspace debugging guide를 참고한다. 일반 원칙은 Documentation/process/debugging/index.rst에 있다.
printk()의 장점과 timing 부작용
22-50CONFIG_PRINTK가 필요하며 보통 기본 enable이다. 새 module을 빠르게 개발하거나 troubleshooting에 필요한 임의 data를 뽑을 때 가장 단순한 방법이다.
| 장점 | 단점 |
|---|---|
| 배울 내용이 거의 없고 사용이 단순하다. | Kernel 또는 module을 다시 build해야 한다. |
| Format과 log visibility를 필요에 맞게 쉽게 바꿀 수 있다. | 실행을 지연시켜 timing-dependent bug가 재현되지 않을 수 있다. |
| 의도적으로 실행을 지연시켜 timing이 원인인지 확인할 수 있다. | 관찰 자체가 system 동작을 바꾸는 probe effect가 생긴다. |
Format은 core-api/printk-formats, 전체 사용법은 core-api/printk-basics를 참고한다.
trace_printk()와 trace_puts()
52-65CONFIG_DYNAMIC_FTRACE와 linux/ftrace.h가 필요하다. Kernel log가 아니라 trace file에서 읽어야 해 printk()보다 조금 불편하지만 printk()의 지연이 문제를 숨기거나 불안정하게 만들 때 유용하다. 처리 비용도 timing을 바꾼다면 trace_puts()를 시도한다.
dev_dbg()와 permanent debug print
67-81dev_dbg()는 관련 device 정보를 포함하고 dynamic debug로 call site를 선택할 수 있다. Upstream에 남길 permanent debug statement는 앞으로 driver 오동작을 조사하는 데 실제로 유용해야 한다.
판단은 완전한 규칙보다 경험이 필요하지만 coding style의 kernel message 지침을 따른다. 정상 동작하는 driver는 조용해야 하므로 거의 모든 임시 debug statement는 upstream에 포함하지 않는다.
Custom printk macro보다 pr_debug()
83-99#define core_dbg(fmt, arg...) do { \
if (core_debug) \
printk(KERN_DEBUG pr_fmt("core: " fmt), ## arg); \
} while (0)
이런 custom macro보다 dynamic debug로 runtime on/off할 수 있는 pr_debug()가 낫다. core_debug 같은 module parameter로 print를 제어하는 driver도 많지만 새 module parameter는 더 이상 권장되지 않는다.
Custom ftrace tracepoint
101-117Tracepoint는 enable되었을 때 호출되고 기록되는 hook을 code에 추가한다. Conditional branch 도달이나 debug session 중 특정 code-flow 지점의 internal state를 추적할 수 있다. 구현법은 trace/tracepoints, event tracing은 trace/events, 전체 ftrace는 trace/ftrace 문서를 참고한다.
DebugFS로 값과 dump 노출
119-160CONFIG_DEBUG_FS와 linux/debugfs.h가 필요하다. DebugFS는 kernel log나 trace 대신 file을 통해 driver state를 다룬다. Register와 memory dump, variable, error, setting을 저장하고 writable file로 설정을 바꾸거나 error를 injection할 수 있다.
Raw bitstream처럼 kernel log에 넣기 큰 dump나 항상 보지 않아도 되지만 필요할 때 확인할 값에 특히 유용하다.
struct dentry *parent = debugfs_create_dir("my_driver", NULL);
debugfs_create_u32("my_value", 0444, parent, &my_variable);
/* /sys/kernel/debug/my_driver/my_value */
...
debugfs_remove(parent);
Probe에서 directory와 file을 만들고 file read가 current my_variable 값을 반환하게 한다. Device 제거 때 directory를 정리한다. 전체 API는 filesystems/debugfs 문서를 참고한다.
KASAN, UBSAN, lockdep, PSI
162-205| 도구 | Configuration | 검출 대상 |
|---|---|---|
| KASAN | CONFIG_KASAN | Compile-time instrumentation으로 모든 memory access를 검사해 use-after-free와 out-of-bounds를 찾는다. |
| UBSAN | CONFIG_UBSAN | Compiler instrumentation과 runtime check로 signed overflow, array index out-of-bounds 등 undefined behavior를 찾는다. |
| lockdep | CONFIG_DEBUG_LOCKDEP | Lock acquire/release dependency graph를 만들고 potential deadlock과 lock ordering 문제를 찾는다. |
| PSI | CONFIG_PSI | Hardware resource overcommit으로 생기는 performance stall과 OOM kill 위험을 측정한다. |
Device coredump
207-235CONFIG_DEV_COREDUMP와 linux/devcoredump.h가 필요하다. Driver가 arbitrary data를 userspace에 전달하는 infrastructure다. Udev 같은 application이 dump 준비를 알리는 kernel uevent를 듣고 file을 장기 저장 위치로 복사한다.
기본 설정에서는 dump data가 5분 뒤 자동 삭제되므로 userspace 수집이 필요하다. 저장한 data는 driver 전용 tool이나 GDB로 분석한다. Vmalloc area, read/free method, scatter/gather list 형태로 coredump를 만들 수 있다.
구현 예는 drivers/media/platform/qcom/venus/core.c, Bluetooth HCI layer, 여러 wireless와 DRM driver에 있다. Interface kernel-doc은 include/linux/devcoredump.h와 drivers/base/devcoredump.c에서 가져온다. 원문 copyright는 ©2024 Collabora다.
printk, dev_dbg와 임시 log
driver_development_debugging_guide.rst:4-100가장 빠른 확인은 pr_* 또는 device context가 붙는 dev_* message다. 정상 path의 반복 message는 timing과 log volume을 바꾸므로 level과 rate limit을 선택한다. dev_dbg는 dynamic debug와 결합해 build를 바꾸지 않고 file·function별로 enable할 수 있다.
trace_printk는 ftrace ring buffer에 기록해 printk console보다 교란이 작지만 production code에 남길 interface가 아니다. Custom debug macro를 만들기 전에 dev_dbg, pr_debug와 dynamic_debug가 요구를 충족하는지 확인한다.