| /* |
| * linux/mm/mem_tracker.c |
| * |
| * Copyright (C) 1993 Linus Torvalds |
| * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 |
| * vmalloc/vfree, Tigran Aivazian <tigran@veritas.com>, May 2000 |
| * Major rework to support vmap/vunmap, Christoph Hellwig, SGI, August 2002 |
| * Numa awareness, Christoph Lameter, SGI, June 2005 |
| */ |
| #include <linux/mm.h> |
| #include <linux/sched.h> |
| #include <linux/spinlock.h> |
| #include <linux/interrupt.h> |
| #include <linux/kallsyms.h> |
| #include <linux/list.h> |
| #include <linux/pfn.h> |
| #include <linux/atomic.h> |
| #include <asm/stacktrace.h> |
| #include <mach/iomap.h> |
| #include <linux/kprobes.h> |
| #include <asm/traps.h> |
| #include <linux/mem_tracker_def.h> |
| |
| /******************************************************************************* |
| * ºê¶¨Òå * |
| *******************************************************************************/ |
| #undef CONFIG_KALLSYMS |
| |
| #ifdef CONFIG_KALLSYMS |
| #define MEM_TRACKER_MAX_STACK_LEN (100) /*Õ»º¯Êý¹ì¼£µÄ×Ö·û¸öÊýÉÏÏÞ£»*/ |
| #endif |
| #define MEM_TRACKER_MAX_STACK_LEVEL (6) /*Õ»µÄ²ãÊý*/ |
| #define MEM_TRACKER_NOT_TRACE_LEVEL (2) |
| #define MEM_TRACKER_ALLOCED 0x00 |
| #define MEM_TRACKER_FREED 0x02 |
| |
| /******************************************************************************* |
| * Êý¾ÝÀàÐͶ¨Òå * |
| *******************************************************************************/ |
| struct heap_record { |
| struct list_head node; |
| void *addr; /*ÉêÇë³öµÄÄÚ´æµØÖ·*/ |
| unsigned int employ:8; |
| unsigned int size:24; /*¼Ç¼ÉêÇëµÄÄڴ泤¶È*/ |
| char task_name[TASK_COMM_LEN]; |
| #ifdef CONFIG_KALLSYMS |
| char stack_frame[MEM_TRACKER_MAX_STACK_LEVEL][MEM_TRACKER_MAX_STACK_LEN]; |
| /*º¯Êý»ØËݹ켣*/ |
| #else |
| unsigned long stack_frame[MEM_TRACKER_MAX_STACK_LEVEL]; |
| #endif |
| }; |
| |
| /******************************************************************************* |
| * º¯ÊýÉùÃ÷ * |
| *******************************************************************************/ |
| /******************************************************************************* |
| * È«¾Ö±äÁ¿¶¨Òå * |
| *******************************************************************************/ |
| int kmem_node_max; |
| int node_list_max; |
| static spinlock_t buddy_track_lock; |
| static spinlock_t kmem_track_lock; |
| |
| |
| /*ÕýÔÚʹÓõÄÊý¾ÝÁ´±í*/ |
| static LIST_HEAD(buddy_employ_head); |
| static LIST_HEAD(kmem_employ_head); |
| |
| /*ÒѾÊͷŵÄÊý¾ÝÁ´±í*/ |
| static LIST_HEAD(buddy_free_head); |
| static LIST_HEAD(kmem_free_head); |
| |
| /******************************************************************************* |
| * ¾Ö²¿º¯ÊýʵÏÖ * |
| *******************************************************************************/ |
| static void memset_tracker(struct heap_record * node) |
| { |
| node->size = 0; |
| node->addr = NULL; |
| |
| memset(node->task_name,0, TASK_COMM_LEN); |
| memset(node->stack_frame, 0, sizeof(node->stack_frame)); |
| } |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: ¶Ôµ±Ç°Õ»½øÐлØËÝ£¬½«º¯Êý¹ì¼£±£´æ |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) my_frame:¹ì¼£ÐÅÏ¢±£´æµÄµØÖ· |
| * |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ÎÞ |
| *******************************************************************************/ |
| #ifdef CONFIG_KALLSYMS |
| void get_stack_process(unsigned char *my_frame) |
| #else |
| void get_stack_process(unsigned long *my_frame) |
| #endif |
| { |
| int urc; |
| int i = 0; |
| #ifdef CONFIG_KALLSYMS |
| char mytmp[MEM_TRACKER_MAX_STACK_LEN] = {0}; |
| #endif |
| struct stackframe frame; |
| register unsigned long current_sp asm ("sp"); |
| |
| #ifndef CONFIG_KALLSYMS |
| memset(my_frame, 0, MEM_TRACKER_MAX_STACK_LEVEL * sizeof(unsigned long)); |
| #endif |
| |
| frame.fp = (unsigned long)__builtin_frame_address(0); |
| frame.lr = (unsigned long)__builtin_return_address(0); |
| frame.sp = current_sp; |
| frame.pc = (unsigned long)get_stack_process; |
| |
| while (1) { |
| urc = unwind_frame(&frame); |
| /*Èô»ØËݽáÊø£¬ÔòÁ¢¼´Í˳ö*/ |
| if (urc < 0) |
| return; |
| |
| /*ΪÁ˽ÚÊ¡Äڴ棬×îÉϲãµÄ2¸öº¯Êý²»¸ú×Ù*/ |
| if (++i >= MEM_TRACKER_NOT_TRACE_LEVEL) { |
| #ifdef CONFIG_KALLSYMS |
| snprintf(mytmp, MEM_TRACKER_MAX_STACK_LEN - 1, "<%ps>", (void *)frame.pc); |
| strcpy(my_frame + ((i - MEM_TRACKER_NOT_TRACE_LEVEL) * MEM_TRACKER_MAX_STACK_LEN), mytmp); |
| #else |
| my_frame[i - MEM_TRACKER_NOT_TRACE_LEVEL] = (unsigned long)frame.pc; |
| #endif |
| if (i - MEM_TRACKER_NOT_TRACE_LEVEL >= MEM_TRACKER_MAX_STACK_LEVEL - 1) |
| return; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: »ñÈ¡×îºóÒ»¸ö½Úµã |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) head:Á´±íÍ· |
| * (´«³ö²ÎÊý) track¸ú×ÙÐÅÏ¢ |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ÎÞ |
| *******************************************************************************/ |
| static struct list_head *list_get_last_node(struct list_head *head) |
| { |
| struct heap_record *h = NULL; |
| struct list_head *tail_node = NULL; |
| |
| if (list_empty(head)) |
| return NULL; |
| |
| tail_node = head->prev; |
| |
| h = container_of(tail_node, struct heap_record, node); |
| |
| if(h->employ == MEM_TRACKER_FREED) |
| list_del(head->prev); |
| else |
| panic("[MEM_TRACKER]list error"); |
| |
| return tail_node; |
| } |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: ÄÚ´æ¸ú×Ù³õʼ»¯ |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) void |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ |
| *******************************************************************************/ |
| void mem_trace_init(void) |
| { |
| int index; |
| unsigned long flags; |
| struct heap_record *record = NULL; |
| void __iomem *base = NULL; |
| |
| spin_lock_init(&buddy_track_lock); |
| |
| spin_lock_irqsave(&buddy_track_lock, flags); |
| |
| base = ZX_MEM_TRACKER_BASE; |
| node_list_max = ZX_MEM_TRACKER_SIZE / sizeof(struct heap_record); |
| |
| /* ³õʼ»¯¿ÕÏÐÄÚ´æ½ÚµãÁ´±í */ |
| for (index = 0; index < node_list_max; index++) |
| { |
| record = (struct heap_record *)(base + index * sizeof(struct heap_record)); |
| record->employ = MEM_TRACKER_FREED; |
| list_add(&record->node, &buddy_free_head); |
| } |
| |
| spin_unlock_irqrestore(&buddy_track_lock, flags); |
| } |
| |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: kmallocÄÚ´æ¸ú×Ù³õʼ»¯ |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) void |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ |
| *******************************************************************************/ |
| void kmalloc_trace_init(void) |
| { |
| int index; |
| unsigned long flags; |
| struct heap_record *record = NULL; |
| void __iomem *base = NULL; |
| |
| spin_lock_init(&kmem_track_lock); |
| spin_lock_irqsave(&kmem_track_lock, flags); |
| |
| base = ZX_KMALLOC_TRACKER_BASE; |
| kmem_node_max = ZX_KMALLOC_TRACKER_SIZE / sizeof(struct heap_record); |
| |
| /* ³õʼ»¯¿ÕÏÐÄÚ´æ½ÚµãÁ´±í */ |
| for (index = 0; index < kmem_node_max; index++) |
| { |
| record = (struct heap_record *)(base + index * sizeof(struct heap_record)); |
| record->employ = MEM_TRACKER_FREED; |
| list_add(&record->node, &kmem_free_head); |
| } |
| |
| spin_unlock_irqrestore(&kmem_track_lock, flags); |
| } |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: ÑéÖ¤entry½ÚµãºÏ·¨ÐÔ |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) void |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ |
| *******************************************************************************/ |
| int check_node_entry(const int entry) |
| { |
| void __iomem *base = NULL; |
| long limit = 0; |
| |
| base = ZX_KMALLOC_TRACKER_BASE; |
| limit = ZX_KMALLOC_TRACKER_SIZE + (int)base; |
| |
| if ((entry >= base) && (entry < limit)) |
| return MEM_TRUE; |
| |
| return MEM_FALSE; |
| } |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: ÉêÇëÄÚ´æµãµ÷Óøýӿڣ¬¼Ç¼ÄÚ´æÉêÇë¹ì¼£ |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) addr:ÄÚ´æµØÖ· |
| * order:Äڴ泤¶È,orderΪµ¥Î»£¬2µÄorder´Î·½¸öÒ³ |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ÎÞ |
| *******************************************************************************/ |
| void mem_alloc_tracker(void *addr, int order) |
| { |
| unsigned long spin_flag; |
| struct heap_record *section = NULL; |
| struct list_head *head_ptr = NULL; |
| struct page *page_addr = NULL; |
| |
| if(!addr) |
| panic("[MEM_TRACKER] input item error\n"); |
| |
| page_addr = (struct page *)(addr); |
| |
| spin_lock_irqsave(&buddy_track_lock, spin_flag); |
| if (!(head_ptr = list_get_last_node(&buddy_free_head))) |
| { |
| printk("[MEM_TRACKER]buddy free_link_node have no node!!!\n"); |
| spin_unlock_irqrestore(&buddy_track_lock, spin_flag); |
| return; |
| } |
| spin_unlock_irqrestore(&buddy_track_lock, spin_flag); |
| section = container_of(head_ptr, struct heap_record, node); |
| |
| /*³õʼ»¯½ÚµãÐÅÏ¢*/ |
| memset_tracker(section); |
| section->addr = addr; |
| section->size = (1 << order) * PAGE_SIZE; |
| section->employ = MEM_TRACKER_ALLOCED; |
| memcpy(section->task_name, current->comm, TASK_COMM_LEN); |
| |
| /*»ñȡջ¹ì¼£*/ |
| get_stack_process(section->stack_frame); |
| page_addr->mem_track_entry = section; |
| |
| /*¼ÓÈëÒÑÉêÇëÄÚ´æ½Úµã¼Ç¼Á´±í*/ |
| spin_lock_irqsave(&buddy_track_lock, spin_flag); |
| list_add(§ion->node, &buddy_employ_head); |
| spin_unlock_irqrestore(&buddy_track_lock,spin_flag); |
| } |
| EXPORT_SYMBOL(mem_alloc_tracker); |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: kmallocÉêÇëÄÚ´æµãµ÷Óøýӿڣ¬¼Ç¼ÄÚ´æÉêÇë¹ì¼£ |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) addr:ÄÚ´æµØÖ· |
| * len:Äڴ泤¶È |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ÎÞ |
| *******************************************************************************/ |
| void kmalloc_alloc_tracker(void *addr, int len) |
| { |
| unsigned long spin_flag; |
| struct heap_record *section = NULL; |
| struct list_head *head_ptr = NULL; |
| struct list_head *free_node_head = NULL; |
| struct list_head *employ_node_head = NULL; |
| struct page *page_addr = NULL; |
| |
| if(!addr) |
| panic("[MEM_TRACKER] input item error\n"); |
| |
| free_node_head = &kmem_free_head; |
| employ_node_head = &kmem_employ_head; |
| |
| spin_lock_irqsave(&kmem_track_lock, spin_flag); |
| |
| if (!(head_ptr = list_get_last_node(free_node_head))) |
| { |
| printk("[MEM_TRACKER]kmalloc free_link_node have no node!!!\n"); |
| spin_unlock_irqrestore(&kmem_track_lock, spin_flag); |
| return; |
| } |
| spin_unlock_irqrestore(&kmem_track_lock, spin_flag); |
| |
| section = container_of(head_ptr, struct heap_record, node); |
| |
| /*³õʼ»¯½ÚµãÐÅÏ¢*/ |
| memset_tracker(section); |
| section->addr = KMALLOC_SETUP(addr); |
| section->size = len; |
| section->employ = MEM_TRACKER_ALLOCED; |
| memcpy(section->task_name, current->comm, TASK_COMM_LEN); |
| |
| /*»ñȡջ¹ì¼£*/ |
| get_stack_process(section->stack_frame); |
| KMALLOC_SET_ENTRY(addr, section); |
| |
| if(MEM_FALSE == check_node_entry(section)) |
| panic("error"); |
| |
| /*¼ÓÈëÒÑÉêÇëÄÚ´æ½Úµã¼Ç¼Á´±í*/ |
| spin_lock_irqsave(&kmem_track_lock, spin_flag); |
| list_add(§ion->node, employ_node_head); |
| |
| spin_unlock_irqrestore(&kmem_track_lock,spin_flag); |
| } |
| EXPORT_SYMBOL(kmalloc_alloc_tracker); |
| |
| /******************************************************************************* |
| * ¹¦ÄÜÃèÊö: ÊÍ·ÅÄÚ´æµãµ÷ÓÃ¸Ã½Ó¿Ú |
| * ²ÎÊý˵Ã÷: |
| * (´«Èë²ÎÊý) entry:ÄÚ´æµØÖ· |
| * (´«³ö²ÎÊý) void |
| * ·µ »Ø Öµ: void |
| * ÆäËü˵Ã÷: ÎÞ |
| *******************************************************************************/ |
| void mem_free_tracker(void *entry, int type) |
| { |
| unsigned long spin_flag; |
| spinlock_t *mem_lock = NULL; |
| struct list_head *free_node_head = NULL; |
| struct heap_record *free_node = (struct heap_record *)entry; |
| |
| if (entry == NULL) |
| return; |
| |
| if (type == MEM_TRACKER_TYPE_BUDDY) |
| { |
| mem_lock = &buddy_track_lock; |
| free_node_head = &buddy_free_head; |
| } |
| else if (type == MEM_TRACKER_TYPE_KMALLOC) |
| { |
| mem_lock = &kmem_track_lock; |
| free_node_head = &kmem_free_head; |
| } |
| else |
| panic("mem_tracker type error\n"); |
| |
| spin_lock_irqsave(mem_lock, spin_flag); |
| |
| if (free_node->employ == MEM_TRACKER_ALLOCED) |
| { |
| free_node->employ = MEM_TRACKER_FREED; |
| list_move(&free_node->node, free_node_head); |
| } |
| else |
| { |
| printk("[MEM_TRACKER]error: employ is 0x%x employ_node MEM_TRACKER_FREED !!\n", free_node->employ); |
| } |
| |
| spin_unlock_irqrestore(mem_lock, spin_flag); |
| } |
| EXPORT_SYMBOL(mem_free_tracker); |
| |
| |
| |