[Kernel_exception3] linux kernel out of memory panic(OOM)

一、out of memory 介绍:

  Linux  kernel out_of_memory(简称OOM)从字面上看可以就可以看出是因为没有内存可供分配导致,OOM的产

生和内存分配相关,分析此类问题需要对linux kernel的内存管理非常了解才好定位问题。产生OOM的原因大部分是

因为内存的泄漏导致,但也不排除部分小内存的设备(512M或者更低)在使用大量耗内存的应用时,设备的内存回收

机制来不及回收内存也会导致出现无内存分配,MTK 平台产生out of memroy时会出现重启并产生KE的DB文件,将

KE的DB文件使用GAT解析后的log可以获取到产生的原因。从LOG中可以看出OOM触发的流程如下:

<4>[91663.895212]-(3)[19301:InputReader][<c010e618>] (dump_backtrace) from [<c010e950>] (show_stack+0x18/0x1c)
<4>[91663.895219]-(3)[19301:InputReader] r6:60010013 r5:c1454288 r4:00000000 r3:00040975
<4>[91663.895229]-(3)[19301:InputReader][<c010e938>] (show_stack) from [<c04a3968>] (dump_stack+0x94/0xa8)
<4>[91663.895238]-(3)[19301:InputReader][<c04a38d4>] (dump_stack) from [<c02a9e70>] (dump_header+0x90/0xe8)
<4>[91663.895244]-(3)[19301:InputReader] r6:cbf1a300 r5:00000001 r4:d712dcac r3:00040975
<4>[91663.895253]-(3)[19301:InputReader][<c02a9de0>] (dump_header) from [<c0253870>] (oom_kill_process+0x3c0/0x4c4)
<4>[91663.895267]-(3)[19301:InputReader][<c02534b0>] (oom_kill_process) from [<c0253d18>] (out_of_memory+0x104/0x438)
<4>[91663.895274]-(3)[19301:InputReader] r10:00000000 r9:c140c300 r8:c140c308 r7:c140c300 r6:c1404548 r5:d712dcac
<4>[91663.895287]-(3)[19301:InputReader][<c0253c14>] (out_of_memory) from [<c0259718>] (__alloc_pages_nodemask+0xfe4/0x1000)
<4>[91663.895295]-(3)[19301:InputReader] r10:00000000 r9:00000003 r8:c1404a54 r7:000003a3 r6:c153a490 r5:c140468c
<4>[91663.895299]-(3)[19301:InputReader] r4:00000000

  OOM处理的机制如下:

  • 检查是否配置了/proc/sys/kernel/panic_on_oom,如果是则直接触发panic;
  • 检查是否配置了oom_kill_allocating_task,即是否需要kill current进程来回收内存,如果是,且current进程是killable的,则kill current进程;
  • 根据既定策略选择需要kill的process,基本策略为:通过进程的内存占用情况计算“点数”,点数最高者被选中;
  • 如果没有选出来可kill的进程,那么直接panic;
  • kill掉被选中的进程,以释放内存。
__alloc_pages
    |-->__alloc_pages_nodemask
       |--> __alloc_pages_slowpath
           |--> __alloc_pages_may_oom
              | --> out_of_memory

二、具体案例分析:

1、slab泄漏导致的out  of  memory

(1)OOM的log分析:

<4>[618440.098972] (1)[14038:InputReader]InputReader invoked oom-killer: gfp_mask=0x240c2c0(GFP_KERNEL|__GFP_NOWARN|__GFP_COMP|__GFP_ZERO), nodemask=0, order=3, oom_score_adj=-900
<6>[618440.099012] (1)[14038:InputReader]InputReader cpuset=foreground mems_allowed=0
<4>[618440.099034]-(1)[14038:InputReader]CPU: 1 PID: 14038 Comm: InputReader Tainted: G        W  O    4.9.77+ #2
<4>[618440.099040]-(1)[14038:InputReader]Hardware name: Generic DT based system
<4>[618440.099047]-(1)[14038:InputReader]Backtrace: 
<4>[618440.099072]-(1)[14038:InputReader][<c020d488>] (dump_backtrace) from [<c020d7a0>] (show_stack+0x18/0x1c)
<4>[618440.099079]-(1)[14038:InputReader] r7:c0fd9d68 r6:600d0013 r5:00000000 r4:c143ec08
<4>[618440.099088]-(1)[14038:InputReader][<c020d788>] (show_stack) from [<c0534740>] (dump_stack+0x8c/0xa0)
<4>[618440.099098]-(1)[14038:InputReader][<c05346b4>] (dump_stack) from [<c0399638>] (dump_header.constprop.4+0x8c/0xc4)
<4>[618440.099120]-(1)[14038:InputReader] r6:c3c13e40 r5:c5a8fc64 r4:c3c13800
<4>[618440.099127]-(1)[14038:InputReader][<c0347e78>] (oom_kill_process) from [<c0348680>] (out_of_memory+0x104/0x42c)
<4>[618440.099146]-(1)[14038:InputReader][<c034857c>] (out_of_memory) from [<c034d928>] (__alloc_pages_nodemask+0xea8/0xf44)
<4>[618440.099153]-(1)[14038:InputReader] r9:00004566 r8:c140456c r7:c1404a54 r6:c140468c r5:00000000 r4:00004662
<4>[618440.099162]-(1)[14038:InputReader][<c034ca80>] (__alloc_pages_nodemask) from [<c036abe8>] (kmalloc_order+0x20/0x3c)
<4>[618440.099169]-(1)[14038:InputReader] r10:00000003 r9:c0392138 r8:024082c0 r7:000040ac r6:da1ac3c0 r5:dd6b1000
<4>[618440.099174]-(1)[14038:InputReader] r4:db7a49c0
<4>[618440.099181]-(1)[14038:InputReader][<c036abc8>] (kmalloc_order) from [<c036ac28>] (kmalloc_order_trace+0x24/0x104)

  在申请不到内存时,会走out of memory的流程,去杀死一些进程后重启系统,从上面的log中,可以看出:

