⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 exmap.c

📁 内存管理工具Exmap。该工具比 ps 或 top 更精确
💻 C
字号:
/* * Dump info on all pages in all VMAs for a given PID. * * (c) John Berthels 2005 <jjberthels@gmail.com>. See COPYING for license. *//* * Locking: * We need to ensure the mm_struct doesn't go away beneath us: * inc mm->mm_count/mmdrop() * * We can't vmalloc whilst holding mm->page_table_lock (otherwise * page_check_address() can deadlock against us). * * We have to hold mm->mmap_sem with read bias (to avoid the vmas changing) * * So we: * inc mm_count * down_read(mm->mmap_sem) * walk vmas, allocating space to hold per-page info * take page-table-lock * walk vmas, walking page tables within vma, filling in alloc'd space * release page_table_lock * release mmap_sem * mmdrop() */#include <linux/module.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/mm.h>#include <linux/pagemap.h>#include <linux/version.h>#include <linux/vmalloc.h>#include <linux/swap.h>#include <linux/swapops.h>/* Allow compilation on some kernels prior to 2.6.11 */#undef HAVE_PUD_T#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)#define HAVE_PUD_T#endif#define PROCFS_NAME "exmap"MODULE_LICENSE ("GPL");MODULE_AUTHOR ("John Berthels <jjberthels@gmail.com>");MODULE_DESCRIPTION ("Show page-level information on all vmas within a task");/* Not very nice. We save the data at write() time and then use it at read(). * We also keep a cursor so that sequential reads work correctly. * I'm pretty sure that there are nicer ways to do this. */struct exmap_data{	struct exmap_vma_data *vma_data;	int num_vmas;	/* Index to next vma_data to examine */	int vma_cursor;};struct exmap_vma_data{	unsigned long vm_start;	int start_shown;	/* Our vmalloc'ed array of pte's */	pte_t *ptes;	unsigned long num_pages;	/* Next page to examine, starting at zero for the first in the vma */	unsigned long page_cursor;};static struct exmap_data local_data;/* Only called once, at load time */static void init_local_data(void){	local_data.vma_data = NULL;	local_data.num_vmas = 0;	local_data.vma_cursor = 0;}static void clear_local_data(void){	struct exmap_vma_data *vma_data;	int i;	if (local_data.vma_data == NULL) {		init_local_data();		return;	}		for (i = 0, vma_data = local_data.vma_data;	     i < local_data.num_vmas;	     ++i, ++vma_data) {		if (vma_data->ptes != NULL) {			vfree(vma_data->ptes);		}	}	vfree(local_data.vma_data);	init_local_data();}/* Modelled on __follow_page. Except we don't support HUGETLB and we * only actually use the pfn or pte, rather than getting hold of the * struct page. */static int walk_page_tables(struct mm_struct *mm,			    unsigned long address,			    pte_t *pte_ret){	pgd_t *pgd;	pmd_t *pmd;	pte_t *ptep;#ifdef HAVE_PUD_T	pud_t *pud;#endif	// No support for HUGETLB as yet	//page = follow_huge_addr(mm, address, write);	//if (! IS_ERR(page))	//return page;	pgd = pgd_offset(mm, address);	if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))		goto out;#ifdef HAVE_PUD_T	pud = pud_offset(pgd, address);	if (pud_none(*pud) || unlikely(pud_bad(*pud)))		goto out;		pmd = pmd_offset(pud, address);#else	pmd = pmd_offset(pgd, address);#endif	if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))		goto out;	ptep = pte_offset_map(pmd, address);	if (!ptep)		goto out;	*pte_ret = *ptep;	pte_unmap(ptep);	return 0;out:	return -1;}static void save_vma_page_info(struct vm_area_struct *vma,			      struct exmap_vma_data *vma_data){	int pageno;	pte_t *ptep = vma_data->ptes;	unsigned long page_addr = vma->vm_start;	for (pageno = 0;	     pageno < vma_data->num_pages;	     ++pageno, page_addr += PAGE_SIZE, ptep++) {				if (walk_page_tables(vma->vm_mm,				     page_addr,				     ptep) < 0) {						/* This appears to happen. It looks like if			 * high enough pages haven't been touched, the			 * pmd doens't yet exist. */			*ptep = __pte(0);		}	}}static int alloc_vmalist_info(struct vm_area_struct *vma_base){	struct vm_area_struct *vma;	struct exmap_vma_data *vma_data;	unsigned long alloc_size;	int errcode = 0;	if (local_data.vma_data != NULL) {		clear_local_data();	}	for (vma = vma_base; vma != NULL; vma = vma->vm_next) {		++local_data.num_vmas;	}	/* Allocate one struct per vma */	alloc_size = sizeof(struct exmap_vma_data) * local_data.num_vmas;	local_data.vma_data = vmalloc(alloc_size);	if (local_data.vma_data == NULL) {		printk (KERN_ALERT "/proc/%s : vmalloc(%lu) failed\n",			PROCFS_NAME, alloc_size);		errcode = -ENOMEM;		goto ErrExit;	}	/* For each vma_data struct, calculate the number of pages	 * and allocate pte space */	for (vma = vma_base, vma_data = local_data.vma_data;	     vma != NULL;	     vma = vma->vm_next, vma_data++) {				vma_data->num_pages			= (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;		alloc_size = sizeof(pte_t) * vma_data->num_pages;		vma_data->ptes = vmalloc(alloc_size);		if (vma_data->ptes == NULL) {			printk (KERN_ALERT "/proc/%s : vma vmalloc(%lu) failed",				PROCFS_NAME, alloc_size);			errcode = -ENOMEM;			goto ErrExit;		}		vma_data->page_cursor = 0;		vma_data->vm_start = vma->vm_start;		vma_data->start_shown = 0;	}	return 0;ErrExit:	if (local_data.vma_data != NULL) {		clear_local_data();	}	return errcode;}static void store_vmalist_info(struct vm_area_struct *vma_base){	struct vm_area_struct *vma;	struct exmap_vma_data *vma_data;	/* For each vma_data struct, fill in the ptes */	for (vma = vma_base, vma_data = local_data.vma_data;	     vma != NULL;	     vma = vma->vm_next, vma_data++) {		save_vma_page_info(vma, vma_data);	}}/* Copied (and modded) from user_atoi in arch/frv somewhere */static unsigned long user_atoul (const char __user * ubuf, int len){	char buf[16];	unsigned long ret;	if (len > 15) {		printk (KERN_ALERT "/proc/%s : user_atoul bad length %d\n",			PROCFS_NAME, len);		return -EINVAL;	}	if (copy_from_user (buf, ubuf, len)) {		printk (KERN_ALERT "/proc/%s : user_atoul cfu failed\n",			PROCFS_NAME);		return -EFAULT;	}	buf[len] = 0;/*	printk(KERN_INFO "user_atoul: examining buffer [%s]\n", buf); */	ret = simple_strtoul (buf, NULL, 0);	return ret;}/* Add the output line for the given page to the buffer, returning * number of chars added. Returns 0 if it can't fit into the * buffer. */static int show_one_page(pte_t pte,			 char *buffer,			 int buflen){	int len;	unsigned long pfn = 0UL;	swp_entry_t swap_entry;	int present;	int writable;	unsigned long cookie;	swap_entry.val = 0UL; /* All zeros not a valid entry */	present = pte_present(pte);	writable = pte_write(pte) ? 1 : 0;	if (present) {		pfn = pte_pfn(pte);		if (!pfn_valid(pfn)) {			pfn = 0;		}		cookie = pfn;	}	else {		swap_entry = pte_to_swp_entry(pte);		cookie = swap_entry.val;	}		len = snprintf (buffer,			buflen,			"%d %d %lx\n",			present,			writable,			cookie);		if (len >= buflen)		goto ETOOLONG;		return len; ETOOLONG:	buffer[0] = '\0';	return 0;}static int show_vma_start(struct exmap_vma_data *vma_data,			  char *buffer,			  int buflen){	int len;		len = snprintf (buffer,			buflen,			"VMA %lx %lu\n",			vma_data->vm_start,			vma_data->num_pages);	if (len >= buflen)		goto ETOOLONG;	return len; ETOOLONG:	buffer[0] = '\0';	return 0;}static int exmap_show_next(char *buffer, int length){	int offset = 0;	struct exmap_vma_data *vma_data;	pte_t pte;	int line_len;	while (local_data.vma_cursor < local_data.num_vmas) {		vma_data = local_data.vma_data + local_data.vma_cursor;//		printk (KERN_INFO//			"exmap: examining vma %08lx [%d/%d] %d\n",//			vma_data->vm_start,//			local_data.vma_cursor,//			local_data.num_vmas,//			vma_data->start_shown);		if (!vma_data->start_shown) {//			printk (KERN_INFO//				"exmap: svs\n");			line_len = show_vma_start(vma_data,						  buffer + offset,						  length - offset);			if (line_len <= 0)				/* Cursor in right position for next read */				goto Finished;			offset += line_len;			vma_data->start_shown = 1;		}		while (vma_data->page_cursor < vma_data->num_pages) {			pte = vma_data->ptes[vma_data->page_cursor];			line_len = show_one_page(pte,						 buffer + offset,						 length - offset);			if (line_len <= 0)				/* Cursor in right position for next read */				goto Finished;			offset += line_len;			vma_data->page_cursor++;		}		local_data.vma_cursor++;	}Finished:	return offset;}int setup_from_pid(pid_t pid){	struct mm_struct *mm = NULL;	struct task_struct *tsk;	int errcode = -EINVAL;	tsk = find_task_by_pid(pid);	if (tsk == NULL) {		printk (KERN_ALERT			"/proc/%s: can't find task for pid %d\n",			PROCFS_NAME, pid);		goto Exit;	}	if (tsk == current) {		printk (KERN_ALERT			"/proc/%s: can't self-examine %d\n",			PROCFS_NAME, pid);		goto Exit;	}		mm = get_task_mm(tsk);	if (mm == NULL) {		printk (KERN_ALERT			"/proc/%s: can't get mm for task for pid %d\n",			PROCFS_NAME, pid);		goto Exit;	}	/* Stop the vma list changing.	 * We can't take page_table_lock yet because we vmalloc */	down_read(&mm->mmap_sem);	if ((errcode = alloc_vmalist_info(mm->mmap)) < 0) {		printk (KERN_ALERT			"/proc/%s: failed to alloc for mm for pid %d\n",			PROCFS_NAME, pid);		goto Exit;	}	/* We've got our space allocated, fill it in */	spin_lock(&mm->page_table_lock);	store_vmalist_info(mm->mmap);	spin_unlock(&mm->page_table_lock);	errcode = 0;Exit:	if (mm) {		up_read(&mm->mmap_sem);		mmput(mm);	}		return errcode;}/* * Writes to the procfile should be of the form: * pid:0xdeadbeef\n * where deadbeef is the hex addr of the vma to examine * and pid is the (decimal) pid of the process to examine */static int procfile_write (struct file *file,			   const char __user *buffer,			   unsigned long count,			   void *data){	pid_t pid;	int errcode = -EINVAL;	pid = user_atoul(buffer, count);	if (pid <= 0) {		printk (KERN_ALERT			"/proc/%s:can't parse buffer to read pid\n",			PROCFS_NAME);		return errcode;	}	if ((errcode = setup_from_pid(pid)) < 0) {		printk (KERN_ALERT			"/proc/%s: failed save info for pid %d [%d]\n",			PROCFS_NAME, pid, errcode);		return errcode;	}		return count;}/* * Only support sequential reading of file from start to finish * (following a write() to set the pid to examine */static int procfile_read (char *buffer,			  char **buffer_location,			  off_t offset,			  int buffer_length,			  int *eof,			  void *data){	int ret;	if (local_data.vma_data == NULL) {		printk (KERN_ALERT "/proc/%s: vma data not set\n",			PROCFS_NAME);		return -EINVAL;	}		ret = exmap_show_next(buffer, buffer_length);	if (ret > 0) {		*buffer_location = buffer;	}	return ret;}int init_module (){	struct proc_dir_entry *exmap_proc_file;	printk (KERN_INFO "/proc/%s: insert\n", PROCFS_NAME);		exmap_proc_file = create_proc_entry (PROCFS_NAME,							0644,							NULL);	if (exmap_proc_file == NULL) {		remove_proc_entry (PROCFS_NAME, &proc_root);		printk (KERN_ALERT "/proc/%s: could not initialize\n",			PROCFS_NAME);		return -ENOMEM;	}		exmap_proc_file->read_proc = procfile_read;	exmap_proc_file->write_proc = procfile_write;	exmap_proc_file->owner = THIS_MODULE;		/*     exmap_proc_file->mode         = S_IFREG | S_IRUGO; */	/* TODO - this is quite probably a security problem */	exmap_proc_file->mode = 0666;		exmap_proc_file->uid = 0;	exmap_proc_file->gid = 0;	exmap_proc_file->size = 0;	init_local_data();	return 0;}void cleanup_module (){	printk (KERN_INFO "/proc/%s: remove\n", PROCFS_NAME);	remove_proc_entry (PROCFS_NAME, &proc_root);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -