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
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}
229