Skip to contentSkip to conversation

tracing: Add perf events to trace buffer

RFC PATCH NAK Steven Rostedt 6 participants 18 messages Nov 18, 2025 – Nov 20, 2025 Tracing

lore
Share & tools
lore
#0 lore raw

cover tracing: Add perf events to trace buffer

This series adds a perf event to the ftrace ring buffer.
It is currently a proof of concept as I'm not happy with the interface and I also think the recorded perf event format may be changed too.

This proof-of-concept interface (which I have no plans on using), currently just adds 6 new trace options.

event_cache_misses
event_cpu_cycles
func-cache-misses
func-cpu-cycles
funcgraph-cache-misses
funcgraph-cpu-cycles

The first two trigger a perf event after every event, the second two trigger a perf event after every function and the last two trigger a perf event right after the start of a function and again at the end of the function.

As this will eventual work with many more perf events than just cache-misses and cpu-cycles , using options is not appropriate. Especially since the options are limited to a 64 bit bitmask, and that can easily go much higher. I'm thinking about having a file instead that will act as a way to enable perf events for events, function and function graph tracing.

set_event_perf, set_ftrace_perf, set_fgraph_perf

And an available_perf_events that show what can be written into these files, (similar to how set_ftrace_filter works). But for now, it was just easier to implement them as options.

As for the perf event that is triggered. It currently is a dynamic array of 64 bit values. Each value is broken up into 8 bits for what type of perf event it is, and 56 bits for the counter. It only writes a per CPU raw counter and does not do any math. That would be needed to be done by any post processing.

Since the values are for user space to do the subtraction to figure out the difference between events, for example, the function_graph tracer may have:

             is_vmalloc_addr() {
               /* cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }

User space would subtract 2869006049 - 2869004572 = 1477

Then 56 bits should be plenty.

2^55 / 1,000,000,000 / 60 / 60 / 24 = 416
416 / 4 = 104

If you have a 4GHz machine, the cpu-cycles will overflow the 55 bits in 104 days. This tooling is not for seeing how many cycles run over 104 days. User space tooling would just need to be aware that the vale is 56 bits and when calculating the difference between start and end do something like:

  if (start > end)
      end |= 1ULL << 56;

  delta = end - start;

The next question is how to label the perf events to be in the 8 bit portion. It could simply be a value that is registered, and listed in the available_perf_events file.

cpu_cycles:1
cach_misses:2
[..]

And this would need to be recorded by any tooling reading the events so that it knows how to map the events with their attached ids.

But again, this is just a proof-of-concept. How this will eventually be implemented is yet to be determined.

But to test these patches (which are based on top of my linux-next branch, which should now be in linux-next):

  # cd /sys/kernel/tracing
  # echo 1 > options/event_cpu_cycles
  # echo 1 > options/event_cache_misses
  # echo 1 > events/syscalls/enable
  # cat trace
[..]
            bash-995     [007] .....    98.255252: sys_write -> 0x2
            bash-995     [007] .....    98.255257: cpu_cycles: 1557241774 cache_misses: 449901166
            bash-995     [007] .....    98.255284: sys_dup2(oldfd: 0xa, newfd: 1)
            bash-995     [007] .....    98.255285: cpu_cycles: 1557260057 cache_misses: 449902679
            bash-995     [007] .....    98.255305: sys_dup2 -> 0x1
            bash-995     [007] .....    98.255305: cpu_cycles: 1557280203 cache_misses: 449906196
            bash-995     [007] .....    98.255343: sys_fcntl(fd: 0xa, cmd: 1, arg: 0)
            bash-995     [007] .....    98.255344: cpu_cycles: 1557322304 cache_misses: 449915522
            bash-995     [007] .....    98.255352: sys_fcntl -> 0x1
            bash-995     [007] .....    98.255353: cpu_cycles: 1557327809 cache_misses: 449916844
            bash-995     [007] .....    98.255361: sys_close(fd: 0xa)
            bash-995     [007] .....    98.255362: cpu_cycles: 1557335383 cache_misses: 449918232
            bash-995     [007] .....    98.255369: sys_close -> 0x0

Comments welcomed.

Steven Rostedt (3):
tracing: Add perf events
ftrace: Add perf counters to function tracing
fgraph: Add perf counters to function graph tracer

----

 include/linux/trace_recursion.h      |   5 +-
 kernel/trace/trace.c                 | 153 ++++++++++++++++++++++++++++++++-
 kernel/trace/trace.h                 |  38 ++++++++
 kernel/trace/trace_entries.h         |  13 +++
 kernel/trace/trace_event_perf.c      | 162 +++++++++++++++++++++++++++++++++++
 kernel/trace/trace_functions.c       | 124 +++++++++++++++++++++++++--
 kernel/trace/trace_functions_graph.c | 117 +++++++++++++++++++++++--
 kernel/trace/trace_output.c          |  70 +++++++++++++++
 8 files changed, 670 insertions(+), 12 deletions(-)
#1 lore raw

RFC PATCH 1/3 tracing: Add perf events

From: Steven Rostedt <[email protected]>

Add perf events into the ftrace ring buffer. Create a new ftrace event called a "perf_event". This event contains a dynamic array of u64 words. Each entry allows to read 56 bits of the raw content of a perf PMU value into the word leaving 8 bits as an identifier for what word that is.

One may ask "what happens when the counter is greater than 56 bits". The answer is that you really shouldn't care. The value is written for user space to consume and do any calculations. If one wants to see the difference between two events, they can simply subtract the previous one from the next one. If there is a wrap over the 56 bits, then adding a "1ULL << 56" to the second value if it is less than the first will give the correct result.

"What happens if the difference of the counters is 1 << 55 apart?" Let's look at CPU cycles, as they probably go up the quickest. At 4GHz, that would be 4,000,000,000 times a second.

1 << 55 / 400000000 = 9007199 seconds
9007199 / 60 = 150119 minutes
150119 / 60 = 2501 hours
2501 / 24 = 104 days!

This will not work if you want to see the number of cycles between two events if those two events are 104 days apart. Do we care?

Currently only cpu cycles and cache misses are supported, but more can be added in the future.

Two new options are added: event_cache_misses and event_cpu_cycles

 # cd /sys/kernel/tracing
 # echo 1 > options/event_cache_misses
 # echo 1 > events/syscalls/enable
 # cat trace
[..]
  bash-1009    [005] .....   566.863956: sys_write -> 0x2
  bash-1009    [005] .....   566.863973: cache_misses: 26544738
  bash-1009    [005] .....   566.864003: sys_dup2(oldfd: 0xa, newfd: 1)
  bash-1009    [005] .....   566.864004: cache_misses: 26546241
  bash-1009    [005] .....   566.864021: sys_dup2 -> 0x1
  bash-1009    [005] .....   566.864022: cache_misses: 26549598
  bash-1009    [005] .....   566.864059: sys_fcntl(fd: 0xa, cmd: 1, arg: 0)
  bash-1009    [005] .....   566.864060: cache_misses: 26558778

The option will cause the perf event to be triggered after every event. If cpu_cycles is also enabled:

 # echo 1 > options/event_cpu_cycles
 # cat trace
[..]
  bash-1009    [006] .....   683.223244: sys_write -> 0x2
  bash-1009    [006] .....   683.223245: cpu_cycles: 273245 cache_misses: 40481492
  bash-1009    [006] .....   683.223262: sys_dup2(oldfd: 0xa, newfd: 1)
  bash-1009    [006] .....   683.223263: cpu_cycles: 286640 cache_misses: 40483017
  bash-1009    [006] .....   683.223278: sys_dup2 -> 0x1
  bash-1009    [006] .....   683.223279: cpu_cycles: 301412 cache_misses: 40486560
  bash-1009    [006] .....   683.223309: sys_fcntl(fd: 0xa, cmd: 1, arg: 0)
  bash-1009    [006] .....   683.223310: cpu_cycles: 335188 cache_misses: 40495672
  bash-1009    [006] .....   683.223317: sys_fcntl -> 0x1
---
 kernel/trace/trace.c            | 113 +++++++++++++++++++++-
 kernel/trace/trace.h            |  28 ++++++
 kernel/trace/trace_entries.h    |  13 +++
 kernel/trace/trace_event_perf.c | 162 ++++++++++++++++++++++++++++++++
 kernel/trace/trace_output.c     |  70 ++++++++++++++
 5 files changed, 385 insertions(+), 1 deletion(-)
kernel/trace/trace.c +112 -1
index 59cd4ed8af6d..64d966a3ec8b 100644
@@ -1110,7 +1110,6 @@ void tracing_on(void)
 }
 EXPORT_SYMBOL_GPL(tracing_on);
 
-
 static __always_inline void
 __buffer_unlock_commit(struct trace_buffer *buffer, struct ring_buffer_event *event)
 {
@@ -2915,6 +2914,103 @@ void trace_event_buffer_commit(struct trace_event_buffer *fbuffer)
 }
 EXPORT_SYMBOL_GPL(trace_event_buffer_commit);
 
+#ifdef CONFIG_PERF_EVENTS
+static inline void record_perf_event(struct trace_array *tr,
+				     struct trace_buffer *buffer,
+				     unsigned int trace_ctx)
+{
+	struct ring_buffer_event *event;
+	struct perf_event_entry *entry;
+	int entries = READ_ONCE(tr->perf_events);
+	struct trace_array_cpu *data;
+	u64 *value;
+	int size;
+	int cpu;
+
+	if (!entries)
+		return;
+
+	guard(preempt_notrace)();
+	cpu = smp_processor_id();
+
+	/* Prevent this from recursing */
+	data = per_cpu_ptr(tr->array_buffer.data, cpu);
+	if (unlikely(!data) || local_read(&data->disabled))
+		return;
+
+	if (local_inc_return(&data->disabled) != 1)
+		goto out;
+
+	size = struct_size(entry, values, entries);
+	event = trace_buffer_lock_reserve(buffer, TRACE_PERF_EVENT, size,
+					  trace_ctx);
+	if (!event)
+		goto out;
+	entry			= ring_buffer_event_data(event);
+	value			= entry->values;
+
+	if (tr->trace_flags & TRACE_ITER(PERF_CYCLES)) {
+		*value++ = TRACE_PERF_VALUE(PERF_TRACE_CYCLES);
+		entries--;
+	}
+
+	if (entries && tr->trace_flags & TRACE_ITER(PERF_CACHE)) {
+		*value++ = TRACE_PERF_VALUE(PERF_TRACE_CACHE);
+		entries--;
+	}
+
+	/* If something changed, zero the rest */
+	if (unlikely(entries))
+		memset(value, 0, sizeof(u64) * entries);
+
+	trace_buffer_unlock_commit_nostack(buffer, event);
+ out:
+	local_dec(&data->disabled);
+}
+
+static int handle_perf_event(struct trace_array *tr, u64 mask, int enabled)
+{
+	int ret = 0;
+	int type;
+
+	switch (mask) {
+
+	case TRACE_ITER(PERF_CYCLES):
+		type = PERF_TRACE_CYCLES;
+		break;
+	case TRACE_ITER(PERF_CACHE):
+		type = PERF_TRACE_CACHE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (enabled)
+		ret = trace_perf_event_enable(type);
+	else
+		trace_perf_event_disable(type);
+
+	if (ret < 0)
+		return ret;
+
+	if (enabled)
+		tr->perf_events++;
+	else
+		tr->perf_events--;
+
+	if (WARN_ON_ONCE(tr->perf_events < 0))
+		tr->perf_events = 0;
+
+	return 0;
+}
+#else
+static inline void record_perf_event(struct trace_array *tr,
+				     struct trace_buffer *buffer,
+				     unsigned int trace_ctx)
+{
+}
+#endif
+
 /*
  * Skip 3:
  *
@@ -2932,6 +3028,8 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
 {
 	__buffer_unlock_commit(buffer, event);
 
+	record_perf_event(tr, buffer, trace_ctx);
+
 	/*
 	 * If regs is not set, then skip the necessary functions.
 	 * Note, we can still get here via blktrace, wakeup tracer
@@ -5287,7 +5385,20 @@ int set_tracer_flag(struct trace_array *tr, u64 mask, int enabled)
 		update_marker_trace(tr, enabled);
 		/* update_marker_trace updates the tr->trace_flags */
 		return 0;
+
+#ifdef CONFIG_PERF_EVENTS
+	case TRACE_ITER(PERF_CACHE):
+	case TRACE_ITER(PERF_CYCLES):
+	{
+		int ret = 0;
+
+		ret = handle_perf_event(tr, mask, enabled);
+		if (ret < 0)
+			return ret;
+		break;
 	}
+#endif
+	} /* switch (mask) */
 
 	if (enabled)
 		tr->trace_flags |= mask;
kernel/trace/trace.h +28 -0
index 58be6d741d72..094a156b0c70 100644
@@ -56,6 +56,7 @@ enum trace_type {
 	TRACE_TIMERLAT,
 	TRACE_RAW_DATA,
 	TRACE_FUNC_REPEATS,
+	TRACE_PERF_EVENT,
 
 	__TRACE_LAST_TYPE,
 };
@@ -363,6 +364,8 @@ struct trace_array {
 
 	int			buffer_disabled;
 
+	int			perf_events;
+
 	struct trace_pid_list	__rcu *filtered_pids;
 	struct trace_pid_list	__rcu *filtered_no_pids;
 	/*
@@ -537,6 +540,7 @@ extern void __ftrace_bad_type(void);
 		IF_ASSIGN(var, ent, struct hwlat_entry, TRACE_HWLAT);	\
 		IF_ASSIGN(var, ent, struct osnoise_entry, TRACE_OSNOISE);\
 		IF_ASSIGN(var, ent, struct timerlat_entry, TRACE_TIMERLAT);\
+		IF_ASSIGN(var, ent, struct perf_event_entry, TRACE_PERF_EVENT);	\
 		IF_ASSIGN(var, ent, struct raw_data_entry, TRACE_RAW_DATA);\
 		IF_ASSIGN(var, ent, struct trace_mmiotrace_rw,		\
 			  TRACE_MMIO_RW);				\
@@ -1382,6 +1386,29 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
 # define TRACE_ITER_PROF_TEXT_OFFSET_BIT	-1
 #endif
 
+#ifdef CONFIG_PERF_EVENTS
+#define PERF_MAKE_VALUE(type, val)	(((type) << 56) | ((val) & ~(0xffULL << 56)))
+/* Not required, but keep consistent with include/uapi/linux/perf_event.h */
+#define PERF_TRACE_CYCLES		0ULL
+#define PERF_TRACE_CACHE		5ULL
+#define TRACE_PERF_VALUE(type)		\
+	PERF_MAKE_VALUE((type), do_trace_perf_event(type))
+#define PERF_TRACE_VALUE(val)		((val) & ~(0xffULL << 56))
+#define PERF_TRACE_TYPE(val)		((val) >> 56)
+# define PERF_FLAGS				\
+		C(PERF_CACHE,		"event_cache_misses"),	\
+		C(PERF_CYCLES,		"event_cpu_cycles"),
+
+u64 do_trace_perf_event(int type);
+int trace_perf_event_enable(int type);
+void trace_perf_event_disable(int type);
+#else
+# define PERF_FLAGS
+static inline u64 do_trace_perf_event(int type) { return 0; }
+static inline int trace_perf_event_enable(int type) { return -ENOTSUPP; }
+static inline void trace_perf_event_disable(int type) { }
+#endif /* CONFIG_PERF_EVENTS */
+
 /*
  * trace_iterator_flags is an enumeration that defines bit
  * positions into trace_flags that controls the output.
@@ -1420,6 +1447,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
 		FUNCTION_FLAGS					\
 		FGRAPH_FLAGS					\
 		STACK_FLAGS					\
+		PERF_FLAGS					\
 		BRANCH_FLAGS					\
 		PROFILER_FLAGS					\
 		FPROFILE_FLAGS
kernel/trace/trace_entries.h +13 -0
index de294ae2c5c5..ecda463a9d8e 100644
@@ -456,3 +456,16 @@ FTRACE_ENTRY(timerlat, timerlat_entry,
 		 __entry->context,
 		 __entry->timer_latency)
 );
+
+#ifdef CONFIG_PERF_EVENTS
+FTRACE_ENTRY(perf_event, perf_event_entry,
+
+	TRACE_PERF_EVENT,
+
+	F_STRUCT(
+		__dynamic_array(u64,		values		)
+	),
+
+	F_printk("values: %lld\n", __entry->values[0])
+);
+#endif
kernel/trace/trace_event_perf.c +162 -0

168 lines not on this page · raw message

kernel/trace/trace_output.c +70 -0

82 lines not on this page · raw message

Signature · 1 line
2.51.0
#2 lore raw

Re: [POC][RFC][PATCH 1/3] tracing: Add perf events

Quoted 19 lines · Steven Rostedt
kernel/trace/trace_event_perf.c +19 -0
+u64 do_trace_perf_event(int type)
+{
+	struct trace_perf_event __percpu **pevents;
+	struct trace_perf_event __percpu *events;
+	struct perf_event *e;
+	int *count;
+	int cpu;
+
+	if (set_perf_type(type, NULL, NULL, &count, &pevents) < 0)
+		return 0;
+
+	if (!*count)
+		return 0;
+
+	guard(preempt)();
+
+	events = READ_ONCE(*pevents);
+	if (!events)
+		return 0;

kernel/trace/trace_event_perf.c void perf_trace_buf_update(void *record, u16 type) · line 504

Quoted 10 lines
kernel/trace/trace_event_perf.c +10 -0
+
+	cpu = smp_processor_id();
+
+	e = per_cpu_ptr(events, cpu)->event;
+	if (!e)
+		return 0;
+
+	e->pmu->read(e);
+	return local64_read(&e->count);
+}

NAK, wtf do you think its okay to use internal stuff like that? And wrongly while at it.

What you wanted to use was perf_event_read_local().

maintainer
#3 lore raw

Re: [POC][RFC][PATCH 1/3] tracing: Add perf events

Quoted 4 lines · On Tue, 18 Nov 2025 09:35:10 +0100 Peter Zijlstra <[email protected]> wrote:
Quoted 4 lines
kernel/trace/trace_event_perf.c +4 -0
+	cpu = smp_processor_id();
+
+	e = per_cpu_ptr(events, cpu)->event;
+	if (!e)
Quoted 8 lines

kernel/trace/trace_event_perf.c void perf_trace_buf_update(void *record, u16 type) · line 504

Quoted 5 lines
kernel/trace/trace_event_perf.c +5 -0
+		return 0;
+
+	e->pmu->read(e);
+	return local64_read(&e->count);
+}  

NAK, wtf do you think its okay to use internal stuff like that? And wrongly while at it.

Peter, this is a PROOF-OF-CONCEPT. It means I'm showing the concept and not the implementation. I'm hoping the NAK is on the implementation and not the concept.

Quoted 2 lines

What you wanted to use was perf_event_read_local().

Great! I didn't know about that. Which is why I posted this as a PROOF-OF-CONCEPT and not even a normal RFC, so that I could learn about the proper way of doing this.

-- Steve

maintainer
#4 lore raw

Re: [POC][RFC][PATCH 1/3] tracing: Add perf events

Quoted 6 lines · On Tue, 18 Nov 2025 08:42:26 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 2 lines

What you wanted to use was perf_event_read_local().

Great! I didn't know about that. Which is why I posted this as a PROOF-OF-CONCEPT and not even a normal RFC, so that I could learn about the proper way of doing this.

I folded in this change:

kernel/trace/trace_event_perf.c +5 -2
index ff864d300251..34962f80dce1 100644
@@ -478,6 +478,7 @@ u64 do_trace_perf_event(int type)
 	struct trace_perf_event __percpu **pevents;
 	struct trace_perf_event __percpu *events;
 	struct perf_event *e;
+	u64 val;
 	int *count;
 	int cpu;
 
@@ -499,8 +500,10 @@ u64 do_trace_perf_event(int type)
 	if (!e)
 		return 0;
 
-	e->pmu->read(e);
-	return local64_read(&e->count);
+	if (perf_event_read_local(e, &val, NULL, NULL) < 0)
+		return 0;
+
+	return val;
 }
 
 static void __free_trace_perf_events(struct trace_perf_event __percpu *events)

Thanks!

-- Steve

#5 lore raw

Re: [POC][RFC][PATCH 1/3] tracing: Add perf events

In reply to Steven Rostedt
kernel test robot noticed the following build errors:

Hi Steven,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build errors:

[auto build test ERROR on trace/for-next]
[also build test ERROR on next-20251119]
[cannot apply to linus/master v6.18-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Steven-Rostedt/tracing-Add-perf-events/20251118-083721
base:   https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
patch link:    https://lore.kernel.org/r/20251118003531.644484343%40kernel.org
patch subject: [POC][RFC][PATCH 1/3] tracing: Add perf events
config: s390-randconfig-001-20251120 (https://download.01.org/0day-ci/archive/20251120/[email protected]/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 0bba1e76581bad04e7d7f09f5115ae5e2989e0d9)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251120/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

>> kernel/trace/trace_output.c:1681:18: error: incomplete definition of type 'struct perf_event_entry'
    1681 |         for (val = field->values; val < end; val++) {
         |                    ~~~~~^
   kernel/trace/trace_output.c:1672:9: note: forward declaration of 'struct perf_event_entry'
    1672 |         struct perf_event_entry *field;
         |                ^
   kernel/trace/trace_output.c:1682:19: error: incomplete definition of type 'struct perf_event_entry'
    1682 |                 if (val != field->values)
         |                            ~~~~~^
   kernel/trace/trace_output.c:1672:9: note: forward declaration of 'struct perf_event_entry'
    1672 |         struct perf_event_entry *field;
         |                ^
>> kernel/trace/trace_output.c:1684:11: error: call to undeclared function 'PERF_TRACE_VALUE'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1684 |                 value = PERF_TRACE_VALUE(*val);
         |                         ^
>> kernel/trace/trace_output.c:1685:11: error: call to undeclared function 'PERF_TRACE_TYPE'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1685 |                 switch (PERF_TRACE_TYPE(*val)) {
         |                         ^
>> kernel/trace/trace_output.c:1686:8: error: use of undeclared identifier 'PERF_TRACE_CYCLES'; did you mean 'PERF_TRACE_TYPE'?
    1686 |                 case PERF_TRACE_CYCLES:
         |                      ^~~~~~~~~~~~~~~~~
         |                      PERF_TRACE_TYPE
   kernel/trace/trace_output.c:1685:11: note: 'PERF_TRACE_TYPE' declared here
    1685 |                 switch (PERF_TRACE_TYPE(*val)) {
         |                         ^
>> kernel/trace/trace_output.c:1686:8: error: integer constant expression must have integer type, not 'int ()'
    1686 |                 case PERF_TRACE_CYCLES:
         |                      ^~~~~~~~~~~~~~~~~
>> kernel/trace/trace_output.c:1689:8: error: use of undeclared identifier 'PERF_TRACE_CACHE'; did you mean 'PERF_TRACE_VALUE'?
    1689 |                 case PERF_TRACE_CACHE:
         |                      ^~~~~~~~~~~~~~~~
         |                      PERF_TRACE_VALUE
   kernel/trace/trace_output.c:1684:11: note: 'PERF_TRACE_VALUE' declared here
    1684 |                 value = PERF_TRACE_VALUE(*val);
         |                         ^
   kernel/trace/trace_output.c:1689:8: error: integer constant expression must have integer type, not 'int ()'
    1689 |                 case PERF_TRACE_CACHE:
         |                      ^~~~~~~~~~~~~~~~
   kernel/trace/trace_output.c:1714:18: error: incomplete definition of type 'struct perf_event_entry'
    1714 |         for (val = field->values; val < end; val++) {
         |                    ~~~~~^
   kernel/trace/trace_output.c:1705:9: note: forward declaration of 'struct perf_event_entry'
    1705 |         struct perf_event_entry *field;
         |                ^
   kernel/trace/trace_output.c:1715:19: error: incomplete definition of type 'struct perf_event_entry'
    1715 |                 if (val != field->values)
         |                            ~~~~~^
   kernel/trace/trace_output.c:1705:9: note: forward declaration of 'struct perf_event_entry'
    1705 |         struct perf_event_entry *field;
         |                ^

10 errors generated.

vim +1681 kernel/trace/trace_output.c

1665
1666 static enum print_line_t
1667 trace_perf_event_print(struct trace_iterator *iter, int flags,
1668 struct trace_event *event)
1669 {
1670 struct trace_entry *entry = iter->ent;
1671 struct trace_seq *s = &iter->seq;
1672 struct perf_event_entry *field;
1673 u64 value;
1674 u64 *val;
1675 u64 *end;
1676
1677 end = (u64 *)((long)iter->ent + iter->ent_size);
1678
1679 trace_assign_type(field, entry);
1680

> 1681		for (val = field->values; val < end; val++) {
  1682			if (val != field->values)
  1683				trace_seq_putc(s, ' ');
> 1684			value = PERF_TRACE_VALUE(*val);
> 1685			switch (PERF_TRACE_TYPE(*val)) {
> 1686			case PERF_TRACE_CYCLES:
  1687				trace_seq_printf(s, "cpu_cycles: %lld", value);
  1688				break;
> 1689			case PERF_TRACE_CACHE:

1690 trace_seq_printf(s, "cache_misses: %lld", value);
1691 break;
1692 default:
1693 trace_seq_printf(s, "unkown(%d): %lld",
1694 (int)PERF_TRACE_TYPE(*val), value);
1695 }
1696 }
1697 trace_seq_putc(s, '\n');
1698 return trace_handle_return(s);
1699 }
1700

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#6 lore raw

Re: [POC][RFC][PATCH 1/3] tracing: Add perf events

In reply to Steven Rostedt
kernel test robot noticed the following build errors:

Hi Steven,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build errors:

[auto build test ERROR on trace/for-next]
[also build test ERROR on next-20251119]
[cannot apply to linus/master v6.18-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Steven-Rostedt/tracing-Add-perf-events/20251118-083721
base:   https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
patch link:    https://lore.kernel.org/r/20251118003531.644484343%40kernel.org
patch subject: [POC][RFC][PATCH 1/3] tracing: Add perf events
config: arc-randconfig-6001-20251120 (https://download.01.org/0day-ci/archive/20251120/[email protected]/config)
compiler: arc-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251120/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

kernel/trace/trace_output.c: In function 'trace_perf_event_print':

>> kernel/trace/trace_output.c:1681:18: error: invalid use of undefined type 'struct perf_event_entry'
    1681 |  for (val = field->values; val < end; val++) {
         |                  ^~
   kernel/trace/trace_output.c:1682:19: error: invalid use of undefined type 'struct perf_event_entry'
    1682 |   if (val != field->values)
         |                   ^~
>> kernel/trace/trace_output.c:1684:11: error: implicit declaration of function 'PERF_TRACE_VALUE' [-Werror=implicit-function-declaration]
    1684 |   value = PERF_TRACE_VALUE(*val);
         |           ^~~~~~~~~~~~~~~~
>> kernel/trace/trace_output.c:1685:11: error: implicit declaration of function 'PERF_TRACE_TYPE' [-Werror=implicit-function-declaration]
    1685 |   switch (PERF_TRACE_TYPE(*val)) {
         |           ^~~~~~~~~~~~~~~
>> kernel/trace/trace_output.c:1686:8: error: 'PERF_TRACE_CYCLES' undeclared (first use in this function)
    1686 |   case PERF_TRACE_CYCLES:
         |        ^~~~~~~~~~~~~~~~~
   kernel/trace/trace_output.c:1686:8: note: each undeclared identifier is reported only once for each function it appears in
>> kernel/trace/trace_output.c:1689:8: error: 'PERF_TRACE_CACHE' undeclared (first use in this function); did you mean 'PERF_TYPE_HW_CACHE'?
    1689 |   case PERF_TRACE_CACHE:
         |        ^~~~~~~~~~~~~~~~
         |        PERF_TYPE_HW_CACHE
   kernel/trace/trace_output.c: In function 'trace_perf_event_raw':
   kernel/trace/trace_output.c:1714:18: error: invalid use of undefined type 'struct perf_event_entry'
    1714 |  for (val = field->values; val < end; val++) {
         |                  ^~
   kernel/trace/trace_output.c:1715:19: error: invalid use of undefined type 'struct perf_event_entry'
    1715 |   if (val != field->values)
         |                   ^~

cc1: some warnings being treated as errors

vim +1681 kernel/trace/trace_output.c

1665
1666 static enum print_line_t
1667 trace_perf_event_print(struct trace_iterator *iter, int flags,
1668 struct trace_event *event)
1669 {
1670 struct trace_entry *entry = iter->ent;
1671 struct trace_seq *s = &iter->seq;
1672 struct perf_event_entry *field;
1673 u64 value;
1674 u64 *val;
1675 u64 *end;
1676
1677 end = (u64 *)((long)iter->ent + iter->ent_size);
1678
1679 trace_assign_type(field, entry);
1680

> 1681		for (val = field->values; val < end; val++) {
  1682			if (val != field->values)
  1683				trace_seq_putc(s, ' ');
> 1684			value = PERF_TRACE_VALUE(*val);
> 1685			switch (PERF_TRACE_TYPE(*val)) {
> 1686			case PERF_TRACE_CYCLES:
  1687				trace_seq_printf(s, "cpu_cycles: %lld", value);
  1688				break;
> 1689			case PERF_TRACE_CACHE:

1690 trace_seq_printf(s, "cache_misses: %lld", value);
1691 break;
1692 default:
1693 trace_seq_printf(s, "unkown(%d): %lld",
1694 (int)PERF_TRACE_TYPE(*val), value);
1695 }
1696 }
1697 trace_seq_putc(s, '\n');
1698 return trace_handle_return(s);
1699 }
1700

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#7 lore raw

RFC PATCH 2/3 ftrace: Add perf counters to function tracing

In reply to Steven Rostedt

From: Steven Rostedt <[email protected]>

Add option to trigger perf events to function tracing.

Two new options are added: func-cpu-cycles and func-cache-misses

 # cd /sys/kernel/tracing
 # echo 1 > options/func-cache-misses
 # echo function > current_tracer
 # cat trace
[..]
    sshd-session-1014    [005] .....   327.836708: __x64_sys_read <-do_syscall_64
    sshd-session-1014    [005] .....   327.836708: cache_misses: 741719054
    sshd-session-1014    [005] .....   327.836712: ksys_read <-do_syscall_64
    sshd-session-1014    [005] .....   327.836713: cache_misses: 741720271
    sshd-session-1014    [005] .....   327.836716: fdget_pos <-ksys_read
    sshd-session-1014    [005] .....   327.836717: cache_misses: 741721483
    sshd-session-1014    [005] .....   327.836720: vfs_read <-ksys_read
    sshd-session-1014    [005] .....   327.836721: cache_misses: 741722726
    sshd-session-1014    [005] .....   327.836724: rw_verify_area <-vfs_read
    sshd-session-1014    [005] .....   327.836725: cache_misses: 741723940
    sshd-session-1014    [005] .....   327.836728: security_file_permission <-rw_verify_area
    sshd-session-1014    [005] .....   327.836729: cache_misses: 741725151

The option will cause the perf event to be triggered after every function called.

If cpu_cycles is also enabled:

 # echo 1 > options/func-cpu-cycles
 # cat trace
[..]
    sshd-session-1014    [005] b..1.   536.844538: preempt_count_sub <-_raw_spin_unlock
    sshd-session-1014    [005] b..1.   536.844539: cpu_cycles: 1919425978 cache_misses: 3431216952
    sshd-session-1014    [005] b....   536.844545: validate_xmit_skb_list <-sch_direct_xmit
    sshd-session-1014    [005] b....   536.844545: cpu_cycles: 1919429935 cache_misses: 3431218535
    sshd-session-1014    [005] b....   536.844551: validate_xmit_skb.isra.0 <-validate_xmit_skb_list
    sshd-session-1014    [005] b....   536.844552: cpu_cycles: 1919433763 cache_misses: 3431220112
    sshd-session-1014    [005] b....   536.844557: netif_skb_features <-validate_xmit_skb.isra.0
    sshd-session-1014    [005] b....   536.844558: cpu_cycles: 1919437574 cache_misses: 3431221688
---
 include/linux/trace_recursion.h |   5 +-
 kernel/trace/trace.c            |  58 ++++++++++++---
 kernel/trace/trace.h            |   6 ++
 kernel/trace/trace_functions.c  | 124 ++++++++++++++++++++++++++++++--
 4 files changed, 178 insertions(+), 15 deletions(-)
include/linux/trace_recursion.h +4 -1
index ae04054a1be3..c42d86d81afa 100644
@@ -132,9 +132,12 @@ static __always_inline int trace_test_and_set_recursion(unsigned long ip, unsign
 		 * will think a recursion occurred, and the event will be dropped.
 		 * Let a single instance happen via the TRANSITION_BIT to
 		 * not drop those events.
+		 *
+		 * When ip is zero, the caller is purposely trying causing
+		 * recursion. Don't record it.
 		 */
 		bit = TRACE_CTX_TRANSITION + start;
-		if (val & (1 << bit)) {
+		if ((val & (1 << bit)) && ip) {
 			do_ftrace_record_recursion(ip, pip);
 			return -1;
 		}
kernel/trace/trace.c +49 -9

88 lines not on this page · raw message

kernel/trace/trace.h +6 -0
index 094a156b0c70..bb764a2255c7 100644
@@ -365,6 +365,8 @@ struct trace_array {
 	int			buffer_disabled;
 
 	int			perf_events;
+	int			ftrace_perf_events;
+	u64			ftrace_perf_mask;
 
 	struct trace_pid_list	__rcu *filtered_pids;
 	struct trace_pid_list	__rcu *filtered_no_pids;
@@ -1402,6 +1404,10 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
 u64 do_trace_perf_event(int type);
 int trace_perf_event_enable(int type);
 void trace_perf_event_disable(int type);
+#ifdef CONFIG_FUNCTION_TRACER
+void ftrace_perf_events(struct trace_array *tr, int perf_events,
+			u64 perf_mask, unsigned int trace_ctx);
+#endif
 #else
 # define PERF_FLAGS
 static inline u64 do_trace_perf_event(int type) { return 0; }
kernel/trace/trace_functions.c +119 -5

184 lines not on this page · raw message

Signature · 1 line
2.51.0
#8 lore raw

Re: [POC][RFC][PATCH 2/3] ftrace: Add perf counters to function tracing

kernel test robot noticed the following build warnings:

Hi Steven,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build warnings:

[auto build test WARNING on trace/for-next]
[also build test WARNING on next-20251120]
[cannot apply to linus/master v6.18-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Steven-Rostedt/tracing-Add-perf-events/20251118-083721
base:   https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
patch link:    https://lore.kernel.org/r/20251118003531.805883570%40kernel.org
patch subject: [POC][RFC][PATCH 2/3] ftrace: Add perf counters to function tracing
config: s390-randconfig-001-20251120 (https://download.01.org/0day-ci/archive/20251120/[email protected]/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 0bba1e76581bad04e7d7f09f5115ae5e2989e0d9)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251120/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> kernel/trace/trace_functions.c:538:5: warning: 'CONFIG_PERF_EVENTS' is not defined, evaluates to 0 [-Wundef]

538 | #if CONFIG_PERF_EVENTS
| ^
1 warning generated.

vim +/CONFIG_PERF_EVENTS +538 kernel/trace/trace_functions.c

529
530 static struct tracer_opt func_opts[] = {
531 #ifdef CONFIG_STACKTRACE
532 { TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) },
533 #endif
534 { TRACER_OPT(func-no-repeats, TRACE_FUNC_OPT_NO_REPEATS) },
535 #ifdef CONFIG_FUNCTION_TRACE_ARGS
536 { TRACER_OPT(func-args, TRACE_FUNC_OPT_ARGS) },
537 #endif

 > 538	#if CONFIG_PERF_EVENTS

539 { TRACER_OPT(func-cpu-cycles, TRACE_FUNC_OPT_PERF_CYCLES) },
540 { TRACER_OPT(func-cache-misses, TRACE_FUNC_OPT_PERF_CACHE) },
541 #endif
542 { } /* Always set a last empty entry */
543 };
544

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#9 lore raw

RFC PATCH 3/3 fgraph: Add perf counters to function graph tracer

In reply to Steven Rostedt

From: Steven Rostedt <[email protected]>

Add option to trigger perf events to function graph tracing.

Two new options are added: funcgraph-cpu-cycles and funcgraph-cache-misses

This adds the perf event right after the start of a function and again just before the end of a function.

 # cd /sys/kernel/tracing
 # echo 1 > options/funcgraph-cache-misses
 # echo vfs_read > set_graph_function
 # echo function_graph > current_tracer
 # cat trace
[..]
 5)               |  vfs_read() {
 5)               |  /* cache_misses: 822565 */
 5)               |    rw_verify_area() {
 5)               |      /* cache_misses: 824003 */
 5)               |      security_file_permission() {
 5)               |        /* cache_misses: 825440 */
 5)               |        apparmor_file_permission() {
 5)               |          /* cache_misses: 826875 */
 5)               |          aa_file_perm() {
 5)               |            /* cache_misses: 828326 */
 5)               |            __rcu_read_lock() {
 5)               |              /* cache_misses: 829766 */
 5)               |              /* cache_misses: 830785 */
 5)   5.116 us    |            }
 5)               |            __rcu_read_unlock() {
 5)               |              /* cache_misses: 832611 */
 5)               |              /* cache_misses: 833632 */
 5)   5.223 us    |            }
 5)               |            /* cache_misses: 835043 */
 5) + 25.462 us   |          }
 5)               |          /* cache_misses: 836454 */
 5) + 35.518 us   |        }
 5)               |        bpf_lsm_file_permission() {
 5)               |          /* cache_misses: 838276 */
 5)               |          /* cache_misses: 839292 */
 5)   4.613 us    |        }
 5)               |        /* cache_misses: 840697 */
 5) + 54.684 us   |      }
 5)               |      /* cache_misses: 842107 */
 5) + 64.449 us   |    }

The option will cause the perf event to be triggered after every function called.

If cpu_cycles is also enabled:

 # echo 1 > options/funcgraph-cpu-cycles
 # cat trace
[..]
 3)               |  vfs_read() {
 3)               |  /* cpu_cycles: 2947481793 cache_misses: 2002984031 */
 3)               |    rw_verify_area() {
 3)               |      /* cpu_cycles: 2947488061 cache_misses: 2002985922 */
 3)               |      security_file_permission() {
 3)               |        /* cpu_cycles: 2947492867 cache_misses: 2002987812 */
 3)               |        apparmor_file_permission() {
 3)               |          /* cpu_cycles: 2947497713 cache_misses: 2002989700 */
 3)               |          aa_file_perm() {
 3)               |            /* cpu_cycles: 2947502560 cache_misses: 2002991604 */
 3)               |            __rcu_read_lock() {
 3)               |              /* cpu_cycles: 2947507398 cache_misses: 2002993497 */
 3)               |              /* cpu_cycles: 2947512435 cache_misses: 2002994969 */
 3)   7.586 us    |            }
 3)               |            __rcu_read_unlock() {
 3)               |              /* cpu_cycles: 2947518226 cache_misses: 2002997248 */
 3)               |              /* cpu_cycles: 2947522328 cache_misses: 2002998722 */
 3)   7.211 us    |            }
 3)               |            /* cpu_cycles: 2947527067 cache_misses: 2003000586 */
 3) + 37.581 us   |          }
 3)               |          /* cpu_cycles: 2947531727 cache_misses: 2003002450 */
 3) + 52.061 us   |        }
 3)               |        bpf_lsm_file_permission() {
 3)               |          /* cpu_cycles: 2947537274 cache_misses: 2003004725 */
 3)               |          /* cpu_cycles: 2947541104 cache_misses: 2003006194 */
 3)   7.029 us    |        }
 3)               |        /* cpu_cycles: 2947545762 cache_misses: 2003008052 */
 3) + 80.971 us   |      }
 3)               |      /* cpu_cycles: 2947550459 cache_misses: 2003009915 */
 3) + 95.515 us   |    }
