1. 程式人生 > >Linux檢測記憶體洩露的指令碼

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}'