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