• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
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 
43 static struct list_head hashes[HASH_SIZE];
44 static struct list_head lru;
45 static size_t nr_lru;
46 
do_lru(struct transient * trans)47 static 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 
hash_anon(pid_t tgid,cookie_t app)71 static unsigned long hash_anon(pid_t tgid, cookie_t app)
72 {
73 	return ((app >> DCOOKIE_SHIFT) ^ (tgid >> 2)) & (HASH_SIZE - 1);
74 }
75 
76 
clear_anon_maps(struct transient * trans)77 static 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 
108 static void
add_anon_mapping(struct transient * trans,vma_t start,vma_t end,char * name)109 add_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 */
get_anon_maps(struct transient * trans)131 static 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 
164 static int
anon_match(struct transient const * trans,struct anon_mapping const * anon)165 anon_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 
find_anon_mapping(struct transient * trans)179 struct 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 
189 retry:
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 
205 success:
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 
anon_init(void)220 void 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 }
229