perf sched: Free callchain nodes in idle thread cleanup

free_idle_threads() relies on the thread priv destructor (free()) to
clean up idle_thread_runtime structs.  But free() doesn't walk the
callchain_cursor linked list or the callchain_root tree allocated
by callchain_cursor__copy() and callchain_append() during --idle-hist
processing.  Every idle thread with callchain data leaks these nodes.

Introduce callchain_cursor_cleanup() to free the cursor's linked list
of callchain_cursor_node entries, and call it together with
free_callchain() in free_idle_threads() before thread__put().

Fixes: 225b24f569980ac9 ("perf sched timehist: Save callchain when entering idle")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2026-06-06 11:33:41 -03:00
parent 33fa2bf560
commit c3e51ed45f
3 changed files with 20 additions and 1 deletions

View File

@ -2500,8 +2500,11 @@ static void free_idle_threads(void)
struct idle_thread_runtime *itr;
itr = thread__priv(idle);
if (itr)
if (itr) {
thread__put(itr->last_thread);
free_callchain(&itr->callchain);
callchain_cursor_cleanup(&itr->cursor);
}
thread__put(idle);
}

View File

@ -1578,6 +1578,21 @@ void free_callchain(struct callchain_root *root)
free_callchain_node(&root->node);
}
void callchain_cursor_cleanup(struct callchain_cursor *cursor)
{
struct callchain_cursor_node *node, *next;
callchain_cursor_reset(cursor);
for (node = cursor->first; node; node = next) {
next = node->next;
free(node);
}
cursor->first = NULL;
cursor->last = &cursor->first;
cursor->curr = NULL;
}
static u64 decay_callchain_node(struct callchain_node *node)
{
struct callchain_node *child;

View File

@ -286,6 +286,7 @@ int callchain_list_counts__printf_value(struct callchain_list *clist,
FILE *fp, char *bf, int bfsize);
void free_callchain(struct callchain_root *root);
void callchain_cursor_cleanup(struct callchain_cursor *cursor);
void decay_callchain(struct callchain_root *root);
int callchain_node__make_parent_list(struct callchain_node *node);