---
 kernel/trace/trace.h                 |   4 +
 kernel/trace/trace_functions_graph.c | 117 +++++++++++++++++++++++++--
 2 files changed, 116 insertions(+), 5 deletions(-)
kernel/trace/trace.h +4 -0
index bb764a2255c7..64cdb6fda3fb 100644
@@ -366,7 +366,9 @@ struct trace_array {
 
 	int			perf_events;
 	int			ftrace_perf_events;
+	int			fgraph_perf_events;
 	u64			ftrace_perf_mask;
+	u64			fgraph_perf_mask;
 
 	struct trace_pid_list	__rcu *filtered_pids;
 	struct trace_pid_list	__rcu *filtered_no_pids;
@@ -946,6 +948,8 @@ static __always_inline bool ftrace_hash_empty(struct ftrace_hash *hash)
 #define TRACE_GRAPH_PRINT_RETVAL_HEX    0x1000
 #define TRACE_GRAPH_PRINT_RETADDR       0x2000
 #define TRACE_GRAPH_ARGS		0x4000
+#define TRACE_GRAPH_PERF_CACHE		0x8000
+#define TRACE_GRAPH_PERF_CYCLES		0x10000
 #define TRACE_GRAPH_PRINT_FILL_SHIFT	28
 #define TRACE_GRAPH_PRINT_FILL_MASK	(0x3 << TRACE_GRAPH_PRINT_FILL_SHIFT)
 
kernel/trace/trace_functions_graph.c +112 -5

171 lines not on this page · raw message

Signature · 1 line
2.51.0
#10 lore raw
In reply to Steven Rostedt

Hi Steve,

Thanks for the great idea!

Quoted 19 lines · On Mon, 17 Nov 2025 19:29:50 -0500 Steven Rostedt <[email protected]> wrote:

This series adds a perf event to the ftrace ring buffer.
It is currently a proof of concept as I'm not happy with the interface and I also think the recorded perf event format may be changed too.

This proof-of-concept interface (which I have no plans on using), currently just adds 6 new trace options.

event_cache_misses
event_cpu_cycles
func-cache-misses
func-cpu-cycles
funcgraph-cache-misses
funcgraph-cpu-cycles

The first two trigger a perf event after every event, the second two trigger a perf event after every function and the last two trigger a perf event right after the start of a function and again at the end of the function.

Quoted 7 lines

As this will eventual work with many more perf events than just cache-misses and cpu-cycles , using options is not appropriate. Especially since the options are limited to a 64 bit bitmask, and that can easily go much higher. I'm thinking about having a file instead that will act as a way to enable perf events for events, function and function graph tracing.

set_event_perf, set_ftrace_perf, set_fgraph_perf

What about adding a global trigger action file so that user can add these "perf" actions to write into it. It is something like stacktrace for events. (Maybe we can move stacktrace/user-stacktrace into it too)

For pre-defined/software counters:

# echo "perf:cpu_cycles" >> /sys/kernel/tracing/trigger

For some hardware event sources (see /sys/bus/event_source/devices/):

# echo "perf:cstate_core.c3-residency" >> /sys/kernel/tracing/trigger

echo "perf:my_counter=pmu/config=M,config1=N" >> /sys/kernel/tracing/trigger

If we need to set those counters for tracers and events separately, we can add events/trigger and tracer-trigger files.

echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/trigger

To disable counters, we can use '!' as same as event triggers.

echo !perf:cpu_cycles > trigger

To add more than 2 counters, connect it with ':'. (or, we will allow to append new perf counters) This allows user to set perf counter options for each events.

Maybe we also should move 'stacktrace'/'userstacktrace' option flags to it too eventually.

Quoted 11 lines

And an available_perf_events that show what can be written into these files, (similar to how set_ftrace_filter works). But for now, it was just easier to implement them as options.

As for the perf event that is triggered. It currently is a dynamic array of 64 bit values. Each value is broken up into 8 bits for what type of perf event it is, and 56 bits for the counter. It only writes a per CPU raw counter and does not do any math. That would be needed to be done by any post processing.

Quoted 7 lines

Since the values are for user space to do the subtraction to figure out the difference between events, for example, the function_graph tracer may have:

             is_vmalloc_addr() {
               /* cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }

Just a style question: Would this mean the first line is for function entry and the second one is function return?

Quoted 18 lines

User space would subtract 2869006049 - 2869004572 = 1477

Then 56 bits should be plenty.

2^55 / 1,000,000,000 / 60 / 60 / 24 = 416
416 / 4 = 104

If you have a 4GHz machine, the cpu-cycles will overflow the 55 bits in 104 days. This tooling is not for seeing how many cycles run over 104 days. User space tooling would just need to be aware that the vale is 56 bits and when calculating the difference between start and end do something like:

  if (start > end)
      end |= 1ULL << 56;

  delta = end - start;
Quoted 7 lines

The next question is how to label the perf events to be in the 8 bit portion. It could simply be a value that is registered, and listed in the available_perf_events file.

cpu_cycles:1
cach_misses:2
[..]

Looks good to me. I think pre-definied events of perf list will be there and have fixed numbers.

Thank you,

Quoted 50 lines

And this would need to be recorded by any tooling reading the events so that it knows how to map the events with their attached ids.

But again, this is just a proof-of-concept. How this will eventually be implemented is yet to be determined.

But to test these patches (which are based on top of my linux-next branch, which should now be in linux-next):

  # cd /sys/kernel/tracing
  # echo 1 > options/event_cpu_cycles
  # echo 1 > options/event_cache_misses
  # echo 1 > events/syscalls/enable
  # cat trace
[..]
            bash-995     [007] .....    98.255252: sys_write -> 0x2
            bash-995     [007] .....    98.255257: cpu_cycles: 1557241774 cache_misses: 449901166
            bash-995     [007] .....    98.255284: sys_dup2(oldfd: 0xa, newfd: 1)
            bash-995     [007] .....    98.255285: cpu_cycles: 1557260057 cache_misses: 449902679
            bash-995     [007] .....    98.255305: sys_dup2 -> 0x1
            bash-995     [007] .....    98.255305: cpu_cycles: 1557280203 cache_misses: 449906196
            bash-995     [007] .....    98.255343: sys_fcntl(fd: 0xa, cmd: 1, arg: 0)
            bash-995     [007] .....    98.255344: cpu_cycles: 1557322304 cache_misses: 449915522
            bash-995     [007] .....    98.255352: sys_fcntl -> 0x1
            bash-995     [007] .....    98.255353: cpu_cycles: 1557327809 cache_misses: 449916844
            bash-995     [007] .....    98.255361: sys_close(fd: 0xa)
            bash-995     [007] .....    98.255362: cpu_cycles: 1557335383 cache_misses: 449918232
            bash-995     [007] .....    98.255369: sys_close -> 0x0

Comments welcomed.

Steven Rostedt (3):
tracing: Add perf events
ftrace: Add perf counters to function tracing
fgraph: Add perf counters to function graph tracer

----

 include/linux/trace_recursion.h      |   5 +-
 kernel/trace/trace.c                 | 153 ++++++++++++++++++++++++++++++++-
 kernel/trace/trace.h                 |  38 ++++++++
 kernel/trace/trace_entries.h         |  13 +++
 kernel/trace/trace_event_perf.c      | 162 +++++++++++++++++++++++++++++++++++
 kernel/trace/trace_functions.c       | 124 +++++++++++++++++++++++++--
 kernel/trace/trace_functions_graph.c | 117 +++++++++++++++++++++++--
 kernel/trace/trace_output.c          |  70 +++++++++++++++
 8 files changed, 670 insertions(+), 12 deletions(-)
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
#11 lore raw
Quoted 3 lines · On Tue, 18 Nov 2025 12:08:21 +0900 Masami Hiramatsu (Google)…

Hi Steve,

Thanks for the great idea!

Thanks!

Quoted 9 lines
Quoted 8 lines

As this will eventual work with many more perf events than just cache-misses and cpu-cycles , using options is not appropriate. Especially since the options are limited to a 64 bit bitmask, and that can easily go much higher. I'm thinking about having a file instead that will act as a way to enable perf events for events, function and function graph tracing.

set_event_perf, set_ftrace_perf, set_fgraph_perf

Quoted 7 lines

What about adding a global trigger action file so that user can add these "perf" actions to write into it. It is something like stacktrace for events. (Maybe we can move stacktrace/user-stacktrace into it too)

For pre-defined/software counters:

# echo "perf:cpu_cycles" >> /sys/kernel/tracing/trigger

For events, it would make more sense to put it into the events directory:

 # echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/trigger

As there is already a events/enable

Heck we could even add it per system:

 # echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/syscalls/trigger
Quoted 5 lines

For some hardware event sources (see /sys/bus/event_source/devices/):

# echo "perf:cstate_core.c3-residency" >> /sys/kernel/tracing/trigger

echo "perf:my_counter=pmu/config=M,config1=N" >> /sys/kernel/tracing/trigger

Still need a way to add an identifier list. Currently, if the size of the type identifier is one byte, then it can only support up to 256 events.

Do we need every event for this? Or just have a subset of events that would be supported?

Quoted 3 lines

If we need to set those counters for tracers and events separately, we can add events/trigger and tracer-trigger files.

As I mentioned, the trigger for events should be in the events directory.

We could add a ftrace_trigger that can affect both function and function graph tracer.

Quoted 6 lines

echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/trigger

To disable counters, we can use '!' as same as event triggers.

echo !perf:cpu_cycles > trigger

Yes, it would follow the current way to disable a trigger.

Quoted 7 lines

To add more than 2 counters, connect it with ':'. (or, we will allow to append new perf counters) This allows user to set perf counter options for each events.

Maybe we also should move 'stacktrace'/'userstacktrace' option flags to it too eventually.

We can add them, but may never be able to remove them due to backward compatibility.

Quoted 14 lines
Quoted 14 lines

And an available_perf_events that show what can be written into these files, (similar to how set_ftrace_filter works). But for now, it was just easier to implement them as options.

As for the perf event that is triggered. It currently is a dynamic array of 64 bit values. Each value is broken up into 8 bits for what type of perf event it is, and 56 bits for the counter. It only writes a per CPU raw counter and does not do any math. That would be needed to be done by any post processing.

Since the values are for user space to do the subtraction to figure out the difference between events, for example, the function_graph tracer may have:

Quoted 7 lines
Quoted 4 lines
             is_vmalloc_addr() {
               /* cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }  

Just a style question: Would this mean the first line is for function entry and the second one is function return?

Yes.

Perhaps we could add field to the perf event to allow for annotation, so the above could look like:

              is_vmalloc_addr() {
               /* --> cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* <-- cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }  

Or something similar?

Quoted 10 lines
Quoted 7 lines

The next question is how to label the perf events to be in the 8 bit portion. It could simply be a value that is registered, and listed in the available_perf_events file.

cpu_cycles:1
cach_misses:2
[..]

Looks good to me. I think pre-definied events of perf list will be there and have fixed numbers.

Thanks for looking at this,

-- Steve

#12 lore raw
Quoted 18 lines · On Mon, 17 Nov 2025 22:42:27 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 8 lines
Quoted 7 lines

As this will eventual work with many more perf events than just cache-misses and cpu-cycles , using options is not appropriate. Especially since the options are limited to a 64 bit bitmask, and that can easily go much higher. I'm thinking about having a file instead that will act as a way to enable perf events for events, function and function graph tracing.

set_event_perf, set_ftrace_perf, set_fgraph_perf

Quoted 7 lines

What about adding a global trigger action file so that user can add these "perf" actions to write into it. It is something like stacktrace for events. (Maybe we can move stacktrace/user-stacktrace into it too)

For pre-defined/software counters:

# echo "perf:cpu_cycles" >> /sys/kernel/tracing/trigger

For events, it would make more sense to put it into the events directory:

Quoted 7 lines
 # echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/trigger

As there is already a events/enable

Heck we could even add it per system:

 # echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/syscalls/trigger

Yes, this will be very useful!

Quoted 9 lines
Quoted 6 lines

For some hardware event sources (see /sys/bus/event_source/devices/):

# echo "perf:cstate_core.c3-residency" >> /sys/kernel/tracing/trigger

echo "perf:my_counter=pmu/config=M,config1=N" >> /sys/kernel/tracing/trigger

Still need a way to add an identifier list. Currently, if the size of the type identifier is one byte, then it can only support up to 256 events.

Yes, so if user adds more than that, it will return -ENOSPC.

Quoted 3 lines

Do we need every event for this? Or just have a subset of events that would be supported?

For the event tracing, maybe those are used as measuring delta between paired events. For such use case, user may want to set it only on those events.

Quoted 7 lines
Quoted 5 lines

If we need to set those counters for tracers and events separately, we can add events/trigger and tracer-trigger files.

As I mentioned, the trigger for events should be in the events directory.

Agreed.

Quoted 3 lines

We could add a ftrace_trigger that can affect both function and function graph tracer.

Got it.

Quoted 15 lines
Quoted 7 lines

echo "perf:cpu_cycles" >> /sys/kernel/tracing/events/trigger

To disable counters, we can use '!' as same as event triggers.

echo !perf:cpu_cycles > trigger

Yes, it would follow the current way to disable a trigger.

Quoted 5 lines

To add more than 2 counters, connect it with ':'. (or, we will allow to append new perf counters) This allows user to set perf counter options for each events.

Quoted 5 lines
Quoted 2 lines

Maybe we also should move 'stacktrace'/'userstacktrace' option flags to it too eventually.

We can add them, but may never be able to remove them due to backward compatibility.

Ah, indeed.

Quoted 28 lines
Quoted 15 lines
Quoted 15 lines

And an available_perf_events that show what can be written into these files, (similar to how set_ftrace_filter works). But for now, it was just easier to implement them as options.

As for the perf event that is triggered. It currently is a dynamic array of 64 bit values. Each value is broken up into 8 bits for what type of perf event it is, and 56 bits for the counter. It only writes a per CPU raw counter and does not do any math. That would be needed to be done by any post processing.

Since the values are for user space to do the subtraction to figure out the difference between events, for example, the function_graph tracer may have:

Quoted 7 lines
Quoted 4 lines
             is_vmalloc_addr() {
               /* cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }  

Just a style question: Would this mean the first line is for function entry and the second one is function return?

Yes.

Perhaps we could add field to the perf event to allow for annotation, so the above could look like:

Quoted 6 lines
              is_vmalloc_addr() {
               /* --> cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* <-- cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }  

Or something similar?

Yeah, it looks more readable.

Thank you!

Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
maintainer
#13 lore raw
Quoted 6 lines · On Tue, 18 Nov 2025 17:11:47 +0900 Masami Hiramatsu (Google)…
Quoted 4 lines
Quoted 1 line

echo "perf:my_counter=pmu/config=M,config1=N" >> /sys/kernel/tracing/trigger

Still need a way to add an identifier list. Currently, if the size of the type identifier is one byte, then it can only support up to 256 events.

Yes, so if user adds more than that, it will return -ENOSPC.

The issue is that the ids are defined by what is possible, not by what the user enables.

-- Steve

maintainer
#14 lore raw
Quoted 4 lines · On Tue, 18 Nov 2025 08:53:24 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 1 line

Yes, so if user adds more than that, it will return -ENOSPC.

The issue is that the ids are defined by what is possible, not by what the user enables.

Now we could take 4 more bits from the mask and bring the raw value down to just 52 bits. 2^51 at 4GHz is still 6 days. Which is plenty more than required.

This will make the id 12 bits, or 4096 different defined events.

-- Steve

maintainer
#15 lore raw
In reply to Masami Hiramatsu (Google)
Quoted 11 lines · On Tue, 18 Nov 2025 17:11:47 +0900 Masami Hiramatsu (Google)…
Quoted 4 lines
Quoted 2 lines

If we need to set those counters for tracers and events separately, we can add events/trigger and tracer-trigger files.

As I mentioned, the trigger for events should be in the events directory.

Agreed.

Quoted 3 lines

We could add a ftrace_trigger that can affect both function and function graph tracer.

Actually, I should add "trigger" files in the ftrace events:

events/ftrace/function/trigger
events/ftrace/funcgraph_entry/tigger
events/ftrace/funcgraph_exit/tigger

Hmm,

-- Steve

#16 lore raw
In reply to Steven Rostedt

Hi Steve,

Quoted 8 lines · Steven Rostedt

This series adds a perf event to the ftrace ring buffer.
It is currently a proof of concept as I'm not happy with the interface and I also think the recorded perf event format may be changed too.

This proof-of-concept interface (which I have no plans on using), currently just adds 6 new trace options.

Quoted 6 lines

event_cache_misses
event_cpu_cycles
func-cache-misses
func-cpu-cycles
funcgraph-cache-misses
funcgraph-cpu-cycles

Unfortunately the hardware cache event is ambiguous on which level it refers to and architectures define it differently. There are encodings to clearly define the cache levels and accesses but the support depends on the hardware capabilities.

Quoted 17 lines

The first two trigger a perf event after every event, the second two trigger a perf event after every function and the last two trigger a perf event right after the start of a function and again at the end of the function.

As this will eventual work with many more perf events than just cache-misses and cpu-cycles , using options is not appropriate. Especially since the options are limited to a 64 bit bitmask, and that can easily go much higher. I'm thinking about having a file instead that will act as a way to enable perf events for events, function and function graph tracing.

set_event_perf, set_ftrace_perf, set_fgraph_perf

And an available_perf_events that show what can be written into these files, (similar to how set_ftrace_filter works). But for now, it was just easier to implement them as options.

Quoted 5 lines

As for the perf event that is triggered. It currently is a dynamic array of 64 bit values. Each value is broken up into 8 bits for what type of perf event it is, and 56 bits for the counter. It only writes a per CPU raw counter and does not do any math. That would be needed to be done by any post processing.

If you want to keep the perf events per CPU, you may consider CPU migrations for the func-graph case. Otherwise userspace may not calculate the diff from the begining correctly.

Quoted 65 lines

Since the values are for user space to do the subtraction to figure out the difference between events, for example, the function_graph tracer may have:

             is_vmalloc_addr() {
               /* cpu_cycles: 5582263593 cache_misses: 2869004572 */
               /* cpu_cycles: 5582267527 cache_misses: 2869006049 */
             }

User space would subtract 2869006049 - 2869004572 = 1477

Then 56 bits should be plenty.

2^55 / 1,000,000,000 / 60 / 60 / 24 = 416
416 / 4 = 104

If you have a 4GHz machine, the cpu-cycles will overflow the 55 bits in 104 days. This tooling is not for seeing how many cycles run over 104 days. User space tooling would just need to be aware that the vale is 56 bits and when calculating the difference between start and end do something like:

  if (start > end)
      end |= 1ULL << 56;

  delta = end - start;

The next question is how to label the perf events to be in the 8 bit portion. It could simply be a value that is registered, and listed in the available_perf_events file.

cpu_cycles:1
cach_misses:2
[..]

And this would need to be recorded by any tooling reading the events so that it knows how to map the events with their attached ids.

But again, this is just a proof-of-concept. How this will eventually be implemented is yet to be determined.

But to test these patches (which are based on top of my linux-next branch, which should now be in linux-next):

  # cd /sys/kernel/tracing
  # echo 1 > options/event_cpu_cycles
  # echo 1 > options/event_cache_misses
  # echo 1 > events/syscalls/enable
  # cat trace
[..]
            bash-995     [007] .....    98.255252: sys_write -> 0x2
            bash-995     [007] .....    98.255257: cpu_cycles: 1557241774 cache_misses: 449901166
            bash-995     [007] .....    98.255284: sys_dup2(oldfd: 0xa, newfd: 1)
            bash-995     [007] .....    98.255285: cpu_cycles: 1557260057 cache_misses: 449902679
            bash-995     [007] .....    98.255305: sys_dup2 -> 0x1
            bash-995     [007] .....    98.255305: cpu_cycles: 1557280203 cache_misses: 449906196
            bash-995     [007] .....    98.255343: sys_fcntl(fd: 0xa, cmd: 1, arg: 0)
            bash-995     [007] .....    98.255344: cpu_cycles: 1557322304 cache_misses: 449915522
            bash-995     [007] .....    98.255352: sys_fcntl -> 0x1
            bash-995     [007] .....    98.255353: cpu_cycles: 1557327809 cache_misses: 449916844
            bash-995     [007] .....    98.255361: sys_close(fd: 0xa)
            bash-995     [007] .....    98.255362: cpu_cycles: 1557335383 cache_misses: 449918232
            bash-995     [007] .....    98.255369: sys_close -> 0x0
Quoted 1 line

Comments welcomed.

Just FYI, I did the similar thing (like fgraph case) in uftrace and I grouped two related events to produce a metric.

  $ uftrace -T a@read=pmu-cycle ~/tmp/abc
  # DURATION     TID      FUNCTION
              [ 521741] | main() {
              [ 521741] |   a() {
              [ 521741] |     /* read:pmu-cycle (cycles=482 instructions=38) */
              [ 521741] |     b() {
              [ 521741] |       c() {
     0.659 us [ 521741] |         getpid();
     1.600 us [ 521741] |       } /* c */
     1.780 us [ 521741] |     } /* b */
              [ 521741] |     /* diff:pmu-cycle (cycles=+7361 instructions=+3955 IPC=0.54) */
    24.485 us [ 521741] |   } /* a */
    34.797 us [ 521741] | } /* main */

It reads cycles and instructions events (specified by 'pmu-cycle') at entry and exit of the given function ('a') and shows the diff with the metric IPC.

Thanks,
Namhyung

Quoted 17 lines

Steven Rostedt (3):
tracing: Add perf events
ftrace: Add perf counters to function tracing
fgraph: Add perf counters to function graph tracer

----

 include/linux/trace_recursion.h      |   5 +-
 kernel/trace/trace.c                 | 153 ++++++++++++++++++++++++++++++++-
 kernel/trace/trace.h                 |  38 ++++++++
 kernel/trace/trace_entries.h         |  13 +++
 kernel/trace/trace_event_perf.c      | 162 +++++++++++++++++++++++++++++++++++
 kernel/trace/trace_functions.c       | 124 +++++++++++++++++++++++++--
 kernel/trace/trace_functions_graph.c | 117 +++++++++++++++++++++++--
 kernel/trace/trace_output.c          |  70 +++++++++++++++
 8 files changed, 670 insertions(+), 12 deletions(-)
maintainer
#17 lore raw
Quoted 9 lines · On Mon, 17 Nov 2025 23:25:56 -0800 Namhyung Kim <[email protected]> wrote:
Quoted 5 lines

As for the perf event that is triggered. It currently is a dynamic array of 64 bit values. Each value is broken up into 8 bits for what type of perf event it is, and 56 bits for the counter. It only writes a per CPU raw counter and does not do any math. That would be needed to be done by any post processing.

If you want to keep the perf events per CPU, you may consider CPU migrations for the func-graph case. Otherwise userspace may not calculate the diff from the begining correctly.

That's easily solved by the user space too adding a sched_switch perf event trigger. ;-)

Quoted 18 lines

Just FYI, I did the similar thing (like fgraph case) in uftrace and I grouped two related events to produce a metric.

  $ uftrace -T a@read=pmu-cycle ~/tmp/abc
  # DURATION     TID      FUNCTION
              [ 521741] | main() {
              [ 521741] |   a() {
              [ 521741] |     /* read:pmu-cycle (cycles=482 instructions=38) */
              [ 521741] |     b() {
              [ 521741] |       c() {
     0.659 us [ 521741] |         getpid();
     1.600 us [ 521741] |       } /* c */
     1.780 us [ 521741] |     } /* b */
              [ 521741] |     /* diff:pmu-cycle (cycles=+7361 instructions=+3955 IPC=0.54) */
    24.485 us [ 521741] |   } /* a */
    34.797 us [ 521741] | } /* main */
Quoted 3 lines

It reads cycles and instructions events (specified by 'pmu-cycle') at entry and exit of the given function ('a') and shows the diff with the metric IPC.

I originally tried to implement this, but then it became more complex than I wanted in the kernel. As then I need to add a hook in the sched_switch and record the perf event counter there, and keep track of it for every task. That would require memory to be saved somewhere. I started adding it to the function graph shadow stack and then just decided that it would be so much easier to let user space figure it out.

By running function graph tracer and showing the start and end counters, as well as the counters at the sched_switch trace event, user space could do all the math and accounting, and the code in the kernel can remain simple.

-- Steve