Windows调试技巧&工具

前言

我们使用调试工具的时候,一般是要处理以下问题:

  1. crash
  2. anr application no response
  3. memory leak
  4. dead lock
  5. high cpu usage

Windows平台上有很多对应的处理方案,不过现有的方案一般都有一个通病:一旦打开某些调试功能或者系统设置,调试目标的性能就会成倍下降。这一点有时候会对调试来带很大的干扰,比如出现目标程序UI线程卡顿,导致无法正常操作,或者没发现要调查的问题,反而是发现了一堆性能方面的问题。

crash

崩溃的情况可以从几个正交的维度来划分:是否必现,开发的机器上是否可以复现,是否抓到crash dump。

一旦接手一个crash问题排查,可以按照下面的步骤来排查:

  1. 发现问题的机器上是否可以复现。可以复现的情况又分为:
    • 必现 : 这个情况是最简单了。
    • 高概率复现 :这个也比较简单。
    • 低概率复现,甚至只出现一次 :这个最棘手,作为开发最怕遇到这类问题。但是理论上讲,只要能发现一次就一定有一个必现的路径,只不过实践中碰到的概率低而已。套用一句话来描述就是:crash只有0次和无数次。
  2. 能复现的话,是否有复现路径。
    • 有的话,开发的机器上是否可以复现:那么直接用调试器来看就好了,这个最简单了,调试器就可以直接抓到crash点。
    • 开发的机器上不能复现的话,qa的机器上是否可以复现:可以通过远程调试的方法来处理。
    • 如果开发和测试的机器都不能复现的话,可能就需要联系客户了。这个要做好准备,这时候可以使用的方法就是:日志+dump。
      • 使用dump还要看是否能(尽量)跑debug版本,因为有时候release版本缺少一些符号信息,可能会出现即使复现了,但由于缺少符号,缺少active信息而导致无法进一步查问题。
      • 提一下,日志对于crash问题来说往往帮助不大,不过聊胜于无。
  3. 走到这一步,那就是说这个问题很可能不好复现,而且拿不到dump信息,那么这时候问题的解决就很可能没希望了。留下最后一个办法就是:耐心。排查代码,不论使用什么方法,在怀疑的点上加日志,做好准备抓dump,部署到目标环境,然后静候crash发生。

对于上面的场景,其实概括起来处理方法就三种:

  1. 本地调试:一般是使用MSVC
  2. 远程调试:MSVC或者WinDbg都可以
  3. 抓dump分析:MSVC或者WinDbg都可以,生成dump的方式,可以在资源管理器里面找到目标进程,右键执行创建转储文件;或者使用MSVC/WinDbg的创建dump功能。

anr

首先,对于anr的问题要注意的一个问题是,一定要确定anr确实发生了,因为有时候可能是程序运行环境配置较差导致的,静置一会儿看看是否就没问题了。当然如果干脆就不允许出现anr,那么应该有两方面的考虑:给出最低环境配置和找出造成anr的问题代码(比较有追求的做法)。

确定是anr问题了,那么接下来就要采取一些措施,比如动态抓dump看UI线程卡在哪里了。或者直接制造一个crash,也是得到dump,调查一下UI线程卡在哪里了。当然如果能上(本地或者远程)调试器的话是最好的了,anr的时候直接让程序停下来就可以在call stack中看到UI线程在干嘛。

另外提一下,mac上有一个不错的工具,可以在活动监视器里面采样,从而直观地看到哪行代码有问题。Windows系统上就只能用msvc工具来查了,但毕竟并不是所有(测试/客户的)环境都有调试工具,不是很好查;同时这个问题还可能与high cpu usage或者dead lock有点关联。

memory leak

内存泄露问题,可以:

dead lock

发现死锁本身就是一个问题,比如非UI线程死锁,可能就没有UI线程卡死那么直观。可能是某些对该线程的同步调用卡住,或者异步调用没有结果返回才能发现。

发现之后可以抓dump,或者上调试器。

high cpu usage

Windows上由于缺少一个对进程的各个线程采样统计的工具(我没发现),所以这个问题查起来没有那么直接。WinDbg上可以用!runaway来查CPU的使用时间,但是这个使用时间,我们无法指定开始统计和结束统计的时间点,也就是说真正耗时的操作可能会被很早运行的线程运行时长超过,导致我们定位不出来。

Profile

