Linux检测内存泄露的脚本

1.针对应用场景下的内存泄露

mm-leak-app.sh

#!/bin/sh
 
if [ $# -ne 1 ]; then
  echo "Usage: `basename $0` process_name"
  exit 1
fi
 
APPNAME=$1
PROC="`ps -ef | grep "$APPNAME" | grep -v "grep" | grep -v "awk" | grep -v $0 | awk '{print $2}'`"
 
if [ -z $PROC ]; then
  echo "invalid process_name"
  exit 1
fi
 
SMAPS="/proc/$PROC/smaps"
STATUS="/proc/$PROC/status"
echo "proc ---$PROC----"
OLDHEAP="0"
while :
do
  #HEAP="`cat $STATUS | grep "VmData" | awk '{print $2}'`"
  HEAP=`cat $SMAPS | grep -A 5 "heap" | grep "Rss" | awk '{print $2}'`
  if [ $HEAP -lt $OLDHEAP ]; then
    echo "`date` HEAP -`expr $OLDHEAP - $HEAP` to $HEAP kb"
    OLDHEAP=$HEAP
  elif [ $HEAP -gt $OLDHEAP ]; then
    echo "`date` HEAP +`expr $HEAP - $OLDHEAP` to $HEAP kb"
    OLDHEAP=$HEAP
  fi
  sleep 1
done

我们来个测试程序:

leak_demo.c

#include <stdio.h>
#include <unistd.h>
void main(void)
{

	char* mm = NULL;
	do{
		mm = (char *)malloc(100);  
		memset(mm,0x0,100);
		sleep(1);
	}while(1);

}

我们先将leak_demo编译后生成a.out,然后运行起来,之后运行脚本

sh mm-leak-app.sh a.out
结果如下:
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 4 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 8 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 12 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 16 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 20 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 24 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 28 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 32 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 36 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 40 kb
2018年 10月 17日 星期三 17:16:48 CST HEAP +4 to 44 kb

我们发现脚本是能够检测到程序的内存泄露的。

2.针对内核场景下的内存泄露

mm-leak-kernel.sh

#!/bin/sh
# arg 1: sleep time(s)

if [ $# -ne 1 ]; then
  echo "Usage: `basename $0` sleep_time(s)"
  exit 1
fi

while  (true)
do
    mm=$(cat /proc/meminfo | grep "Slab" | awk '{print $2}')
    echo $mm KB
    sleep $1
done
如上我们实际上是统计的是/proc/meminfo 下面Slab的占用的内存,我们需要特别注意一点,Slab只会统计内核通过kmalloc单次分配8KB以下的内存,如果单次通过kmalloc申请的内存在8KB以上的话,那么在/proc/meminfo中的Slab是不会体现出来的,只会在free中有体现。

测试demo如下:

static ssize_t workqueue_proc_store(struct file *file, const char __user *buffer,
			      size_t count, loff_t *ppos)
{
	int cnt = 0;

#ifdef MM_LEAK_DEBUG
		do{
				char *mm =	kmalloc(1024, GFP_KERNEL);
				printk("mem leak,ptr = %p\n",mm);
				if(mm)
				{
					memset(mm,0x0,1024);
					mm = kmalloc(1024, GFP_KERNEL);
					printk("mem leak,ptr = %p\n",mm);
					kfree(mm);
				}
				cnt++;
		}while(cnt <= 1024);
#endif

	
	return count;
}

3.测试Slab统计不到的内核内存泄露

测试脚本如下:

cat /proc/buddyinfo | awk '{sum=0;for(i=5;i<=NF;i++) sum+=$i*(2^(i-5))};{total+=sum/256};{print $1 " " $2 " " $3 " " $4 "\t : " sum/256 "M"} END {print "total\t\t\t : " total "M"}'

脚本实际上是统计的buddy剩余的内存,查看这个值在不断减少也只能确定内存在泄露,无法确认是否是内核或者应用的泄露。

测试程序:

static ssize_t workqueue_proc_store(struct file *file, const char __user *buffer,
			      size_t count, loff_t *ppos)
{
	int cnt = 0;
#ifdef MM_LEAK_DEBUG
		do{
				char *mm =	kmalloc(1024*1024, GFP_KERNEL);
				printk("mem leak,ptr = %p\n",mm);
				if(mm)
				{
					memset(mm,0x0,1024*1024);
					mm = kmalloc(1024*1024, GFP_KERNEL);
					printk("mem leak,ptr = %p\n",mm);
					kfree(mm);
				}
				//cnt++;
				break;
		}while(cnt <= 1024);
#endif
	return count;
}

内核中构造了一个缺陷,单次内存泄露1M,我们先看看Slab能否统计到

/ # cat /proc/meminfo | grep "Slab"
Slab:              18072 kB
/ # cat /proc/buddyinfo | awk '{sum=0;for(i=5;i<=NF;i++) sum+=$i*(2^(i-5))};{tot
al+=sum/256};{print $1 " " $2 " " $3 " " $4 "\t : " sum/256 "M"} END {print "tot
al\t\t\t : " total "M"}'
Node 0, zone Normal      : 472.902M
total                    : 472.902M

/ # echo 1 > /proc/workqueue    ---->内存泄露1M
mem leak,ptr = 9de00000
mem leak,ptr = 9df00000
/ # 
/ # 
/ # cat /proc/buddyinfo | awk '{sum=0;for(i=5;i<=NF;i++) sum+=$i*(2^(i-5))};{tot
al+=sum/256};{print $1 " " $2 " " $3 " " $4 "\t : " sum/256 "M"} END {print "tot
al\t\t\t : " total "M"}'
Node 0, zone Normal      : 471.766M
total                    : 471.766M
/ # cat /proc/meminfo | grep "Slab"
Slab:              18112 kB

如上结果,很明显,buddy已经将内存分配出去了,但是/proc/meminfo中的Slab已经统计不到了。原因就在于kmalloc分配的内存大小超过了8KB,不会被统计到Slab,而是以2的指数幂直接从buddy中分配走了。

4.如何查看所有应用程序消耗的内存(不包含内核),单元是MB

注意这个脚本统计的内存偏大,例如共享库使用的内存存在重复统计的情况。
ps aux|awk '{sum+=$6} END {print sum/1024}'

原理是统计ps aux的第六列,并求和。该列实际上就是各个进程占用的物理内存

但是需要特别注意的是部分系统可能无法通过ps aux查看物理内存。

注意这个脚本统计的内存更加合理,例如共享库使用的内存不会重复统计,会按比例统计到各个进程中。
grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {print total}'

猜你喜欢

转载自blog.csdn.net/zhuyong006/article/details/83115788