假设有一个名为LongFunction的1000行代码的函数,我们使用它:
bool bSuccess = LongFunction();
assert(bSuccess);
这里我在调试时得到了一个断言,我知道LongFunction有问题,所以我需要找到函数遇到问题的地方并返回:
我可能会一步一步地调试它,它可以工作但很耗时,我们不会这样做。
我可以搜索关键字“返回”(或者更精确的搜索使用RegExp),并在那些返回时设置断点,它应该更快,但它仍然是乏味的手动工作,无法自动化。
#define返回TRACE( LINE );返回
它有效但有以下问题:
您对如何查明问题有任何其他创意吗?
修改 以下是一些让我们关注问题的细节。
这是关于C ++,而不是平台规范。
我们不想重构函数(是的,我知道我们应该),我们甚至不想更改任何代码 - 此时我们只想提供一些工具来进行应用程序调试更轻松。我也相信这应该是一个共同的要求,难道你不碰到这个吗?
LongFunction()有多个退出点,返回类型不是必须bool(HRESULT,用户定义的错误代码......)
修改:当前讨论的摘要:
我们有一些争议:
你应该重构这个功能 是的,每个人都知道我们应该,但这不是重点。如果我打电话重构函数,我不会在这里问这个问题。
查找LongFunction()返回失败的位置无效。
我总是首先找到错误发生的地方,知道发生了什么,我很好奇为什么这没有用,你在这种情况下做了什么? (假设我已经熟悉该功能的工作原理)
我们有两个合理的解决方案:
来自Crashworks的ReturnMarker,函数中的堆栈对象将在函数返回时进行破坏,在析构函数中设置断点将显示它在debuger中返回的位置
来自Binary&的CMyBool(x) Sadsido,将LongFunction的返回类型更改为可以从bool构造的CMyBool,从LongFunction返回将构造该对象,因此只需在构造函数中设置断点即可。
答案 0 :(得分:13)
显然你应该重构这个函数,但是在C ++中你可以使用这个简单的权宜之计在五分钟内解决这个问题:
class ReturnMarker
{
public:
ReturnMarker() {};
~ReturnMarker()
{
dummy += 1; //<-- put your breakpoint here
}
static int dummy;
}
int ReturnMarker::dummy = 0;
然后在函数顶部实例化一个ReturnMarker。当它返回时,该实例将超出范围,你将点击析构函数。
void LongFunction()
{
ReturnMarker foo;
// ...
}
答案 1 :(得分:8)
听起来好像重构LongFunction()......
1000行功能是一种糟糕的代码味道。花时间将其重构为更小,更易维护的功能。你会发现这些错误,这对未来来说是一项值得的投资。
答案 2 :(得分:4)
这是C还是C ++?
如果是C ++,请创建一个包含bool(比如CMyBool)的新类,它会自动转换为bool。
现在让LongFunction返回CMyBool(快速搜索和替换会将LongFuntion中的所有返回值更改为“返回CMyBool(x)”。
现在在CMyBool的ctor中设置一个断点,调试器现在将在创建CMyBool时停止,这将在LongFunction中的正确return语句上。
自动强制转换为bool将阻止CMyBool破坏使用CMyBool的代码。
这会让你解决最初的问题,但更大的问题是LongFunction需要重构。
希望这有帮助。
答案 3 :(得分:4)
如果你的问题只是普通的懒惰(没有错误),请确保LongFunction中的所有return语句都是
形式return(value);
而不是
return value;
(例如使用正则表达式搜索和替换)
然后,使用比原始建议更简洁的修改后的预处理器宏:
#define return(value) { if (!value) TRACE(__LINE__); return(value); }
......甚至
#define return(value) { assert(value); return(value); }
......或者你认为合适的任何东西
答案 4 :(得分:0)
#define return { TRACE(LINE); return; }
这解决了你的问题4.
就其他问题而言,这就是编码的问题。这就是为什么许多系统在出现问题时将更复杂的错误(例如来自COM对象的HRESULT)和/或垃圾邮件返回到调试流的原因。
虽然应该重新制作1000行功能。正如您所看到的那样,长期功能难以维护。
编辑:以下工作会比上面更好吗?
#define return TRACE(LINE), return
喝了几杯,所以可能没有。
答案 5 :(得分:0)
你还没有说过你的平台。根据{{1}}的内容,以下是我使用gdb:
的方法让我们假设你的文件'f.cc'有以下几行:
LongFunction
以下是gdb中的步骤:
在断言的同一行添加断点:1: bool LongFunction () { /* ... */ }
2:
3: void bar ()
4: {
5: bool bSuccess = LongFunction ();
6: assert (bSuccess);
7: }
在bSuccess为false时为该断点添加条件:break f.cc:6
运行程序直到达到该断点
在函数体的开头设置断点:condition 1 (bSuccess==0)
跳回该位置(它将在断点处停止):break f.cc:4
调试LongFunction的内容以查看它失败的原因。
我说它取决于LongFunction的内容的原因是,如果LongFunction从流中读取输入,或者修改全局变量等,那么第二轮的行为可能会有所不同。您应该将上述步骤视为代码如下:
jump f.cc:4
答案 6 :(得分:0)
它是visual studio我会在断言上放置一个断点,然后使用堆栈跟踪窗口单击下一行,然后将进入该方法的出口点。
答案 7 :(得分:0)
“我可能会一步一步地调试它,它可以工作但很耗时,我们不会这样做。”
你怎么调试?
我曾经通过以下方式进行调试:
1)找到一组重现问题的参数。
2)使用那组参数逐步执行代码。
如果有1000行代码,那么在不完全知道函数的作用和应该做什么的情况下,你将如何“重构”。
如果不单独执行该功能,你将如何做到这一点。我认为这是一个带有良好调试器的IDE。
坦率地说,我发现这个问题几乎是一种非常悲伤的问题。
答案 8 :(得分:0)
1982年,我的任务是修复一个破碎的流程图程序。该程序是用依赖于机器的Harris FORTRAN编写的(我现在不记得它是在FORTRAN IV还是FORTRAN 77中),由一个1100行的主程序,一个900行子程序和大约十几个子程序组成。长10至20行。
哦,程序中几乎没有空格(空行),评论根本没用。
花了我160个小时 - 四个星期,全部时间,我的桌子上没有其他东西 - 弄清楚这些代码,以便进行适当的修理。
你处于类似情况。你需要花费一些真正的CPU时间来投资这个1000行的生物,足以解决所有可能出错的问题,以及如何解决这个问题。
找到回报很容易。对于Microsoft AbysmalC ++:搜索“return”的每个实例,并在其前面加上
“printf”(“\ n \ n ---&gt;&gt;&gt;”在第%d行打出\ n \ n \ n \ n \ n \ n \ n“,_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _显然,你不能自动这样做;你必须看看当地的包围。然后运行测试,看看它告诉你的位置。
事实就是这样:在现实的计算世界中,几乎没有真正的例程,实际上需要1000行长。在近40年的时间里,作为学生和专业人士,我只遇到了一个需要比一个打印机页面(大约60行)长的例程,而且这个例子非常特殊。全部讲述了大约三页。
另一方面,我已经看到很多运行模块,编写它的人太懒,或者太无能,无法正确地考虑因素,维护程序员太懒,太无能,或者太害怕他们的经理回去重构它。
最后:考虑到这可能不是最后一次有人必须处理这个模块,而且可能不是你最后一次必须处理它。这些东西往往是焦油宝宝:一旦你触摸它们,你永远不会从它们中脱离出来。至少可能值得花时间进行重构,并且很可能从SCRATCH和第一原则重新设计/重写它。