怎样分析crash dump(内存错误)

本文介绍了一次内核Slab内存错误的排查过程,包括Crash命令的使用、CONFIG_DEBUG_SLAB配置的作用及如何定位导致问题的具体代码位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Memory错误

在内核中,内存是以cache的形式组织的,每个对象类型对应一个cache,如(inod_cache,dentry_cache, buffer_head,vm_area_strutct等);每个cache包含多个slab(slab由一个或多个页组成,这些页物理上是连续的);每个slab包含多个初始化的对象。

Cache可以分为两类【kmalloc使用的和其他】,当然他们是用同一个函数创建的。

1.Crash> Kmem -s

CACHE NAME OBJSIZE ALLOCATED TOTAL SLABS SSIZE

ee2bb0c0  scsi_sense_cache 120 2 32 1 4k

ee2bbac0 scsi_cmd_cache 176 2 22 1 4k

ee000c60 size-4194304 4194304 0 0 0 4096k

ee000bc0 size-2097152 2097152 0 0 0 2048k

ee000b20 size-1048576 1048576 1 1 1 1024k

ee000a80 size-524288 524288 0 0 0 512k

ee0009e0 size-262144 262144 0 0 0 256k

ee000940 size-131072 131072 1 1 1 128k

ee0008a0 size-65536 65536 2 2 2 64k

ee000800 size-32768 32768 1 1 1 32k

ee000760 size-16384 16384 9 9 9 16k

ee0006c0 size-8192 8192 10 10 10 8k

ee000620 size-4096 4096 26 44 44 4k

ee000580 size-2048 2072 53 60 20 8k

ee0004e0 size-1024 1048 332 343 49 8k

ee000440 size-512 536 771 798 114 4k

ee0003a0 size-256 280 54 84 6 4k

ee000300 size-192 216 734 756 42 4k

ee000260 size-128 152 2301 2314 89 4k

ee0001c0 size-32 56 13439 13668 204 4k

ee000120 size-96 120 1067 1120 35 4k

ee000080 size-64 88 3137 3168 72 4k

调用函数kmalloc(xxx)得到的memory都来名字为size-xxcache.


  1. 2.CONFIG_DEBUG_SLAB

为便于调试,在每个对象可以添加SLAB_RED_ZONE,添加这块内存的最后使用者SLAB_STORE_USER,且用SLAB_POISON初始化对象。

这些特殊的元素是什么?

Include/linux/poison.h

/*

*Magic nums for obj red zoning.

*Placed in the first word before and the first word after an obj.

*/

#define RED_INACTIVE 0x09F911029D74E35BULL /*when obj is inactive */

#define RED_ACTIVE 0xD84156C5635688C0ULL /*when obj is active */


/*...and for poisoning */

#define POISON_INUSE 0x5a /*for use-uninitialised poisoning */

#definePOISON_FREE 0x6b /* foruse-after-free poisoning */

#define POISON_END 0xa5 /*end-byte of poisoning */

  1. 3.log | tail xxx

slab出错时,会把cachename/ objecet的开始地址(没有考虑redzone)

redzone的内容/lastuserobject的内容都会打印出来,另外还包含该对象前后对象的信息。

[ 45.501503:0] Slab corruption (Not tainted): size-64 start=e74b3398,len=64

[ 45.508518:0] Redzone: 0x9f911029d74e35b/0x9f911029d74e35b.

[ 45.514118:0] Last user: [<c03446e8>](__DWC_FREE+0x1c/0x20)

[ 45.519816:0] 010: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b d8 b0 54 e8 kkkkkkkkkkkk..T.


[ 45.527633:0] Prev obj: start=e74b3340, len=64

[ 45.532108:0] Redzone: 0xd84156c5635688c0/0xd84156c5635688c0.

[ 45.537880:0] Last user:[<c045ad78>](binder_get_ref_for_node+0x6c/0x204)

[ 45.544777:0] 000: 47 34 00 00 2c 8b e5 e7 00 00 00 00 00 00 00 00 G4..,...........

[ 45.552584:0] 010: f1 31 4b e7 50 83 e5 e7 10 46 61 e7 00 00 00 00 .1K.P....Fa.....


[ 45.560393:0] Next obj: start=e74b33f0, len=64

[ 45.564866:0] Redzone: 0xd84156c5635688c0/0xd84156c5635688c0.

[ 45.570636:0] Last user: [<c004df30>](copy_creds+0x108/0x1e4)

[ 45.576479:0] 000: 01 00 00 00 5a 5a 5a 5a 00 00 00 00 5a 5a 5a 5a ....ZZZZ....ZZZZ

