9 软件调试技术汇总
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
• Logger message
– Display messages at strategic locations: • Logger.global.info("message"); – Turn logging off and on using the setLevel method • Logger.global.setLevel(Level.OFF);
• 可插入Assertion检查中间运行结果
(6) Display Output
Print values of important variables at critical spots – Poor: stdout is buffered;
printf("%d", keyvariable);
– Better:
调试过程中可显示的其它一些内容
• Trace message
– Output designed to record the path of execution and display strategic values
• Stack trace
– new Throwable().printStackTrace();
软件调试的一些经验
Debugging Heuristic
(1) Understand error messages (2) Think before writing
When Applicable
Build-time
(3) Look for familiar bugs
(4) Divide and conquer (5) Add more internal tests (6) Display output (7) Dump States (8) Use a debugger Run-time
– 解释出现问题的代码
• 向自己 • 向其他人 • A teddy bear
– 记录错误推导的过程(制定了哪些错 误假设,是否通过假设检验?)有计 划地更改假设,逼近最终错误点
(3) Look for Familiar Bugs
一些常见的出错场景:
switch (i) { case 0: … /* missing break */ case 1: … break; … } if (i = 5) … if (5 < i < 10) … int i; … scanf("%d", i);
• Bug通常是由于程序员的mental model在代码中 得不到正确实现造成,调试器可帮助修正此问题 • 很多时候(并非所有)使用调试器比在程序中插 入print语句更加方便 • 调试器可以加载 “core dumps”并允许在程序已 经运行终结的情况下分析其执行
• 调试器可以“attach”到正在运行的程序上,不需 要重新编译构建
调试的一般方法
• 3种主要方法:
– 手工分析代码 以一定的测试用例,手工推演程序的执行步骤,检查 执行结果的正确性 – 编程追踪 在程序执行的可疑点附近,插入print等语句,显示内 部变量的执行状态,从而检查运行是否正常 – 使用交互式调试器 用调试器运行程序,观察关键点上的变量取值,调用 栈等,甚至做出修改,以检查程序执行是否正确
printf("%d", keyvariable); fflush(stdout);
program may crash before output appears Call fflush() to flush stdout buffer explicitly To stderr: debugging output can be separated from normal output via redirection
• • • • •Leabharlann Baidu•
write test cases when you write your code if something should be true assert() it Optimize only when necessary create functions to help visualize your data design for testing/debugging from the start test early, test often
Write to a log file
使用print进行调试的一些经验
• print variables other than just those you think suspect. • print valuable statements (not just “hi\n”). • move print through a program to track down a bug • print messages, variables/test results in useful places throughout program. • use exit() to concentrate on a part of a program. • 使用全局标识 debug/debug_level 来控制print的程度 • 可以使用宏 (#ifdef) 来控制何时print,如仅在debug版 本中print
(5) Add More Internal Tests
• Internal tests help finding bugs • Internal test also can help eliminate bugs
– Checking invariants and conservation properties can eliminate some functions from the bug hunt
Debugging Timing and Threading Bugs
• Ensure the functionality works for a single thread • If adding a printf() removes the bug it is almost certainly a timing/threading bug or a trashed memory bug • Try using coarse grained locking to narrow down the objects involved • Try keeping an event (transaction) log
(9) Focus on recent changes (10) Designing for Debug/Test
(2) Think Before Writing
不适当的改动可能使事情变得更加糟糕,所以…
– 尽可能深入、准确地理解系统 – 稍事休息, 保持对问题的距离
• relaxes but let your mind continue to spin in the background!
修正错误后思考
• 该bug是否在系 统其它地方重复 • 未来如何避免这 种bug
char c; … c = getchar();
if (i & j) …
Note: 编译检查可以发现一些问题,但不是全部
(4) Divide and Conquer
• 定位导致软件失效的最小、最简单输入
– 二分法: 设程序在大文件filex下失效
Bonus: stderr is unbuffered
– Maybe even better:
fprintf(stderr,"%d",keyvariable);
– Maybe better still:
FILE *fp = fopen("logfile", "w"); … fprintf(fp, "%d", keyvariable); fflush(fp);
• Divde filex to 1st half and 2nd half • Delete 2nd half of filex • Run program on filex – pass => bugs in 2nd half – Fail => bugs in 1st half • Recurse until no lines of filex can be discarded
(7) Dump State
• 有时候用调试器逐步跟踪,分析状态可能 很耗费时间,很麻烦。
• 对某些问题,可以先将内部状态dump到文 件中,然后分析dump文件来定位错误
• 比如,对网络软件可以用tcpdump工具dump 所有网络数据包,来分析网络通信中的问题
(8) Use a Debugger
(10) Designing for Debug/Test
Consider [Testability & Debuggability]
• When you write code think about how you are going to test/debug it
- lack of thought always translates into bugs
九. 软件调试
Debugging is part art, part science.
内容提要
• 什么是调试 • 调试的一般方法 • 调试器及其使用 • 调试器原理 • 从手工调试到自动调试
什么是调试
• 测试:发现软件失效 • 调试:定位软件错误并将其修复
bug未消除 测试 报告
错误假定
错误验证
(9) Focus on Recent Changes
If you find a NEW bug, ask: what code did I change recently? This favors: - writing and testing code incrementally - using 'svn diff' to see recent changes - regression testing (making sure new changes don't break old code).
– 另一种选择: Start with small subset of filex, and incrementally add lines until bug appears
(4) Divide and Conquer
• 定位出错代码
– Incrementally find smallest code subset that illustrates the bug
– Example: Test client for your module fails
• In test client, comment out calls of some function – Or in your module, create stub definition of some function • Run test client • Bug disappears => it’s in commented-out code • Bug remains => it’s in remaining code, so repeat for another function
Use a Debugger
• 调试工具 – The most critical tool in your arsenal is your brain – 2nd most important tool is a debugger • Core dumps are your friends – Memory debugging tools 3rd most important – Profiler 4th
修正错误
回归测试
定位失败
定位错误
调试时最耗费时间的工作之一
调试的困难
• 错误可能是偶然出现的,难以复现 • 错误依赖于很久以前的某个系统状态,或某个操作 • 造成软件失效的现象和根源可能并不在本地主机上 • 外部表现和内部结构、运行逻辑之间的关系往往并不清晰 • 软件的一些新变化(新功能,新修复等) 可能会掩盖错误 • ……