aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2015-06-02 10:53:26 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2015-06-08 09:31:40 -0400
commitd3a7c489c7fd2463e3b2c3a2179c7be879dd9cb4 (patch)
tree5b89c379e629ed3c23f22102be66a99025c496da
parente88078442232f3bbcb4ff1d24b3f9ab3dca472b9 (diff)
perf tools: Reference count struct dso
This has a different model than the 'thread' and 'map' struct lifetimes: there is not a definitive "don't use this DSO anymore" event, i.e. we may get many 'struct map' holding references to the '/usr/lib64/libc-2.20.so' DSO but then at some point some DSO may have no references but we still don't want to straight away release its resources, because "soon" we may get a new 'struct map' that needs it and we want to reuse its symtab or other resources. So we need some way to garbage collect it when crossing some memory usage threshold, which is left for anoter patch, for now it is sufficient to release it when calling dsos__exit(), i.e. when deleting the whole list as part of deleting the 'struct machine' containing it, which will leave only referenced objects being used. Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: David Ahern <dsahern@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Link: http://lkml.kernel.org/n/tip-majzgz07cm90t2tejrjy4clf@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/tests/dso-data.c4
-rw-r--r--tools/perf/tests/hists_common.c6
-rw-r--r--tools/perf/util/dso.c37
-rw-r--r--tools/perf/util/dso.h14
-rw-r--r--tools/perf/util/header.c1
-rw-r--r--tools/perf/util/machine.c15
-rw-r--r--tools/perf/util/map.c11
-rw-r--r--tools/perf/util/probe-finder.c2
-rw-r--r--tools/perf/util/symbol-elf.c2
-rw-r--r--tools/perf/util/symbol.c2
-rw-r--r--tools/perf/util/vdso.c1
11 files changed, 81 insertions, 14 deletions
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c
index 3e41c61bd861..a218aeaf56a0 100644
--- a/tools/perf/tests/dso-data.c
+++ b/tools/perf/tests/dso-data.c
@@ -166,7 +166,7 @@ int test__dso_data(void)
166 free(buf); 166 free(buf);
167 } 167 }
168 168
169 dso__delete(dso); 169 dso__put(dso);
170 unlink(file); 170 unlink(file);
171 return 0; 171 return 0;
172} 172}
@@ -226,7 +226,7 @@ static void dsos__delete(int cnt)
226 struct dso *dso = dsos[i]; 226 struct dso *dso = dsos[i];
227 227
228 unlink(dso->name); 228 unlink(dso->name);
229 dso__delete(dso); 229 dso__put(dso);
230 } 230 }
231 231
232 free(dsos); 232 free(dsos);
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 915f60af6a0e..ce80b274b097 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -134,11 +134,15 @@ struct machine *setup_fake_machine(struct machines *machines)
134 134
135 sym = symbol__new(fsym->start, fsym->length, 135 sym = symbol__new(fsym->start, fsym->length,
136 STB_GLOBAL, fsym->name); 136 STB_GLOBAL, fsym->name);
137 if (sym == NULL) 137 if (sym == NULL) {
138 dso__put(dso);
138 goto out; 139 goto out;
140 }
139 141
140 symbols__insert(&dso->symbols[MAP__FUNCTION], sym); 142 symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
141 } 143 }
144
145 dso__put(dso);
142 } 146 }
143 147
144 return machine; 148 return machine;
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index ff0204ac4321..7c0c08386a1d 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1049,6 +1049,7 @@ struct dso *dso__new(const char *name)
1049 INIT_LIST_HEAD(&dso->node); 1049 INIT_LIST_HEAD(&dso->node);
1050 INIT_LIST_HEAD(&dso->data.open_entry); 1050 INIT_LIST_HEAD(&dso->data.open_entry);
1051 pthread_mutex_init(&dso->lock, NULL); 1051 pthread_mutex_init(&dso->lock, NULL);
1052 atomic_set(&dso->refcnt, 1);
1052 } 1053 }
1053 1054
1054 return dso; 1055 return dso;
@@ -1083,6 +1084,19 @@ void dso__delete(struct dso *dso)
1083 free(dso); 1084 free(dso);
1084} 1085}
1085 1086
1087struct dso *dso__get(struct dso *dso)
1088{
1089 if (dso)
1090 atomic_inc(&dso->refcnt);
1091 return dso;
1092}
1093
1094void dso__put(struct dso *dso)
1095{
1096 if (dso && atomic_dec_and_test(&dso->refcnt))
1097 dso__delete(dso);
1098}
1099
1086void dso__set_build_id(struct dso *dso, void *build_id) 1100void dso__set_build_id(struct dso *dso, void *build_id)
1087{ 1101{
1088 memcpy(dso->build_id, build_id, sizeof(dso->build_id)); 1102 memcpy(dso->build_id, build_id, sizeof(dso->build_id));
@@ -1153,6 +1167,27 @@ void __dsos__add(struct dsos *dsos, struct dso *dso)
1153{ 1167{
1154 list_add_tail(&dso->node, &dsos->head); 1168 list_add_tail(&dso->node, &dsos->head);
1155 __dso__findlink_by_longname(&dsos->root, dso, NULL); 1169 __dso__findlink_by_longname(&dsos->root, dso, NULL);
1170 /*
1171 * It is now in the linked list, grab a reference, then garbage collect
1172 * this when needing memory, by looking at LRU dso instances in the
1173 * list with atomic_read(&dso->refcnt) == 1, i.e. no references
1174 * anywhere besides the one for the list, do, under a lock for the
1175 * list: remove it from the list, then a dso__put(), that probably will
1176 * be the last and will then call dso__delete(), end of life.
1177 *
1178 * That, or at the end of the 'struct machine' lifetime, when all
1179 * 'struct dso' instances will be removed from the list, in
1180 * dsos__exit(), if they have no other reference from some other data
1181 * structure.
1182 *
1183 * E.g.: after processing a 'perf.data' file and storing references
1184 * to objects instantiated while processing events, we will have
1185 * references to the 'thread', 'map', 'dso' structs all from 'struct
1186 * hist_entry' instances, but we may not need anything not referenced,
1187 * so we might as well call machines__exit()/machines__delete() and
1188 * garbage collect it.
1189 */
1190 dso__get(dso);
1156} 1191}
1157 1192
1158void dsos__add(struct dsos *dsos, struct dso *dso) 1193void dsos__add(struct dsos *dsos, struct dso *dso)
@@ -1206,7 +1241,7 @@ struct dso *dsos__findnew(struct dsos *dsos, const char *name)
1206{ 1241{
1207 struct dso *dso; 1242 struct dso *dso;
1208 pthread_rwlock_wrlock(&dsos->lock); 1243 pthread_rwlock_wrlock(&dsos->lock);
1209 dso = __dsos__findnew(dsos, name); 1244 dso = dso__get(__dsos__findnew(dsos, name));
1210 pthread_rwlock_unlock(&dsos->lock); 1245 pthread_rwlock_unlock(&dsos->lock);
1211 return dso; 1246 return dso;
1212} 1247}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index c16ab5d849c3..2fe98bb0e95b 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -1,6 +1,7 @@
1#ifndef __PERF_DSO 1#ifndef __PERF_DSO
2#define __PERF_DSO 2#define __PERF_DSO
3 3
4#include <linux/atomic.h>
4#include <linux/types.h> 5#include <linux/types.h>
5#include <linux/rbtree.h> 6#include <linux/rbtree.h>
6#include <stdbool.h> 7#include <stdbool.h>
@@ -179,7 +180,7 @@ struct dso {
179 void *priv; 180 void *priv;
180 u64 db_id; 181 u64 db_id;
181 }; 182 };
182 183 atomic_t refcnt;
183 char name[0]; 184 char name[0];
184}; 185};
185 186
@@ -206,6 +207,17 @@ void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated);
206 207
207int dso__name_len(const struct dso *dso); 208int dso__name_len(const struct dso *dso);
208 209
210struct dso *dso__get(struct dso *dso);
211void dso__put(struct dso *dso);
212
213static inline void __dso__zput(struct dso **dso)
214{
215 dso__put(*dso);
216 *dso = NULL;
217}
218
219#define dso__zput(dso) __dso__zput(&dso)
220
209bool dso__loaded(const struct dso *dso, enum map_type type); 221bool dso__loaded(const struct dso *dso, enum map_type type);
210 222
211bool dso__sorted_by_name(const struct dso *dso, enum map_type type); 223bool dso__sorted_by_name(const struct dso *dso, enum map_type type);
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index ac5aaaeed7ff..21a77e7a171e 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1277,6 +1277,7 @@ static int __event_process_build_id(struct build_id_event *bev,
1277 sbuild_id); 1277 sbuild_id);
1278 pr_debug("build id event received for %s: %s\n", 1278 pr_debug("build id event received for %s: %s\n",
1279 dso->long_name, sbuild_id); 1279 dso->long_name, sbuild_id);
1280 dso__put(dso);
1280 } 1281 }
1281 1282
1282 err = 0; 1283 err = 0;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 0cf56d6f073a..132e35765101 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -82,7 +82,7 @@ out_delete:
82 return NULL; 82 return NULL;
83} 83}
84 84
85static void dsos__exit(struct dsos *dsos) 85static void dsos__purge(struct dsos *dsos)
86{ 86{
87 struct dso *pos, *n; 87 struct dso *pos, *n;
88 88
@@ -90,12 +90,16 @@ static void dsos__exit(struct dsos *dsos)
90 90
91 list_for_each_entry_safe(pos, n, &dsos->head, node) { 91 list_for_each_entry_safe(pos, n, &dsos->head, node) {
92 RB_CLEAR_NODE(&pos->rb_node); 92 RB_CLEAR_NODE(&pos->rb_node);
93 list_del(&pos->node); 93 list_del_init(&pos->node);
94 dso__delete(pos); 94 dso__put(pos);
95 } 95 }
96 96
97 pthread_rwlock_unlock(&dsos->lock); 97 pthread_rwlock_unlock(&dsos->lock);
98}
98 99
100static void dsos__exit(struct dsos *dsos)
101{
102 dsos__purge(dsos);
99 pthread_rwlock_destroy(&dsos->lock); 103 pthread_rwlock_destroy(&dsos->lock);
100} 104}
101 105
@@ -524,6 +528,7 @@ static struct dso *machine__findnew_module_dso(struct machine *machine,
524 dso__set_long_name(dso, strdup(filename), true); 528 dso__set_long_name(dso, strdup(filename), true);
525 } 529 }
526 530
531 dso__get(dso);
527out_unlock: 532out_unlock:
528 pthread_rwlock_unlock(&machine->dsos.lock); 533 pthread_rwlock_unlock(&machine->dsos.lock);
529 return dso; 534 return dso;
@@ -1205,8 +1210,10 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
1205 goto out_problem; 1210 goto out_problem;
1206 1211
1207 kernel->kernel = kernel_type; 1212 kernel->kernel = kernel_type;
1208 if (__machine__create_kernel_maps(machine, kernel) < 0) 1213 if (__machine__create_kernel_maps(machine, kernel) < 0) {
1214 dso__put(kernel);
1209 goto out_problem; 1215 goto out_problem;
1216 }
1210 1217
1211 if (strstr(kernel->long_name, "vmlinux")) 1218 if (strstr(kernel->long_name, "vmlinux"))
1212 dso__set_short_name(kernel, "[kernel.vmlinux]", false); 1219 dso__set_short_name(kernel, "[kernel.vmlinux]", false);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 365011c233a6..1241ab989cf5 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -132,7 +132,7 @@ void map__init(struct map *map, enum map_type type,
132 map->end = end; 132 map->end = end;
133 map->pgoff = pgoff; 133 map->pgoff = pgoff;
134 map->reloc = 0; 134 map->reloc = 0;
135 map->dso = dso; 135 map->dso = dso__get(dso);
136 map->map_ip = map__map_ip; 136 map->map_ip = map__map_ip;
137 map->unmap_ip = map__unmap_ip; 137 map->unmap_ip = map__unmap_ip;
138 RB_CLEAR_NODE(&map->rb_node); 138 RB_CLEAR_NODE(&map->rb_node);
@@ -198,6 +198,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
198 if (type != MAP__FUNCTION) 198 if (type != MAP__FUNCTION)
199 dso__set_loaded(dso, map->type); 199 dso__set_loaded(dso, map->type);
200 } 200 }
201 dso__put(dso);
201 } 202 }
202 return map; 203 return map;
203out_delete: 204out_delete:
@@ -224,9 +225,15 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
224 return map; 225 return map;
225} 226}
226 227
227void map__delete(struct map *map) 228static void map__exit(struct map *map)
228{ 229{
229 BUG_ON(!RB_EMPTY_NODE(&map->rb_node)); 230 BUG_ON(!RB_EMPTY_NODE(&map->rb_node));
231 dso__zput(map->dso);
232}
233
234void map__delete(struct map *map)
235{
236 map__exit(map);
230 free(map); 237 free(map);
231} 238}
232 239
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c50da392e256..2da65a710893 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -130,7 +130,7 @@ struct debuginfo *debuginfo__new(const char *path)
130 continue; 130 continue;
131 dinfo = __debuginfo__new(buf); 131 dinfo = __debuginfo__new(buf);
132 } 132 }
133 dso__delete(dso); 133 dso__put(dso);
134 134
135out: 135out:
136 /* if failed to open all distro debuginfo, open given binary */ 136 /* if failed to open all distro debuginfo, open given binary */
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index a93ba85509b2..65f7e389ae09 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1016,7 +1016,7 @@ int dso__load_sym(struct dso *dso, struct map *map,
1016 curr_map = map__new2(start, curr_dso, 1016 curr_map = map__new2(start, curr_dso,
1017 map->type); 1017 map->type);
1018 if (curr_map == NULL) { 1018 if (curr_map == NULL) {
1019 dso__delete(curr_dso); 1019 dso__put(curr_dso);
1020 goto out_elf_end; 1020 goto out_elf_end;
1021 } 1021 }
1022 if (adjust_kernel_syms) { 1022 if (adjust_kernel_syms) {
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index eaee5d32d39d..504f2d73b7ee 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -786,7 +786,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta,
786 786
787 curr_map = map__new2(pos->start, ndso, map->type); 787 curr_map = map__new2(pos->start, ndso, map->type);
788 if (curr_map == NULL) { 788 if (curr_map == NULL) {
789 dso__delete(ndso); 789 dso__put(ndso);
790 return -1; 790 return -1;
791 } 791 }
792 792
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index c646c74c34f8..4b89118f158d 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -314,6 +314,7 @@ struct dso *machine__findnew_vdso(struct machine *machine,
314 } 314 }
315 315
316out_unlock: 316out_unlock:
317 dso__get(dso);
317 pthread_rwlock_unlock(&machine->dsos.lock); 318 pthread_rwlock_unlock(&machine->dsos.lock);
318 return dso; 319 return dso;
319} 320}