lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * linux/mm/mem_tracker.c |
| 3 | * |
| 4 | * Copyright (C) 1993 Linus Torvalds |
| 5 | * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 |
| 6 | * vmalloc/vfree, Tigran Aivazian <tigran@veritas.com>, May 2000 |
| 7 | * Major rework to support vmap/vunmap, Christoph Hellwig, SGI, August 2002 |
| 8 | * Numa awareness, Christoph Lameter, SGI, June 2005 |
| 9 | */ |
| 10 | #include <linux/mm.h> |
| 11 | #include <linux/sched.h> |
| 12 | #include <linux/spinlock.h> |
| 13 | #include <linux/interrupt.h> |
| 14 | #include <linux/kallsyms.h> |
| 15 | #include <linux/list.h> |
| 16 | #include <linux/pfn.h> |
| 17 | #include <linux/atomic.h> |
| 18 | #include <asm/stacktrace.h> |
| 19 | #include <mach/iomap.h> |
| 20 | #include <linux/kprobes.h> |
| 21 | #include <asm/traps.h> |
| 22 | #include <linux/mem_tracker_def.h> |
| 23 | |
| 24 | /******************************************************************************* |
| 25 | * ºê¶¨Òå * |
| 26 | *******************************************************************************/ |
| 27 | #ifdef CONFIG_KALLSYMS |
| 28 | #define MEM_TRACKER_MAX_STACK_LEN (100) /*Õ»º¯Êý¹ì¼£µÄ×Ö·û¸öÊýÉÏÏÞ£»*/ |
| 29 | #endif |
| 30 | #define MEM_TRACKER_MAX_STACK_LEVEL (6) /*Õ»µÄ²ãÊý*/ |
| 31 | #define MEM_TRACKER_NOT_TRACE_LEVEL (2) |
| 32 | #define MEM_TRACKER_ALLOCED 0x00 |
| 33 | #define MEM_TRACKER_FREED 0x02 |
| 34 | |
| 35 | /******************************************************************************* |
| 36 | * Êý¾ÝÀàÐͶ¨Òå * |
| 37 | *******************************************************************************/ |
| 38 | struct heap_record { |
| 39 | struct list_head node; |
| 40 | void *addr; /*ÉêÇë³öµÄÄÚ´æµØÖ·*/ |
| 41 | unsigned int employ:8; |
| 42 | unsigned int size:24; /*¼Ç¼ÉêÇëµÄÄڴ泤¶È*/ |
| 43 | char task_name[TASK_COMM_LEN]; |
| 44 | #ifdef CONFIG_KALLSYMS |
| 45 | char stack_frame[MEM_TRACKER_MAX_STACK_LEVEL][MEM_TRACKER_MAX_STACK_LEN]; |
| 46 | /*º¯Êý»ØËݹ켣*/ |
| 47 | #else |
| 48 | unsigned long stack_frame[MEM_TRACKER_MAX_STACK_LEVEL]; |
| 49 | #endif |
| 50 | }; |
| 51 | |
| 52 | /******************************************************************************* |
| 53 | * º¯ÊýÉùÃ÷ * |
| 54 | *******************************************************************************/ |
| 55 | /******************************************************************************* |
| 56 | * È«¾Ö±äÁ¿¶¨Òå * |
| 57 | *******************************************************************************/ |
| 58 | int kmem_node_max; |
| 59 | int node_list_max; |
| 60 | static spinlock_t buddy_track_lock; |
| 61 | static spinlock_t kmem_track_lock; |
| 62 | |
| 63 | |
| 64 | /*ÕýÔÚʹÓõÄÊý¾ÝÁ´±í*/ |
| 65 | static LIST_HEAD(buddy_employ_head); |
| 66 | static LIST_HEAD(kmem_employ_head); |
| 67 | |
| 68 | /*ÒѾÊͷŵÄÊý¾ÝÁ´±í*/ |
| 69 | static LIST_HEAD(buddy_free_head); |
| 70 | static LIST_HEAD(kmem_free_head); |
| 71 | |
| 72 | /******************************************************************************* |
| 73 | * ¾Ö²¿º¯ÊýʵÏÖ * |
| 74 | *******************************************************************************/ |
| 75 | static void memset_tracker(struct heap_record * node) |
| 76 | { |
| 77 | node->size = 0; |
| 78 | node->addr = NULL; |
| 79 | |
| 80 | memset(node->task_name,0, TASK_COMM_LEN); |
| 81 | memset(node->stack_frame, 0, sizeof(node->stack_frame)); |
| 82 | } |
| 83 | |
| 84 | /******************************************************************************* |
| 85 | * ¹¦ÄÜÃèÊö: ¶Ôµ±Ç°Õ»½øÐлØËÝ£¬½«º¯Êý¹ì¼£±£´æ |
| 86 | * ²ÎÊý˵Ã÷: |
| 87 | * (´«Èë²ÎÊý) my_frame:¹ì¼£ÐÅÏ¢±£´æµÄµØÖ· |
| 88 | * |
| 89 | * (´«³ö²ÎÊý) void |
| 90 | * ·µ »Ø Öµ: void |
| 91 | * ÆäËü˵Ã÷: ÎÞ |
| 92 | *******************************************************************************/ |
| 93 | #ifdef CONFIG_KALLSYMS |
| 94 | void get_stack_process(unsigned char *my_frame) |
| 95 | #else |
| 96 | void get_stack_process(unsigned long *my_frame) |
| 97 | #endif |
| 98 | { |
| 99 | int urc; |
| 100 | int i = 0; |
| 101 | #ifdef CONFIG_KALLSYMS |
| 102 | char mytmp[MEM_TRACKER_MAX_STACK_LEN] = {0}; |
| 103 | #endif |
| 104 | struct stackframe frame; |
| 105 | register unsigned long current_sp asm ("sp"); |
| 106 | |
| 107 | #ifndef CONFIG_KALLSYMS |
| 108 | memset(my_frame, 0, MEM_TRACKER_MAX_STACK_LEVEL * sizeof(unsigned long)); |
| 109 | #endif |
| 110 | |
| 111 | frame.fp = (unsigned long)__builtin_frame_address(0); |
| 112 | frame.lr = (unsigned long)__builtin_return_address(0); |
| 113 | frame.sp = current_sp; |
| 114 | frame.pc = (unsigned long)get_stack_process; |
| 115 | |
| 116 | while (1) { |
| 117 | urc = unwind_frame(&frame); |
| 118 | /*Èô»ØËݽáÊø£¬ÔòÁ¢¼´Í˳ö*/ |
| 119 | if (urc < 0) |
| 120 | return; |
| 121 | |
| 122 | /*ΪÁ˽ÚÊ¡Äڴ棬×îÉϲãµÄ2¸öº¯Êý²»¸ú×Ù*/ |
| 123 | if (++i >= MEM_TRACKER_NOT_TRACE_LEVEL) { |
| 124 | #ifdef CONFIG_KALLSYMS |
| 125 | snprintf(mytmp, MEM_TRACKER_MAX_STACK_LEN - 1, "<%ps>", (void *)frame.pc); |
| 126 | strcpy(my_frame + ((i - MEM_TRACKER_NOT_TRACE_LEVEL) * MEM_TRACKER_MAX_STACK_LEN), mytmp); |
| 127 | #else |
| 128 | my_frame[i - MEM_TRACKER_NOT_TRACE_LEVEL] = (unsigned long)frame.pc; |
| 129 | #endif |
| 130 | if (i - MEM_TRACKER_NOT_TRACE_LEVEL >= MEM_TRACKER_MAX_STACK_LEVEL - 1) |
| 131 | return; |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | /******************************************************************************* |
| 137 | * ¹¦ÄÜÃèÊö: »ñÈ¡×îºóÒ»¸ö½Úµã |
| 138 | * ²ÎÊý˵Ã÷: |
| 139 | * (´«Èë²ÎÊý) head:Á´±íÍ· |
| 140 | * (´«³ö²ÎÊý) track¸ú×ÙÐÅÏ¢ |
| 141 | * ·µ »Ø Öµ: void |
| 142 | * ÆäËü˵Ã÷: ÎÞ |
| 143 | *******************************************************************************/ |
| 144 | static struct list_head *list_get_last_node(struct list_head *head) |
| 145 | { |
| 146 | struct heap_record *h = NULL; |
| 147 | struct list_head *tail_node = NULL; |
| 148 | |
| 149 | if (list_empty(head)) |
| 150 | return NULL; |
| 151 | |
| 152 | tail_node = head->prev; |
| 153 | |
| 154 | h = container_of(tail_node, struct heap_record, node); |
| 155 | |
| 156 | if(h->employ == MEM_TRACKER_FREED) |
| 157 | list_del(head->prev); |
| 158 | else |
| 159 | panic("[MEM_TRACKER]list error"); |
| 160 | |
| 161 | return tail_node; |
| 162 | } |
| 163 | |
| 164 | /******************************************************************************* |
| 165 | * ¹¦ÄÜÃèÊö: ÄÚ´æ¸ú×Ù³õʼ»¯ |
| 166 | * ²ÎÊý˵Ã÷: |
| 167 | * (´«Èë²ÎÊý) void |
| 168 | * (´«³ö²ÎÊý) void |
| 169 | * ·µ »Ø Öµ: void |
| 170 | * ÆäËü˵Ã÷: ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ |
| 171 | *******************************************************************************/ |
| 172 | void mem_trace_init(void) |
| 173 | { |
| 174 | int index; |
| 175 | unsigned long flags; |
| 176 | struct heap_record *record = NULL; |
| 177 | void __iomem *base = NULL; |
| 178 | |
| 179 | spin_lock_init(&buddy_track_lock); |
| 180 | |
| 181 | spin_lock_irqsave(&buddy_track_lock, flags); |
| 182 | |
| 183 | base = ZX_MEM_TRACKER_BASE; |
| 184 | node_list_max = ZX_MEM_TRACKER_SIZE / sizeof(struct heap_record); |
| 185 | |
| 186 | /* ³õʼ»¯¿ÕÏÐÄÚ´æ½ÚµãÁ´±í */ |
| 187 | for (index = 0; index < node_list_max; index++) |
| 188 | { |
| 189 | record = (struct heap_record *)(base + index * sizeof(struct heap_record)); |
| 190 | record->employ = MEM_TRACKER_FREED; |
| 191 | list_add(&record->node, &buddy_free_head); |
| 192 | } |
| 193 | |
| 194 | spin_unlock_irqrestore(&buddy_track_lock, flags); |
| 195 | } |
| 196 | |
| 197 | |
| 198 | /******************************************************************************* |
| 199 | * ¹¦ÄÜÃèÊö: kmallocÄÚ´æ¸ú×Ù³õʼ»¯ |
| 200 | * ²ÎÊý˵Ã÷: |
| 201 | * (´«Èë²ÎÊý) void |
| 202 | * (´«³ö²ÎÊý) void |
| 203 | * ·µ »Ø Öµ: void |
| 204 | * ÆäËü˵Ã÷: ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ |
| 205 | *******************************************************************************/ |
| 206 | void kmalloc_trace_init(void) |
| 207 | { |
| 208 | int index; |
| 209 | unsigned long flags; |
| 210 | struct heap_record *record = NULL; |
| 211 | void __iomem *base = NULL; |
| 212 | |
| 213 | spin_lock_init(&kmem_track_lock); |
| 214 | spin_lock_irqsave(&kmem_track_lock, flags); |
| 215 | |
| 216 | base = ZX_KMALLOC_TRACKER_BASE; |
| 217 | kmem_node_max = ZX_KMALLOC_TRACKER_SIZE / sizeof(struct heap_record); |
| 218 | |
| 219 | /* ³õʼ»¯¿ÕÏÐÄÚ´æ½ÚµãÁ´±í */ |
| 220 | for (index = 0; index < kmem_node_max; index++) |
| 221 | { |
| 222 | record = (struct heap_record *)(base + index * sizeof(struct heap_record)); |
| 223 | record->employ = MEM_TRACKER_FREED; |
| 224 | list_add(&record->node, &kmem_free_head); |
| 225 | } |
| 226 | |
| 227 | spin_unlock_irqrestore(&kmem_track_lock, flags); |
| 228 | } |
| 229 | |
| 230 | /******************************************************************************* |
| 231 | * ¹¦ÄÜÃèÊö: ÑéÖ¤entry½ÚµãºÏ·¨ÐÔ |
| 232 | * ²ÎÊý˵Ã÷: |
| 233 | * (´«Èë²ÎÊý) void |
| 234 | * (´«³ö²ÎÊý) void |
| 235 | * ·µ »Ø Öµ: void |
| 236 | * ÆäËü˵Ã÷: ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ |
| 237 | *******************************************************************************/ |
| 238 | int check_node_entry(const int entry) |
| 239 | { |
| 240 | void __iomem *base = NULL; |
| 241 | long limit = 0; |
| 242 | |
| 243 | base = ZX_KMALLOC_TRACKER_BASE; |
| 244 | limit = ZX_KMALLOC_TRACKER_SIZE + (int)base; |
| 245 | |
| 246 | if ((entry >= base) && (entry < limit)) |
| 247 | return MEM_TRUE; |
| 248 | |
| 249 | return MEM_FALSE; |
| 250 | } |
| 251 | |
| 252 | /******************************************************************************* |
| 253 | * ¹¦ÄÜÃèÊö: ÉêÇëÄÚ´æµãµ÷Óøýӿڣ¬¼Ç¼ÄÚ´æÉêÇë¹ì¼£ |
| 254 | * ²ÎÊý˵Ã÷: |
| 255 | * (´«Èë²ÎÊý) addr:ÄÚ´æµØÖ· |
| 256 | * order:Äڴ泤¶È,orderΪµ¥Î»£¬2µÄorder´Î·½¸öÒ³ |
| 257 | * (´«³ö²ÎÊý) void |
| 258 | * ·µ »Ø Öµ: void |
| 259 | * ÆäËü˵Ã÷: ÎÞ |
| 260 | *******************************************************************************/ |
| 261 | void mem_alloc_tracker(void *addr, int order) |
| 262 | { |
| 263 | unsigned long spin_flag; |
| 264 | struct heap_record *section = NULL; |
| 265 | struct list_head *head_ptr = NULL; |
| 266 | struct page *page_addr = NULL; |
| 267 | |
| 268 | if(!addr) |
| 269 | panic("[MEM_TRACKER] input item error\n"); |
| 270 | |
| 271 | page_addr = (struct page *)(addr); |
| 272 | |
| 273 | spin_lock_irqsave(&buddy_track_lock, spin_flag); |
| 274 | if (!(head_ptr = list_get_last_node(&buddy_free_head))) |
| 275 | { |
| 276 | printk("[MEM_TRACKER]buddy free_link_node have no node!!!\n"); |
| 277 | spin_unlock_irqrestore(&buddy_track_lock, spin_flag); |
| 278 | return; |
| 279 | } |
| 280 | spin_unlock_irqrestore(&buddy_track_lock, spin_flag); |
| 281 | section = container_of(head_ptr, struct heap_record, node); |
| 282 | |
| 283 | /*³õʼ»¯½ÚµãÐÅÏ¢*/ |
| 284 | memset_tracker(section); |
| 285 | section->addr = addr; |
| 286 | section->size = (1 << order) * PAGE_SIZE; |
| 287 | section->employ = MEM_TRACKER_ALLOCED; |
| 288 | memcpy(section->task_name, current->comm, TASK_COMM_LEN); |
| 289 | |
| 290 | /*»ñȡջ¹ì¼£*/ |
| 291 | get_stack_process(section->stack_frame); |
| 292 | page_addr->mem_track_entry = section; |
| 293 | |
| 294 | /*¼ÓÈëÒÑÉêÇëÄÚ´æ½Úµã¼Ç¼Á´±í*/ |
| 295 | spin_lock_irqsave(&buddy_track_lock, spin_flag); |
| 296 | list_add(§ion->node, &buddy_employ_head); |
| 297 | spin_unlock_irqrestore(&buddy_track_lock,spin_flag); |
| 298 | } |
| 299 | EXPORT_SYMBOL(mem_alloc_tracker); |
| 300 | |
| 301 | /******************************************************************************* |
| 302 | * ¹¦ÄÜÃèÊö: kmallocÉêÇëÄÚ´æµãµ÷Óøýӿڣ¬¼Ç¼ÄÚ´æÉêÇë¹ì¼£ |
| 303 | * ²ÎÊý˵Ã÷: |
| 304 | * (´«Èë²ÎÊý) addr:ÄÚ´æµØÖ· |
| 305 | * len:Äڴ泤¶È |
| 306 | * (´«³ö²ÎÊý) void |
| 307 | * ·µ »Ø Öµ: void |
| 308 | * ÆäËü˵Ã÷: ÎÞ |
| 309 | *******************************************************************************/ |
| 310 | void kmalloc_alloc_tracker(void *addr, int len) |
| 311 | { |
| 312 | unsigned long spin_flag; |
| 313 | struct heap_record *section = NULL; |
| 314 | struct list_head *head_ptr = NULL; |
| 315 | struct list_head *free_node_head = NULL; |
| 316 | struct list_head *employ_node_head = NULL; |
| 317 | struct page *page_addr = NULL; |
| 318 | |
| 319 | if(!addr) |
| 320 | panic("[MEM_TRACKER] input item error\n"); |
| 321 | |
| 322 | free_node_head = &kmem_free_head; |
| 323 | employ_node_head = &kmem_employ_head; |
| 324 | |
| 325 | spin_lock_irqsave(&kmem_track_lock, spin_flag); |
| 326 | |
| 327 | if (!(head_ptr = list_get_last_node(free_node_head))) |
| 328 | { |
| 329 | printk("[MEM_TRACKER]kmalloc free_link_node have no node!!!\n"); |
| 330 | spin_unlock_irqrestore(&kmem_track_lock, spin_flag); |
| 331 | return; |
| 332 | } |
| 333 | spin_unlock_irqrestore(&kmem_track_lock, spin_flag); |
| 334 | |
| 335 | section = container_of(head_ptr, struct heap_record, node); |
| 336 | |
| 337 | /*³õʼ»¯½ÚµãÐÅÏ¢*/ |
| 338 | memset_tracker(section); |
| 339 | section->addr = KMALLOC_SETUP(addr); |
| 340 | section->size = len; |
| 341 | section->employ = MEM_TRACKER_ALLOCED; |
| 342 | memcpy(section->task_name, current->comm, TASK_COMM_LEN); |
| 343 | |
| 344 | /*»ñȡջ¹ì¼£*/ |
| 345 | get_stack_process(section->stack_frame); |
| 346 | KMALLOC_SET_ENTRY(addr, section); |
| 347 | |
| 348 | if(MEM_FALSE == check_node_entry(section)) |
| 349 | panic("error"); |
| 350 | |
| 351 | /*¼ÓÈëÒÑÉêÇëÄÚ´æ½Úµã¼Ç¼Á´±í*/ |
| 352 | spin_lock_irqsave(&kmem_track_lock, spin_flag); |
| 353 | list_add(§ion->node, employ_node_head); |
| 354 | |
| 355 | spin_unlock_irqrestore(&kmem_track_lock,spin_flag); |
| 356 | } |
| 357 | EXPORT_SYMBOL(kmalloc_alloc_tracker); |
| 358 | |
| 359 | /******************************************************************************* |
| 360 | * ¹¦ÄÜÃèÊö: ÊÍ·ÅÄÚ´æµãµ÷ÓÃ¸Ã½Ó¿Ú |
| 361 | * ²ÎÊý˵Ã÷: |
| 362 | * (´«Èë²ÎÊý) entry:ÄÚ´æµØÖ· |
| 363 | * (´«³ö²ÎÊý) void |
| 364 | * ·µ »Ø Öµ: void |
| 365 | * ÆäËü˵Ã÷: ÎÞ |
| 366 | *******************************************************************************/ |
| 367 | void mem_free_tracker(void *entry, int type) |
| 368 | { |
| 369 | unsigned long spin_flag; |
| 370 | spinlock_t *mem_lock = NULL; |
| 371 | struct list_head *free_node_head = NULL; |
| 372 | struct heap_record *free_node = (struct heap_record *)entry; |
| 373 | |
| 374 | if (entry == NULL) |
| 375 | return; |
| 376 | |
| 377 | if (type == MEM_TRACKER_TYPE_BUDDY) |
| 378 | { |
| 379 | mem_lock = &buddy_track_lock; |
| 380 | free_node_head = &buddy_free_head; |
| 381 | } |
| 382 | else if (type == MEM_TRACKER_TYPE_KMALLOC) |
| 383 | { |
| 384 | mem_lock = &kmem_track_lock; |
| 385 | free_node_head = &kmem_free_head; |
| 386 | } |
| 387 | else |
| 388 | panic("mem_tracker type error\n"); |
| 389 | |
| 390 | spin_lock_irqsave(mem_lock, spin_flag); |
| 391 | |
| 392 | if (free_node->employ == MEM_TRACKER_ALLOCED) |
| 393 | { |
| 394 | free_node->employ = MEM_TRACKER_FREED; |
| 395 | list_move(&free_node->node, free_node_head); |
| 396 | } |
| 397 | else |
| 398 | { |
| 399 | printk("[MEM_TRACKER]error: employ is 0x%x employ_node MEM_TRACKER_FREED !!\n", free_node->employ); |
| 400 | } |
| 401 | |
| 402 | spin_unlock_irqrestore(mem_lock, spin_flag); |
| 403 | } |
| 404 | EXPORT_SYMBOL(mem_free_tracker); |
| 405 | |
| 406 | |
| 407 | |