blob: 396376309f3a78813e3105e49801bc7c1126475d [file] [log] [blame]
/*
* 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>
/*******************************************************************************
* ºê¶¨Òå *
*******************************************************************************/
#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(&section->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(&section->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);