Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf hist: Avoid 'struct hist_entry_iter' mem_info memory leak

'struct mem_info' is reference counted while 'struct branch_info' and
he_cache (struct hist_entry **) are not.

Break apart the priv field in 'struct hist_entry_iter' so that we can
know which values are owned by the iter and do the appropriate free or
put.

Move hide_unresolved to marginally shrink the size of the now grown
struct.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Ben Gainey <ben.gainey@arm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: K Prateek Nayak <kprateek.nayak@amd.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Li Dong <lidong@vivo.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: Paran Lee <p4ranlee@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Sun Haiyong <sunhaiyong@loongson.cn>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: Yanteng Si <siyanteng@loongson.cn>
Cc: Yicong Yang <yangyicong@hisilicon.com>
Link: https://lore.kernel.org/r/20240507183545.1236093-9-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
d561e170 1a8c2e01

+19 -28
+14 -25
tools/perf/util/hist.c
··· 476 476 he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map); 477 477 } 478 478 479 - if (he->mem_info) { 480 - mem_info__iaddr(he->mem_info)->ms.map = 481 - map__get(mem_info__iaddr(he->mem_info)->ms.map); 482 - mem_info__daddr(he->mem_info)->ms.map = 483 - map__get(mem_info__daddr(he->mem_info)->ms.map); 484 - } 485 - 486 479 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) 487 480 callchain_init(he->callchain); 488 481 ··· 567 574 he = NULL; 568 575 } 569 576 } 570 - 571 577 return he; 572 578 } 573 579 ··· 739 747 .filtered = symbol__parent_filter(sym_parent) | al->filtered, 740 748 .hists = hists, 741 749 .branch_info = bi, 742 - .mem_info = mi, 750 + .mem_info = mem_info__get(mi), 743 751 .kvm_info = ki, 744 752 .block_info = block_info, 745 753 .transaction = sample->transaction, ··· 828 836 if (mi == NULL) 829 837 return -ENOMEM; 830 838 831 - iter->priv = mi; 839 + iter->mi = mi; 832 840 return 0; 833 841 } 834 842 ··· 836 844 iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) 837 845 { 838 846 u64 cost; 839 - struct mem_info *mi = iter->priv; 847 + struct mem_info *mi = iter->mi; 840 848 struct hists *hists = evsel__hists(iter->evsel); 841 849 struct perf_sample *sample = iter->sample; 842 850 struct hist_entry *he; ··· 883 891 err = hist_entry__append_callchain(he, iter->sample); 884 892 885 893 out: 886 - /* 887 - * We don't need to free iter->priv (mem_info) here since the mem info 888 - * was either already freed in hists__findnew_entry() or passed to a 889 - * new hist entry by hist_entry__new(). 890 - */ 891 - iter->priv = NULL; 894 + mem_info__zput(iter->mi); 892 895 893 896 iter->he = NULL; 894 897 return err; ··· 902 915 iter->curr = 0; 903 916 iter->total = sample->branch_stack->nr; 904 917 905 - iter->priv = bi; 918 + iter->bi = bi; 906 919 return 0; 907 920 } 908 921 ··· 916 929 static int 917 930 iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) 918 931 { 919 - struct branch_info *bi = iter->priv; 932 + struct branch_info *bi = iter->bi; 920 933 int i = iter->curr; 921 934 922 935 if (bi == NULL) ··· 945 958 int i = iter->curr; 946 959 int err = 0; 947 960 948 - bi = iter->priv; 961 + bi = iter->bi; 949 962 950 963 if (iter->hide_unresolved && !(bi[i].from.ms.sym && bi[i].to.ms.sym)) 951 964 goto out; ··· 974 987 iter_finish_branch_entry(struct hist_entry_iter *iter, 975 988 struct addr_location *al __maybe_unused) 976 989 { 977 - zfree(&iter->priv); 990 + zfree(&iter->bi); 978 991 iter->he = NULL; 979 992 980 993 return iter->curr >= iter->total ? 0 : -1; ··· 1042 1055 if (he_cache == NULL) 1043 1056 return -ENOMEM; 1044 1057 1045 - iter->priv = he_cache; 1058 + iter->he_cache = he_cache; 1046 1059 iter->curr = 0; 1047 1060 1048 1061 return 0; ··· 1055 1068 struct evsel *evsel = iter->evsel; 1056 1069 struct hists *hists = evsel__hists(evsel); 1057 1070 struct perf_sample *sample = iter->sample; 1058 - struct hist_entry **he_cache = iter->priv; 1071 + struct hist_entry **he_cache = iter->he_cache; 1059 1072 struct hist_entry *he; 1060 1073 int err = 0; 1061 1074 ··· 1113 1126 { 1114 1127 struct evsel *evsel = iter->evsel; 1115 1128 struct perf_sample *sample = iter->sample; 1116 - struct hist_entry **he_cache = iter->priv; 1129 + struct hist_entry **he_cache = iter->he_cache; 1117 1130 struct hist_entry *he; 1118 1131 struct hist_entry he_tmp = { 1119 1132 .hists = evsel__hists(evsel), ··· 1179 1192 iter_finish_cumulative_entry(struct hist_entry_iter *iter, 1180 1193 struct addr_location *al __maybe_unused) 1181 1194 { 1182 - zfree(&iter->priv); 1195 + mem_info__zput(iter->mi); 1196 + zfree(&iter->bi); 1197 + zfree(&iter->he_cache); 1183 1198 iter->he = NULL; 1184 1199 1185 1200 return 0;
+5 -3
tools/perf/util/hist.h
··· 132 132 int total; 133 133 int curr; 134 134 135 - bool hide_unresolved; 136 - 137 135 struct evsel *evsel; 138 136 struct perf_sample *sample; 139 137 struct hist_entry *he; 140 138 struct symbol *parent; 141 - void *priv; 139 + 140 + struct mem_info *mi; 141 + struct branch_info *bi; 142 + struct hist_entry **he_cache; 142 143 143 144 const struct hist_iter_ops *ops; 144 145 /* user-defined callback function (optional) */ 145 146 int (*add_entry_cb)(struct hist_entry_iter *iter, 146 147 struct addr_location *al, bool single, void *arg); 148 + bool hide_unresolved; 147 149 }; 148 150 149 151 extern const struct hist_iter_ops hist_iter_normal;