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>
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);
}
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;
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);