]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf sched: Free callchain nodes in idle thread cleanup
authorArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 6 Jun 2026 14:33:41 +0000 (11:33 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 6 Jun 2026 17:42:19 +0000 (14:42 -0300)
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>
tools/perf/builtin-sched.c
tools/perf/util/callchain.c
tools/perf/util/callchain.h

index b7ccdc6a985d1c7ba32e3a44d330c18429fe628a..1ff01f03d2ad1ad33bb9c13109a9fd4889af6d27 100644 (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);
                }
index 8981ae879ebb887c4aa08b4e7105030f319e7f77..31c675cbab63187ba53832b51a511b22c066200e 100644 (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;
index 0eb5d7a1a41d81e1ff3c695b17886dfc86151b14..8b1e405d1cdf4660c3778505e12a7de0cdf66080 100644 (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);