1.针对应用场景下的内存泄露
#!/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
#!/system/bin/sh
if [ $# -ne 1 ]; then
echo "Usage: `basename $0` process_name"
exit 1
fi
APPNAME=$1
PROC="`ps | grep "$APPNAME" | grep -v "grep" | grep -v "busybox awk" | grep -v $0 | busybox awk '{print $2}'`"
if [ -z $PROC ]; then
echo "invalid process_name"
exit 1
fi
echo "proc ---$PROC----"
OLDHEAP="0"
while :
do
HEAP=`ps | grep "$APPNAME" | grep -v "grep" | grep -v "busybox awk" | grep -v $0 | busybox awk '{print $5}'`
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.针对内核场景下的内存泄露
#!/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}'
最后由于Android下没有awk的命令因此需要移植busybox来完成。