What is Linux Kernel Trace Events Guide-Learn Embedded Linux for Free

Free Linux Kernel Development Course

Linux Kernel Trace Events Guide

Understanding TRACE_EVENT, enabling kernel trace points, and filtering them by parameter in this free linux kernel development course

The function and function_graph tracers we covered previously tell you when a function ran. They cannot tell you what data it was working on. That is the job of trace events — static, parameterized trace points defined with the TRACE_EVENT macro and shared by Ftrace, perf, and LTTng. This lecture in our free linux kernel development course shows how to enable them, read their parameters, and filter them by value.

This is core material for anyone following our free linux device drivers course or free embedded systems course track, since trace events are how real driver and memory-subsystem debugging is done in production kernels.

Key Terms Covered
TRACE_EVENT available_events set_event event filter trigger

What You Will Learn

By the end of this lecture you will be able to:

  • Explain what a trace event is and how it differs from function tracing
  • Enable individual trace events under events/<subsystem>/<event>
  • Read event parameters from the format file and the live trace
  • Write a filter expression that matches on an event parameter
  • Define a small original trace event in a demo kernel module

Prerequisites

Before you continue, you should be comfortable with:
  • Basic Ftrace navigation under tracefs (covered in the previous lecture)
  • Building and inserting an out-of-tree kernel module
  • Basic C macros and header files

Why Trace Events Exist

A raw function tracer entry only tells you a function was called. A trace event, by contrast, is hand-written by the subsystem’s developer to capture the specific parameters that matter — for example, the size and pointer returned by an allocator, or the device and register touched by a driver. Because trace events are defined once with TRACE_EVENT and compiled into the kernel, there are well over a thousand of them available today across memory management, scheduling, block I/O, networking, and more, listed at runtime in available_events.

Each event lives under a subdirectory named tracing/events/<subsystem>/<event>, for example events/kmem/kmalloc, and exposes a small, consistent set of files:

FilePurpose
enableWrite 1 to turn this specific event on, 0 to turn it off
filterAn expression; only matching occurrences are recorded
formatThe event’s field layout and print format
idNumeric identifier for the event
triggerA command executed when the event fires

An Original Demo Event

To see this end to end, we define a small original trace event, ep_alloc, inside a demo module that mimics a simple allocation path. This is written from scratch and does not reuse any existing kernel subsystem’s event definitions.

// ep_trace_events.h
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ep_demo

#if !defined(_EP_TRACE_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _EP_TRACE_EVENTS_H

#include <linux/tracepoint.h>

TRACE_EVENT(ep_alloc,
    TP_PROTO(size_t bytes, void *ptr),
    TP_ARGS(bytes, ptr),

    TP_STRUCT__entry(
        __field(size_t, bytes)
        __field(void *, ptr)
    ),

    TP_fast_assign(
        __entry->bytes = bytes;
        __entry->ptr = ptr;
    ),

    TP_printk("bytes=%zu ptr=%p", __entry->bytes, __entry->ptr)
);

#endif

#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE ep_trace_events
#include <trace/define_trace.h>
// ep_trace_demo.c
#define CREATE_TRACE_POINTS
#include "ep_trace_events.h"

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/slab.h>

static struct kobject *ep_kobj;

static ssize_t alloc_store(struct kobject *kobj, struct kobj_attribute *attr,
                            const char *buf, size_t count)
{
    size_t bytes;
    void *ptr;

    if (kstrtoul(buf, 10, &bytes))
        return -EINVAL;

    ptr = kmalloc(bytes, GFP_KERNEL);
    trace_ep_alloc(bytes, ptr);
    kfree(ptr);

    return count;
}

static struct kobj_attribute alloc_attr = __ATTR_WO(alloc);

static int __init ep_trace_demo_init(void)
{
    ep_kobj = kobject_create_and_add("ep_trace_demo", kernel_kobj);
    if (!ep_kobj)
        return -ENOMEM;

    return sysfs_create_file(ep_kobj, &alloc_attr.attr);
}

static void __exit ep_trace_demo_exit(void)
{
    kobject_put(ep_kobj);
}