PC Profile工具

手工复原堆栈

参考 Manually Walking a Stack

堆栈失效,可能是由于对无效地址的调用导致调试器丢失了返回地址的位置;或者您可能遇到了无法直接获取堆栈跟踪的堆栈指针;或者可能存在其他一些调试器问题。

思路是:

  • 先从检查符号开始 x *! 命令
  • dd esp/rsp 查看栈指针附近的信息,找出可能是函数指针的值,这里要注意排除掉下面的值:
    • 一般整数比较小,dd的时候大部分位是0,这类值不太可能是函数地址
    • 英文字符的范围一般在 [20,7f] 区间
    • 指向栈上的指针变量大小一般与esp/rsp接近
    • 函数指针一般不太可能重复
    • 状态码通常以 ac (c00000d6) 开头
    • 最后,栈的地址应该位于模块的区间内,也就是在第一步x命令找到的模块的地址区间内
  • 这样得到一组函数指针备选集,然后逐个使用ln address查看对应符号,得到一组可能的函数调用符号
  • 然后通过反汇编指令u 函数指针备选集合中的每一个元素的前一个地址,看看得到的结果是否匹配上面那组符号,从而构建出调用堆栈来。

GFlags

Windows上好多调试功能都需要 gflags 这个工具。

GFlags(全局标志编辑器)gflags.exe 启用和禁用高级调试、诊断和故障排除功能

gflags官方有一个实例说明:GFlags Examples

包含以下工具 accesschk.exe accesschk64.exe AccessEnum.exe AdExplorer.chm ADExplorer.exe ADInsight.chm ADInsight.exe adrestore.exe Autologon.exe autoruns.chm Autoruns.exe Autoruns64.exe autorunsc.exe autorunsc64.exe Bginfo.exe Cacheset.exe Clockres.exe Clockres64.exe Contig.exe Contig64.exe Coreinfo.exe ctrl2cap.amd.sys ctrl2cap.exe ctrl2cap.nt4.sys ctrl2cap.nt5.sys dbgview.chm Dbgview.exe Desktops.exe Disk2vhd.chm disk2vhd.exe diskext.exe diskext64.exe Diskmon.exe DISKMON.HLP DiskView.exe DMON.SYS du.exe du64.exe efsdump.exe Eula.txt FindLinks.exe FindLinks64.exe handle.exe handle64.exe hex2dec.exe hex2dec64.exe junction.exe junction64.exe ldmdump.exe Listdlls.exe Listdlls64.exe livekd.exe LoadOrd.exe LoadOrd64.exe LoadOrdC.exe LoadOrdC64.exe logonsessions.exe logonsessions64.exe movefile.exe movefile64.exe notmyfault.exe notmyfault64.exe notmyfaultc.exe notmyfaultc64.exe ntfsinfo.exe ntfsinfo64.exe pagedfrg.exe pagedfrg.hlp pendmoves.exe pendmoves64.exe pipelist.exe pipelist64.exe PORTMON.CNT portmon.exe PORTMON.HLP procdump.exe procdump64.exe procexp.chm procexp.exe procmon.chm Procmon.exe PsExec.exe PsExec64.exe psfile.exe psfile64.exe PsGetsid.exe PsGetsid64.exe PsInfo.exe PsInfo64.exe pskill.exe pskill64.exe pslist.exe pslist64.exe PsLoggedon.exe PsLoggedon64.exe psloglist.exe pspasswd.exe pspasswd64.exe psping.exe psping64.exe PsService.exe PsService64.exe psshutdown.exe pssuspend.exe pssuspend64.exe Pstools.chm psversion.txt RAMMap.exe readme.txt RegDelNull.exe RegDelNull64.exe regjump.exe RootkitRevealer.chm RootkitRevealer.exe ru.exe ru64.exe sdelete.exe sdelete64.exe ShareEnum.exe ShellRunas.exe sigcheck.exe sigcheck64.exe streams.exe streams64.exe strings.exe strings64.exe sync.exe sync64.exe Sysmon.exe Sysmon64.exe Tcpvcon.exe tcpview.chm Tcpview.exe TCPVIEW.HLP Testlimit.exe Testlimit64.exe Vmmap.chm vmmap.exe Volumeid.exe Volumeid64.exe whois.exe whois64.exe Winobj.exe WINOBJ.HLP ZoomIt.exe
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值