亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1968 | 回復: 0
打印 上一主題 下一主題

MAC內(nèi)存泄漏的調(diào)試 [復制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2007-12-29 14:22 |只看該作者 |倒序瀏覽
qiuhan
2007.12.7
現(xiàn)象:
對apache使用LoadRunner進行壓力測試,使用top命令發(fā)現(xiàn)wired內(nèi)存不斷增長,導致
內(nèi)存耗盡。(內(nèi)存大小為512M)
分析:
我們從wired的內(nèi)存的分配開始,自底向上開始分析。
首先,我們對wired內(nèi)存的分配方式進行分類,內(nèi)核中使用cnt.v_wire_count來統(tǒng)計wired頁面數(shù)量,
atomic_add_int(&cnt.v_wire_count, 1);
是增加wired頁面(調(diào)用函數(shù)有vm_page_alloc vm_page_wire)
atomic_subtract_int(&cnt.v_wire_count, 1);
是減少wired頁面(調(diào)用函數(shù)有_pmap_unwire_pte_hold pmap_release vm_page_unwire)
我們覺得vm_page_wire過于泛泛,再對調(diào)用vm_page_wire的函數(shù)進行分類,有
socow_setup, do_sendfile, allocbuf, vm_fault, vm_thread_swapin, vm_page_grab
因此加上vm_page_alloc分成7類,用數(shù)組wired_page_cnt來分別表示每一類的wired頁面數(shù).
在struct vm_page中加入int type;來區(qū)分不同類別,在增加wired頁面時對type進行賦值,
并以type值為下標增加wired_page_cnt數(shù)組中對應的元素;在減少wired頁面時也以type為
下標減少wired_page_cnt數(shù)組中對應的元素。
為了便于查看,我們增加了一個ddb的show wired命令來查看數(shù)組中的元素:
DB_SHOW_COMMAND(wired, vm_page_print_wired_info)
我們用修改后的內(nèi)核進行壓力測試,當top顯示wired為117M時,進行查看:
db> show wired
socow_setup: 0
do_sendfile: 0
allocbuf: 351
vm_fault: 486
vm_thread_swapin: 0
vm_page_grab: 0
vm_page_alloc: 29150
db> show page
...
cnt.v_wire_count: 29987
...
我們發(fā)現(xiàn)show wired顯示值之和剛好為cnt.v_wire_count的值,證明我們的代碼無誤。
另一方面,我們粗略計算一下29987*4k大小剛好差不多為117M
另外,令我們感興趣的是vm_page_alloc的值最大,而且隨著wired內(nèi)存的增加,僅
vm_page_alloc的數(shù)值增加,其它基本不動。從這里來看,我們先前對vm_page_wire
進行的分類是不必要的。下面我們要關注的就是vm_page_alloc
調(diào)用vm_page_alloc的有pmap_pinit,vm_fault等17個函數(shù),我們?nèi)匀话凑障惹暗乃悸罚?br /> 分別統(tǒng)計這些函數(shù)對wired頁面的貢獻。我們也增加一個ddb的命令show alloc來查看。
重新用新內(nèi)核進行壓力測試,當wired內(nèi)存為128M時:
db> show alloc
pmap_pinit: 196
_pmap_allocpte: 975
pmap_growkernel: 61
allocbuf: 14932
obj_alloc: 2163
kmem_malloc: 13165
vm_page_grab: 520
total wired: 32013
等待wired內(nèi)存增加約1M(使用top):
db> show alloc
pmap_pinit: 196
_pmap_allocpte: 975
pmap_growkernel: 61
allocbuf: 14932
obj_alloc: 2163
kmem_malloc: 13560
vm_page_grab: 520
total wired: 32408
我們發(fā)現(xiàn)僅有kmem_malloc的值在增加。注意:
1 為了清晰,我們打印時忽略了小于5的項目
2 我們關注的是增長趨勢,而不是大小
3 實際分析時,開始時allocbuf增長最快,但后來基本不增加反而下降了
調(diào)用kmem_malloc僅有page_alloc(我們沒有編譯memguard_alloc),這是一個好消息,
但隨之我們卻發(fā)現(xiàn)一個壞消息:調(diào)用page_alloc的地方會很分散。因為存在:
keg->uk_allocf = page_alloc;
這時,我們才意識到這種分析方法有著明顯的缺陷:
1 調(diào)用的函數(shù)不能太多,而且對于通過指針調(diào)用的方式不太適合
2 每一層的推進都需要比較多的編碼,而且都需要通過增加參數(shù)或者標志位來區(qū)分
就在我們將要陷入絕望的時候,zzy創(chuàng)造性的提出了用db_backtrace來回溯的方法。
我們依據(jù)i386/i386/db_trace.c中的db_trace_self函數(shù)寫了一個函數(shù)db_trace_self1,
它只有一個參數(shù)depth,標識回溯的深度,實現(xiàn)的功能是返回回溯depth次時的調(diào)用地址(就和我們在
bt一樣,只是注釋掉了db_printf,以免打印出信息)
在struct vm_page中增加u_int32_t addr;標識調(diào)用者的地址(函數(shù)入口+偏移),提供一個大小為
0x10000的數(shù)組uma_bts(注意,這里我們不能使用鏈表或者動態(tài)數(shù)組,以免導致循環(huán)的內(nèi)存分配),
元素記錄了addr以及一個統(tǒng)計計數(shù),并以addr的尾4位作為hash值定位元素。
在vm_page_alloc中調(diào)用db_trace_self1得到addr,并將uma_bts中的對應元素的統(tǒng)計計數(shù)加1;
在vm_page_unwire中如果發(fā)現(xiàn)該page是從vm_page_alloc分配的,便取出vm_page中的addr,并
將uma_bts中的對應元素的統(tǒng)計計數(shù)減1.
增加一個ddb的命令show pagebt來查看數(shù)組uma_bts的內(nèi)容。
關于參數(shù)depth的選取,我們在ddb中對vm_page_alloc設置一個斷點:
db> break vm_page_alloc
db> c
[thread pid 39 tid 100034 ]
Breakpoint at   vm_page_alloc:  pushl   %ebp
db> bt
Tracing pid 39 tid 100034 td 0xc22e2600
vm_page_alloc(c14610a8,1000,101) at vm_page_alloc
page_alloc(c1442000,1000,d42d9933,101) at page_alloc+0x4c
slab_zalloc(c1442000,101) at slab_zalloc+0xbe
uma_zone_slab(c1442000,1) at uma_zone_slab+0x164
uma_zalloc_bucket(c1442000,1) at uma_zalloc_bucket+0x121
uma_zalloc_arg(c1442000,0,1) at uma_zalloc_arg+0x36c
uma_zalloc(c1442000,1) at uma_zalloc+0x10
mac_labelzone_alloc(1) at mac_labelzone_alloc+0x17
mac_inpcb_label_alloc(1) at mac_inpcb_label_alloc+0xe
mac_init_inpcb(c4639bf4,1) at mac_init_inpcb+0x12
in_pcballoc(c2ac6ac0,c0af6e20,c09c7b88) at in_pcballoc+0x71
tcp_attach(c2ac6ac0) at tcp_attach+0x70
我們發(fā)現(xiàn)uma_zalloc之類都是類似封裝的函數(shù),而我們真正感興趣的是從mac_labelzone_alloc
開始的,所以我們把depth設置為8
db> show pagebt
avtab_insertf   0xc0c5501b      745
fo_write        0xc06e7021      13991
vm_map_entry_create     0xc08c118c      617
uma_zalloc      0xc06c71b8      520
pmap_insert_entry       0xc094d38f      2158
mac_labelzone_alloc     0xc087146f      2527
fork    0xc069f5ee      928
scopen  0xc0929646      408
malloc  0xc06ae812      1352
vmspace_alloc   0xc08c0a50      210
vm_object_allocate      0xc08c7a89      131
uma_zalloc_bucket       0xc08b8e83      131
begin   0xc0448ea5      186
VOP_CACHEDLOOKUP        0xc0716f1e      176
Max: fo_write   0xc06e7021      13991
這里我們發(fā)現(xiàn)fo_write和mac_labelzone_alloc都增長的比較快,而fo_write位于sys/file.h中,
是一個inline函數(shù),不容易判定是誰調(diào)用的。沒關系,我們把depth提高到9
這時,有三個函數(shù)引起了我們的注意:mac_socket_label_alloc mac_socket_peer_label_alloc 和
mac_inpcb_label_alloc. 隨著內(nèi)存的減少,我們發(fā)現(xiàn)內(nèi)存的減少就是被它們3個給分享了。
db> show pagebt
sigacts_alloc   0xc06c113e      226
vmspace_fork    0xc08c417e      180
mac_socket_label_alloc  0xc0876186      5300
thread_alloc    0xc06c71d5      500
vm_object_shadow        0xc08c92be      119
mac_socket_peer_label_alloc     0xc087630a      5786
vmspace_fork    0xc08c43e0      203
mac_inpcb_label_alloc   0xc087060a      7252
pmap_copy       0xc094e7fe      2103
giant_open      0xc068b911      384
vm_map_insert   0xc08c193c      392
uma_zalloc_arg  0xc08b89a0      148
syscall 0xc09529d6      885
begin   0xc0448ea5      243
avtab_read_item 0xc0c54eaf      745
kobj_class_compile      0xc06d8f23      124
dofilewrite     0xc06e6f42      14793
Max: dofilewrite        0xc06e6f42      14793
我們的調(diào)試工作到這里其實就可以結束了,剩下的是一個推理過程。
先理一下推理的思路: 這3個函數(shù)都是為MAC分配內(nèi)存的,導致內(nèi)存泄漏的原因肯定是沒有調(diào)用相應的釋放內(nèi)存函數(shù)。而且,我們知道
mac label是依附于內(nèi)核對象的,例如進程,創(chuàng)建進程時,會為label分配內(nèi)存; 銷毀進程時,也應該釋放label。
所以我們要找的就是沒有釋放label的地方。
我們以mac_inpcb_label_alloc為例,發(fā)現(xiàn)調(diào)用它的函數(shù)是mac_init_inpcb, 而調(diào)用mac_init_inpcb的是
in_pcballoc, 在同一文件(netinet/in_pcb.c)中我們可以看到in_pcbdetach; 再看看調(diào)用in_pcbdetach
的地方,有7處,根據(jù)我們的測試環(huán)境我們覺得應該考慮的是tcp_close。
tcp_close中有段代碼如下:
776 #ifdef INET6
777     if (INP_CHECK_SOCKAF(so, AF_INET6))
778         in6_pcbdetach(inp);
779     else
780 #endif
781         in_pcbdetach(inp);
我們編譯的內(nèi)核使能了INET6,所以調(diào)用的是in6_pcbdetach。
我們比較一下in6_pcbdetach和in_pcbdetach這兩個函數(shù),就會發(fā)現(xiàn)后者含有:
746 #ifdef MAC
747     mac_destroy_inpcb(inp);
748 #endif
而前者沒有,這就是導致內(nèi)存泄漏的地方。
總結一下吧:
1 對于具有統(tǒng)計特性的bug,gdb可能束手無策,ddb反而可以起到意想不到的效果
2 db_backtrace對于多個函數(shù)調(diào)用同一個函數(shù)的函數(shù)調(diào)用關系的統(tǒng)計分析很方便
3 調(diào)試需要清晰的思路和敏銳的嗅覺,敢于推斷問題可能的原因
This bug has been reported and accepted by Robert Watson.
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/netinet6/in6_pcb.c
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/security/mac/mac_posix_sem.c
               
               
               

本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u1/41770/showart_452936.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術有限公司. 版權所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP