* [PATCHSET 0/8] perf sched: Fix various memory leaks
@ 2025-07-03 1:49 Namhyung Kim
2025-07-03 1:49 ` [PATCH 1/8] perf sched: Make sure it frees the usage string Namhyung Kim
` (9 more replies)
0 siblings, 10 replies; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
Hello,
While running the new perf sched test with ASAN, it fails due to
memory leaks. They comes mostly from missing thread__put() but
sometimes it needs to release other data structures.
Fix those leaks and add more subcommands to the test.
Thanks,
Namhyung
Namhyung Kim (8):
perf sched: Make sure it frees the usage string
perf sched: Free thread->priv using priv_destructor
perf sched: Fix memory leaks in 'perf sched map'
perf sched: Fix thread leaks in 'perf sched timehist'
perf sched: Fix memory leaks for evsel->priv in timehist
perf sched: Use RC_CHK_EQUAL() to compare pointers
perf sched: Fix memory leaks in 'perf sched latency'
perf test: Add more test cases to sched test
tools/perf/builtin-sched.c | 147 +++++++++++++++++++++++---------
tools/perf/tests/shell/sched.sh | 39 +++++++--
tools/perf/util/evsel.c | 11 +++
tools/perf/util/evsel.h | 2 +
4 files changed, 153 insertions(+), 46 deletions(-)
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/8] perf sched: Make sure it frees the usage string
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:04 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 2/8] perf sched: Free thread->priv using priv_destructor Namhyung Kim
` (8 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
The parse_options_subcommand() allocates the usage string based on the
given subcommands. So it should reach the end of the function to free
the string to prevent memory leaks.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 26ece6e9bfd167b3..b7bbfad0ed600eee 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -3902,9 +3902,9 @@ int cmd_sched(int argc, const char **argv)
* Aliased to 'perf script' for now:
*/
if (!strcmp(argv[0], "script")) {
- return cmd_script(argc, argv);
+ ret = cmd_script(argc, argv);
} else if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
- return __cmd_record(argc, argv);
+ ret = __cmd_record(argc, argv);
} else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) {
sched.tp_handler = &lat_ops;
if (argc > 1) {
@@ -3913,7 +3913,7 @@ int cmd_sched(int argc, const char **argv)
usage_with_options(latency_usage, latency_options);
}
setup_sorting(&sched, latency_options, latency_usage);
- return perf_sched__lat(&sched);
+ ret = perf_sched__lat(&sched);
} else if (!strcmp(argv[0], "map")) {
if (argc) {
argc = parse_options(argc, argv, map_options, map_usage, 0);
@@ -3924,13 +3924,14 @@ int cmd_sched(int argc, const char **argv)
sched.map.task_names = strlist__new(sched.map.task_name, NULL);
if (sched.map.task_names == NULL) {
fprintf(stderr, "Failed to parse task names\n");
- return -1;
+ ret = -1;
+ goto out;
}
}
}
sched.tp_handler = &map_ops;
setup_sorting(&sched, latency_options, latency_usage);
- return perf_sched__map(&sched);
+ ret = perf_sched__map(&sched);
} else if (strlen(argv[0]) > 2 && strstarts("replay", argv[0])) {
sched.tp_handler = &replay_ops;
if (argc) {
@@ -3938,7 +3939,7 @@ int cmd_sched(int argc, const char **argv)
if (argc)
usage_with_options(replay_usage, replay_options);
}
- return perf_sched__replay(&sched);
+ ret = perf_sched__replay(&sched);
} else if (!strcmp(argv[0], "timehist")) {
if (argc) {
argc = parse_options(argc, argv, timehist_options,
@@ -3954,19 +3955,19 @@ int cmd_sched(int argc, const char **argv)
parse_options_usage(NULL, timehist_options, "w", true);
if (sched.show_next)
parse_options_usage(NULL, timehist_options, "n", true);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
ret = symbol__validate_sym_arguments();
- if (ret)
- return ret;
-
- return perf_sched__timehist(&sched);
+ if (!ret)
+ ret = perf_sched__timehist(&sched);
} else {
usage_with_options(sched_usage, sched_options);
}
+out:
/* free usage string allocated by parse_options_subcommand */
free((void *)sched_usage[0]);
- return 0;
+ return ret;
}
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 2/8] perf sched: Free thread->priv using priv_destructor
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
2025-07-03 1:49 ` [PATCH 1/8] perf sched: Make sure it frees the usage string Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:05 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 3/8] perf sched: Fix memory leaks in 'perf sched map' Namhyung Kim
` (7 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
In many perf sched subcommand saves priv data structure in the thread
but it forgot to free them. As it's an opaque type with 'void *', it
needs to register that knows how to free the data. In this case, just
regular 'free()' is fine.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index b7bbfad0ed600eee..fa4052e040201105 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -3898,6 +3898,8 @@ int cmd_sched(int argc, const char **argv)
if (!argc)
usage_with_options(sched_usage, sched_options);
+ thread__set_priv_destructor(free);
+
/*
* Aliased to 'perf script' for now:
*/
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/8] perf sched: Fix memory leaks in 'perf sched map'
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
2025-07-03 1:49 ` [PATCH 1/8] perf sched: Make sure it frees the usage string Namhyung Kim
2025-07-03 1:49 ` [PATCH 2/8] perf sched: Free thread->priv using priv_destructor Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:06 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 4/8] perf sched: Fix thread leaks in 'perf sched timehist' Namhyung Kim
` (6 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
It maintains per-cpu pointers for the current thread but it doesn't
release the refcounts.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 31 ++++++++++++++++++++-----------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index fa4052e040201105..b73989fb6acef8d6 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1634,6 +1634,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
const char *color = PERF_COLOR_NORMAL;
char stimestamp[32];
const char *str;
+ int ret = -1;
BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0);
@@ -1664,17 +1665,20 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
sched_in = map__findnew_thread(sched, machine, -1, next_pid);
sched_out = map__findnew_thread(sched, machine, -1, prev_pid);
if (sched_in == NULL || sched_out == NULL)
- return -1;
+ goto out;
tr = thread__get_runtime(sched_in);
- if (tr == NULL) {
- thread__put(sched_in);
- return -1;
- }
+ if (tr == NULL)
+ goto out;
+
+ thread__put(sched->curr_thread[this_cpu.cpu]);
+ thread__put(sched->curr_out_thread[this_cpu.cpu]);
sched->curr_thread[this_cpu.cpu] = thread__get(sched_in);
sched->curr_out_thread[this_cpu.cpu] = thread__get(sched_out);
+ ret = 0;
+
str = thread__comm_str(sched_in);
new_shortname = 0;
if (!tr->shortname[0]) {
@@ -1769,12 +1773,10 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
color_fprintf(stdout, color, "\n");
out:
- if (sched->map.task_name)
- thread__put(sched_out);
-
+ thread__put(sched_out);
thread__put(sched_in);
- return 0;
+ return ret;
}
static int process_sched_switch_event(const struct perf_tool *tool,
@@ -3556,10 +3558,10 @@ static int perf_sched__map(struct perf_sched *sched)
sched->curr_out_thread = calloc(MAX_CPUS, sizeof(*(sched->curr_out_thread)));
if (!sched->curr_out_thread)
- return rc;
+ goto out_free_curr_thread;
if (setup_cpus_switch_event(sched))
- goto out_free_curr_thread;
+ goto out_free_curr_out_thread;
if (setup_map_cpus(sched))
goto out_free_cpus_switch_event;
@@ -3590,7 +3592,14 @@ static int perf_sched__map(struct perf_sched *sched)
out_free_cpus_switch_event:
free_cpus_switch_event(sched);
+out_free_curr_out_thread:
+ for (int i = 0; i < MAX_CPUS; i++)
+ thread__put(sched->curr_out_thread[i]);
+ zfree(&sched->curr_out_thread);
+
out_free_curr_thread:
+ for (int i = 0; i < MAX_CPUS; i++)
+ thread__put(sched->curr_thread[i]);
zfree(&sched->curr_thread);
return rc;
}
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 4/8] perf sched: Fix thread leaks in 'perf sched timehist'
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (2 preceding siblings ...)
2025-07-03 1:49 ` [PATCH 3/8] perf sched: Fix memory leaks in 'perf sched map' Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:08 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 5/8] perf sched: Fix memory leaks for evsel->priv in timehist Namhyung Kim
` (5 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
Add missing thread__put() after machine__findnew_thread() or
timehist_get_thread(). Also idle threads' last_thread should be
refcounted properly.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 48 +++++++++++++++++++++++++++++---------
1 file changed, 37 insertions(+), 11 deletions(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index b73989fb6acef8d6..83b5a85a91b7ffbd 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -2313,8 +2313,10 @@ static void save_task_callchain(struct perf_sched *sched,
return;
}
- if (!sched->show_callchain || sample->callchain == NULL)
+ if (!sched->show_callchain || sample->callchain == NULL) {
+ thread__put(thread);
return;
+ }
cursor = get_tls_callchain_cursor();
@@ -2323,10 +2325,12 @@ static void save_task_callchain(struct perf_sched *sched,
if (verbose > 0)
pr_err("Failed to resolve callchain. Skipping\n");
+ thread__put(thread);
return;
}
callchain_cursor_commit(cursor);
+ thread__put(thread);
while (true) {
struct callchain_cursor_node *node;
@@ -2403,8 +2407,17 @@ static void free_idle_threads(void)
return;
for (i = 0; i < idle_max_cpu; ++i) {
- if ((idle_threads[i]))
- thread__delete(idle_threads[i]);
+ struct thread *idle = idle_threads[i];
+
+ if (idle) {
+ struct idle_thread_runtime *itr;
+
+ itr = thread__priv(idle);
+ if (itr)
+ thread__put(itr->last_thread);
+
+ thread__delete(idle);
+ }
}
free(idle_threads);
@@ -2441,7 +2454,7 @@ static struct thread *get_idle_thread(int cpu)
}
}
- return idle_threads[cpu];
+ return thread__get(idle_threads[cpu]);
}
static void save_idle_callchain(struct perf_sched *sched,
@@ -2496,7 +2509,8 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
if (itr == NULL)
return NULL;
- itr->last_thread = thread;
+ thread__put(itr->last_thread);
+ itr->last_thread = thread__get(thread);
/* copy task callchain when entering to idle */
if (evsel__intval(evsel, sample, "next_pid") == 0)
@@ -2567,6 +2581,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
/* show wakeup unless both awakee and awaker are filtered */
if (timehist_skip_sample(sched, thread, evsel, sample) &&
timehist_skip_sample(sched, awakened, evsel, sample)) {
+ thread__put(thread);
return;
}
@@ -2583,6 +2598,8 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
printf("awakened: %s", timehist_get_commstr(awakened));
printf("\n");
+
+ thread__put(thread);
}
static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused,
@@ -2611,8 +2628,10 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool,
return -1;
tr = thread__get_runtime(thread);
- if (tr == NULL)
+ if (tr == NULL) {
+ thread__put(thread);
return -1;
+ }
if (tr->ready_to_run == 0)
tr->ready_to_run = sample->time;
@@ -2622,6 +2641,7 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool,
!perf_time__skip_sample(&sched->ptime, sample->time))
timehist_print_wakeup_event(sched, evsel, sample, machine, thread);
+ thread__put(thread);
return 0;
}
@@ -2649,6 +2669,7 @@ static void timehist_print_migration_event(struct perf_sched *sched,
if (timehist_skip_sample(sched, thread, evsel, sample) &&
timehist_skip_sample(sched, migrated, evsel, sample)) {
+ thread__put(thread);
return;
}
@@ -2676,6 +2697,7 @@ static void timehist_print_migration_event(struct perf_sched *sched,
printf(" cpu %d => %d", ocpu, dcpu);
printf("\n");
+ thread__put(thread);
}
static int timehist_migrate_task_event(const struct perf_tool *tool,
@@ -2695,8 +2717,10 @@ static int timehist_migrate_task_event(const struct perf_tool *tool,
return -1;
tr = thread__get_runtime(thread);
- if (tr == NULL)
+ if (tr == NULL) {
+ thread__put(thread);
return -1;
+ }
tr->migrations++;
tr->migrated = sample->time;
@@ -2706,6 +2730,7 @@ static int timehist_migrate_task_event(const struct perf_tool *tool,
timehist_print_migration_event(sched, evsel, sample,
machine, thread);
}
+ thread__put(thread);
return 0;
}
@@ -2728,10 +2753,10 @@ static void timehist_update_task_prio(struct evsel *evsel,
return;
tr = thread__get_runtime(thread);
- if (tr == NULL)
- return;
+ if (tr != NULL)
+ tr->prio = next_prio;
- tr->prio = next_prio;
+ thread__put(thread);
}
static int timehist_sched_change_event(const struct perf_tool *tool,
@@ -2743,7 +2768,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool,
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
struct perf_time_interval *ptime = &sched->ptime;
struct addr_location al;
- struct thread *thread;
+ struct thread *thread = NULL;
struct thread_runtime *tr = NULL;
u64 tprev, t = sample->time;
int rc = 0;
@@ -2867,6 +2892,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool,
evsel__save_time(evsel, sample->time, sample->cpu);
+ thread__put(thread);
addr_location__exit(&al);
return rc;
}
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 5/8] perf sched: Fix memory leaks for evsel->priv in timehist
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (3 preceding siblings ...)
2025-07-03 1:49 ` [PATCH 4/8] perf sched: Fix thread leaks in 'perf sched timehist' Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:09 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 6/8] perf sched: Use RC_CHK_EQUAL() to compare pointers Namhyung Kim
` (4 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
It uses evsel->priv to save per-cpu timing information. It should be
freed when the evsel is released.
Add the priv destructor for evsel same as thread to handle that.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 12 ++++++++++++
tools/perf/util/evsel.c | 11 +++++++++++
tools/perf/util/evsel.h | 2 ++
3 files changed, 25 insertions(+)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 83b5a85a91b7ffbd..a6eb0462dd5be20f 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -2020,6 +2020,16 @@ static u64 evsel__get_time(struct evsel *evsel, u32 cpu)
return r->last_time[cpu];
}
+static void timehist__evsel_priv_destructor(void *priv)
+{
+ struct evsel_runtime *r = priv;
+
+ if (r) {
+ free(r->last_time);
+ free(r);
+ }
+}
+
static int comm_width = 30;
static char *timehist_get_commstr(struct thread *thread)
@@ -3314,6 +3324,8 @@ static int perf_sched__timehist(struct perf_sched *sched)
setup_pager();
+ evsel__set_priv_destructor(timehist__evsel_priv_destructor);
+
/* prefer sched_waking if it is captured */
if (evlist__find_tracepoint_by_name(session->evlist, "sched:sched_waking"))
handlers[1].handler = timehist_sched_wakeup_ignore;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 9c50c3960487feee..3896a04d90af71f3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1657,6 +1657,15 @@ static void evsel__free_config_terms(struct evsel *evsel)
free_config_terms(&evsel->config_terms);
}
+static void (*evsel__priv_destructor)(void *priv);
+
+void evsel__set_priv_destructor(void (*destructor)(void *priv))
+{
+ assert(evsel__priv_destructor == NULL);
+
+ evsel__priv_destructor = destructor;
+}
+
void evsel__exit(struct evsel *evsel)
{
assert(list_empty(&evsel->core.node));
@@ -1687,6 +1696,8 @@ void evsel__exit(struct evsel *evsel)
hashmap__free(evsel->per_pkg_mask);
evsel->per_pkg_mask = NULL;
zfree(&evsel->metric_events);
+ if (evsel__priv_destructor)
+ evsel__priv_destructor(evsel->priv);
perf_evsel__object.fini(evsel);
if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME ||
evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME)
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 6dbc9690e0c9258c..b84ee274602d7d2b 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -280,6 +280,8 @@ void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx);
void evsel__exit(struct evsel *evsel);
void evsel__delete(struct evsel *evsel);
+void evsel__set_priv_destructor(void (*destructor)(void *priv));
+
struct callchain_param;
void evsel__config(struct evsel *evsel, struct record_opts *opts,
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 6/8] perf sched: Use RC_CHK_EQUAL() to compare pointers
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (4 preceding siblings ...)
2025-07-03 1:49 ` [PATCH 5/8] perf sched: Fix memory leaks for evsel->priv in timehist Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:10 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 7/8] perf sched: Fix memory leaks in 'perf sched latency' Namhyung Kim
` (3 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
So that it can check two pointers to the same object properly when
REFCNT_CHECKING is on.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index a6eb0462dd5be20f..087d4eaba5f7160d 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -994,7 +994,7 @@ thread_atoms_search(struct rb_root_cached *root, struct thread *thread,
else if (cmp < 0)
node = node->rb_right;
else {
- BUG_ON(thread != atoms->thread);
+ BUG_ON(!RC_CHK_EQUAL(thread, atoms->thread));
return atoms;
}
}
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 7/8] perf sched: Fix memory leaks in 'perf sched latency'
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (5 preceding siblings ...)
2025-07-03 1:49 ` [PATCH 6/8] perf sched: Use RC_CHK_EQUAL() to compare pointers Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:10 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 8/8] perf test: Add more test cases to sched test Namhyung Kim
` (2 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
The work_atoms should be freed after use. Add free_work_atoms() to
make sure to release all. It should use list_splice_init() when merging
atoms to prevent accessing invalid pointers.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-sched.c | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 087d4eaba5f7160d..4bbebd6ef2e4a791 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1111,6 +1111,21 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
atoms->nb_atoms++;
}
+static void free_work_atoms(struct work_atoms *atoms)
+{
+ struct work_atom *atom, *tmp;
+
+ if (atoms == NULL)
+ return;
+
+ list_for_each_entry_safe(atom, tmp, &atoms->work_list, list) {
+ list_del(&atom->list);
+ free(atom);
+ }
+ thread__zput(atoms->thread);
+ free(atoms);
+}
+
static int latency_switch_event(struct perf_sched *sched,
struct evsel *evsel,
struct perf_sample *sample,
@@ -3426,13 +3441,13 @@ static void __merge_work_atoms(struct rb_root_cached *root, struct work_atoms *d
this->total_runtime += data->total_runtime;
this->nb_atoms += data->nb_atoms;
this->total_lat += data->total_lat;
- list_splice(&data->work_list, &this->work_list);
+ list_splice_init(&data->work_list, &this->work_list);
if (this->max_lat < data->max_lat) {
this->max_lat = data->max_lat;
this->max_lat_start = data->max_lat_start;
this->max_lat_end = data->max_lat_end;
}
- zfree(&data);
+ free_work_atoms(data);
return;
}
}
@@ -3511,7 +3526,6 @@ static int perf_sched__lat(struct perf_sched *sched)
work_list = rb_entry(next, struct work_atoms, node);
output_lat_thread(sched, work_list);
next = rb_next(next);
- thread__zput(work_list->thread);
}
printf(" -----------------------------------------------------------------------------------------------------------------\n");
@@ -3525,6 +3539,13 @@ static int perf_sched__lat(struct perf_sched *sched)
rc = 0;
+ while ((next = rb_first_cached(&sched->sorted_atom_root))) {
+ struct work_atoms *data;
+
+ data = rb_entry(next, struct work_atoms, node);
+ rb_erase_cached(next, &sched->sorted_atom_root);
+ free_work_atoms(data);
+ }
out_free_cpus_switch_event:
free_cpus_switch_event(sched);
return rc;
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 8/8] perf test: Add more test cases to sched test
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (6 preceding siblings ...)
2025-07-03 1:49 ` [PATCH 7/8] perf sched: Fix memory leaks in 'perf sched latency' Namhyung Kim
@ 2025-07-03 1:49 ` Namhyung Kim
2025-07-03 3:11 ` Ian Rogers
2025-07-03 3:45 ` [PATCHSET 0/8] perf sched: Fix various memory leaks Ian Rogers
2025-07-04 17:55 ` Namhyung Kim
9 siblings, 1 reply; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 1:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
$ sudo ./perf test -vv 92
92: perf sched tests:
--- start ---
test child forked, pid 1360101
Sched record
pid 1360105's current affinity list: 0-3
pid 1360105's new affinity list: 0
pid 1360107's current affinity list: 0-3
pid 1360107's new affinity list: 0
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 4.330 MB /tmp/__perf_test_sched.perf.data.b3319 (12246 samples) ]
Sched latency
Sched script
Sched map
Sched timehist
Samples of sched_switch event do not have callchains.
---- end(0) ----
92: perf sched tests : Ok
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/tests/shell/sched.sh | 39 ++++++++++++++++++++++++++-------
1 file changed, 31 insertions(+), 8 deletions(-)
diff --git a/tools/perf/tests/shell/sched.sh b/tools/perf/tests/shell/sched.sh
index c030126d1a0c918d..b9b81eaf856e6555 100755
--- a/tools/perf/tests/shell/sched.sh
+++ b/tools/perf/tests/shell/sched.sh
@@ -56,38 +56,61 @@ cleanup_noploops() {
kill "$PID1" "$PID2"
}
-test_sched_latency() {
- echo "Sched latency"
+test_sched_record() {
+ echo "Sched record"
start_noploops
perf sched record --no-inherit -o "${perfdata}" sleep 1
+
+ cleanup_noploops
+}
+
+test_sched_latency() {
+ echo "Sched latency"
+
if ! perf sched latency -i "${perfdata}" | grep -q perf-noploop
then
echo "Sched latency [Failed missing output]"
err=1
fi
-
- cleanup_noploops
}
test_sched_script() {
echo "Sched script"
- start_noploops
-
- perf sched record --no-inherit -o "${perfdata}" sleep 1
if ! perf sched script -i "${perfdata}" | grep -q perf-noploop
then
echo "Sched script [Failed missing output]"
err=1
fi
+}
- cleanup_noploops
+test_sched_map() {
+ echo "Sched map"
+
+ if ! perf sched map -i "${perfdata}" | grep -q perf-noploop
+ then
+ echo "Sched map [Failed missing output]"
+ err=1
+ fi
+}
+
+test_sched_timehist() {
+ echo "Sched timehist"
+
+ if ! perf sched timehist -i "${perfdata}" | grep -q perf-noploop
+ then
+ echo "Sched timehist [Failed missing output]"
+ err=1
+ fi
}
+test_sched_record
test_sched_latency
test_sched_script
+test_sched_map
+test_sched_timehist
cleanup
exit $err
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 1/8] perf sched: Make sure it frees the usage string
2025-07-03 1:49 ` [PATCH 1/8] perf sched: Make sure it frees the usage string Namhyung Kim
@ 2025-07-03 3:04 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:04 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> The parse_options_subcommand() allocates the usage string based on the
> given subcommands. So it should reach the end of the function to free
> the string to prevent memory leaks.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 25 +++++++++++++------------
> 1 file changed, 13 insertions(+), 12 deletions(-)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index 26ece6e9bfd167b3..b7bbfad0ed600eee 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -3902,9 +3902,9 @@ int cmd_sched(int argc, const char **argv)
> * Aliased to 'perf script' for now:
> */
> if (!strcmp(argv[0], "script")) {
> - return cmd_script(argc, argv);
> + ret = cmd_script(argc, argv);
> } else if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
> - return __cmd_record(argc, argv);
> + ret = __cmd_record(argc, argv);
> } else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) {
> sched.tp_handler = &lat_ops;
> if (argc > 1) {
> @@ -3913,7 +3913,7 @@ int cmd_sched(int argc, const char **argv)
> usage_with_options(latency_usage, latency_options);
> }
> setup_sorting(&sched, latency_options, latency_usage);
> - return perf_sched__lat(&sched);
> + ret = perf_sched__lat(&sched);
> } else if (!strcmp(argv[0], "map")) {
> if (argc) {
> argc = parse_options(argc, argv, map_options, map_usage, 0);
> @@ -3924,13 +3924,14 @@ int cmd_sched(int argc, const char **argv)
> sched.map.task_names = strlist__new(sched.map.task_name, NULL);
> if (sched.map.task_names == NULL) {
> fprintf(stderr, "Failed to parse task names\n");
> - return -1;
> + ret = -1;
> + goto out;
> }
> }
> }
> sched.tp_handler = &map_ops;
> setup_sorting(&sched, latency_options, latency_usage);
> - return perf_sched__map(&sched);
> + ret = perf_sched__map(&sched);
> } else if (strlen(argv[0]) > 2 && strstarts("replay", argv[0])) {
> sched.tp_handler = &replay_ops;
> if (argc) {
> @@ -3938,7 +3939,7 @@ int cmd_sched(int argc, const char **argv)
> if (argc)
> usage_with_options(replay_usage, replay_options);
> }
> - return perf_sched__replay(&sched);
> + ret = perf_sched__replay(&sched);
> } else if (!strcmp(argv[0], "timehist")) {
> if (argc) {
> argc = parse_options(argc, argv, timehist_options,
> @@ -3954,19 +3955,19 @@ int cmd_sched(int argc, const char **argv)
> parse_options_usage(NULL, timehist_options, "w", true);
> if (sched.show_next)
> parse_options_usage(NULL, timehist_options, "n", true);
> - return -EINVAL;
> + ret = -EINVAL;
> + goto out;
> }
> ret = symbol__validate_sym_arguments();
> - if (ret)
> - return ret;
> -
> - return perf_sched__timehist(&sched);
> + if (!ret)
> + ret = perf_sched__timehist(&sched);
> } else {
> usage_with_options(sched_usage, sched_options);
> }
>
> +out:
> /* free usage string allocated by parse_options_subcommand */
> free((void *)sched_usage[0]);
>
> - return 0;
> + return ret;
> }
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/8] perf sched: Free thread->priv using priv_destructor
2025-07-03 1:49 ` [PATCH 2/8] perf sched: Free thread->priv using priv_destructor Namhyung Kim
@ 2025-07-03 3:05 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:05 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> In many perf sched subcommand saves priv data structure in the thread
> but it forgot to free them. As it's an opaque type with 'void *', it
> needs to register that knows how to free the data. In this case, just
> regular 'free()' is fine.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index b7bbfad0ed600eee..fa4052e040201105 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -3898,6 +3898,8 @@ int cmd_sched(int argc, const char **argv)
> if (!argc)
> usage_with_options(sched_usage, sched_options);
>
> + thread__set_priv_destructor(free);
> +
> /*
> * Aliased to 'perf script' for now:
> */
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/8] perf sched: Fix memory leaks in 'perf sched map'
2025-07-03 1:49 ` [PATCH 3/8] perf sched: Fix memory leaks in 'perf sched map' Namhyung Kim
@ 2025-07-03 3:06 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:06 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> It maintains per-cpu pointers for the current thread but it doesn't
> release the refcounts.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 31 ++++++++++++++++++++-----------
> 1 file changed, 20 insertions(+), 11 deletions(-)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index fa4052e040201105..b73989fb6acef8d6 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -1634,6 +1634,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
> const char *color = PERF_COLOR_NORMAL;
> char stimestamp[32];
> const char *str;
> + int ret = -1;
>
> BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0);
>
> @@ -1664,17 +1665,20 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
> sched_in = map__findnew_thread(sched, machine, -1, next_pid);
> sched_out = map__findnew_thread(sched, machine, -1, prev_pid);
> if (sched_in == NULL || sched_out == NULL)
> - return -1;
> + goto out;
>
> tr = thread__get_runtime(sched_in);
> - if (tr == NULL) {
> - thread__put(sched_in);
> - return -1;
> - }
> + if (tr == NULL)
> + goto out;
> +
> + thread__put(sched->curr_thread[this_cpu.cpu]);
> + thread__put(sched->curr_out_thread[this_cpu.cpu]);
>
> sched->curr_thread[this_cpu.cpu] = thread__get(sched_in);
> sched->curr_out_thread[this_cpu.cpu] = thread__get(sched_out);
>
> + ret = 0;
> +
> str = thread__comm_str(sched_in);
> new_shortname = 0;
> if (!tr->shortname[0]) {
> @@ -1769,12 +1773,10 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
> color_fprintf(stdout, color, "\n");
>
> out:
> - if (sched->map.task_name)
> - thread__put(sched_out);
> -
> + thread__put(sched_out);
> thread__put(sched_in);
>
> - return 0;
> + return ret;
> }
>
> static int process_sched_switch_event(const struct perf_tool *tool,
> @@ -3556,10 +3558,10 @@ static int perf_sched__map(struct perf_sched *sched)
>
> sched->curr_out_thread = calloc(MAX_CPUS, sizeof(*(sched->curr_out_thread)));
> if (!sched->curr_out_thread)
> - return rc;
> + goto out_free_curr_thread;
>
> if (setup_cpus_switch_event(sched))
> - goto out_free_curr_thread;
> + goto out_free_curr_out_thread;
>
> if (setup_map_cpus(sched))
> goto out_free_cpus_switch_event;
> @@ -3590,7 +3592,14 @@ static int perf_sched__map(struct perf_sched *sched)
> out_free_cpus_switch_event:
> free_cpus_switch_event(sched);
>
> +out_free_curr_out_thread:
> + for (int i = 0; i < MAX_CPUS; i++)
> + thread__put(sched->curr_out_thread[i]);
> + zfree(&sched->curr_out_thread);
> +
> out_free_curr_thread:
> + for (int i = 0; i < MAX_CPUS; i++)
> + thread__put(sched->curr_thread[i]);
> zfree(&sched->curr_thread);
> return rc;
> }
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 4/8] perf sched: Fix thread leaks in 'perf sched timehist'
2025-07-03 1:49 ` [PATCH 4/8] perf sched: Fix thread leaks in 'perf sched timehist' Namhyung Kim
@ 2025-07-03 3:08 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:08 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Add missing thread__put() after machine__findnew_thread() or
> timehist_get_thread(). Also idle threads' last_thread should be
> refcounted properly.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 48 +++++++++++++++++++++++++++++---------
> 1 file changed, 37 insertions(+), 11 deletions(-)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index b73989fb6acef8d6..83b5a85a91b7ffbd 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -2313,8 +2313,10 @@ static void save_task_callchain(struct perf_sched *sched,
> return;
> }
>
> - if (!sched->show_callchain || sample->callchain == NULL)
> + if (!sched->show_callchain || sample->callchain == NULL) {
> + thread__put(thread);
> return;
> + }
>
> cursor = get_tls_callchain_cursor();
>
> @@ -2323,10 +2325,12 @@ static void save_task_callchain(struct perf_sched *sched,
> if (verbose > 0)
> pr_err("Failed to resolve callchain. Skipping\n");
>
> + thread__put(thread);
> return;
> }
>
> callchain_cursor_commit(cursor);
> + thread__put(thread);
>
> while (true) {
> struct callchain_cursor_node *node;
> @@ -2403,8 +2407,17 @@ static void free_idle_threads(void)
> return;
>
> for (i = 0; i < idle_max_cpu; ++i) {
> - if ((idle_threads[i]))
> - thread__delete(idle_threads[i]);
> + struct thread *idle = idle_threads[i];
> +
> + if (idle) {
> + struct idle_thread_runtime *itr;
> +
> + itr = thread__priv(idle);
> + if (itr)
> + thread__put(itr->last_thread);
> +
> + thread__delete(idle);
> + }
> }
>
> free(idle_threads);
> @@ -2441,7 +2454,7 @@ static struct thread *get_idle_thread(int cpu)
> }
> }
>
> - return idle_threads[cpu];
> + return thread__get(idle_threads[cpu]);
> }
>
> static void save_idle_callchain(struct perf_sched *sched,
> @@ -2496,7 +2509,8 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
> if (itr == NULL)
> return NULL;
>
> - itr->last_thread = thread;
> + thread__put(itr->last_thread);
> + itr->last_thread = thread__get(thread);
>
> /* copy task callchain when entering to idle */
> if (evsel__intval(evsel, sample, "next_pid") == 0)
> @@ -2567,6 +2581,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
> /* show wakeup unless both awakee and awaker are filtered */
> if (timehist_skip_sample(sched, thread, evsel, sample) &&
> timehist_skip_sample(sched, awakened, evsel, sample)) {
> + thread__put(thread);
> return;
> }
>
> @@ -2583,6 +2598,8 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
> printf("awakened: %s", timehist_get_commstr(awakened));
>
> printf("\n");
> +
> + thread__put(thread);
> }
>
> static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused,
> @@ -2611,8 +2628,10 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool,
> return -1;
>
> tr = thread__get_runtime(thread);
> - if (tr == NULL)
> + if (tr == NULL) {
> + thread__put(thread);
> return -1;
> + }
>
> if (tr->ready_to_run == 0)
> tr->ready_to_run = sample->time;
> @@ -2622,6 +2641,7 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool,
> !perf_time__skip_sample(&sched->ptime, sample->time))
> timehist_print_wakeup_event(sched, evsel, sample, machine, thread);
>
> + thread__put(thread);
> return 0;
> }
>
> @@ -2649,6 +2669,7 @@ static void timehist_print_migration_event(struct perf_sched *sched,
>
> if (timehist_skip_sample(sched, thread, evsel, sample) &&
> timehist_skip_sample(sched, migrated, evsel, sample)) {
> + thread__put(thread);
> return;
> }
>
> @@ -2676,6 +2697,7 @@ static void timehist_print_migration_event(struct perf_sched *sched,
> printf(" cpu %d => %d", ocpu, dcpu);
>
> printf("\n");
> + thread__put(thread);
> }
>
> static int timehist_migrate_task_event(const struct perf_tool *tool,
> @@ -2695,8 +2717,10 @@ static int timehist_migrate_task_event(const struct perf_tool *tool,
> return -1;
>
> tr = thread__get_runtime(thread);
> - if (tr == NULL)
> + if (tr == NULL) {
> + thread__put(thread);
> return -1;
> + }
>
> tr->migrations++;
> tr->migrated = sample->time;
> @@ -2706,6 +2730,7 @@ static int timehist_migrate_task_event(const struct perf_tool *tool,
> timehist_print_migration_event(sched, evsel, sample,
> machine, thread);
> }
> + thread__put(thread);
>
> return 0;
> }
> @@ -2728,10 +2753,10 @@ static void timehist_update_task_prio(struct evsel *evsel,
> return;
>
> tr = thread__get_runtime(thread);
> - if (tr == NULL)
> - return;
> + if (tr != NULL)
> + tr->prio = next_prio;
>
> - tr->prio = next_prio;
> + thread__put(thread);
> }
>
> static int timehist_sched_change_event(const struct perf_tool *tool,
> @@ -2743,7 +2768,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool,
> struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
> struct perf_time_interval *ptime = &sched->ptime;
> struct addr_location al;
> - struct thread *thread;
> + struct thread *thread = NULL;
> struct thread_runtime *tr = NULL;
> u64 tprev, t = sample->time;
> int rc = 0;
> @@ -2867,6 +2892,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool,
>
> evsel__save_time(evsel, sample->time, sample->cpu);
>
> + thread__put(thread);
> addr_location__exit(&al);
> return rc;
> }
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 5/8] perf sched: Fix memory leaks for evsel->priv in timehist
2025-07-03 1:49 ` [PATCH 5/8] perf sched: Fix memory leaks for evsel->priv in timehist Namhyung Kim
@ 2025-07-03 3:09 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:09 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> It uses evsel->priv to save per-cpu timing information. It should be
> freed when the evsel is released.
>
> Add the priv destructor for evsel same as thread to handle that.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 12 ++++++++++++
> tools/perf/util/evsel.c | 11 +++++++++++
> tools/perf/util/evsel.h | 2 ++
> 3 files changed, 25 insertions(+)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index 83b5a85a91b7ffbd..a6eb0462dd5be20f 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -2020,6 +2020,16 @@ static u64 evsel__get_time(struct evsel *evsel, u32 cpu)
> return r->last_time[cpu];
> }
>
> +static void timehist__evsel_priv_destructor(void *priv)
> +{
> + struct evsel_runtime *r = priv;
> +
> + if (r) {
> + free(r->last_time);
> + free(r);
> + }
> +}
> +
> static int comm_width = 30;
>
> static char *timehist_get_commstr(struct thread *thread)
> @@ -3314,6 +3324,8 @@ static int perf_sched__timehist(struct perf_sched *sched)
>
> setup_pager();
>
> + evsel__set_priv_destructor(timehist__evsel_priv_destructor);
> +
> /* prefer sched_waking if it is captured */
> if (evlist__find_tracepoint_by_name(session->evlist, "sched:sched_waking"))
> handlers[1].handler = timehist_sched_wakeup_ignore;
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index 9c50c3960487feee..3896a04d90af71f3 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -1657,6 +1657,15 @@ static void evsel__free_config_terms(struct evsel *evsel)
> free_config_terms(&evsel->config_terms);
> }
>
> +static void (*evsel__priv_destructor)(void *priv);
> +
> +void evsel__set_priv_destructor(void (*destructor)(void *priv))
> +{
> + assert(evsel__priv_destructor == NULL);
> +
> + evsel__priv_destructor = destructor;
> +}
> +
> void evsel__exit(struct evsel *evsel)
> {
> assert(list_empty(&evsel->core.node));
> @@ -1687,6 +1696,8 @@ void evsel__exit(struct evsel *evsel)
> hashmap__free(evsel->per_pkg_mask);
> evsel->per_pkg_mask = NULL;
> zfree(&evsel->metric_events);
> + if (evsel__priv_destructor)
> + evsel__priv_destructor(evsel->priv);
> perf_evsel__object.fini(evsel);
> if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME ||
> evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME)
> diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
> index 6dbc9690e0c9258c..b84ee274602d7d2b 100644
> --- a/tools/perf/util/evsel.h
> +++ b/tools/perf/util/evsel.h
> @@ -280,6 +280,8 @@ void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx);
> void evsel__exit(struct evsel *evsel);
> void evsel__delete(struct evsel *evsel);
>
> +void evsel__set_priv_destructor(void (*destructor)(void *priv));
> +
> struct callchain_param;
>
> void evsel__config(struct evsel *evsel, struct record_opts *opts,
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 6/8] perf sched: Use RC_CHK_EQUAL() to compare pointers
2025-07-03 1:49 ` [PATCH 6/8] perf sched: Use RC_CHK_EQUAL() to compare pointers Namhyung Kim
@ 2025-07-03 3:10 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:10 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> So that it can check two pointers to the same object properly when
> REFCNT_CHECKING is on.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index a6eb0462dd5be20f..087d4eaba5f7160d 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -994,7 +994,7 @@ thread_atoms_search(struct rb_root_cached *root, struct thread *thread,
> else if (cmp < 0)
> node = node->rb_right;
> else {
> - BUG_ON(thread != atoms->thread);
> + BUG_ON(!RC_CHK_EQUAL(thread, atoms->thread));
> return atoms;
> }
> }
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 7/8] perf sched: Fix memory leaks in 'perf sched latency'
2025-07-03 1:49 ` [PATCH 7/8] perf sched: Fix memory leaks in 'perf sched latency' Namhyung Kim
@ 2025-07-03 3:10 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:10 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> The work_atoms should be freed after use. Add free_work_atoms() to
> make sure to release all. It should use list_splice_init() when merging
> atoms to prevent accessing invalid pointers.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-sched.c | 27 ++++++++++++++++++++++++---
> 1 file changed, 24 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index 087d4eaba5f7160d..4bbebd6ef2e4a791 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -1111,6 +1111,21 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
> atoms->nb_atoms++;
> }
>
> +static void free_work_atoms(struct work_atoms *atoms)
> +{
> + struct work_atom *atom, *tmp;
> +
> + if (atoms == NULL)
> + return;
> +
> + list_for_each_entry_safe(atom, tmp, &atoms->work_list, list) {
> + list_del(&atom->list);
> + free(atom);
> + }
> + thread__zput(atoms->thread);
> + free(atoms);
> +}
> +
> static int latency_switch_event(struct perf_sched *sched,
> struct evsel *evsel,
> struct perf_sample *sample,
> @@ -3426,13 +3441,13 @@ static void __merge_work_atoms(struct rb_root_cached *root, struct work_atoms *d
> this->total_runtime += data->total_runtime;
> this->nb_atoms += data->nb_atoms;
> this->total_lat += data->total_lat;
> - list_splice(&data->work_list, &this->work_list);
> + list_splice_init(&data->work_list, &this->work_list);
> if (this->max_lat < data->max_lat) {
> this->max_lat = data->max_lat;
> this->max_lat_start = data->max_lat_start;
> this->max_lat_end = data->max_lat_end;
> }
> - zfree(&data);
> + free_work_atoms(data);
> return;
> }
> }
> @@ -3511,7 +3526,6 @@ static int perf_sched__lat(struct perf_sched *sched)
> work_list = rb_entry(next, struct work_atoms, node);
> output_lat_thread(sched, work_list);
> next = rb_next(next);
> - thread__zput(work_list->thread);
> }
>
> printf(" -----------------------------------------------------------------------------------------------------------------\n");
> @@ -3525,6 +3539,13 @@ static int perf_sched__lat(struct perf_sched *sched)
>
> rc = 0;
>
> + while ((next = rb_first_cached(&sched->sorted_atom_root))) {
> + struct work_atoms *data;
> +
> + data = rb_entry(next, struct work_atoms, node);
> + rb_erase_cached(next, &sched->sorted_atom_root);
> + free_work_atoms(data);
> + }
> out_free_cpus_switch_event:
> free_cpus_switch_event(sched);
> return rc;
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 8/8] perf test: Add more test cases to sched test
2025-07-03 1:49 ` [PATCH 8/8] perf test: Add more test cases to sched test Namhyung Kim
@ 2025-07-03 3:11 ` Ian Rogers
0 siblings, 0 replies; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:11 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> $ sudo ./perf test -vv 92
> 92: perf sched tests:
> --- start ---
> test child forked, pid 1360101
> Sched record
> pid 1360105's current affinity list: 0-3
> pid 1360105's new affinity list: 0
> pid 1360107's current affinity list: 0-3
> pid 1360107's new affinity list: 0
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 4.330 MB /tmp/__perf_test_sched.perf.data.b3319 (12246 samples) ]
> Sched latency
> Sched script
> Sched map
> Sched timehist
> Samples of sched_switch event do not have callchains.
> ---- end(0) ----
> 92: perf sched tests : Ok
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/tests/shell/sched.sh | 39 ++++++++++++++++++++++++++-------
> 1 file changed, 31 insertions(+), 8 deletions(-)
>
> diff --git a/tools/perf/tests/shell/sched.sh b/tools/perf/tests/shell/sched.sh
> index c030126d1a0c918d..b9b81eaf856e6555 100755
> --- a/tools/perf/tests/shell/sched.sh
> +++ b/tools/perf/tests/shell/sched.sh
> @@ -56,38 +56,61 @@ cleanup_noploops() {
> kill "$PID1" "$PID2"
> }
>
> -test_sched_latency() {
> - echo "Sched latency"
> +test_sched_record() {
> + echo "Sched record"
>
> start_noploops
>
> perf sched record --no-inherit -o "${perfdata}" sleep 1
> +
> + cleanup_noploops
> +}
> +
> +test_sched_latency() {
> + echo "Sched latency"
> +
> if ! perf sched latency -i "${perfdata}" | grep -q perf-noploop
> then
> echo "Sched latency [Failed missing output]"
> err=1
> fi
> -
> - cleanup_noploops
> }
>
> test_sched_script() {
> echo "Sched script"
>
> - start_noploops
> -
> - perf sched record --no-inherit -o "${perfdata}" sleep 1
> if ! perf sched script -i "${perfdata}" | grep -q perf-noploop
> then
> echo "Sched script [Failed missing output]"
> err=1
> fi
> +}
>
> - cleanup_noploops
> +test_sched_map() {
> + echo "Sched map"
> +
> + if ! perf sched map -i "${perfdata}" | grep -q perf-noploop
> + then
> + echo "Sched map [Failed missing output]"
> + err=1
> + fi
> +}
> +
> +test_sched_timehist() {
> + echo "Sched timehist"
> +
> + if ! perf sched timehist -i "${perfdata}" | grep -q perf-noploop
> + then
> + echo "Sched timehist [Failed missing output]"
> + err=1
> + fi
> }
>
> +test_sched_record
> test_sched_latency
> test_sched_script
> +test_sched_map
> +test_sched_timehist
>
> cleanup
> exit $err
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHSET 0/8] perf sched: Fix various memory leaks
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (7 preceding siblings ...)
2025-07-03 1:49 ` [PATCH 8/8] perf test: Add more test cases to sched test Namhyung Kim
@ 2025-07-03 3:45 ` Ian Rogers
2025-07-03 18:25 ` Namhyung Kim
2025-07-04 17:55 ` Namhyung Kim
9 siblings, 1 reply; 20+ messages in thread
From: Ian Rogers @ 2025-07-03 3:45 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hello,
>
> While running the new perf sched test with ASAN, it fails due to
> memory leaks. They comes mostly from missing thread__put() but
> sometimes it needs to release other data structures.
>
> Fix those leaks and add more subcommands to the test.
Thanks Namhyung, it may be worth adding Fixes tags for the benefit of backports.
Tested-by: Ian Rogers <irogers@google.com>
Ian
> Thanks,
> Namhyung
>
>
> Namhyung Kim (8):
> perf sched: Make sure it frees the usage string
> perf sched: Free thread->priv using priv_destructor
> perf sched: Fix memory leaks in 'perf sched map'
> perf sched: Fix thread leaks in 'perf sched timehist'
> perf sched: Fix memory leaks for evsel->priv in timehist
> perf sched: Use RC_CHK_EQUAL() to compare pointers
> perf sched: Fix memory leaks in 'perf sched latency'
> perf test: Add more test cases to sched test
>
> tools/perf/builtin-sched.c | 147 +++++++++++++++++++++++---------
> tools/perf/tests/shell/sched.sh | 39 +++++++--
> tools/perf/util/evsel.c | 11 +++
> tools/perf/util/evsel.h | 2 +
> 4 files changed, 153 insertions(+), 46 deletions(-)
>
> --
> 2.50.0.727.gbf7dc18ff4-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHSET 0/8] perf sched: Fix various memory leaks
2025-07-03 3:45 ` [PATCHSET 0/8] perf sched: Fix various memory leaks Ian Rogers
@ 2025-07-03 18:25 ` Namhyung Kim
0 siblings, 0 replies; 20+ messages in thread
From: Namhyung Kim @ 2025-07-03 18:25 UTC (permalink / raw)
To: Ian Rogers
Cc: Arnaldo Carvalho de Melo, Kan Liang, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
On Wed, Jul 02, 2025 at 08:45:18PM -0700, Ian Rogers wrote:
> On Wed, Jul 2, 2025 at 6:49 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hello,
> >
> > While running the new perf sched test with ASAN, it fails due to
> > memory leaks. They comes mostly from missing thread__put() but
> > sometimes it needs to release other data structures.
> >
> > Fix those leaks and add more subcommands to the test.
>
> Thanks Namhyung, it may be worth adding Fixes tags for the benefit of backports.
I was too lazy to find the commits. ;-p
>
> Tested-by: Ian Rogers <irogers@google.com>
Thanks for your review and test!
Namhyung
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHSET 0/8] perf sched: Fix various memory leaks
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
` (8 preceding siblings ...)
2025-07-03 3:45 ` [PATCHSET 0/8] perf sched: Fix various memory leaks Ian Rogers
@ 2025-07-04 17:55 ` Namhyung Kim
9 siblings, 0 replies; 20+ messages in thread
From: Namhyung Kim @ 2025-07-04 17:55 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, Kan Liang, Namhyung Kim
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users
On Wed, 02 Jul 2025 18:49:34 -0700, Namhyung Kim wrote:
> While running the new perf sched test with ASAN, it fails due to
> memory leaks. They comes mostly from missing thread__put() but
> sometimes it needs to release other data structures.
>
> Fix those leaks and add more subcommands to the test.
>
> Thanks,
> Namhyung
>
> [...]
Applied to perf-tools-next, thanks!
Best regards,
Namhyung
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2025-07-04 17:55 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-03 1:49 [PATCHSET 0/8] perf sched: Fix various memory leaks Namhyung Kim
2025-07-03 1:49 ` [PATCH 1/8] perf sched: Make sure it frees the usage string Namhyung Kim
2025-07-03 3:04 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 2/8] perf sched: Free thread->priv using priv_destructor Namhyung Kim
2025-07-03 3:05 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 3/8] perf sched: Fix memory leaks in 'perf sched map' Namhyung Kim
2025-07-03 3:06 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 4/8] perf sched: Fix thread leaks in 'perf sched timehist' Namhyung Kim
2025-07-03 3:08 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 5/8] perf sched: Fix memory leaks for evsel->priv in timehist Namhyung Kim
2025-07-03 3:09 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 6/8] perf sched: Use RC_CHK_EQUAL() to compare pointers Namhyung Kim
2025-07-03 3:10 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 7/8] perf sched: Fix memory leaks in 'perf sched latency' Namhyung Kim
2025-07-03 3:10 ` Ian Rogers
2025-07-03 1:49 ` [PATCH 8/8] perf test: Add more test cases to sched test Namhyung Kim
2025-07-03 3:11 ` Ian Rogers
2025-07-03 3:45 ` [PATCHSET 0/8] perf sched: Fix various memory leaks Ian Rogers
2025-07-03 18:25 ` Namhyung Kim
2025-07-04 17:55 ` Namhyung Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).