opd_anon.c revision cc2ee177dbb3befca43e36cfc56778b006c3d050
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 */
18
19#include "opd_anon.h"
20#include "opd_trans.h"
21#include "opd_sfile.h"
22#include "opd_printf.h"
23#include "op_libiberty.h"
24
25#include <limits.h>
26#include <stdlib.h>
27#include <stdio.h>
28
29#define HASH_SIZE 1024
30#define HASH_BITS (HASH_SIZE - 1)
31
32#define LRU_SIZE 1000
33#define LRU_AMOUNT (LRU_SIZE/5)
34
35static struct list_head hashes[HASH_SIZE];
36static struct list_head lru;
37static size_t nr_lru;
38
39/* never called in buffer-processing context, so we don't need
40 * to update struct transient. Note the 'U' of LRU is based on
41 * parsing time, not actual use.
42 */
43static void do_lru(struct transient * trans)
44{
45	size_t nr_to_kill = LRU_AMOUNT;
46	struct list_head * pos;
47	struct list_head * pos2;
48	struct anon_mapping * entry;
49
50	list_for_each_safe(pos, pos2, &lru) {
51		entry = list_entry(pos, struct anon_mapping, lru_list);
52		if (trans->anon == entry)
53			clear_trans_current(trans);
54		if (trans->last_anon == entry)
55			clear_trans_last(trans);
56		list_del(&entry->list);
57		list_del(&entry->lru_list);
58		--nr_lru;
59		free(entry);
60		if (nr_to_kill-- == 0)
61			break;
62	}
63
64	sfile_clear_anon();
65}
66
67
68static unsigned long hash_anon(pid_t tgid, cookie_t app)
69{
70	return ((app >> DCOOKIE_SHIFT) ^ (tgid >> 2)) & (HASH_SIZE - 1);
71}
72
73
74static void clear_anon_maps(struct transient * trans)
75{
76	unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
77	pid_t tgid = trans->tgid;
78	cookie_t app = trans->app_cookie;
79	struct list_head * pos;
80	struct list_head * pos2;
81	struct anon_mapping * entry;
82
83	clear_trans_current(trans);
84
85	list_for_each_safe(pos, pos2, &hashes[hash]) {
86		entry = list_entry(pos, struct anon_mapping, list);
87		if (entry->tgid == tgid && entry->app_cookie == app) {
88			if (trans->last_anon == entry)
89				clear_trans_last(trans);
90			list_del(&entry->list);
91			list_del(&entry->lru_list);
92			--nr_lru;
93			free(entry);
94		}
95	}
96
97	sfile_clear_anon();
98	if (vmisc) {
99		char const * name = verbose_cookie(app);
100		printf("Cleared anon maps for tgid %u (%s).\n", tgid, name);
101	}
102}
103
104
105static void
106add_anon_mapping(struct transient * trans, vma_t start, vma_t end)
107{
108	unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
109	struct anon_mapping * m = xmalloc(sizeof(struct anon_mapping));
110	m->tgid = trans->tgid;
111	m->app_cookie = trans->app_cookie;
112	m->start = start;
113	m->end = end;
114	list_add_tail(&m->list, &hashes[hash]);
115	list_add_tail(&m->lru_list, &lru);
116	if (++nr_lru == LRU_SIZE)
117		do_lru(trans);
118	if (vmisc) {
119		char const * name = verbose_cookie(m->app_cookie);
120		printf("Added anon map 0x%llx-0x%llx for tgid %u (%s).\n",
121		       start, end, m->tgid, name);
122	}
123}
124
125
126/* 42000000-4212f000 r-xp 00000000 16:03 424334 /lib/tls/libc-2.3.2.so */
127static void get_anon_maps(struct transient * trans)
128{
129	FILE * fp = NULL;
130	char buf[PATH_MAX];
131	unsigned long start;
132	unsigned long end;
133	int ret;
134
135	snprintf(buf, PATH_MAX, "/proc/%d/maps", trans->tgid);
136	fp = fopen(buf, "r");
137	if (!fp)
138		return;
139
140	while (fgets(buf, PATH_MAX, fp) != NULL) {
141		char tmp[20];
142		/* Note that this actually includes all mappings,
143		 * since we want stuff like [heap]
144		 */
145		ret = sscanf(buf, "%lx-%lx %20s %20s %20s %20s %20s",
146		             &start, &end, tmp, tmp, tmp, tmp, tmp);
147		if (ret < 6)
148			continue;
149
150		add_anon_mapping(trans, start, end);
151	}
152
153	fclose(fp);
154}
155
156
157static int
158anon_match(struct transient const * trans, struct anon_mapping const * anon)
159{
160	if (!anon)
161		return 0;
162	if (trans->tgid != anon->tgid)
163		return 0;
164	if (trans->app_cookie != anon->app_cookie)
165		return 0;
166	if (trans->pc < anon->start)
167		return 0;
168	return (trans->pc < anon->end);
169}
170
171
172struct anon_mapping * find_anon_mapping(struct transient * trans)
173{
174	unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
175	struct list_head * pos;
176	struct anon_mapping * entry;
177	int tried = 0;
178
179	if (anon_match(trans, trans->anon))
180		return (trans->anon);
181
182retry:
183	list_for_each(pos, &hashes[hash]) {
184		entry = list_entry(pos, struct anon_mapping, list);
185		if (anon_match(trans, entry))
186			goto success;
187	}
188
189	if (!tried) {
190		clear_anon_maps(trans);
191		get_anon_maps(trans);
192		tried = 1;
193		goto retry;
194	}
195
196	return NULL;
197
198success:
199	/*
200	 * Typically, there's one big mapping that matches. Let's go
201	 * faster.
202	 */
203	list_del(&entry->list);
204	list_add(&entry->list, &hashes[hash]);
205
206	verbprintf(vmisc, "Found range 0x%llx-0x%llx for tgid %u, pc %llx.\n",
207	           entry->start, entry->end, (unsigned int)entry->tgid,
208		   trans->pc);
209	return entry;
210}
211
212
213void anon_init(void)
214{
215	size_t i;
216
217	for (i = 0; i < HASH_SIZE; ++i)
218		list_init(&hashes[i]);
219
220	list_init(&lru);
221}
222