2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
# include <linux/file.h>
2008-05-04 19:12:55 +04:00
# include <linux/fdtable.h>
2009-03-30 03:50:06 +04:00
# include <linux/fs_struct.h>
2005-04-17 02:20:36 +04:00
# include <linux/mount.h>
2007-05-08 11:26:04 +04:00
# include <linux/ptrace.h>
2005-04-17 02:20:36 +04:00
# include <linux/seq_file.h>
# include "internal.h"
/*
* Logic : we ' ve got two memory sums for each process , " shared " , and
2008-10-16 21:02:37 +04:00
* " non-shared " . Shared memory may get counted more than once , for
2005-04-17 02:20:36 +04:00
* each process that owns it . Non - shared memory is counted
* accurately .
*/
2008-02-08 15:18:33 +03:00
void task_mem ( struct seq_file * m , struct mm_struct * mm )
2005-04-17 02:20:36 +04:00
{
2009-01-08 15:04:47 +03:00
struct vm_area_struct * vma ;
2009-01-08 15:04:47 +03:00
struct vm_region * region ;
2009-01-08 15:04:47 +03:00
struct rb_node * p ;
2009-01-08 15:04:47 +03:00
unsigned long bytes = 0 , sbytes = 0 , slack = 0 , size ;
2005-04-17 02:20:36 +04:00
down_read ( & mm - > mmap_sem ) ;
2009-01-08 15:04:47 +03:00
for ( p = rb_first ( & mm - > mm_rb ) ; p ; p = rb_next ( p ) ) {
vma = rb_entry ( p , struct vm_area_struct , vm_rb ) ;
2005-04-17 02:20:36 +04:00
2009-01-08 15:04:47 +03:00
bytes + = kobjsize ( vma ) ;
2009-01-08 15:04:47 +03:00
region = vma - > vm_region ;
if ( region ) {
size = kobjsize ( region ) ;
size + = region - > vm_end - region - > vm_start ;
} else {
size = vma - > vm_end - vma - > vm_start ;
}
2005-04-17 02:20:36 +04:00
if ( atomic_read ( & mm - > mm_count ) > 1 | |
2009-01-08 15:04:47 +03:00
vma - > vm_flags & VM_MAYSHARE ) {
2009-01-08 15:04:47 +03:00
sbytes + = size ;
2005-04-17 02:20:36 +04:00
} else {
2009-01-08 15:04:47 +03:00
bytes + = size ;
if ( region )
slack = region - > vm_end - vma - > vm_end ;
2005-04-17 02:20:36 +04:00
}
}
if ( atomic_read ( & mm - > mm_count ) > 1 )
sbytes + = kobjsize ( mm ) ;
else
bytes + = kobjsize ( mm ) ;
2009-03-30 15:20:30 +04:00
if ( current - > fs & & current - > fs - > users > 1 )
2005-04-17 02:20:36 +04:00
sbytes + = kobjsize ( current - > fs ) ;
else
bytes + = kobjsize ( current - > fs ) ;
if ( current - > files & & atomic_read ( & current - > files - > count ) > 1 )
sbytes + = kobjsize ( current - > files ) ;
else
bytes + = kobjsize ( current - > files ) ;
if ( current - > sighand & & atomic_read ( & current - > sighand - > count ) > 1 )
sbytes + = kobjsize ( current - > sighand ) ;
else
bytes + = kobjsize ( current - > sighand ) ;
bytes + = kobjsize ( current ) ; /* includes kernel stack */
2008-02-08 15:18:33 +03:00
seq_printf ( m ,
2005-04-17 02:20:36 +04:00
" Mem: \t %8lu bytes \n "
" Slack: \t %8lu bytes \n "
" Shared: \t %8lu bytes \n " ,
bytes , slack , sbytes ) ;
up_read ( & mm - > mmap_sem ) ;
}
unsigned long task_vsize ( struct mm_struct * mm )
{
2009-01-08 15:04:47 +03:00
struct vm_area_struct * vma ;
struct rb_node * p ;
2005-04-17 02:20:36 +04:00
unsigned long vsize = 0 ;
down_read ( & mm - > mmap_sem ) ;
2009-01-08 15:04:47 +03:00
for ( p = rb_first ( & mm - > mm_rb ) ; p ; p = rb_next ( p ) ) {
vma = rb_entry ( p , struct vm_area_struct , vm_rb ) ;
2009-01-08 15:04:47 +03:00
vsize + = vma - > vm_end - vma - > vm_start ;
2005-04-17 02:20:36 +04:00
}
up_read ( & mm - > mmap_sem ) ;
return vsize ;
}
int task_statm ( struct mm_struct * mm , int * shared , int * text ,
int * data , int * resident )
{
2009-01-08 15:04:47 +03:00
struct vm_area_struct * vma ;
2009-01-08 15:04:47 +03:00
struct vm_region * region ;
2009-01-08 15:04:47 +03:00
struct rb_node * p ;
2005-04-17 02:20:36 +04:00
int size = kobjsize ( mm ) ;
down_read ( & mm - > mmap_sem ) ;
2009-01-08 15:04:47 +03:00
for ( p = rb_first ( & mm - > mm_rb ) ; p ; p = rb_next ( p ) ) {
vma = rb_entry ( p , struct vm_area_struct , vm_rb ) ;
size + = kobjsize ( vma ) ;
2009-01-08 15:04:47 +03:00
region = vma - > vm_region ;
if ( region ) {
size + = kobjsize ( region ) ;
size + = region - > vm_end - region - > vm_start ;
}
2005-04-17 02:20:36 +04:00
}
size + = ( * text = mm - > end_code - mm - > start_code ) ;
size + = ( * data = mm - > start_stack - mm - > start_data ) ;
up_read ( & mm - > mmap_sem ) ;
* resident = size ;
return size ;
}
2009-01-08 15:04:47 +03:00
/*
* display a single VMA to a sequenced file
*/
static int nommu_vma_show ( struct seq_file * m , struct vm_area_struct * vma )
{
unsigned long ino = 0 ;
struct file * file ;
dev_t dev = 0 ;
int flags , len ;
flags = vma - > vm_flags ;
file = vma - > vm_file ;
if ( file ) {
struct inode * inode = vma - > vm_file - > f_path . dentry - > d_inode ;
dev = inode - > i_sb - > s_dev ;
ino = inode - > i_ino ;
}
seq_printf ( m ,
2009-04-03 03:56:32 +04:00
" %08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n " ,
2009-01-08 15:04:47 +03:00
vma - > vm_start ,
vma - > vm_end ,
flags & VM_READ ? ' r ' : ' - ' ,
flags & VM_WRITE ? ' w ' : ' - ' ,
flags & VM_EXEC ? ' x ' : ' - ' ,
flags & VM_MAYSHARE ? flags & VM_SHARED ? ' S ' : ' s ' : ' p ' ,
2009-04-03 03:56:32 +04:00
( unsigned long long ) vma - > vm_pgoff < < PAGE_SHIFT ,
2009-01-08 15:04:47 +03:00
MAJOR ( dev ) , MINOR ( dev ) , ino , & len ) ;
if ( file ) {
len = 25 + sizeof ( void * ) * 6 - len ;
if ( len < 1 )
len = 1 ;
seq_printf ( m , " %*c " , len , ' ' ) ;
seq_path ( m , & file - > f_path , " " ) ;
}
seq_putc ( m , ' \n ' ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
2006-09-27 12:50:19 +04:00
* display mapping lines for a particular process ' s / proc / pid / maps
2005-04-17 02:20:36 +04:00
*/
2009-01-08 15:04:47 +03:00
static int show_map ( struct seq_file * m , void * _p )
2005-04-17 02:20:36 +04:00
{
2009-01-08 15:04:47 +03:00
struct rb_node * p = _p ;
2007-05-08 11:26:04 +04:00
2009-01-08 15:04:47 +03:00
return nommu_vma_show ( m , rb_entry ( p , struct vm_area_struct , vm_rb ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-27 12:50:19 +04:00
2005-04-17 02:20:36 +04:00
static void * m_start ( struct seq_file * m , loff_t * pos )
{
2006-09-27 12:50:19 +04:00
struct proc_maps_private * priv = m - > private ;
struct mm_struct * mm ;
2009-01-08 15:04:47 +03:00
struct rb_node * p ;
2006-09-27 12:50:19 +04:00
loff_t n = * pos ;
/* pin the task and mm whilst we play with them */
priv - > task = get_pid_task ( priv - > pid , PIDTYPE_PID ) ;
if ( ! priv - > task )
return NULL ;
2008-01-02 17:09:57 +03:00
mm = mm_for_maps ( priv - > task ) ;
2006-09-27 12:50:19 +04:00
if ( ! mm ) {
put_task_struct ( priv - > task ) ;
priv - > task = NULL ;
return NULL ;
}
/* start from the Nth VMA */
2009-01-08 15:04:47 +03:00
for ( p = rb_first ( & mm - > mm_rb ) ; p ; p = rb_next ( p ) )
2006-09-27 12:50:19 +04:00
if ( n - - = = 0 )
2009-01-08 15:04:47 +03:00
return p ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2006-09-27 12:50:19 +04:00
static void m_stop ( struct seq_file * m , void * _vml )
2005-04-17 02:20:36 +04:00
{
2006-09-27 12:50:19 +04:00
struct proc_maps_private * priv = m - > private ;
if ( priv - > task ) {
struct mm_struct * mm = priv - > task - > mm ;
up_read ( & mm - > mmap_sem ) ;
mmput ( mm ) ;
put_task_struct ( priv - > task ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-09-27 12:50:19 +04:00
2009-01-08 15:04:47 +03:00
static void * m_next ( struct seq_file * m , void * _p , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2009-01-08 15:04:47 +03:00
struct rb_node * p = _p ;
2006-09-27 12:50:19 +04:00
( * pos ) + + ;
2009-01-08 15:04:47 +03:00
return p ? rb_next ( p ) : NULL ;
2005-04-17 02:20:36 +04:00
}
2006-09-27 12:50:19 +04:00
2008-02-08 15:21:19 +03:00
static const struct seq_operations proc_pid_maps_ops = {
2005-04-17 02:20:36 +04:00
. start = m_start ,
. next = m_next ,
. stop = m_stop ,
. show = show_map
} ;
2006-06-26 11:25:48 +04:00
static int maps_open ( struct inode * inode , struct file * file )
{
2006-09-27 12:50:19 +04:00
struct proc_maps_private * priv ;
int ret = - ENOMEM ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( priv ) {
priv - > pid = proc_pid ( inode ) ;
ret = seq_open ( file , & proc_pid_maps_ops ) ;
if ( ! ret ) {
struct seq_file * m = file - > private_data ;
m - > private = priv ;
} else {
kfree ( priv ) ;
}
2006-06-26 11:25:48 +04:00
}
return ret ;
}
2007-02-12 11:55:34 +03:00
const struct file_operations proc_maps_operations = {
2006-06-26 11:25:48 +04:00
. open = maps_open ,
. read = seq_read ,
. llseek = seq_lseek ,
2006-09-27 12:50:19 +04:00
. release = seq_release_private ,
2006-06-26 11:25:48 +04:00
} ;