* [PATCH 1/4] perf timechart: Don't pass @event to cat_backtrace()
2026-06-04 19:08 [PATCH 0/4] perf timechart: Small optimization for backtrace Namhyung Kim
@ 2026-06-04 19:08 ` Namhyung Kim
2026-06-04 19:08 ` [PATCH 2/4] perf timechart: Generate backtrace only if needed Namhyung Kim
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Namhyung Kim @ 2026-06-04 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark, Peter Zijlstra,
Ingo Molnar, LKML, linux-perf-users
The cat_backtrace() is only called from process_sample_event() which
means the event type is always PERF_RECORD_SAMPLE. We don't need to
pass the event just because to print already known info.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-timechart.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 85a9ad0455aecccd..dafd361ecf9d8cd7 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -493,8 +493,7 @@ static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
* Returns a malloc'd backtrace string built via open_memstream, or NULL
* on error. Caller must free() the returned pointer.
*/
-static char *cat_backtrace(union perf_event *event,
- struct perf_sample *sample,
+static char *cat_backtrace(struct perf_sample *sample,
struct machine *machine)
{
struct addr_location al;
@@ -516,9 +515,8 @@ static char *cat_backtrace(union perf_event *event,
goto exit;
if (machine__resolve(machine, &al, sample) < 0) {
- pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
- perf_event__name(event->header.type), event->header.type,
- sample->file_offset);
+ pr_err("problem processing SAMPLE (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ PERF_RECORD_SAMPLE, sample->file_offset);
goto exit;
}
@@ -578,7 +576,7 @@ typedef int (*tracepoint_handler)(struct timechart *tchart,
const char *backtrace);
static int process_sample_event(const struct perf_tool *tool,
- union perf_event *event,
+ union perf_event *event __maybe_unused,
struct perf_sample *sample,
struct machine *machine)
{
@@ -595,7 +593,7 @@ static int process_sample_event(const struct perf_tool *tool,
if (evsel->handler != NULL) {
tracepoint_handler f = evsel->handler;
- char *backtrace = cat_backtrace(event, sample, machine);
+ char *backtrace = cat_backtrace(sample, machine);
ret = f(tchart, sample, backtrace);
free(backtrace);
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/4] perf timechart: Generate backtrace only if needed
2026-06-04 19:08 [PATCH 0/4] perf timechart: Small optimization for backtrace Namhyung Kim
2026-06-04 19:08 ` [PATCH 1/4] perf timechart: Don't pass @event to cat_backtrace() Namhyung Kim
@ 2026-06-04 19:08 ` Namhyung Kim
2026-06-04 19:08 ` [PATCH 3/4] perf timechart: Remove unused backtrace in trace_handler Namhyung Kim
2026-06-04 19:08 ` [PATCH 4/4] perf timechart: Remove unnecessary copy of backtrace Namhyung Kim
3 siblings, 0 replies; 8+ messages in thread
From: Namhyung Kim @ 2026-06-04 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark, Peter Zijlstra,
Ingo Molnar, LKML, linux-perf-users
The backtrace was used by sched-switch and sched-wakeup only. No need
to call cat_backtrace() in the process_sample_event(). Let's pass NULL
backtrace and generate it from the sched events.
As it needs a pointer to the 'machine', let's save the session in the
timechart struct and use the host machine of the session instead of
passing the pointer to all handlers. It should be fine to assume the
host machine as timechart command doesn't deal with guest machines and
there's no way to get tracepoints from the guest events.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-timechart.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index dafd361ecf9d8cd7..337ae547d008ea0b 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -56,6 +56,7 @@ struct timechart {
struct per_pid *all_data;
struct power_event *power_events;
struct wake_event *wake_events;
+ struct perf_session *session;
int proc_num;
unsigned int numcpus;
u64 min_freq, /* Lowest CPU frequency seen */
@@ -578,7 +579,7 @@ typedef int (*tracepoint_handler)(struct timechart *tchart,
static int process_sample_event(const struct perf_tool *tool,
union perf_event *event __maybe_unused,
struct perf_sample *sample,
- struct machine *machine)
+ struct machine *machine __maybe_unused)
{
struct timechart *tchart = container_of(tool, struct timechart, tool);
struct evsel *evsel = sample->evsel;
@@ -593,10 +594,8 @@ static int process_sample_event(const struct perf_tool *tool,
if (evsel->handler != NULL) {
tracepoint_handler f = evsel->handler;
- char *backtrace = cat_backtrace(sample, machine);
- ret = f(tchart, sample, backtrace);
- free(backtrace);
+ ret = f(tchart, sample, NULL);
}
return ret;
@@ -656,7 +655,10 @@ process_sample_sched_wakeup(struct timechart *tchart,
sample->file_offset, sample->cpu);
return -1;
}
+
+ backtrace = cat_backtrace(sample, &tchart->session->machines.host);
sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
+ free((char *)backtrace);
return 0;
}
@@ -675,8 +677,11 @@ process_sample_sched_switch(struct timechart *tchart,
sample->file_offset, sample->cpu);
return -1;
}
+
+ backtrace = cat_backtrace(sample, &tchart->session->machines.host);
sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
prev_state, backtrace);
+ free((char *)backtrace);
return 0;
}
@@ -1661,6 +1666,7 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
if (IS_ERR(session))
return PTR_ERR(session);
+ tchart->session = session;
symbol__init(perf_session__env(session));
(void)perf_header__process_sections(&session->header,
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 3/4] perf timechart: Remove unused backtrace in trace_handler
2026-06-04 19:08 [PATCH 0/4] perf timechart: Small optimization for backtrace Namhyung Kim
2026-06-04 19:08 ` [PATCH 1/4] perf timechart: Don't pass @event to cat_backtrace() Namhyung Kim
2026-06-04 19:08 ` [PATCH 2/4] perf timechart: Generate backtrace only if needed Namhyung Kim
@ 2026-06-04 19:08 ` Namhyung Kim
2026-06-04 19:08 ` [PATCH 4/4] perf timechart: Remove unnecessary copy of backtrace Namhyung Kim
3 siblings, 0 replies; 8+ messages in thread
From: Namhyung Kim @ 2026-06-04 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark, Peter Zijlstra,
Ingo Molnar, LKML, linux-perf-users
Now the backtrace argument is not used in any handler. Let's get rid of
it.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-timechart.c | 64 ++++++++++++----------------------
1 file changed, 23 insertions(+), 41 deletions(-)
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 337ae547d008ea0b..4efac73a714c5e5f 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -573,8 +573,7 @@ static char *cat_backtrace(struct perf_sample *sample,
}
typedef int (*tracepoint_handler)(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace);
+ struct perf_sample *sample);
static int process_sample_event(const struct perf_tool *tool,
union perf_event *event __maybe_unused,
@@ -595,7 +594,7 @@ static int process_sample_event(const struct perf_tool *tool,
if (evsel->handler != NULL) {
tracepoint_handler f = evsel->handler;
- ret = f(tchart, sample, NULL);
+ ret = f(tchart, sample);
}
return ret;
@@ -603,8 +602,7 @@ static int process_sample_event(const struct perf_tool *tool,
static int
process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
u32 state = perf_sample__intval(sample, "state");
u32 cpu_id = perf_sample__intval(sample, "cpu_id");
@@ -624,8 +622,7 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
static int
process_sample_cpu_frequency(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
u32 state = perf_sample__intval(sample, "state");
u32 cpu_id = perf_sample__intval(sample, "cpu_id");
@@ -642,12 +639,12 @@ process_sample_cpu_frequency(struct timechart *tchart,
static int
process_sample_sched_wakeup(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace)
+ struct perf_sample *sample)
{
u8 flags = perf_sample__intval(sample, "common_flags");
int waker = perf_sample__intval(sample, "common_pid");
int wakee = perf_sample__intval(sample, "pid");
+ char *backtrace;
/* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
@@ -664,12 +661,12 @@ process_sample_sched_wakeup(struct timechart *tchart,
static int
process_sample_sched_switch(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace)
+ struct perf_sample *sample)
{
int prev_pid = perf_sample__intval(sample, "prev_pid");
int next_pid = perf_sample__intval(sample, "next_pid");
u64 prev_state = perf_sample__intval(sample, "prev_state");
+ char *backtrace;
/* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
@@ -688,8 +685,7 @@ process_sample_sched_switch(struct timechart *tchart,
#ifdef SUPPORT_OLD_POWER_EVENTS
static int
process_sample_power_start(struct timechart *tchart __maybe_unused,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
u64 cpu_id = perf_sample__intval(sample, "cpu_id");
u64 value = perf_sample__intval(sample, "value");
@@ -706,8 +702,7 @@ process_sample_power_start(struct timechart *tchart __maybe_unused,
static int
process_sample_power_end(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
/* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
@@ -721,8 +716,7 @@ process_sample_power_end(struct timechart *tchart,
static int
process_sample_power_frequency(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
u64 cpu_id = perf_sample__intval(sample, "cpu_id");
u64 value = perf_sample__intval(sample, "value");
@@ -895,8 +889,7 @@ static int pid_end_io_sample(struct timechart *tchart, int pid, int type,
static int
process_enter_read(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long fd = perf_sample__intval(sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ,
@@ -905,8 +898,7 @@ process_enter_read(struct timechart *tchart,
static int
process_exit_read(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long ret = perf_sample__intval(sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ,
@@ -915,8 +907,7 @@ process_exit_read(struct timechart *tchart,
static int
process_enter_write(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long fd = perf_sample__intval(sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE,
@@ -925,8 +916,7 @@ process_enter_write(struct timechart *tchart,
static int
process_exit_write(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long ret = perf_sample__intval(sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE,
@@ -935,8 +925,7 @@ process_exit_write(struct timechart *tchart,
static int
process_enter_sync(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long fd = perf_sample__intval(sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC,
@@ -945,8 +934,7 @@ process_enter_sync(struct timechart *tchart,
static int
process_exit_sync(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long ret = perf_sample__intval(sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC,
@@ -955,8 +943,7 @@ process_exit_sync(struct timechart *tchart,
static int
process_enter_tx(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long fd = perf_sample__intval(sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX,
@@ -965,8 +952,7 @@ process_enter_tx(struct timechart *tchart,
static int
process_exit_tx(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long ret = perf_sample__intval(sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX,
@@ -975,8 +961,7 @@ process_exit_tx(struct timechart *tchart,
static int
process_enter_rx(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long fd = perf_sample__intval(sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX,
@@ -985,8 +970,7 @@ process_enter_rx(struct timechart *tchart,
static int
process_exit_rx(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long ret = perf_sample__intval(sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX,
@@ -995,8 +979,7 @@ process_exit_rx(struct timechart *tchart,
static int
process_enter_poll(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long fd = perf_sample__intval(sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL,
@@ -1005,8 +988,7 @@ process_enter_poll(struct timechart *tchart,
static int
process_exit_poll(struct timechart *tchart,
- struct perf_sample *sample,
- const char *backtrace __maybe_unused)
+ struct perf_sample *sample)
{
long ret = perf_sample__intval(sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL,
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 4/4] perf timechart: Remove unnecessary copy of backtrace
2026-06-04 19:08 [PATCH 0/4] perf timechart: Small optimization for backtrace Namhyung Kim
` (2 preceding siblings ...)
2026-06-04 19:08 ` [PATCH 3/4] perf timechart: Remove unused backtrace in trace_handler Namhyung Kim
@ 2026-06-04 19:08 ` Namhyung Kim
2026-06-04 19:18 ` sashiko-bot
3 siblings, 1 reply; 8+ messages in thread
From: Namhyung Kim @ 2026-06-04 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark, Peter Zijlstra,
Ingo Molnar, LKML, linux-perf-users
The pattern of strdup() and free() is found, and I think it just can
use the original backtrace directly.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-timechart.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 4efac73a714c5e5f..2fd24d3aaa837743 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -300,7 +300,7 @@ static void pid_put_sample(struct timechart *tchart, int pid, int type,
sample->type = type;
sample->next = c->samples;
sample->cpu = cpu;
- sample->backtrace = backtrace ? strdup(backtrace) : NULL;
+ sample->backtrace = backtrace;
c->samples = sample;
if (sample->type == TYPE_RUNNING && end > start && start > 0) {
@@ -434,7 +434,7 @@ static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp,
we->time = timestamp;
we->waker = waker;
- we->backtrace = backtrace ? strdup(backtrace) : NULL;
+ we->backtrace = backtrace;
if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
we->waker = -1;
@@ -655,7 +655,6 @@ process_sample_sched_wakeup(struct timechart *tchart,
backtrace = cat_backtrace(sample, &tchart->session->machines.host);
sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
- free((char *)backtrace);
return 0;
}
@@ -678,7 +677,6 @@ process_sample_sched_switch(struct timechart *tchart,
backtrace = cat_backtrace(sample, &tchart->session->machines.host);
sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
prev_state, backtrace);
- free((char *)backtrace);
return 0;
}
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread