lldb 调试 linux下 .net Core 总结及开源扩展 yinuo
相信很多朋友在跟隨微軟.net core 從windows平臺(tái)遷移至linux平臺(tái)的過(guò)程中遇到很多別扭的地方,這里我只聊聊 運(yùn)行時(shí) 調(diào)試的那些事兒。
首先從工具上來(lái)講Windows上的windbg肯定是運(yùn)行時(shí)的首選調(diào)試工具(因?yàn)橛袑?duì)應(yīng)版本的SOS.dll),在linux平臺(tái)運(yùn)行時(shí)調(diào)試需要切換到lldb (Only lldb is supported by the SOS plugin. gdb can be used to debug the coreclr code but with no SOS support.)
調(diào)試器的原理和功能基本一樣,但細(xì)節(jié)到某個(gè)功能的命令自然會(huì)有區(qū)別,尤其是熟練了其中1個(gè)的命令之后(比如之前在看匯編的時(shí)候是Intel格式,現(xiàn)在要適用AT&T格式)…
這里先總結(jié)一些個(gè)人常用的命令在 windbg下和lldb下的對(duì)比:
非托管命令:
| 列出當(dāng)前模塊 | image list | lmf |
| 當(dāng)前線程 | thread list | ~ |
| 當(dāng)前線程棧回溯 | thread backtrace | kp |
| 所有線程棧回溯 | thread backtrace all | ~* kp |
| 切換線程 | thread select 2 | ~2s kp |
| 查看寄存器 | re r | r |
| 查看內(nèi)存(8字節(jié)) | memory read –size 8 –format x <address> | dq <address> |
LLDB同GDB的命令對(duì)比:https://lldb.llvm.org/lldb-gdb.html
托管命令:
這里先介紹下自己寫(xiě)的開(kāi)源lldb調(diào)試.net Core擴(kuò)展模塊?Yinuo
在使用lldb調(diào)試linux .net Core程序的過(guò)程中,有很多不適應(yīng)的地方,比如遍歷并查看所有線程的托管棧回溯 在windbg下可以~*e !clrstack?在lldb里雖然有bt all和clrstack?但是卻只能手動(dòng)切換單個(gè)線程再回溯,沒(méi)有辦法結(jié)合到一起,還有一個(gè)原因是lldb內(nèi)命令輸出的內(nèi)容顏色統(tǒng)一,不太好區(qū)分重點(diǎn)關(guān)注的點(diǎn),比如線程回溯比較關(guān)注方法名,托管對(duì)象轉(zhuǎn)儲(chǔ)比較關(guān)注內(nèi)部對(duì)象地址等等,lldb的好處是支持python或者c++接口,可以通過(guò)接口方式寫(xiě)lldb的擴(kuò)展來(lái)輔助我們調(diào)試過(guò)程,提高調(diào)試效率。
下面介紹下調(diào)試擴(kuò)展 Yinuo 的加載過(guò)程:
以下的軟件環(huán)境 CentOS7(x64),lldb-3.6.0,python-2.7.5
首先git下載模塊?git clone https://github.com/espider/yinuo
目錄沒(méi)有要求,但要記得,因?yàn)榧虞d模塊的時(shí)候要知道在哪。啟動(dòng)lldb 并附加被調(diào)試的進(jìn)程?(lldb) attach -p PID
加載Yinuo調(diào)試模塊?(lldb) command script import ~/yinuo/ynlldb.py?之前git下來(lái)的目錄里的python文件
成功加載ynlldb.py后可以?help?看下當(dāng)前注冊(cè)進(jìn)來(lái)的命令,都以 yn_ 為前綴,加載模塊的時(shí)候會(huì)判斷當(dāng)前的.net Core版本號(hào),并自動(dòng)加載對(duì)應(yīng)版本的調(diào)試插件libsosplugin.so
接下來(lái)介紹下當(dāng)前注冊(cè)進(jìn)lldb的輔助調(diào)試命令
yn_heap_dump
查看當(dāng)前托管堆信息命令,以色塊和色塊的比例直觀的感受 Gen 0,1,2 LOH 在同一個(gè)堆內(nèi)的比例 以及其實(shí)際大小(這里的比例按托管堆的地址空間計(jì)算,并沒(méi)有排除Free的和Gen0沒(méi)有使用的地址空間,由于比例可能相去甚遠(yuǎn)所以有可能看不到某個(gè)堆的色塊)
yn_object_dump
轉(zhuǎn)儲(chǔ)托管對(duì)象,可以根據(jù)類型的方法表、類型名、對(duì)象地址 進(jìn)行批量或者單個(gè)轉(zhuǎn)儲(chǔ),同時(shí)計(jì)算對(duì)象(按方法表或類型的話針對(duì)每1個(gè)單獨(dú)對(duì)象)所屬托管堆的位置Gen 0,1,2,LOH,并統(tǒng)計(jì)4類堆內(nèi)的數(shù)量。
支持選項(xiàng)和參數(shù)–methodtable/-m?轉(zhuǎn)儲(chǔ)此方法表的所有對(duì)象,后跟方法表地址;
–type/-t?轉(zhuǎn)儲(chǔ)此類型名的所有對(duì)象,后跟類型名(同方法表選項(xiàng)互斥 2選1);
–offset/-o?轉(zhuǎn)儲(chǔ)對(duì)象的同時(shí)是否深入轉(zhuǎn)儲(chǔ)其內(nèi)部偏移對(duì)象,后跟該對(duì)象的偏移量(目前只支持1級(jí)內(nèi)部偏移)
–address/-a?轉(zhuǎn)儲(chǔ)單個(gè)對(duì)象,后跟對(duì)象地址
–dumpobj/-d?是否轉(zhuǎn)儲(chǔ)對(duì)象,默認(rèn)為True,如果不轉(zhuǎn)儲(chǔ)則只返回對(duì)象地址;
–gen/-g?是否顯示對(duì)象所在的堆Gen0,1,2,LOH,默認(rèn)True;
例如想要查看 類型Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame`1[[Microsoft.AspNetCore.Hosting.Internal.HostingApplication+Context, Microsoft.AspNetCore.Hosting]] 方法表地址?00007ff8711df620?可以這樣寫(xiě)轉(zhuǎn)儲(chǔ)命令:
這里會(huì)對(duì)輸出內(nèi)容做下顏色處理,比如我們比較關(guān)心其內(nèi)部成員的Value這列,如果是地址的話會(huì)顯示成黃色,方便調(diào)試時(shí)候的快速定位。
如果想要轉(zhuǎn)儲(chǔ)某個(gè)對(duì)象可以這樣寫(xiě),根據(jù)對(duì)象地址:
發(fā)現(xiàn)對(duì)象內(nèi)的某個(gè)成員比較感興趣,例如 剛剛的對(duì)象內(nèi)偏移?0xe0?位置是?RawTarget
想對(duì)其進(jìn)行偏移轉(zhuǎn)儲(chǔ) 可以這樣寫(xiě):
yn_thread_clrstack
顯示某個(gè)線程或者所有線程的托管棧回溯,不指定選項(xiàng)?–thread/-t?的話默認(rèn)顯示當(dāng)前線程的托管棧回溯,?-t?跟線程index可以回溯指定線程,或者 跟?all,來(lái)批量顯示所有線程的托管棧回溯,參數(shù)?-a?可選(此為SOS命令clrstack可選參數(shù))
這里也對(duì)輸出的內(nèi)容做了顏色處理,比如IP指令指針列和CallSite是我們比較關(guān)注的,這里分別用黃色和綠色標(biāo)注。
yn_thread_pe
顯示某個(gè)線程或者所有線程托管異常,選項(xiàng)同 yn_thread_clrstack 一樣
yn_transfer
此命令只用于轉(zhuǎn)移執(zhí)行其他lldb命令,因?yàn)閥inuo項(xiàng)目調(diào)試的時(shí)候會(huì)在當(dāng)前目錄生成一個(gè)log文件(ynlldb.log)會(huì)把所有執(zhí)行的yinuo命令及輸出寫(xiě)入日志便于以后的查詢,使用例如:yn_transfer dumpheap -stat?會(huì)執(zhí)行?dumpheap -stat?并把結(jié)果輸出到終端和日志文件里。Yinuo?項(xiàng)目 License 采用 BSD,大家有興趣可以自己調(diào)整或者聯(lián)系我共同維護(hù),實(shí)現(xiàn)自己的調(diào)試命令比較簡(jiǎn)單,git項(xiàng)目?jī)?nèi)的 commandlist 目錄,所有自動(dòng)注冊(cè)的調(diào)試命令都在這里以,自命名.py文件即可,內(nèi)容例子及說(shuō)明如下:
#!/usr/bin/python # coding:utf-8 import lldb import commandlist.ynbase as yn from util.colorstyle import * from util.exportcontent import * """這里把自定義類型注冊(cè)進(jìn)來(lái)等待加載的時(shí)候自動(dòng)注冊(cè)""" def register_lldb_commands(): return [ YNTransfer() ] """自定義類型繼承自yn.YNCommand即可""" class YNTransfer(yn.YNCommand): """ transfer lldb command and exe it for log """ def __init__(self): pass """這個(gè)是注冊(cè)到lldb里的命令名字""" def name(self): # register function name in lldb return 'yn_transfer' """如果有選項(xiàng)的話在這里定義,基于python argparse模塊實(shí)現(xiàn)的 """ def options(self): return [ ] """描述信息""" def description(self): return 'Transfer lldb command for log,e.g. arguments: dumpheap -stat' """這里是實(shí)際命令的具體實(shí)現(xiàn)了""" def run(self, options, arguments): target = lldb.debugger.GetSelectedTarget() if target: if arguments: YNTransfer.handle_command(arguments) else: export_content(' ? ?no arguments in yn_transfer.') else: export_content(' ? ?no target in current debugger.') def handle_command(args): (ci, result) = yn.run_log_command( " " + (" ".join(args) if len(args) > 0 else '')) success = result.Succeeded() if success: output = result.GetOutput() contents = output.strip() lines = contents.splitlines(False) for i in range(len(lines)): export_content(' ? ?%s' % lines[i]) else: export_content( ' ? ?error="%s"' % use_style_level( important_level['high2'], result.GetError())) export_content( ' ? ?%s ? ' % use_style_level( important_level['low2'], '-------------')) |
參考文檔:
https://github.com/dotnet/coreclr/blob/master/Documentation/building/debugging-instructions.md
https://lldb.llvm.org/python-reference.html
https://github.com/llvm-mirror/lldb
https://github.com/facebook/chisel
相關(guān)文章:?
快速搭建本地 .NET Core 運(yùn)行時(shí)調(diào)試環(huán)境
原文地址:https://espider.github.io/NET-Core/lldb-dotnet-core-python/
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的lldb 调试 linux下 .net Core 总结及开源扩展 yinuo的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .NET Core快速入门教程 1、开篇
- 下一篇: SQL Server 2017 RC1