Introduction
Debugging is one of the most critical skills in Embedded Linux development.
Before reaching for advanced tools like ftrace or kgdb, most developers start with something simple:
๐ Printing logs from the kernel
If youโve ever used printk, you already know how powerful it can be.
However, modern Linux kernel development has evolved. Today, using printk blindly is not considered best practice.
In this blog, you will learn:
- How
printkworks and its log levels - Why
pr_*anddev_*macros are preferred - The role of
pr_debugin production kernels - How dynamic debugging helps control logs at runtime
Letโs start from the basics.
๐จ๏ธ printk โ The Foundation of Kernel Debugging
The printk function is the kernel equivalent of printf.
printk("Hello from kernel\n");
But unlike printf, printk supports log levels, which define the importance of messages.
๐ข printk Log Levels
Each log message is tagged with a severity level:
| Level | Macro | Description |
|---|---|---|
| 0 | KERN_EMERG | System unusable |
| 1 | KERN_ALERT | Immediate action required |
| 2 | KERN_CRIT | Critical conditions |
| 3 | KERN_ERR | Error conditions |
| 4 | KERN_WARNING | Warning messages |
| 5 | KERN_NOTICE | Normal but important |
| 6 | KERN_INFO | Informational |
| 7 | KERN_DEBUG | Debug-level messages |
Example:
printk(KERN_INFO "Driver initialized\n");
printk(KERN_ERR "Failed to allocate memory\n");
โ ๏ธ Limitations of printk
While printk is useful, it has some drawbacks:
- No structured formatting
- No device context
- Hard to maintain in large drivers
- Verbose and inconsistent usage
Because of this, modern kernel code prefers higher-level macros.
pr_* Macros โ Cleaner and Structured Logging
The pr_* macros are built on top of printk and simplify logging.
Common pr_* macros:
pr_info("Driver loaded\n");
pr_err("Initialization failed\n");
pr_warn("Voltage unstable\n");
pr_debug("Debug message\n");
Advantages of pr_* over printk
โ No need to specify log levels manually
โ Cleaner and more readable code
โ Consistent logging format
โ Easier maintenance
Important: pr_debug in Production
pr_debug() is special.
๐ It is compiled out in production kernels unless debugging is enabled.
This means:
- No runtime overhead
- No unnecessary logs in release builds
This is extremely useful when writing debug-heavy code during development.
dev_* Macros โ Device-Aware Debugging
When writing drivers, you often need context about the device.
Thatโs where dev_* macros come in.
Example:
dev_info(dev, "Device initialized\n");
dev_err(dev, "Probe failed\n");
dev_dbg(dev, "Register value: %d\n", val);
Advantages of dev_* Macros
โ Automatically include device information
โ Helps identify which device generated the log
โ Essential for multi-device systems
โ Better debugging for platform and bus drivers
Dynamic Debugging in Linux Kernel
One major challenge in debugging is controlling logs without recompiling the kernel.
This is where Dynamic Debugging comes into play.
What is Dynamic Debugging?
Dynamic Debug allows you to:
๐ Enable or disable debug logs at runtime
๐ Control pr_debug and dev_dbg messages
๐ Avoid recompiling the kernel
How It Works
Linux provides a control interface:
/sys/kernel/debug/dynamic_debug/control
You can enable logs like this:
echo 'file driver.c +p' > /sys/kernel/debug/dynamic_debug/control
Or disable:
echo 'file driver.c -p' > /sys/kernel/debug/dynamic_debug/control
Benefits of Dynamic Debug
โ Fine-grained control over logs
โ No kernel rebuild required
โ Debug only what you need
โ Reduce noise in logs
Best Practices for Kernel Debugging
- Use
pr_*macros instead of rawprintk - Use
dev_*macros in driver code - Avoid excessive logging in production
- Use
pr_debugfor development-only logs - Leverage dynamic debugging for runtime control
Summary
Kernel debugging using printing has evolved:
| Method | Use Case |
|---|---|
| printk | Basic debugging |
| pr_* | General logging |
| dev_* | Device-specific logging |
| pr_debug | Development-only debug |
| Dynamic Debug | Runtime control |
Understanding these tools helps you write cleaner, maintainable, and production-ready kernel code.
Go Beyond Debugging โ Master Embedded Linux
Debugging is powerful โ
but it becomes truly effective only when you understand the system deeply.
If you want to build that strong foundation across Embedded Linux, Kernel, and Drivers, check out:
๐ https://embitude.in/embedded-linux-bundle/
This project-driven bundle helps you:
โ Understand system-level concepts
โ Work on real-world problems
โ Build confidence in debugging and development