module_init(ep_trace_demo_init);
module_exit(ep_trace_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala trace event demo module");
make -C /lib/modules/$(uname -r)/build M=$PWD modules
sudo insmod ep_trace_demo.ko

Enabling and Reading the Event

Event tracing is independent of the function tracers, so we start from the nop tracer and enable only the event we defined.

cd /sys/kernel/tracing
echo nop > current_tracer
echo 1 > events/ep_demo/ep_alloc/enable

echo 256 | sudo tee /sys/kernel/kernel/ep_trace_demo/alloc
echo 4096 | sudo tee /sys/kernel/kernel/ep_trace_demo/alloc

cat trace
Trace Event Output (Expected Shape)
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
tee-3110 [000] 5551.100210: ep_alloc: bytes=256 ptr=00000000a1b2c3d4
tee-3122 [000] 5551.220874: ep_alloc: bytes=4096 ptr=00000000e5f6a7b8

Unlike the plain function tracer, this line carries real payload: the exact byte count and pointer for each allocation, exactly as the developer chose to record it.

You can also enable multiple events at once by writing to set_event instead of toggling each enable file individually:

echo "ep_demo:ep_alloc" > set_event

Filtering by Parameter

The real power of trace events is the filter file, which lets you match on the fields you saw in format — without writing any kernel code.

# Only record allocations larger than 1KB
echo "bytes > 1024" > events/ep_demo/ep_alloc/filter

echo 256 | sudo tee /sys/kernel/kernel/ep_trace_demo/alloc
echo 4096 | sudo tee /sys/kernel/kernel/ep_trace_demo/alloc

cat trace
# Only the 4096-byte allocation appears
OperatorMeaning
==, !=Equal to / not equal to
>, >=, <, <=Numeric comparison
&&, ||Combine multiple conditions

Real-World Use Cases

  • Memory debugging: watching kmem:kmalloc and kmem:kfree to spot allocation size regressions or leaks.
  • Driver validation: confirming an interrupt handler fires with the expected register values during hardware bring-up.
  • Filtered production diagnostics: enabling one narrowly filtered event on a live system with negligible overhead, instead of a full function trace.

Common Mistakes and Troubleshooting

Watch out for these:
  • Forgetting CREATE_TRACE_POINTS — it must be defined in exactly one .c file before including your trace header, or you get linker errors.
  • Leaving an old filter active — a stale filter file silently drops events you expect to see.
  • Confusing event enable with current_tracer — events work independently of function/function_graph; you don’t need a tracer selected beyond nop.
  • Not checking format — field names in filters must match format exactly, including case.

Best Practices

  • Prefer trace events over function tracing whenever you need parameter data, not just call presence.
  • Use filter to narrow high-frequency events (like memory allocation) before enabling them on a busy system.
  • Clear set_event and any filters when you finish, to avoid leaving hidden overhead for the next person using the system.

Performance and Security Considerations

Performance

A disabled trace event costs essentially nothing, similar to dynamic Ftrace. An enabled, unfiltered high-frequency event (such as a per-allocation event on a busy system) can add measurable overhead — always prefer a filter over enabling broadly and post-processing.

Security: trace event data can expose kernel pointers and internal state; access to tracefs is root-only by default, and that restriction should not be loosened on shared or production systems.

Summary and Key Takeaways

  • Trace events are hand-defined, parameterized trace points created with TRACE_EVENT, distinct from generic function tracing.
  • Each event exposes enable, filter, format, id, and trigger files under tracing/events/<subsystem>/<event>.
  • set_event lets you enable multiple events at once; filter lets you match on their actual parameter values.

Conclusion

Trace events close the gap that function tracing leaves open: they tell you not just that something happened, but exactly what data was involved. Combined with the function and function_graph tracers from the previous lecture, you now have the two core building blocks of Ftrace-based kernel debugging — one for call flow, one for parameter-level detail. This wraps up the Ftrace portion of our free linux kernel development course.

Frequently Asked Questions

What is the difference between a tracer and a trace event?

A tracer like function or function_graph records generic call flow for any function. A trace event is a specific, hand-defined point that also records chosen parameters.

Do I need to modify the kernel to add a new trace event?

Only if you want an event inside the mainline kernel itself. In your own module, you can define one using TRACE_EVENT in a local header, as shown in this lecture.

Can I enable many events at once?

Yes, write a space-separated list of subsystem:event pairs to set_event, or use wildcards.

Are trace event filters expensive?

Filter evaluation adds a small per-event cost, but it is far cheaper than recording and later filtering every event in a large buffer.

Are trace events used outside Ftrace?

Yes, the same TRACE_EVENT-based infrastructure is also consumed by perf and LTTng.

Where do I find all available events?

In /sys/kernel/tracing/available_events, one per line, named subsystem:event.

What does the trigger file do?

It lets you attach a command that runs automatically when the event fires, such as starting or stopping tracing.

Continue Your Free Linux Kernel Development Course

Explore more chapters in this free linux device drivers course and free embedded systems course series.

Next Lecture Course Index

2 Comments

Leave a Reply

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