blob: 9caea3d36a44232da5e8f4fcdbcdfb79807a2abe [file] [log] [blame]
The Android Open Source Project10e23ee2009-03-03 19:30:30 -08001/**
2 * @file opd_anon.c
3 * Anonymous region handling.
4 *
5 * Our caching of maps has some problems: if we get tgid reuse,
6 * and it's the same application, we might end up with wrong
7 * maps. The same happens in an unmap-remap case. There's not much
8 * we can do about this, we just hope it's not too common...
9 *
10 * What is relatively common is expanding anon maps, which leaves us
11 * with lots of separate sample files.
12 *
13 * @remark Copyright 2005 OProfile authors
14 * @remark Read the file COPYING
15 *
16 * @author John Levon
17 * @Modifications Gisle Dankel
18 */
19
20#include "opd_anon.h"
21#include "opd_trans.h"
22#include "opd_sfile.h"
23#include "opd_printf.h"
24#include "op_libiberty.h"
25
26#include <limits.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30
31#define HASH_SIZE 1024
32#define HASH_BITS (HASH_SIZE - 1)
33
34/*
35 * Note that this value is tempered by the fact that when we miss in the
36 * anon cache, we'll tear down all the mappings for that tgid. Thus, LRU
37 * of a mapping can potentially clear out a much larger number of
38 * mappings.
39 */
40#define LRU_SIZE 8192
41#define LRU_AMOUNT (LRU_SIZE/8)
42
43static struct list_head hashes[HASH_SIZE];
44static struct list_head lru;
45static size_t nr_lru;
46
47static void do_lru(struct transient * trans)
48{
49 size_t nr_to_kill = LRU_AMOUNT;
50 struct list_head * pos;
51 struct list_head * pos2;
52 struct anon_mapping * entry;
53
54 list_for_each_safe(pos, pos2, &lru) {
55 entry = list_entry(pos, struct anon_mapping, lru_list);
56 if (trans->anon == entry)
57 clear_trans_current(trans);
58 if (trans->last_anon == entry)
59 clear_trans_last(trans);
60 sfile_clear_anon(entry);
61 list_del(&entry->list);
62 list_del(&entry->lru_list);
63 --nr_lru;
64 free(entry);
65 if (nr_to_kill-- == 0)
66 break;
67 }
68}
69
70
71static unsigned long hash_anon(pid_t tgid, cookie_t app)
72{
73 return ((app >> DCOOKIE_SHIFT) ^ (tgid >> 2)) & (HASH_SIZE - 1);
74}
75
76
77static void clear_anon_maps(struct transient * trans)
78{
79 unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
80 pid_t tgid = trans->tgid;
81 cookie_t app = trans->app_cookie;
82 struct list_head * pos;
83 struct list_head * pos2;
84 struct anon_mapping * entry;
85
86 clear_trans_current(trans);
87
88 list_for_each_safe(pos, pos2, &hashes[hash]) {
89 entry = list_entry(pos, struct anon_mapping, list);
90 if (entry->tgid == tgid && entry->app_cookie == app) {
91 if (trans->last_anon == entry)
92 clear_trans_last(trans);
93 sfile_clear_anon(entry);
94 list_del(&entry->list);
95 list_del(&entry->lru_list);
96 --nr_lru;
97 free(entry);
98 }
99 }
100
101 if (vmisc) {
102 char const * name = verbose_cookie(app);
103 printf("Cleared anon maps for tgid %u (%s).\n", tgid, name);
104 }
105}
106
107
108static void
109add_anon_mapping(struct transient * trans, vma_t start, vma_t end, char * name)
110{
111 unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
112 struct anon_mapping * m = xmalloc(sizeof(struct anon_mapping));
113 m->tgid = trans->tgid;
114 m->app_cookie = trans->app_cookie;
115 m->start = start;
116 m->end = end;
117 strncpy(m->name, name, MAX_IMAGE_NAME_SIZE + 1);
118 list_add_tail(&m->list, &hashes[hash]);
119 list_add_tail(&m->lru_list, &lru);
120 if (++nr_lru == LRU_SIZE)
121 do_lru(trans);
122 if (vmisc) {
123 char const * name = verbose_cookie(m->app_cookie);
124 printf("Added anon map 0x%llx-0x%llx for tgid %u (%s).\n",
125 start, end, m->tgid, name);
126 }
127}
128
129
130/* 42000000-4212f000 r-xp 00000000 16:03 424334 /lib/tls/libc-2.3.2.so */
131static void get_anon_maps(struct transient * trans)
132{
133 FILE * fp = NULL;
134 char buf[PATH_MAX];
135 vma_t start, end;
136 int ret;
137
138 snprintf(buf, PATH_MAX, "/proc/%d/maps", trans->tgid);
139 fp = fopen(buf, "r");
140 if (!fp)
141 return;
142
143 while (fgets(buf, PATH_MAX, fp) != NULL) {
144 char tmp[MAX_IMAGE_NAME_SIZE + 1];
145 char name[MAX_IMAGE_NAME_SIZE + 1];
146 /* Some anon maps have labels like
147 * [heap], [stack], [vdso], [vsyscall] ...
148 * Keep track of these labels. If a map has no name, call it "anon".
149 * Ignore all mappings starting with "/" (file or shared memory object)
150 */
151 strcpy(name, "anon");
152 ret = sscanf(buf, "%llx-%llx %20s %20s %20s %20s %20s",
153 &start, &end, tmp, tmp, tmp, tmp, name);
154 if (ret < 6 || name[0] == '/')
155 continue;
156
157 add_anon_mapping(trans, start, end, name);
158 }
159
160 fclose(fp);
161}
162
163
164static int
165anon_match(struct transient const * trans, struct anon_mapping const * anon)
166{
167 if (!anon)
168 return 0;
169 if (trans->tgid != anon->tgid)
170 return 0;
171 if (trans->app_cookie != anon->app_cookie)
172 return 0;
173 if (trans->pc < anon->start)
174 return 0;
175 return (trans->pc < anon->end);
176}
177
178
179struct anon_mapping * find_anon_mapping(struct transient * trans)
180{
181 unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
182 struct list_head * pos;
183 struct anon_mapping * entry;
184 int tried = 0;
185
186 if (anon_match(trans, trans->anon))
187 return (trans->anon);
188
189retry:
190 list_for_each(pos, &hashes[hash]) {
191 entry = list_entry(pos, struct anon_mapping, list);
192 if (anon_match(trans, entry))
193 goto success;
194 }
195
196 if (!tried) {
197 clear_anon_maps(trans);
198 get_anon_maps(trans);
199 tried = 1;
200 goto retry;
201 }
202
203 return NULL;
204
205success:
206 /*
207 * Typically, there's one big mapping that matches. Let's go
208 * faster.
209 */
210 list_del(&entry->list);
211 list_add(&entry->list, &hashes[hash]);
212
213 verbprintf(vmisc, "Found range 0x%llx-0x%llx for tgid %u, pc %llx.\n",
214 entry->start, entry->end, (unsigned int)entry->tgid,
215 trans->pc);
216 return entry;
217}
218
219
220void anon_init(void)
221{
222 size_t i;
223
224 for (i = 0; i < HASH_SIZE; ++i)
225 list_init(&hashes[i]);
226
227 list_init(&lru);
228}