<4>[618440.098972] (1)[14038:InputReader]InputReader invoked oom-killer: gfp_mask=0x240c2c0(
//GFP_KERNEL会从normal memory申请内存
GFP_KERNEL|__GFP_NOWARN|__GFP_COMP|__GFP_ZERO), 
nodemask=0, 
//order = 3说明申请2^8个page,也就是8*4kB
order=3, 
oom_score_adj=-900

  触发OOM时会打印出各个zone 的水位情况,从水位情况中可以获取很多信息,并初步判断分析问题:

<4>[618440.099391]Normal free:71056kB min:2796kB low:3492kB high:4188kB active_anon:0kB inactive_anon:0kB active_file:452kB inactive_file:468kB unevictable:32kB writepending:528kB
 present:532224kB managed:493076kB mlocked:0kB slab_reclaimable:16588kB slab_unreclaimable:363468kB kernel_stack:7800kB pagetables:9536kB bounce:0kB free_pcp:432kB local_pcp:8kB free_cma:0kB

<4>[618440.099411]HighMem free:654256kB min:512kB low:2184kB high:3856kB active_anon:77476kB inactive_anon:38400kB active_file:155684kB inactive_file:183968kB unevictable:1788kB writepending:0kB 
present:1435064kB managed:1366648kB mlocked:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB bounce:0kB free_pcp:632kB local_pcp:0kB free_cma:0kB

<4>[618440.099425]Normal: 9062*4kB (UH) 3258*8kB (UMEH) 547*16kB (UEH) 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 71064kB
<4>[618440.099457]HighMem: 27302*4kB (UMH) 24645*8kB (UMH) 7102*16kB (UMH) 5357*32kB (UMH) 808*64kB (UMH) 83*128kB (UMH) 2*256kB (UH) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 654272kB
  • 从上面的log可以看出手机的内存大小为(present):532224kB + 1435064kB = 1.87GB;
  • 活跃或非活跃匿名页面/匿名文件(可被回收)的占用内存的大小active_anon/inactive_anon/active_file/inactive_file;
  • slab占用内存的大小:slab_unreclaimable:363468kB(此数值一般250M以下属于正常),故此题属于slab泄漏;
  • 从0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB可以看出无连续的32kB可供分配;

(2)debug方法:

  从上面的log中可以看出可能存在slab泄漏的情况存在,这里需要抓取 /proc/slabinfo和/proc/mtk_memcfg/slabtrace

继续分析,抓取的方法可以通过脚本后台没两分钟抓取一次:

#Confidential
# adb push dump_slabtrace.sh sdcard
# sh dump_slabtrace.sh &
testlog="/sdcard/lognew.txt"
i=0
while true
do 
	echo "`date +%Y/%m/%d-%H:%M:%S` - the ${i}th test start" >> $testlog
   	echo "******************KB3 meminfo:******************" >> $testlog
	meminfo=`cat /proc/meminfo`
	echo "$meminfo" >> $testlog
    
    echo "******************KB3 slabinfo:******************" >> $testlog
	slabinfo=`cat /proc/slabinfo`
	echo "$slabinfo" >> $testlog
    echo "******************KB3 slabtrace:*********************" >> $testlog
    slabtrace=`cat /proc/mtk_memcfg/slabtrace`
    echo "$slabtrace" >> $testlog
    
	i=$(($i+1))
	sleep 120
done

   上面的脚本会将抓取的信息都保存到:/sdcard/lognew.txt文件中,可以将此文件导出来,查看slabtrace中数值

