diff options
author | Arnaldo Carvalho de Melo <acme@infradead.org> | 2010-02-25 10:57:40 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-02-25 11:39:14 -0500 |
commit | 628ada0cb03666dd463f7c25947eaccdf440c309 (patch) | |
tree | 2338cf888ccecb1bb9df32031cef43f527b05ca0 /tools/perf | |
parent | 3846df2e0a99a2bf10023de0e9c1496592012d4c (diff) |
perf annotate: Defer allocating sym_priv->hist array
Because symbol->end is not fixed up at symbol_filter time, only
after all symbols for a DSO are loaded, and that, for asm
symbols, may be bogus, causing segfaults when hits happen in
these symbols.
Reported-by: David Miller <davem@davemloft.net>
Reported-by: Anton Blanchard <anton@samba.org>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: <stable@kernel.org> # for .33.x. Does not apply cleanly, needs backport.
LKML-Reference: <20100225155740.GB8553@ghostprotocols.net>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/builtin-annotate.c | 57 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 2 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 2 |
3 files changed, 32 insertions, 29 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 28ea4e0c3658..e47dd1587e39 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -53,32 +53,20 @@ struct sym_priv { | |||
53 | 53 | ||
54 | static const char *sym_hist_filter; | 54 | static const char *sym_hist_filter; |
55 | 55 | ||
56 | static int symbol_filter(struct map *map __used, struct symbol *sym) | 56 | static int sym__alloc_hist(struct symbol *self) |
57 | { | 57 | { |
58 | if (sym_hist_filter == NULL || | 58 | struct sym_priv *priv = symbol__priv(self); |
59 | strcmp(sym->name, sym_hist_filter) == 0) { | 59 | const int size = (sizeof(*priv->hist) + |
60 | struct sym_priv *priv = symbol__priv(sym); | 60 | (self->end - self->start) * sizeof(u64)); |
61 | const int size = (sizeof(*priv->hist) + | ||
62 | (sym->end - sym->start) * sizeof(u64)); | ||
63 | 61 | ||
64 | priv->hist = malloc(size); | 62 | priv->hist = zalloc(size); |
65 | if (priv->hist) | 63 | return priv->hist == NULL ? -1 : 0; |
66 | memset(priv->hist, 0, size); | ||
67 | return 0; | ||
68 | } | ||
69 | /* | ||
70 | * FIXME: We should really filter it out, as we don't want to go thru symbols | ||
71 | * we're not interested, and if a DSO ends up with no symbols, delete it too, | ||
72 | * but right now the kernel loading routines in symbol.c bail out if no symbols | ||
73 | * are found, fix it later. | ||
74 | */ | ||
75 | return 0; | ||
76 | } | 64 | } |
77 | 65 | ||
78 | /* | 66 | /* |
79 | * collect histogram counts | 67 | * collect histogram counts |
80 | */ | 68 | */ |
81 | static void hist_hit(struct hist_entry *he, u64 ip) | 69 | static int annotate__hist_hit(struct hist_entry *he, u64 ip) |
82 | { | 70 | { |
83 | unsigned int sym_size, offset; | 71 | unsigned int sym_size, offset; |
84 | struct symbol *sym = he->sym; | 72 | struct symbol *sym = he->sym; |
@@ -88,11 +76,11 @@ static void hist_hit(struct hist_entry *he, u64 ip) | |||
88 | he->count++; | 76 | he->count++; |
89 | 77 | ||
90 | if (!sym || !he->map) | 78 | if (!sym || !he->map) |
91 | return; | 79 | return 0; |
92 | 80 | ||
93 | priv = symbol__priv(sym); | 81 | priv = symbol__priv(sym); |
94 | if (!priv->hist) | 82 | if (priv->hist == NULL && sym__alloc_hist(sym) < 0) |
95 | return; | 83 | return -ENOMEM; |
96 | 84 | ||
97 | sym_size = sym->end - sym->start; | 85 | sym_size = sym->end - sym->start; |
98 | offset = ip - sym->start; | 86 | offset = ip - sym->start; |
@@ -100,7 +88,7 @@ static void hist_hit(struct hist_entry *he, u64 ip) | |||
100 | pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); | 88 | pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); |
101 | 89 | ||
102 | if (offset >= sym_size) | 90 | if (offset >= sym_size) |
103 | return; | 91 | return 0; |
104 | 92 | ||
105 | h = priv->hist; | 93 | h = priv->hist; |
106 | h->sum++; | 94 | h->sum++; |
@@ -108,18 +96,31 @@ static void hist_hit(struct hist_entry *he, u64 ip) | |||
108 | 96 | ||
109 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, | 97 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, |
110 | he->sym->name, ip, ip - he->sym->start, h->ip[offset]); | 98 | he->sym->name, ip, ip - he->sym->start, h->ip[offset]); |
99 | return 0; | ||
111 | } | 100 | } |
112 | 101 | ||
113 | static int perf_session__add_hist_entry(struct perf_session *self, | 102 | static int perf_session__add_hist_entry(struct perf_session *self, |
114 | struct addr_location *al, u64 count) | 103 | struct addr_location *al, u64 count) |
115 | { | 104 | { |
116 | bool hit; | 105 | bool hit; |
117 | struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL, | 106 | struct hist_entry *he; |
118 | count, &hit); | 107 | |
108 | if (sym_hist_filter != NULL && | ||
109 | (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { | ||
110 | /* We're only interested in a symbol named sym_hist_filter */ | ||
111 | if (al->sym != NULL) { | ||
112 | rb_erase(&al->sym->rb_node, | ||
113 | &al->map->dso->symbols[al->map->type]); | ||
114 | symbol__delete(al->sym); | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | he = __perf_session__add_hist_entry(self, al, NULL, count, &hit); | ||
119 | if (he == NULL) | 120 | if (he == NULL) |
120 | return -ENOMEM; | 121 | return -ENOMEM; |
121 | hist_hit(he, al->addr); | 122 | |
122 | return 0; | 123 | return annotate__hist_hit(he, al->addr); |
123 | } | 124 | } |
124 | 125 | ||
125 | static int process_sample_event(event_t *event, struct perf_session *session) | 126 | static int process_sample_event(event_t *event, struct perf_session *session) |
@@ -129,7 +130,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
129 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, | 130 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, |
130 | event->ip.pid, event->ip.ip); | 131 | event->ip.pid, event->ip.ip); |
131 | 132 | ||
132 | if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) { | 133 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { |
133 | pr_warning("problem processing %d event, skipping it.\n", | 134 | pr_warning("problem processing %d event, skipping it.\n", |
134 | event->header.type); | 135 | event->header.type); |
135 | return -1; | 136 | return -1; |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7aab4e5f3669..323c0aea0a91 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -144,7 +144,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) | |||
144 | return self; | 144 | return self; |
145 | } | 145 | } |
146 | 146 | ||
147 | static void symbol__delete(struct symbol *self) | 147 | void symbol__delete(struct symbol *self) |
148 | { | 148 | { |
149 | free(((void *)self) - symbol_conf.priv_size); | 149 | free(((void *)self) - symbol_conf.priv_size); |
150 | } | 150 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 1b4192ee5300..280dadd32a08 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -51,6 +51,8 @@ struct symbol { | |||
51 | char name[0]; | 51 | char name[0]; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | void symbol__delete(struct symbol *self); | ||
55 | |||
54 | struct strlist; | 56 | struct strlist; |
55 | 57 | ||
56 | struct symbol_conf { | 58 | struct symbol_conf { |