[ 45.584281:0] 010: ad 4e ad de ff ff ff ff ff ff ff ff 00 00 00 00 .N..............


[ 45.592154:0] ------------[ cut here ]------------

[ 45.596974:0] kernel BUG atmm/slab.c:2037!

[ 45.601188:0] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM

[ 45.607223:0] Modules linked in:

[ 45.610516:0] CPU: 0 Not tainted (3.4.0-gc37fe8c-dirty #657)

[ 45.616585:0] PC is at check_poison_obj+0x194/0x1b4

[ 45.621512:0] LR is at console_unlock+0x1c0/0x1d4

[ 45.626260:0] pc : [<c00d46f8>] lr : [<c0028fe0>] psr: 60000093

[ 45.626294:0] sp : e58e1c68 ip : e58e1a40 fp : e58e1c9c

[ 45.638134:0] r10: e74b3398 r9 : 00000008 r8 : e74b3338

[ 45.643565:0] r7 : 00000040 r6 : e74b3000 r5 : e74b33e8 r4 :ee000080

[ 45.650296:0] r3 : 00000001 r2 : 00000010 r1 : 00000020 r0 :e74b33f0

[ 45.657033:0] Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel

[ 45.664635:0] Control: 10c53c7d Table: af4e404a DAC: 00000015


kmem-S size-64

从打印的信息siize-64start=e74b3398, 得知出问题的是cache:size-64,地址为

e74b3390.

  1. 4.Crash> kmem -S cache name

crash>kmem -S size-64 | grep e74b3390

[e74b3390]

crash>rd e74b3390 0x64

e74b3390: 9d74e35b 09f91102 6b6b6b6b6b6b6b6b [.t.....kkkkkkkk

e74b33a0: 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b kkkkkkkkkkkkkkkk

e74b33b0: 6b6b6b6b e854b0d8 6b6b6b6b6b6b6b6b kkkk..T.kkkkkkkk

e74b33c0: 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b kkkkkkkkkkkkkkkk

e74b33d0: 6b6b6b6b a56b6b6b 9d74e35b09f91102 kkkkkkk.[.t.....

e74b33e0: ff000000 c03446e8


从出问题的这个object的用于debug的信息可知:

redzone: 9d74e35b 09f91102 :RED_INACTIVE

poisondata: 6b : POISON_FREE

usercall : c03446e8

  1. 5.last caller

最后操作这块内存的函数为:

crash>dis -r c03446e8

0xc03446cc<__DWC_FREE>: mov r12, sp

0xc03446d0<__DWC_FREE+4>: push {r11, r12, lr, pc}

0xc03446d4<__DWC_FREE+8>: sub r11, r12, #4

0xc03446d8<__DWC_FREE+12>: stmfd sp!, {lr}

0xc03446dc<__DWC_FREE+16>: ldmfd sp!, {lr}

0xc03446e0<__DWC_FREE+20>: mov r0, r1

0xc03446e4<__DWC_FREE+24>: bl 0xc00d51fc <kfree>

0xc03446e8<__DWC_FREE+28>: ldm sp, {r11, sp, pc}


结合如下的backtrace,可知问题就是从slab申请的一个对象,但发现其中的元素不对(不是0x6b,却出现了e854b0d8,所以crash)

[ 45.596974:0] kernel BUG at mm/slab.c:2037!

[ 46.736498:0] Backtrace:

[ 46.739243:0] [<c00d4564>] (check_poison_obj+0x0/0x1b4) from[<c00d4f84>] (cache_alloc_debugcheck_after+0x3c/0x1a0)

[ 46.749743:0] [<c00d4f48>](cache_alloc_debugcheck_after+0x0/0x1a0) from [<c00d6df0>](__kmalloc+0x140/0x234)

[ 46.759680:0] r7:00000038 r6:00008020 r5:a0000093 r4:ee000080

[ 46.765709:0] [<c00d6cb0>] (__kmalloc+0x0/0x234) from[<c03448c0>] (__DWC_ALLOC_ATOMIC+0x20/0x24)

[ 46.774674:0] [<c03448a0>] (__DWC_ALLOC_ATOMIC+0x0/0x24) from[<c0340dbc>] (dwc_otg_hcd_urb_alloc+0x30/0x40)

[ 46.784585:0] [<c0340d8c>] (dwc_otg_hcd_urb_alloc+0x0/0x40)from [<c0352f00>] (urb_enqueue+0xf0/0x258)

[ 46.793916:0] r4:db95faa8 r3:00000003

[ 46.797796:0] [<c0352e10>] (urb_enqueue+0x0/0x258) from[<c031f32c>] (usb_hcd_submit_urb+0x5c4/0x6a4)

[ 46.807069:0] [<c031ed68>] (usb_hcd_submit_urb+0x0/0x6a4) from[<c031fa60>] (usb_submit_urb+0x2a0/0x2bc)

[ 46.816603:0] [<c031f7c0>] (usb_submit_urb+0x0/0x2bc) from[<c03208bc>] (usb_sg_wait+0x4c/0x138)


  1. 6.解决问题

从上面的分析可知,问题是memoryfree后,代码又操作了这块内存。

现在问题是__DWC_FREE最后操作了这块内存,__DWC_FREE只是封装了kfree,

如果直接使用kfree,通过SLAB_STORE_USER就可以知道那个函数调用了kfree,

进而知道申请的对象类型,看那个该对象类型的那个成员被赋值,就可以在代码中搜索。


如该例知道了类型为dwc_otg_qtd_t,且成员qh在内存释放后被赋值,就可以在代码中详细查找,分析。

crash>dwc_otg_qtd_t e74b3398

structdwc_otg_qtd_t {

data_toggle= 0x6b,

control_phase= (DWC_OTG_CONTROL_DATA | DWC_OTG_CONTROL_STATUS | unknown:1802201960),

complete_split= 0x6b,

ssplit_out_xfer_count= 0x6b6b6b6b,

error_count= 0x6b,

isoc_frame_index= 0x6b6b,

isoc_split_pos= 0x6b,

isoc_split_offset= 0x6b6b,

urb= 0x6b6b6b6b,

qh= 0xe854b0d8,

qtd_list_entry= {

cqe_next= 0x6b6b6b6b,

cqe_prev= 0x6b6b6b6b

},

in_process= 0x6b,

n_desc= 0x6b,

isoc_frame_index_last= 0x6b6b

}

在相关的代码中搜索哪里操作了数据结构dwc_otg_qtd_t的qh成员。

在进行崩溃转储分析Crash Dump Analysis)时,通常是为了调查操作系统或应用程序在运行过程中出现的严重错误,例如系统崩溃(蓝屏)、应用程序异常终止等。这种分析可以帮助开发人员和系统管理员识别导致崩溃的根本原因,并采取相应的修复措施。 在 Windows 操作系统中,崩溃转储可以通过配置系统在崩溃时生成内存转储文件(Memory Dump),然后使用调试工具(如 WinDbg)进行分析。Windows 支持多种类型的内存转储文件,包括: - **小型内存转储(MiniDump)**:大小约为 64KB(32 位系统)或 128KB(64 位系统),包含崩溃时的基本信息,例如错误代码、加载的驱动程序、当前进程和线程的信息等。 - **内核内存转储(Kernel Memory Dump)**:包含崩溃时所有内核模式内存的内容,通常大小在 50-100MB 之间,适用于大多数系统(内存小于等于 2GB)。 - **完整内存转储(Full Memory Dump)**:包含整个物理内存的内容,文件大小与系统物理内存大小相同,适用于需要全面分析的场景。 当系统配置为调试模式启动(Debugging Mode)并在崩溃时连接调试器时,系统不会显示蓝屏,而是直接进入主机的内核调试器(Kernel Debugger),从而可以实时查看崩溃原因,并使用调试命令进行初步分析。调试器中可以使用 `.dump` 命令保存崩溃时的内存状态,以便后续离线调试。 在调试器中,用户可以使用各种命令来检查崩溃上下文,例如: - `!analyze -v`:自动分析崩溃原因并提供详细信息。 - `kb`:显示调用堆栈。 - `lm`:列出加载的模块。 - `dt`:查看结构体定义。 - `!process` 和 `!thread`:查看进程和线程信息。 此外,在用户模式下启用堆栈跟踪数据库(User Mode Stack Trace Database)后,可以更精确地追踪到导致内存泄漏或异常增长的代码路径,特别是在分析内存转储时识别可疑的堆分配。 ### 示例调试命令 ```shell !analyze -v ``` 该命令会自动分析当前崩溃的原因,并输出详细的错误信息、调用堆栈、模块信息等。 ```shell kb ``` 该命令用于显示当前线程的调用堆栈,有助于定位崩溃发生的具体位置。 ```shell lm ``` 列出所有加载的模块,包括驱动程序和 DLL 文件,有助于识别可能引发问题的组件。 ```shell !process 0 0 ``` 显示所有进程的信息,便于查看崩溃发生时的进程上下文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值