比较大的分配内存的接口(大于10000),然后看每一时间段的此数值是否有增加或者减少,然后再对应的代码中

去查找是否有泄漏的可能,是否存在申请的内存没有释放的地方,从而定位修改问题。

2、kernel  stack 泄漏导致的out  of  memory

  上面已经介绍了如何去分析定位,kernel stack泄漏的问题,主要表现在如下:

<4>[25809.987231]Normal free:69976kB min:2792kB low:6576kB high:7272kB active_anon:0kB inactive_anon:4kB active_file:460kB inactive_file:328kB unevictable:0kB writepending:412kB
 present:532224kB managed:488928kB mlocked:0kB slab_reclaimable:35176kB slab_unreclaimable:244568kB kernel_stack:110192kB pagetables:7496kB bounce:0kB free_pcp:1040kB local_pcp:532kB free_cma:0kB

  kernel_stack的大小为110192kB,正常情况下的kernel_stack的大小一般在20MB的大小左右。

3、用户进程泄漏导致的out  of  memory

  如下是一个用户进程storaged进程泄漏的问题,从zone的水位看Normal free已经等于0,已经没有内存可分配:

<4>[75788.814669] (4)[616:storaged]DMA free:8632kB min:5104kB low:33716kB high:34992kB active_anon:1087072kB inactive_anon:362988kB active_file:980kB inactive_file:1820kB unevictable:0kB isolated(anon):372kB isolated(file):0kB 
present:2008512kB managed:1830132kB mlocked:0kB dirty:204kB writeback:0kB mapped:680kB shmem:60kB slab_reclaimable:32436kB slab_unreclaimable:102264kB kernel_stack:14816kB pagetables:24664kB 

<4>[75788.814693] (4)[616:storaged]Normal free:0kB min:5836kB low:38552kB high:40012kB active_anon:1044668kB inactive_anon:1045396kB active_file:0kB inactive_file:0kB unevictable:2988kB isolated(anon):0kB isolated(file):0kB 
present:2097152kB managed:2093056kB mlocked:2988kB dirty:0kB writeback:0kB mapped:2920kB shmem:784kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB

<4>[75788.814714] (4)[616:storaged]DMA: 1265*4kB (UM) 8*8kB (UM) 4*16kB (UMH) 1*32kB (H) 1*64kB (H) 1*128kB (H) 1*256kB (H) 1*512kB (H) 1*1024kB (H) 1*2048kB (H) 0*4096kB = 9252kB
<4>[75788.814753] (4)[616:storaged]Normal: 1*4kB (C) 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 4kB
//用于内存回收的交换分区swap的空间也为0kb,无法继续回收内存
<4>[75788.814790] (4)[616:storaged]Free swap  = 0kB
<4>[75788.814793] (4)[616:storaged]Total swap = 1048572kB

<6>[75788.814813] (4)[616:storaged][ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
<6>[75788.814882] (4)[616:storaged][  305]     0   305     2158       97       7       3      134         -1000 ueventd
<6>[75788.814893] (4)[616:storaged][  338]  1000   338     3363      577       9       3      284         -1000 teei_daemon
<6>[75788.814900] (4)[616:storaged][  339]  1036   339     5918     1277      15       3      352         -1000 logd
......
<6>[75788.815344] (4)[616:storaged][  588]     0   588  2697648   876266    4357      13   216885         -1000 storaged
<6>[75788.815351] (4)[616:storaged][  590]  1046   590    15520      534      48       2     1402         -1000 [email protected]

  再看rss这一栏的数值大小(rss是物理内存申请空间的大小),上面问题在中的storage 进程的消耗空间的大小:

876266 * 4kB = 3.3GB,消耗了非常大的空间,说明storage可能存在泄漏的情况,可以使用malloc debug的工具

来一步步的抓取到问题的现场:

(1)打开内存监控功能:

    adb shell "echo libc.debug.malloc.options=backtrace=8 >> /data/local.prop"
    adb shell "echo libc.debug.malloc.program= mediaserver >> /data/local.prop"
    adb shell "chmod 644 /data/local.prop"
    adb reboot

(2) 检查是否存在内存泄漏。

  打开mtklogger,不断通过adb shell procrank -u > procrank.txt查看当前系统内存的使用情况。

  当看到被监控的进程占用内存(USS字段)超过了正常值很多,则可能存在内存泄漏。

(3)得到该进程内存使用分布情况($pid为被监控进程pid)
  adb shell dumpsys meminfo $pid >meminfo_$pid.txt
  adb shell procmem $pid > procmem_$pid.txt

(4)抓取该进程的coredump。

  adb shell kill -5 $pid

(5)查看prorank中进程的数值如果存在超过128M以上就存在泄漏的情况。

作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。

猜你喜欢

转载自blog.csdn.net/frank_zyp/article/details/83746253