Home | History | Annotate | Download | only in daemon
      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 
     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 
     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 
     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
    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 */
    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
    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 
    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 
    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