设为主页 | 加入收藏 | 繁體中文

通往WinDbg的捷径(一)

  原文:http://www.debuginfo.com/articles/easywindbg.html
  译者:arhat
  工夫:2006年4月13日
  要害词:CDB WinDbg
  导言
  你钟情什么样的调试器?如果你问我这个题目,我会回答是“Visual Studio + WinDbg”。我比较喜好Visual Studio那朴素无华且易操作的接口,更喜好它能敏捷把我必要的信息以可视的形式展示出来。但遗憾的是,Visual Studio调试器无法获取某些信息。比方,假设我想晓得哪个线程正在占用特殊的临界区?大概是哪个函数占用了大部分的栈空间?不用担心,有WinDbg呢。它的下令能回答这些题目,以及调试过程中呈现的别的风趣的题目。乃至不加入Visual Studio,WinDbg就可以附上目标使用步伐??谢谢WinDbg支持入侵形式的调试(本文后面会细致讨论),我们可以把Visual Studio GUI和WinDbg的下令行联合起来利用。
  唯一的题目是WinDbg不太好用。必要花些工夫适应它的用户界面,而掌握它的下令则要花更多的工夫。但是假设你现在就必要它,顿时用它调试紧急的题目?有什么疾速简便的方法吗?当然。WinDbg的小弟CDB,功效和WinDbg差未几;因为它是基于下令行的,所以用起来更简略一些。在这篇文章里,我将把CDB作为Visual Studio调试器的增补,介绍怎样利用CDB。在这篇文章里,你将会看到怎样设置装备摆设CDB,怎样用它解决实际的题目。别的,我还会提供一些批处理文件,它们可以隐蔽CDB下令行接口的大部分复杂性,也让你少打几个字。
  安装与设置装备摆设
  安装
  当然,在利用CDB前,必须先安装并设置装备摆设它。WinDbg和CDB是Debugging Tools for Windows 的一部分,可以从这里下载。安装很简略,你可以用默认设置安装,除非你预备用WinDbg SDK开辟使用步伐。(如果你预备用SDK,必要选择定制安装,并启用SDK安装;推荐你把它安装在不包含空格的目次名的目次中)。安装完成后,安装目次里将包含全部必需的文件,包罗WinDbg(windbg.exe)和CDB(cdb.exe)。
  调试东西也支持“xcopy”类型的安装。也便是说,在一台呆板上安装后,如果你想在别的的呆板上利用,不用再安装,直接把曾经安装的目次直接拷过去就行了。
  符号文件服务器途径
  如果不能拜访操作体系DLL的最新的符号文件,有些重要的WinDbg下令将不能正常工作。在以往,我们可以从微软的FTP服务器上下载宏大的符号文件包,然后从中找出必要的符号文件。这非常糜费工夫,并且在操作体系更新或晋级后,符号文件就过期了(因而也就变得毫无用途)。幸运的是,现在有更简便的方法来获得符号文件??符号文件服务器。WinDbg和Visual Studio都支持这个方法,在必要时直接从微软维护的服务上下载最新的符号文件。有了符号文件服务器,我们再也不用下载整个符号文件包了(那着实是太大了),因为调试器晓得必要用到哪个DLLs,所以直接下载单个符号文件就行了。如果符号文件在操作体系更新或晋级当前过期了,调试器会注意到这种情况,并再次下载必需的符号文件。
  为了使符号文件服务器起作用,我们应该让调试器晓得符号文件服务器的途径。最简略的方法是在_NT_SYMBOL_PATH环境变量里指定符号文件服务器的途径。可以用如下的途径:
  "srv*c:\symbolcache*http://msdl.microsoft.com/download/symbols"
  (c:\symbolcache目次将被用来生存从符号文件服务器下载上去的符号文件;当然,你可以用任何有用的当地或网络途径)。比方:
  set _NT_SYMBOL_PATH=srv*c:\symbols*http://msdl.microsoft.com/download/symbols
  在你设置_NT_SYMBOL_PATH环境变量之后,就可以利用符号文件服务器了。关于符号文件服务器的更多信息,相关设置,以及可能会用到的排除故障的小技巧,可以从WinDbg的文档中找到(Debuggers | Symbols section)。
  如果你必要从一台需登录的署理服务器后拜访符号文件服务器。参见本篇文章中CDB and proxy servers部分,了解更多信息。
  CDB 下令行基础介绍
  启动调试会话
  当我们利用新的调试器时,第一个题目通常是:怎样开端调试会话呢?像大少数调试器一样,CDB允许我们调试使用步伐的新实例,大概附上一个曾经运行的过程。启动新实例就象下面一样简略:
  cdb c:\myapp.exe
  如果我们想附上曾经运行的过程,可能会用上下列某个选项:
  ----------------------------------------------------------------------------------------------------------------------
  选项                形貌                                                                            例子
  ----------------------------------------------------------------------------------------------------------------------
  -p Pid              这个选项允许CDB附上指定进程ID的进程。可以用使命管理器或雷同的东西得到进程ID。   cdb -p 1034
  ----------------------------------------------------------------------------------------------------------------------
  -pn ExeName         这个选项允许CDB用指定的可执行文件名(.exe)附上进程。这个选项比“-p Pid”更
  方便,因为我们通常晓得执行的步伐名,不必在使命管理器中探求进程的ID。但是如果
  多个进程利用同一个名字(CDB将报错),就不能用这个选项了。                       cdb -pn myapp.exe
  ----------------------------------------------------------------------------------------------------------------------
  -psn ServiceName    这个选项允许CDB附上指定服务的进程。比方,倘使你想附上Windows Management
  Instrumentation服务,应该用WinMgmt作为服务名。                                  cdb -psn MyService
  ----------------------------------------------------------------------------------------------------------------------
  CDB也可以阐发故障转储。用-z选项打开故障转储:
  cdb -z DumpFile
  比方:
  cdb -z c:\myapp.dmp
  结束调试会话
  启动新的调试会话后,CDB会显示它自己的下令行提示符。你可以在这个提示符下执行CDB支持的任何下令。'q'下令结束调试会话并加入CDB:


  0:000> q
  quit:
  >
  告诫:当你结束调试会话,加入CDB时,操作体系也将终止被调试的步伐。如果你想加入CDB并连结被调试步伐,可以用.detach下令(Windows XP或更新的操作体系才支持),大概用非入侵的形式(下面讨论)。
  运行下令
  虽然可以在CDB下令行提示符下执行调试器下令,但在下令行里指定必要的下令通常更快一些,用-c选项。
  cdb -pn myapp.exe -c "command1;command2"
  (用分号分开多个下令)
  比方,下列下令即将把CDB附上我们的使用步伐,显示已加载的模块,然退却出:
  cdb -pn myapp.exe -c "lm;q"
  注意,在下令列表的结尾加上'q'下令??将在全部的调试器下令执行后封闭CDB。
  入侵形式调试
  在默认环境下,当我们用CDB调试一个曾经运行的进程时,它通常作为全功效的调试器附上进程(利用Win32 Debugging API)。在这种形式下,可以设置断点,单步骤试代码,得到各种调试事件的通知(比方,非常,加载/卸载模块,启动/加入线程,等等)。Visual Studio也可以做到这些,并提供更友爱的用户界面。别的,每个进程每次只能被一个调试器附上。这能否意味着如果我们用Visual Studio调试器调试使用步伐,就不能再用CDB得到它的附加信息了?不,不完满是这样,因为除了全功效调试形式外,CDB还支持入侵调试形式。
  CDB以入侵形式附上目标进程时,并没有利用Win32 Debugging API,而是先停息目标进程的全部线程,执行用户指定的下令。在全部的下令执行之后,CDB加入之前,规复停息的线程。因而,目标进程可以继续运行,好像什么事也没产生一样。纵然像Visual Studio之类的全功效调试器正在调试目标进程,CDB仍可以用入侵形式附上它,并获得所必要的信息。在CDB完成使命并分离附上的进程后,我们可以继续用Visual Studio调试器调试这个使用步伐。
  怎样启用CDB的入侵形式?用-pv下令行选项。比方,下列下令即将以入侵形式附上使用步伐,显示已加载模块的列表,然退却出。在CDB加入之后,使用步伐将继续运行。
  cdb -pv -pn myapp.exe -c "lm;q"
  把输出内容生存到日记文件
  有些CDB下令的输出内容可能会很长,从控制台窗口阅读十分不便。因而,把输出内容生存到日记文件,再用别的的编辑器查看会更好一些,CDB允许我们用-loga和-logo选项来实现('-loga '把输出内容追加到指定文件的结尾;而'-logo '将覆盖原有的文件,如果文件曾经存在的话)。
  在我们的例子下令(列出目标进程里的模块)里增长记录功效,把输出内容生存到当前目次的out.txt文件里:
  cdb -pv -pn myapp.exe -logo out.txt -c "lm;q"
  源行号信息
  CDB支持的别的一个重要选项是-lines。这个选项打开源行号信息支持,比方,当报告挪用栈时,允许CDB显示源文件及源行号。(在默认环境下,源行号支持是封闭的,CDB不显示源文件/行号信息)。
  CDB 和署理服务器
  如果你在必要登录的署理服务器后用CDB,在默认环境下,将不能拜访符号文件服务器。缘故原由是在默认设置装备摆设下,当CDB实验连接符号文件服务器时,不显示署理服务器的登录提示。为了更改这个行为,使我们可以拜访符号文件服务器,必要在下令行之前加上两条下令:
  !sym prompts;.reload
  比方:
  cdb -pv -pn myapp.exe -logo out.txt -c "!sym prompts;.reload;lm;q"
  启动消息
  当CDB调试新使用步伐,附上曾经存在的进程,或打开故障转储时,将显示一系列的启动消息。CBD下令(可以用-c选项指定,或手动输出)的输出内容跟在这些消息之后。通常环境下,启动消息只显示一些无关紧急信息;但是如果在执行时出错了,它将包含这个题目的形貌,偶然间也会提供解决方法。
  比方,下列输出内容通知我们没有设置符号途径,因而,有些调试器下令不能工作:
  D:\Progs\DbgTools>cdb myapp.exe
  Microsoft (R) Windows Debugger  Version 6.5.0003.7
  Copyright (c) Microsoft Corporation. All rights reserved.
  CommandLine: myapp.exe
  Symbol search path is: *** Invalid ***
  指导指导指导指导指导指导指导指导指导指导指导指导****
  * Symbol loading may be unreliable without a symbol search path.           *
  * Use .symfix to have the debugger choose a symbol path.                   *
  * After setting your symbol path, use .reload to refresh symbol locations. *
  指导指导指导指导指导指导指导指导指导指导指导指导****
  总结
  这里是一些罕见的CDB下令行模板,本篇文章的剩下部分将会用到它们(我们总是用同样的模板,然后凭据我们要解决的题目,改变-c选项外部的下令行列表)。
  用入侵形式附上运行的进程(通常是进程ID),执行一组下令,并把输出内容生存在out.txt文件里:
  cdb -pv -p -logo out.txt -lines -c "command1;command2;...;commandN;q"
  用入侵形式附上运行的进程(用可执行文件名),执行一组下令,并把输出内容生存在out.txt文件里:
  cdb -pv -pn -logo out.txt -lines -c "command1;command2;...;commandN;q"
  用入侵形式附上运行的进程(通常是服务名),执行一组下令,并把输出内容生存在out.txt文件里:
  cdb -pv -psn -logo out.txt -lines -c "command1;command2;...;commandN;q"
  打开故障转储文件,执行一组下令,并把输出内容生存在out.txt文件里:
  cdb -z -logo out.txt -lines -c "command1;command2;...;commandN;q"
  如果我们在必要登录的署理服务器后利用CDB,要拜访符号文件服务器,必要增长两条下令。比方:
  cdb -pv -pn -logo out.txt -lines -c "!sym prompts;.reload;command1;command2;...;commandN;q"
  好像要打好多字?着实不是这样,稍后,我将提供一些批处理文件,它们将为我们隐蔽重复的下令行选项,把要我们输出的内容减至最小。
  解决实际的题目
  调试去世锁题目
  当我们的使用步伐挂起或制止相合时,最天然的题目是:它现在正在做什么?它在那边被困住了?当然,我们可以用Visual Studio调试器附上使用步伐,检查全部线程的挪用栈。但我们同样可以用CDB,并且会更快一些。下列下令将使CDB以入侵形式附上使用步伐,打印全部的挪用栈,把结果生存在日记文件里,然退却出:
  cdb -pv -pn myapp.exe -logo out.txt -lines -c "~*kb;q"
  ('kb'下令要求CDB打印当火线程的挪用栈;'~*'前缀要求CDB在进程全部已存在的线程里重复执行'kb'下令)。
  [/URL] DeadLockDemo.cpp是一个演示典范的去世锁题目的例子。如果你编译并运行,它的工作线程顿时会被困住,如果我们运行上述的下令来查看使用步伐的线程正在做什么,将看到下列雷同的内容(在这,以及后面,我们将省略启动消息):
  .  0  Id: 6fc.4fc Suspend: 1 Teb: 7ffdf000 Unfrozen
  ChildEBP RetAddr  Args to Child             
  0012fdf8 7c90d85c 7c8023ed 00000000 0012fe2c ntdll!KiFastSystemCallRet
  0012fdfc 7c8023ed 00000000 0012fe2c 0012ff54 ntdll!NtDelayExecution+0xc
  0012fe54 7c802451 0036ee80 00000000 0012ff54 kernel32!SleepEx+0x61
  0012fe64 004308a9 0036ee80 a0f63080 01c63442 kernel32!Sleep+0xf
  0012ff54 00432342 00000001 003336e8 003337c8 DeadLockDemo!wmain+0xd9
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 154]
  0012ffb8 004320fd 0012fff0 7c816d4f a0f63080 DeadLockDemo!__tmainCRTStartup+0x232
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\crt0.c @ 318]
  0012ffc0 7c816d4f a0f63080 01c63442 7ffdd000 DeadLockDemo!wmainCRTStartup+0xd
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\crt0.c @ 187]
  0012fff0 00000000 0042e5aa 00000000 78746341 kernel32!BaseProcessStart+0x23
  1  Id: 6fc.3d8 Suspend: 1 Teb: 7ffde000 Unfrozen
  ChildEBP RetAddr  Args to Child             
  005afc14 7c90e9c0 7c91901b 000007d4 00000000 ntdll!KiFastSystemCallRet
  005afc18 7c91901b 000007d4 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc
  005afca0 7c90104b 004a0638 00430b7f 004a0638 ntdll!RtlpWaitForCriticalSection+0x132
  005afca8 00430b7f 004a0638 005afe6c 005afe78 ntdll!RtlEnterCriticalSection+0x46
  005afd8c 00430b15 005aff60 005afe78 003330a0 DeadLockDemo!CCriticalSection::Lock+0x2f
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 62]
  005afe6c 004309f1 004a0638 f3d065d5 00334fc8 DeadLockDemo!CCritSecLock::CCritSecLock+0x35
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 90]
  005aff6c 004311b1 00000000 f3d06511 00334fc8 DeadLockDemo!ThreadOne+0xa1
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 182]
  005affa8 00431122 00000000 005affec 7c80b50b DeadLockDemo!_callthreadstartex+0x51
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
  005affb4 7c80b50b 003330a0 00334fc8 00330001 DeadLockDemo!_threadstartex+0xa2
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
  005affec 00000000 00431080 003330a0 00000000 kernel32!BaseThreadStart+0x37
  2  Id: 6fc.284 Suspend: 1 Teb: 7ffdc000 Unfrozen
  ChildEBP RetAddr  Args to Child             
  006afc14 7c90e9c0 7c91901b 000007d8 00000000 ntdll!KiFastSystemCallRet
  006afc18 7c91901b 000007d8 00000000 ;00000000 ntdll!ZwWaitForSingleObject+0xc
  006afca0 7c90104b 004a0620 00430b7f 004a0620 ntdll!RtlpWaitForCriticalSection+0x132
  006afca8 00430b7f 004a0620 006afe6c 006afe78 ntdll!RtlEnterCriticalSection+0x46
  006afd8c 00430b15 006aff60 006afe78 003332e0 DeadLockDemo!CCriticalSection::Lock+0x2f
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 62]
  006afe6c 00430d11 004a0620 f3e065d5 00334fc8 DeadLockDemo!CCritSecLock::CCritSecLock+0x35
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 90]
  006aff6c 004311b1 00000000 f3e06511 00334fc8 DeadLockDemo!ThreadTwo+0xa1
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 202]
  006affa8 00431122 00000000 006affec 7c80b50b DeadLockDemo!_callthreadstartex+0x51
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
  006affb4 7c80b50b 003332e0 00334fc8 00330001 DeadLockDemo!_threadstartex+0xa2
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
  006affec 00000000 00431080 003332e0 00000000 kernel32!BaseThreadStart+0x37
  挪用栈(和源行号)表示ThreadOne正在占用临界区CritSecOne并等待临界区CritSecTwo,但是ThreadTwo正占用临界区CritSecTwo并等待临界区CritSecOne。这是典范的“lock acquisition order”去世锁例子,在那边,两个线程必要得到同一组同步的东西,以差别的顺序利用。如果你想制止这种类型的去世锁,必须包管全部的线程以相同的顺序得到所需的同步东西(在这个例子里,ThreadOne和ThreadTwo能赞同首先得到CritSecOne,然后得到CritSecTwo来制止去世锁)。
  在默认环境下,'kb'下令只显示挪用栈的前20帧。如果你想查看更多的栈帧,你可以显式指明显示的栈帧数量(比方,'kb100'下令要求调试器显示100帧)。在WinDbg会话里,可以用.kframes下令改变随后下令的默认限定。
  我们的例子只包含了三个简略的线程,很容易看出哪个线程应该为去世锁卖力。在大使用步伐里,很难找出可疑的线程并进行验证。那我们应该怎样做呢?在大部分环境下,我们应该晓得谁人没有正常运转的线程(否则,我们怎样会注意到使用步伐呈现非常了呢?)。通常,这个线程是在等待同步东西,这个东西因为某些缘故原由临时不可用。这个东西为什么不可用呢?如果我们晓得哪个线程正在占用这个东西(拥有它,换句话说),应该能答出这个题目。如果这个东西可巧在临界区,!locks下令应该能资助我们识别出它的当前全部者。当不带参数利用时,这条下令显示使用步伐线程正在占用的临界区的列表。输出的内容不包罗已开释的临界区。
  让我看看实际利用中的!locks下令:
  cdb -pv -pn myapp.exe -logo out.txt -lines -c "!locks;q"
  下面是这条下令的输出内容(同样以DeadLockDemo.cpp为例):
  CritSec DeadLockDemo!CritSecOne+0 at 004A0620
  LockCount          1
  RecursionCount     1
  OwningThread       3d8
  EntryCount         1
  ContentionCount    1
  *** Locked
  CritSec DeadLockDemo!CritSecTwo+0 at 004A0638
  LockCount          1
  RecursionCount     1
  OwningThread       284
  EntryCount         1
  ContentionCount    1
  *** Locked
  仔细查看了40个临界区
  查看!locks下令的输出(尤其是OwningThread字段),我们可以推测出临界区CritSecOne被ID为0x3d8的线程占用,临界区CritSecTwo被ID为0x284的线程占用。我们可以在'kb'下令的输出内容(在后面的输出里)里找出这些IDs对应的线程。
  如果使用步伐利用别的种类的同步东西(比方,互斥),识别它们的全部者将更难一些(必要内核调试器),我预备在当前的文章中再介绍这部分内容。
  调试CPU高斲丧的题目
  对大少数软件来说,太高的CPU斲丧率(凭据使命管理器的显示,在单CPU上接近100%)明显指出软件中有bug。通常意味着使用步伐的某个线程堕入了去世循环。当然,调试这个题目的、最普通的方法是用Visual Studio调试器附上这个进程,查找哪个线程在捣乱。但是我们应该检查哪个线程呢?CDB为我们提供了简便的方法??!runaway下令。当不带参数利用时,这条下令显示使用步伐每个线程执行用户形式代码时所花的工夫(利用别的的参数,可以显示在内核形式下所花的工夫,自线程启动后占用的工夫等)。
  如下是在CDB下利用这条下令的示例:
  cdb -pv -pn myapp.exe -logo out.txt -c "!runaway;q"
  下面是!runaway下令的输出示例:
  0:000> !runaway
  User Mode Time
  Thread       Time
  1:358       0 days 0:00:47.408
  2:150       0 days 0:00:03.495
  0:d8        0 days 0:00:00.000
  看起来好像是ID为0x358的线程占用了大部分的CPU工夫。但这个消息还不足以证明线程0x358便是罪魁罪魁,因为这条下令显示的CPU工夫是线程在它整个生命期中所花的。我们还必要进一步查看线程所用CPU工夫的变化环境。让我们再次运行这条下令。这次,我们可以看到雷同于下列的内容:
  0:000> !runaway
  User Mode Time
  Thread       Time
  1:358       0 days 0:00:47.408
  2:150       0 days 0:00:06.859
  0:d8        0 days 0:00:00.000
  现在,我们可以把这个输出内容与上次的输出内容做个比较,找出CPU工夫增长最快的线程。在这个例子里,很明显便是线程0x150。现在,我们可以用Visual Studio调试器附上这个使用步伐,切换到这个线程下,检查它为什么转个不绝。
  调试栈溢出
  当我们想找出栈溢出非常的缘故原由时,CDB也非常有资助。当然,无控制的递归挪用是栈溢出最典范的缘故原由,通常来说,查看破坏了的线程的挪用栈,找出它从那边离开控制就可以了。Visual Studio在这方面可以做的很好,那为什么还要用CDB呢?让我们设想一个更复杂的例子。比方,假设我们的使用步伐中包含一个依赖递归的算法?我们在计划算法时利用有符号数,在全部可能的情况下控制递归的运行,但某个工夫栈仍溢出了。为什么?或许是因为在某种环境下,算法利用的某些函数占用了太多的栈空间。我们怎样确定函数占用的总的栈空间呢?不幸地是,Visual Studio调试器没有简便的方法可以做到。
  纵然挪用栈没有显示任何递归的迹象时,使用步伐也可能会呈现栈溢出非常。比方,查看StackOvfDemo.cpp例子。如果你编译,并在调试器下运行它,将立即呈现栈溢出。但此刻的挪用栈看起来统统正常:
  StackOvfDemo.exe!_woutput
  StackOvfDemo.exe!wprintf
  StackOvfDemo.exe!ProcessStringW
  StackOvfDemo.exe!ProcessStrings
  StackOvfDemo.exe!main
  StackOvfDemo.exe!mainCRTStartup
  KERNEL32.DLL!_BaseProcessStart@4
  显然,挪用栈上的某个函数利用了太多的栈空间。但是我们怎样找出这个函数呢?不用担心,有了CDB的'kf'下令的资助,可以显示每个函数在挪用栈上占用的字节数。在使用步伐还停在Visual Studio调试器里的工夫,我们可以运行下列下令:
  cdb -pv -pn stackovfdemo.exe -logo out.txt -c "~*kf;q"
  ('kf'默认显示挪用栈上最后的20帧,像我们在“调试去世锁题目”部分讨论的那样。如果你想多显示一些,可以增长前缀,比方,~*kf1000。别的要注意的是,~*kf将报告全部线程的挪用栈。如果使用包含大量的线程,它就不太得当了,这时,可以把它改成'~~[tid]kf', 'tid'是目标线程的线程ID(比方,'~~[0x3a8]kf'))
  这条下令显示的内容如下:
  .  0  Id: 210.3a8 Suspend: 1 Teb: 7ffde000 Unfrozen
  Memory  ChildEBP RetAddr 
  00033440 0041aca5 StackOvfDemo!_woutput+0x22
  44 00033484 00415eed StackOvfDemo!wprintf+0x85
  d8 0003355c 00415cc5 StackOvfDemo!ProcessStringW+0x2d
  fc878 0012fdd4 00415a44 StackOvfDemo!ProcessStrings+0xe5
  108 0012fedc 0041c043 StackOvfDemo!main+0x64
  e4 0012ffc0 7c4e87f5 StackOvfDemo!mainCRTStartup+0x183
  30 0012fff0 00000000 KERNEL32!BaseProcessStart+0x3d
  注意第一列的内容??它报告栈上函数所占用的字节数。很显然,ProcessStrings函数用了可用栈空间的最大份额,因而,它可能要为栈溢出卖力。
  如果你想晓得ProcessStrings函数为什么必要云云多的栈空间,这里有一些表明。这个函数利用ATL的A2W宏把字符串从ANSI格式转换成Unicode格式,这个宏在外部用_alloca函数在栈上分派内存。用_alloca分派的内存只有当它的挪用者(在这个例子里是ProcessStrings)前往后才被开释。直到ProcessStrings前往控制之前,A2W(因而,也便是_alloca)在栈上为每个后续的挪用分派别的的空间,这将敏捷耗尽栈空间。
  底线:不要在循环里利用_alloca。
 


    文章作者: 福州军威计算机技术有限公司
    军威网络是福州最专业的电脑维修公司,专业承接福州电脑维修、上门维修、IT外包、企业电脑包年维护、局域网网络布线、网吧承包等相关维修服务。
    版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和声明。否则将追究法律责任。

TAG:
评论加载中...
内容:
评论者: 验证码: