用Q+编号代表问题,A+编号代表答案。用这种方式组织。如无特别说明,这些技巧都是针对Visual Studio 2003的

汇编级的问题我作为一个逻辑程序只能说略知皮毛,内容仅为抛砖引玉,说法不严谨之处希望能毫不客气的指出,以便改正。但大部分信息都是有经验或参考资料确认的,有问题可以与我探讨。

 Q1:Release版本不能调试吗?

A1: Release版本、Debug版本的区别,据我目前所知有3处:编译是否“编译器优化”过;是否有完整调试信息;_DEBUG宏和NDEBUG宏;
先说这三个选项的位置: “编译器优化”,VS2003里,位于 工程属性(在Solution Explorer面板的某个工程上点右键)-> C/C++ -> Optimization -> 第一项Optimization。一般Debug版设置为Disabled,Release版设置成Full Optimization。
 “是否有调试信息”,VS2003里,位于 工程属性 -> C/C++ -> General -> Debug Infomation Format。Release版默认是Disabled,Debug版用最完整的Program Database for Edit & Continue。
 _DEBUG宏和NDEBUG宏,VS2003里,位于 工程属性 -> C/C++ -> Preprocesser -> Preprocesser Definitions。Release版一般设置为NDEBUG,Debug版设置为_DEBUG
“编译器优化”,是指编译器在编译过程中,并不按照源码逐行翻译成汇编,而是为了效率对代码做出更改,比如:消除不必要的局部变量,去掉完全没有影响的代码。现代编译器已经发展的非常完善,甚至可以做到调整循环次序、把循环内的语句提到循环外、消除函数调用等等,可以在保证结果完全一致的情况下达到最高效。
 值得注意的 1.越是高级的编译器,优化功能越强大,VS2010和VS2003就有不小的区别,可以写些简单的算法,然后看看汇编结果。
2.甚至对于“高手”来说,编译器优化的代码往往比人工优化的代码要更好(因为写编译器的是优化砖家),所以写代码时,应该更关注算法方面、函数调用结构方面的问题,不必过于关注局部变量之类的细节,局部问题编译器会做的很好。
 所以有些人用Release版调试的时候,发现执行顺序非常怪异,和源码对不上,其实就是编译器优化造成的。 “是否有调试信息”,决定了pdb文件是不是完全,pdb文件既要记录源代码的位置,以便跟踪,还得记录一些额外的地址信息,以便显示出局部变量的值。有时候调试Release版本,看不到变量的值,原因——要么是这个变量已经被优化没了,要么是调试信息不全造成的。
 _DEBUG宏和NDEBUG宏,这个比较简单,DEBUG版本在编译时,会自动定义一个宏#define _DEBUG,所以有些代码希望只在debug版生效时,只需要
    #ifdef _DEBUG
    //debug版才编译的代码
   #else
    //release版需要的代码
   #endif
 这样写一下就可以达到目的了,最常见的是 assert 宏。assert在寻仙底层代码非常常见,assert函数只在debug版被执行,release版被忽略。所以是个开发时测试用的好东西。如果在release版也暂时需要用assert,就修改一下VC设置,把NDEBUG宏改为_DEBUG就行了,不过这样修改也可能会影响其他代码。(另外还有一些现象,我还没有搞清:为什么DEBUG版的局部变量会被初始化成0,而release版不初始化,是通过哪个选项控制的,求解答。)调试Release的意义在于:有些BUG只有在Release方式下能重现,所以搞清楚Release和Debug的区别,适当修改设置,关键时刻显得非常重要。

 Q2:想输出调试信息,不知道应该输出到哪里

A2:控制台程序很容易通过printf或者std::cout输出信息,但是windows窗口程序比较麻烦。写windows小工具时,可以用MessageBox输出信息,不过对于寻仙这种大型游戏恐怕太不合适了。
 能输出LOG,就用LOG输出
逻辑层的模块,大部分可以使用LOG模块,建议多输出LOG进行调试。查看log文件务必采用UltraEdit、Notepad++、Vim之类能够自动检测到文件变化并重新打开log文件的工具,以便随时查看输出,用记事本可能会给自己带来麻烦。不能输出LOG,用OutputDebugString
 底层模块不能输出LOG,想输出LOG的话源码修改太多过于繁琐。这时候也有绝招:windows.h 头文件,提供了一个输出信息的函数:OutputDebugString(包括两个,OutputDebugStringA和OutputDebugStringW,分别支持MultiByte和WideChar)。需要使用时,只要在你需要调试的cpp文件里包含windows.h,然后自己拼装字符串,输出即可。

查看OutputDebugString的输出结果。OutputDebugString的结果,输出到哪里了呢?答案是“系统调试信息”,这玩意如果用VC调试程序,可以在Output窗口里看到。如果不调试,如何看到呢?答案是用“DebugView”小工具。上网搜一下应该能找到,也可以问别人要。

OutputDebugString也失效。由于Windows.h文件过于庞大,有可能造成和源文件冲突编译不过,这种情况。。。只能具体问题,具体解决了。问题总比方法多(龚大侠语录)。不使用调试器的原因是:调试器很可能会在关键时刻不给力,特别是内存已经写乱的情况。过于依赖调试器不是什么好事。

 Q3:我在调试core dump文件,程序是release版的,代码被优化过,变量的值全都看不见,执行顺序也不清楚,只能看到汇编代码,怎么办?

A3:这种情况,如果不想(不能)放弃的话,就埋头看汇编代码吧!
如果运气不是太差,源码能和core dump文件对应上,VS2003就可以打开 汇编、C++ 的混合视图,可以对照着看。 1.崩溃基本上肯定是访问了不可访问的地址。所以……专心看访问哪个地址时出错了,这个地址的值是怎么一路算过来的。
 2.分析每一句C++代码和哪些汇编代码对应。特别的,if、for、while在汇编里最终肯定表现为跳转(jmp、jne之类),所以看懂了跳转,就明白了执行流程。还有函数调用(call)也可以帮你定位。
 3.如果发现C++和汇编不容易对应上,也不要惊慌,很可能是优化导致一些函数被展开了,考虑到优化以后再分析分析即可。
非常有效的方法:找个和你水平差不多或者比你更懂汇编的人,一起研究。可以拓宽思路,也可以互相鼓励。

 Q4:来不及下断点怎么办?下断点断不下来怎么办?

A4:我以前一直认为,如果自己有代码,想用断点就总是可以断下来的,实在不行启动程序以后用attach也总是可以调试的。但是事实证明,如果程序员有N种调试程序的方法,那么大自然就有N2种调戏程序员的方法。例如:
 1.程序逻辑和系统时间密切相关。比如调试冷却时间CoolDown这种的,如果CoolDown时间只有1秒,基本你断下来断点就2秒了,你可能总是无法进入你想进入的那条逻辑分支。还有比如网络层,调试时间非常有限,5秒就超时,非常尴尬。
 2.操作系统抽风了,怎么都断不下来。
3.错误是在程序刚启动时出现的,如果从VS2003里启动,就不会出错;如果不从VS2003启动,就会出错。但是你启动程序再Attach也来不及,因为从启动到出错只有半秒时间。
 4.错误重现概率极低,重复十次出现一次,而且直接从VC2003启动程序就更难重现了。
一般来说,前两种现象还是可以解决的,第一种情况可以改改逻辑,给自己多一些时间。第二种情况查一下系统服务,甚至重装一下VS,还是有办法的。第三种情况我在这个月刚刚遇到,发生在关键的变量是个随机值的情形。(而且这个现象说明从VS启动程序、直接启动程序、和attach之后,三种情形程序内存布局有着微妙的区别。)第四种情况曾发生在内存访问越界的情形。是因为访问非法内存时,操作系统有时候可以检测到异常,有时候不会。有几种方法,列举出来,但具体怎么用还得发挥聪明才智 :)

1.强制断点 INT 3。 具体做法:在程序里加一句__asm int 3 ( 如果你在用 gcc 可以加 asm ("int $3"); ),程序执行到这里会崩溃,然后windows会问你是否需要调试,然后你就可以从容的用VS2003进去调试了。很好的办法,自己写工具时不妨一试。可惜的是这种做法在咱们的项目里无效,因为这个异常会被底层catch到(JException::init()),然后打印一堆信息以后退出……没有调试的机会。
 2.assert(表达式)、OutputDebugString等。如果你对问题已经有了大概想法,比如“这个地方如果index是3,那么就会越界错误了”,那么赶紧加一条assert(index==3),在DEBUG模式下试试看你猜想的对不对。而且assert不要删除,因为它在release模式下不会被运行。写一些有意义的assert对代码健壮性很有帮助。
 3.加一句if。加一句if,并在if的{}里加一些没意义的代码,比如int i=0; ++i;然后把断点下在++i上。很常见的做法,但是如果你遇到非常诡异的情况,debug不容易重现,关闭release的优化选项也不容易重现,你就只能开着优化,但是优化会把你那句无用的代码整个删掉。这时候……不妨用下面while(true);的方法。
 4.while(true);,用assert的不方便之处在于,一旦代码进入assert里面,就不好回到之前的地方了;用上一条办法在开着优化的情况下也不好办。而往往捕捉到错误之后你希望能继续单步调试。所以,不妨在捕捉到错误之后,让程序进入死循环,然后程序会卡住等你进去慢慢调试了。开始调试的时候,VS2003可以拖曳那个箭头改变当前执行的代码,从死循环里跳出来。或者你用一个很多次数的空循环,比如100000000次,或者有些程序可以用sleep(10000),思路都是一样的。

 Q5:调试的时候,在watch里查看对象的成员变量的值,但是感觉watch的语法与C++不同,有什么技巧吗?

A5:watch我也用的不是很好。用watch需要解决几个问题:
调试STL容器,我只会调试vector,调试vector的方法类似于调试C++数组,比如有个数组a[10],监视里面的10个值:
   a[0],10
 像这样写就行,vector也差不多。VS2003调试map、list时不是很给力。求解。调试对象的成员变量里面的成员变量里面的成员变量……;由于watch里面对"->"的支持不是很好,写一大串语句watch识别不了。超哥传授了一个绝招:使用quick watch,在quick watch里选择成员变量时,quick watch输入框里的文字也会变……原来输入框里的东西就是你想要的变量的表示方法。把它粘到watch窗口里试试吧!
 在watch里面调用函数,可能会造成诡异的崩溃。因为watch里调用函数也可能会遭遇异常。调试时遇到奇怪的崩溃可以看看是不是自己在watch里写了不该写的东西。

 Q6:遇到非常费解的客户端或服务器很卡的现象,不知从何入手

A5:如果程序速度突然变得非常明显的慢,而且可以重现,那就可以运行以后用VS attach到进程里,然后Break All,多试几次(碰概率)。
看看是否多次卡在同一个地方。如果多次卡在同一个地方,那么就可以大胆的猜测和试验了。虽然这种方法很傻,但是至今用这种方法调试出来的问题有:1、技能系统错误填写,导致循环10000000……帧;2、客户端在外服运行遭遇大量LOG导致卡死。据我所知已经有这两个成功的经验,实乃神技。

常用调试方法——Debug技巧总结相关推荐

  1. VC++调试方法和技巧

    VC++调试方法和技巧 便于调试的代码风格: 1.       不用全局变量: 2.       所有变量都要初始化,成员变量在构造函数中初始化: 3.       尽量使用const: 4.     ...

  2. linux下gdb调试方法和技巧详解

    linux下gdb调试方法和技巧整理 简介 UNIX或者UNIX-like下调试工具 启动gdb # 1. 在可执行程序不需要输入参数时,我们可以使用 gdb + 可执行程序 gdb ./typeid ...

  3. Makefile常用调试方法

    转载自 陈皓<跟我一起写 Makefile><GNU Make项目管理> GNU make 提供了若干可以协助调试的内置函数以及命令行选项. 1.warning函数 $(war ...

  4. 调试方法和技巧(zz)

    便于调试的代码风格: 不用全局变量 所有变量都要初始化,成员变量在构造函数中初始化 尽量使用const 详尽的注释 VC++编译选项: 总是使用/W4警告级别 在调试版本里总是使用/GZ编译选项,用来 ...

  5. 【C语言】调试方法和技巧详解

    推荐一个软件,禅定空间,放下手机,珍惜时间,为未来搏一搏,为了未来美好的生活,冲鸭!!!! 文章目录 前言 1. 什么是调试 2. 调试步骤 2.1 发现错误 2.1.1 编译型错误 2.2.2 链接 ...

  6. JLINK在线调试——软件调试方法与技巧

    目录 JTAG/SWD调试原理 在实验中讲解调试方法 JTAG/SWD调试原理简析 调试 SWJ调试端口脚 灵活的SWJ-DP引脚分配 JTAG/SWD模式设置库函数 JTAG/SWD接口常见硬件图

  7. linux下gdb调试方法与技巧整理

    目录 一.gdb简介 二.gdb使用流程 1.启动gdb 2.查看源码 3.运行程序 4.设置断点 5.单步执行 6.查看变量 7.退出gdb 三.gdb基本使用命令 1.运行命令 2.设置断点 3. ...

  8. onvif规范的实现:onvif开发常用调试方法 和常见的segmentation fault错误

    在前几篇中,虽然已经实现了rtsp视频流的对接,但是还要做的工作还非常多,onvif本来就是一个覆盖面非常广的一个协议,每一个功能都要填充大量的函数.而且稍不注意就会出现segmentation fa ...

  9. 常用事件方法及技巧(二) -- MouseEvent(鼠标事件)

    先说明一下,我并不会把所有的内容都写出来,只列我认为有必要讲解一下的内容.如果要了解全部内容,请参看Flash自带的帮助文件.该系列文章都是按照这个思路写的. 先说一个本人觉得很实用的通用方法:toS ...

最新文章

  1. XML和JSON数据格式对比
  2. 服务器开机修改grub,修改 grub
  3. 跟着Artech学习WCF扩展(2) 自定义Channel与执行的顺序
  4. Windows 7 几个小问题的解决方法(二)
  5. win11什么时候发布的_2021年初级会计师考试大纲什么时候发布?
  6. Oracle编程入门经典 第1章 了解Oracle
  7. 小米C++开发 面试 准备阶段和部分真题
  8. Java执行外部命令,并把结果回显到控制台
  9. 入选 Gartner 魔力象限,剖析华为云 GaussDB 数据库演进之路
  10. 让自己的软件出现在选择打开列表的软件中
  11. 2021年老杨通信工程师中级互联网技术视频讲解
  12. 如何使用css美化你的页面?
  13. 探索性测试:常见误区
  14. wiFI基础知识----wpa_supplicant
  15. 常见Web安全问题及防御策略
  16. [maya学习笔记(18)] 粒子系统 - 落叶纷飞
  17. html文字浮雕效果不起作用,ps浮雕效果 怎么把文字弄成浮雕效果
  18. 消除WordPress上的渲染阻止JavaScript和CSS
  19. AWVS安装(未来即将到来)
  20. UniAPP Android 蓝牙 ESCPOS打印机 打印图片和二维码

热门文章

  1. 关于 office 即点即用 和 Window Install 和 Project 安装心得
  2. 深入理解MVC和jstl
  3. 在网盘上传大文件,一个G的文件有可能几秒中就上传完了。称为极速秒传,请问它是怎么做到的?
  4. mysql navicat关系图_navicat怎么看表关系
  5. Part 2.2 离散类别值与连续值
  6. iOS NSLog使用技巧
  7. 瑞萨单片机之data flash的使用(五)
  8. c语言计算极值范围用粒子最优算法,智能优化算法——粒子群算法小实践
  9. 用git拉取微信小程序项目到本地运行(简单实用)
  10. 字谜分组_数组中的字谜分组