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