Embitude Infotech1

๐Ÿง Kernel Debugging in Linux Using printk, pr_* and dev_* Macros

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 printk works and its log levels
  • Why pr_* and dev_* macros are preferred
  • The role of pr_debug in 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:

LevelMacroDescription
0KERN_EMERGSystem unusable
1KERN_ALERTImmediate action required
2KERN_CRITCritical conditions
3KERN_ERRError conditions
4KERN_WARNINGWarning messages
5KERN_NOTICENormal but important
6KERN_INFOInformational
7KERN_DEBUGDebug-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 raw printk
  • Use dev_* macros in driver code
  • Avoid excessive logging in production
  • Use pr_debug for development-only logs
  • Leverage dynamic debugging for runtime control

Summary

Kernel debugging using printing has evolved:

MethodUse Case
printkBasic debugging
pr_*General logging
dev_*Device-specific logging
pr_debugDevelopment-only debug
Dynamic DebugRuntime 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top