zoukankan      html  css  js  c++  java
  • Visual Studio高级调试技巧

    1. 设置软件断点,运行到目标位置启动调试器

    方法①:使用汇编指令(:x64 c++不支持嵌入汇编)

    _asm int 3

    方法②:编译器提供的方法

    __debugbreak();

    方法③:使用windows API

    DebugBreak();

    WerFault.exe进程(Windows Error Reporting)弹出ConsoleTest.exe已停止工作:

    要想出现“调试程序”选项,需要将Windows Error Reporting注册表信息设置成如下图所示(:特别是红框的内容)

    如果在注册表AeDebug的Debugger项配置了VSJitDebugger路径,且VSJitDebugger安装正常

    HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionAeDebug
    
    HKEY_LOCAL_MACHINESoftwareWow6432NodeMicrosoftWindows NTCurrentVersionAeDebug  // 注:64位系统上的32位程序使用该注册表项

    点击“调试程序”选项就会弹出Visual Studio实时调试器对话框,选择对应的调试器后,点击“是”就可以启动调试器并中断到软件断点位置了

    需要注意的是,软件断点也是一种异常,一旦被处理,就不会传到WerFault.exe进程上,那么这种方法也就失效了!

    下面两种情况软件断点异常会被处理:

    ① 被SEH异常捕获并处理

    ② 被自定义的全局异常函数处理

    :可以将上面两种情况中的EXCEPTION_EXECUTE_HANDLER修改为EXCEPTION_CONTINUE_SEARCH来指明异常未得到处理

    2. 修改变量:在悬停出来小面板、Locals窗口、Autos窗口、Watch窗口、或Quick Watch窗口中进行修改;也可以在Immediate窗口中执行:bFlag=false)

    3. 格式化变量

    :d,i:有符号的十进制数
          u:无符号的十进制数
          o:无符号的八进制数
          x:十六进制数(字母小写)
          X:十六进制数(字母大写)

    更多详见:Format specifiers for C++ in the Visual Studio debugger

    4. 修改内存:在内存窗口中,将光标定位到要修改的地方,直接按0-9输入十六进制;要输入a-f则需通过右键菜单中的“Edit Value”进行输入)

    5. 格式化显示内存

     

    6. 设置下一个运行位置:直接拖动黄色箭头到想要的运行位置)

    示例中:传入的bFlag为true,代码开始运行到断点处(43行),然后重新把黄色箭头拖回39行,此时bFlag的值为false,按F10会进入else分支

    :(1)跳过中间所有指令。意味着:printf("True ")及CTest的析构函数均不会被执行

          (2)当拖动箭头到一个新的函数中时,vs会将原来的函数从栈中弹出,将新函数压入栈顶;

                 由于新函数与上层函数没有调用关系,输出类型的参数及返回值很有可能写坏上层函数的栈数据

          (3)该调试技巧为一种事后行为,应谨慎使用,最好是只在函数内局部使用

    7. 编辑然后继续运行

    (1)不能在64位代码上使用

    (2)使用“Program Database for Edit & Continue (/ZI)”生成pdb文件

    (3)仅适用于函数内部改变(若要修改函数原型或增加新函数,只能选择重启程序)

    8. 变量的一些特殊查看方法

    以$和@开头的伪变量:(:$和@两个符号是一样的,随便用哪个都可以)

    $err -- 获取GetLastError()的返回值

    $err,hr -- 获取GetLastError()的返回值并解释返回值的含义

    @eax -- 查看eax的值(64位为@rax)

    @esp+4 -- 函数的第一个参数地址

    $handles -- 查看打开的句柄数

    $pid -- 当前进程id

    $tid  -- 当前线程id

    $vframe  -- 当前栈帧的ebp

    $clk  -- 以时钟周期为单位显示时间

    $ReturnValue -- 查看函数的返回值

    Message,wm --以windows消息的宏形式显示 如:Message为15时,显示为WM_PAINT(:Message为unsigned int类型)

    hResult,hr --hResult为0x80070005时,显示为E_ACCESSDENIED(:hResult为void*类型)

    pArray,10 --从pArray地址起显示后续10个int类型的数据(:pArray为int*类型)

    (pArray+5),3 --从pArray[5]地址起显示后续3个int类型的数据(:pArray为int*类型)

    更多详见:Pseudovariables in the Visual Studio debugger

    9. 获取简单类型的函数返回值

    注1:不能为inline函数

    注2:执行函数的下一条语句时,查看eax或伪变量ReturnValue的值

    10. 使用指针类型转换查看某个地址的变量

    :有时候,尽管对象仍然存在,在调试符号越界后,watch窗口中的变量是被禁用的,不能再查看(也不能更新)。

          若知道对象的地址,则可以将地址转换为该对象类型的指针,放在watch窗中来继续观察它。

    11. 查看其他模块的全局变量

    {,,UE4Editor-Core.dll}GIsEditor

    {,,UE4Editor-Core.dll}GIsClient 

    注:UE4Editor-Core.dll为全局变量GIsEditor、GIsClient所在的模块

    还要另外一个方法是:通过看汇编代码,找到全局变量对应的地址,然后在Memory中查看对应的值或在Watch窗口里面将地址转换成对应的类型

    12. Command窗口

    通过命令来完成vs中的功能(不仅仅在调试状态时使用),另外其调试相关命令与windbg保持一致。

    ? nLocal  //查看变量nLocal的值
    ?? nLocal //将nLocal添加到Quick Watch窗口中
    ? nLocal=100 //修改nLocal的值为100
    ? MySum(20,30) //调用全局函数MySum,并返回结果
    k //打印当前线程堆栈
    ~ //查看线程情况
    ~*k  //打印出所有线程的堆栈信息
    watch //打开watch窗口
    memory2 //打开memory2窗口
    g //继续执行,F5功能
    q //结束调试

    13. 内存断点

    (1)在84行断点停住后,查看&s.Age的地址为0x0042FCEC

    (2)点击"Debug"-"New Breakpoint"-"New Data Breakpoint...",在弹出的对话框Address填入:0x0042FCEC,长度为4即可

    (3)当运行到88行时,由于Scores数组越界引发了s.Age的内存修改,触发了内存断点

    14. 条件断点

    断点说明:

    (1)设置断点条件:i>6;且被命中次数>=2时才断住程序,所以第一次断住时i=8

    (2)命中时,在Output窗口中打印当前函数名及线程ID(也可以打印相关变量的值,详见"When Breakpoint Is Hit"面板上的说明);在Command窗口中打印出堆栈信息

    (3)若不想断住程序,可以把"When Breakpoint Is Hit"对话框中的"Continue execution"勾选上

    注1:对于字符串的条件断点,不能写如下条件pStr=="Hello"(pStr为char*类型),应该写成:pStr[0]=='H' && pStr[1]=='e' && pStr[2]=='l' && pStr[3]=='l' && pStr[4]=='o' && pStr[5]==''

         vs2010及以上版本中,条件断点中可使用字符串:strcmp(pStr, "Hello")==0

         支持的字符串函数有:strlen, wcslen, strnlen, wcsnlen, strcmp, wcscmp, _stricmp, _wcsicmp, strncmp, wcsncmp, _strnicmp, _wcsnicmp, strchr, wcschr, strstr, wcsstr.

    注2:也可以创建自己的宏,具体方法:"Tools"-"Macros"-"Macro Explorer",然后在下图:MyMacros-Module1上右键快捷菜单中选择"New macro",

         如ChangeExpression宏函数会在Output窗口的Debugger过滤器下打印出"Hello World",然后修改变量code的值为1000

         编写自己的宏时,可以参考大量vs已有的宏(见:Samples节点下)

    Public Module Module1
        Function GetOutputWindowPane(ByVal Name As String, Optional ByVal show As Boolean = True)
            Dim window As Window
            Dim outputWindow As OutputWindow
            Dim outputWindowPane As OutputWindowPane
    
            window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput)
            If show Then window.Visible = show
            outputWindow = window.Object
            Try
                outputWindowPane = outputWindow.OutputWindowPanes.Item(Name)
            Catch ex As Exception
                outputWindowPane = outputWindow.OutputWindowPanes.Add(Name)
            End Try
            outputWindowPane.Activate()
            Return outputWindowPane
        End Function
    
    
        Sub ChangeExpression()
            Dim bppane As EnvDTE.OutputWindowPane
    bppane
    = GetOutputWindowPane("Debugger") bppane.OutputString("Hello World") DTE.Debugger.ExecuteStatement("code = 1000;") End Sub End Module

    注3:如果弹出以下框,表明vs没有开启macro权限(Tools -- Options... -- Environment -- Add-in/Macro Security中勾选“Allow macros to run”)

    注4:2014.2月windows系统更新后,各个版本的vs的宏失效。

            详见:https://visualstudioextensions.vlasovstudio.com/2014/02/13/visual-studio-2010-macros-stop-working-after-february-2014-windows-update/

            vs2005sp1补丁  vs2008sp1补丁   vs2010sp1补丁

    15.在windows API上打断点

    (1)例如:对SetWindowText打断点。首先当前程序字符集为未设置或多字节,则SetWindowTextA;为Unicode则为SetWindowTextW。下面以SetWindowTextA为例。

    (2)调试运行程序断住后,打开Modules窗口可以看到所有已经加载的模块,找到windows API所在的模块,右击鼠标执行"Load Symbols From" - "Microsoft Symbol Servers"下载并加载对应模块的pdb

    (3)新建一个Break At Function断点,填入:{,,user32.dll}_SetWindowTextA@8。可以看到,VS里面的符号跟windbg相比多了一些字符,其中‘_’表示stdcall类型,后面‘@8’表示所有参数的字节数的和。

           有些函数Symbol Name与导出函数名可能不一致,例如GetDC(HWND),其Symbol Name为NtUserGetDC,最后断点应填入:{,,user32.dll}_NtUserGetDC@4

           :查找windows API符号名可以使用windbg的x命令或者使用pdb解析工具(symView

    也可以直接使用地址对windows API打断点(这种方式不需要符号的支持):如对GetDC打断点,可以用Dependency查看其在user32.dll中导出函数地址(Entry Point列):0x000172CC

    然后在Modules窗口中获得user32.dll模块起始地址0x75840000,最后对两个值相加后的绝对地址处直接设置断点:{,,user32.dll}0x758572CC

    通过以上方法也可以给malloc函数(vs2008 debug其dll为msvcr90d.dll,release的dll为msvcr90.dll;在UE4 vs2017的dll为ucrtbase.dll)打上断点

    16. 异常(First-chance)时断住程序

    在调试程序时,Output窗口有时会出现“First-chance exception in xxx.exe...”这样的信息。
    一般来说,这是由于程序中发生了异常,被调试器捕获而产生的输出。
    在调试器中运行程序时,如果程序产生异常,调试器会首先获得通知(即First-chance exception)。
    若程序没有捕获该异常,则在结束进程之前,操作系统会再次通知调试器(即Second-chance exception,Last-chance exception)。

    (1)在"Debug"-"Exceptions...",弹出如下对话框:点击Add按钮,新增一个int类型的C++ Exceptions异常,并勾选Thrown

    (2)当int、int*、int&的异常被catch到时,会断住程序进入调试状态(:以上void类型对应:char*、void*的异常)

    17. 单步调试自动跳过不必进入的函数  (:仅适用于Native c++)

    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftVisualStudio9.0NativeDEStepOver]
    "1"="\scope:CString.*\:\:.*=NoStepInto"

    注1:如果是32位windows,删除上面路径中的Wow6432Node

    注2:不进入任何CString的方法(前面的1表示优先级,该值越大优先级越高)

    ++++++++++++++++++++++++++++++

    NoStepInto 不可进入匹配函数
    StepInto   可进入匹配函数

    特殊字符串:

    cid 代表一个C/C++标识符
    funct 代表一个C/C++函数名
    scope 代表一个函数的作用范围(命名空间+类名 如:ATL::CFoo::CBar::)
    anything 代表一个字符串
    oper 代表一个C/C++操作符

    正则表达式:

    转义字符 如:要使用 “” 本身, 则应该使用“\”
    : 代表字符:
    . 匹配任意字符
    * 其左边的字符被匹配任意次(0次或多次)。如:be*匹配“b”,“be”或“bee”
    .* 匹配0个或多个字符

    更多例子:

    例1:不进入重载操作符函数:
    10 scope:operatoroper:=NoStepInto
    例2:除了CComBSTRs的非操作符函数,不进入任何ATL::开头的函数:
    20 ATL::CComBSTR::funct:=StepInto
    10 ATL::.*=NoStepInto
    例3:除了全局模版函数外,不进入任何模板函数:
    20 scope:funct:=StepInto
    10 .*[<>].NoStepInto

    ++++++++++++++++++++++++++++++

    18. 使用OutputDebugString进行日志调试

    (1)调试状态时,会将日志输出到Debug过滤器的Output窗口中

    (2)非调试状态时,可采用DbgView.exe来捕捉程序日志

    19. 使用autoexp.dat自定义调试时变量的显示格式

    文件所在位置:Microsoft Visual Studio 9.0Common7PackagesDebuggerautoexp.dat

    在autoexp.dat中的[Visualizer]域可以对各种类型变量的显示格式进行配置,来优化变量在调试时显示,提高效率。

    注1:在vs中要让autoexp.dat生效需要去掉"Tools"-"Options..."对话框中,

         "Debugging"-"General"-"Show raw structure of objects in variables windows"的勾选

    注2:vs2012版本后,autoexp.dat被废弃,改用.natvis文件,该文件需要安装到"%USERPROFILE%DocumentsVisual Studio 2012Visualizers"目录中

        如UE4引擎源码中的%EngineDir%EngineExtrasVisualStudioDebuggingUE4.natvis

    (1) STL之string、vector、map

    ①原始显示结果:

    ②配置了autoexp.dat的显示结果:

    -->对应的配置内容如下:

    ;  std::string -- char
    std::basic_string<char,*>{
        preview        ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,s]) #else ( [$e._Bx._Ptr,s]))
        stringview    ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,sb]) #else ( [$e._Bx._Ptr,sb]))
        children
        (
            #if(($e._Myres) < ($e._BUF_SIZE))
            (
                #([actual members]: [$e,!] , #array( expr: $e._Bx._Buf[$i], size: $e._Mysize))
            )
            #else
            (
                #([actual members]: [$e,!],  #array( expr: $e._Bx._Ptr[$i], size: $e._Mysize))
            )
        )
    }
    ;------------------------------------------------------------------------------
    ;  std::vector
    ;------------------------------------------------------------------------------
    std::vector<*>{
        children
        (
            #array
            (
                expr :        ($e._Myfirst)[$i],
                size :        $e._Mylast-$e._Myfirst
            )
        )
        preview
        (
            #(
                "[", $e._Mylast - $e._Myfirst , "](",
                #array
                (
                    expr :    ($e._Myfirst)[$i],
                    size :    $e._Mylast-$e._Myfirst
                ),
                ")"
            )
        )
    }
    ;------------------------------------------------------------------------------
    ;  std::map
    ;------------------------------------------------------------------------------
    std::map<*>{
        children
        (
            #tree
            (
                head : $e._Myhead->_Parent,
                skip : $e._Myhead,
                size : $e._Mysize,
                left : _Left,
                right : _Right
            ) : $e._Myval
        )
        preview
        (
            #(
                "[", $e._Mysize, "](",
                #tree
                (
                    head : $e._Myhead->_Parent,
                    skip : $e._Myhead,
                    size : $e._Mysize,
                    left : _Left,
                    right : _Right
                ) : $e._Myval,
                ")"
            )
        )
    }

    (2) 自定义类MyArray

    ①原始显示结果:

    ②配置了autoexp.dat的显示结果:

    -->对应的配置内容如下:

    MyArray{
        preview
        (
            #(
                "[size is ", $c.m_nSize, "] m_pData is (",
                #array
                (
                    expr: ($c.m_pData)[$i],
                    size: $c.m_nSize
                ),
                ")..."
            )
        )
        stringview
        (
            #(
                "Hello MyArray!!!"
            )
        )
        children
        (
            #(  
                #array
                (
                    expr: ($c.m_pData)[$i],
                    size: $c.m_nSize
                )
            )
        )
    }

    注1:双引号中字符串不能含有冒号,如:"[size is "不能写成"size: "

    注2:多个类型使用 | 进行连接。如:MyArray|ArrayEx

    注3:preview、stringview及children。对于不需要的部分可以不用定义,且三个部分没有先后顺序之分。

    注4:格式的定义的最外层用大括号{},其中的每个部分使用小括号()。

    注5:格式定义出错时,运行VS会弹出提示窗口,对于格式配置错误的类型,在调试期间无法正常显示。

    注6:最外层的左边的大括号{必须紧挨着最后一个类型名,否则无论后面的格式正确与否,都无法正常显示。

    注7:符号;为行注释符。

    注8:$c表示当前所定义数据结构的对象,#array表示用数组形式显示内容,$i表示数组中的每个元素的索引,$e表示数组中的每个元素的值

    注9:array结构必须同时包含expr和size两个部分,缺少其中一个部分都将导致信息无法正确显示。

    注10:可使用#switch、#if进行条件分支判断,要注意的是:#switch结构不能用于#array结构中,否则可能导致VS挂死。

  • 相关阅读:
    Atitit (Sketch Filter)素描滤镜的实现  图像处理  attilax总结v2
    JS设置cookie、读取cookie、删除cookie
    Atitit 图像处理30大经典算法attilax总结
    Atitit数据库层次架构表与知识点 attilax 总结
    Atitit 游戏的通常流程 attilax 总结 基于cocos2d api
    Atitti css transition Animation differ区别
    Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结
    Atitit 全屏模式的cs桌面客户端软件gui h5解决方案 Kiosk模式
    Atitit 混合叠加俩张图片的处理 图像处理解决方案 javafx blend
    Atitit  rgb yuv  hsv HSL 模式和 HSV(HSB) 图像色彩空间的区别
  • 原文地址:https://www.cnblogs.com/kekec/p/5635854.html
Copyright © 2011-2022 走看看