如何持续地组织代码以进行调试?

4
在大型需要调试的项目中,人们在使用IDE内置调试器之前,通常会喜欢使用“printf”函数。这意味着:
  • 有时您需要将变量值渲染到屏幕上(特别是用于交互式调试)。
  • 有时需要将它们记录在文件中。
  • 有时你需要更改可见性(使其公开)以允许另一个类访问它(例如日志记录器或渲染器)。
  • 有时候您需要在成员变量中保存先前的值,以便在调试期间与新值进行对比。
  • ......
当项目规模变得非常庞大且有很多人一起开发时,所有这些针对调试的特定代码可能会变得凌乱且难以与普通代码区分开来。对于那些需要更新/更改其他人的代码或为发布做准备的人来说,这可能会很困惑。
那么如何解决这个问题呢?
始终遵循命名标准是很好的习惯,我认为“调试编码标准”应该非常有用(例如,将每个调试变量都标记为_DGB后缀)。但是我也认为仅有命名是不够的。也许需要将其集中到一个友好的跟踪器类中,或者创建一个强大的宏基础,以便在发布时将其全部删除。我不知道。
如果让你为整个项目编写调试编码文档,你会采取哪些设计技术、模式和标准呢?我指的不是工具、库或IDE特定命令,而是面向对象的设计决策。
谢谢。
10个回答

1

不要提交调试代码,只需使用调试工具。

另外,日志记录在异常处理程序等方面有自然的位置。在一些常用的API中放置一些适当的日志记录语句也有助于调试。

例如,可以使用一个日志记录语句来记录系统中执行的所有SQL。


0

我会选择你所描述的友好追踪器类。这个类可以将所有内容集中在一起,甚至可以动态地更改调试/日志记录策略。

我会避免使用宏,因为那只是编译器的技巧,而不是真正的面向对象编程。通过抽象调试/日志记录的概念,您有机会做很多事情,包括在需要时将其设置为无操作。


0
记录日志还是调试?我认为,设计良好且经过适当单元测试的应用程序不需要永久性地进行调试。另一方面,记录日志可以非常有用,既可以发现错误,也可以审计程序操作。与其涵盖很多你可以在其他地方获取的信息,我会指向logging.apache.org,以获取可用的具体实现或合理设计日志基础设施的模板。

0

我认为避免直接使用System.outs / printfs,而是使用(甚至自定义的)日志类尤为重要。这至少为所有日志记录提供了一个集中的 kill-switch(Java 中的调用成本除外)。

此外,拥有信息/警告/错误/注意等级别的日志类也很有用。

我会谨慎处理错误级别、用户 ID、元数据等,因为人们并不总是添加它们。

另外,我见过最常见的问题之一是人们在调试某些东西时将临时的printfs放入代码中,然后忘记了它们的位置。我使用一个跟踪我所做的一切的工具,以便我可以快速识别自从抽象检查点以来的所有最近编辑,并删除它们。然而,在大多数情况下,您可能希望对可以检入源代码控制的调试代码施加特殊规则。


0

我们通常开始使用一个静态类来写跟踪信息。这个类非常基础,仍然需要从执行方法中调用,但它能够满足我们的需求。

在.NET世界中,已经有相当数量的内置跟踪信息可用,因此我们不需要担心调用哪些方法或执行时间是多少。这些更多是针对代码执行中发生的特定事件。

如果您的语言不支持通过其跟踪结构对消息进行分类,那么应该将其添加到跟踪代码中。识别不同重要性和/或功能区域的内容是一个很好的开始。


0

避免通过修改代码来检测它。学会使用调试器。使日志记录和错误处理变得简单。看一下面向切面编程


0

调试/日志记录代码确实可能会有干扰性。在我们的C++项目中,我们使用宏包装常见的调试/日志代码 - 就像断言一样。我经常发现,在较低级别的组件中记录日志最有用,这样就不必到处都加。

其他答案中有很多可以同意和不同意的地方 :) 调试/日志记录代码可以是解决问题的非常有价值的工具。在Windows中,有许多技术 - 两个主要的技术是:

  • 广泛使用已检查(DBG)构建断言以及对DBG构建进行大量测试。
  • 在我们所谓的“fre”或“retail”构建中使用ETW。

已检查的构建(大多数人称为DEBUG构建)对我们也非常有帮助。我们在“fre”和“chk”构建上运行所有测试(在x86和AMD64上运行所有服务器内容也在Itanium上运行...)。有些人甚至在已检查的构建上自我托管(dogfood)。这做了两件事情:

  1. 找到许多否则不会被发现的错误。
  2. 快速消除嘈杂或不必要的断言。
在Windows中,我们广泛使用Windows事件跟踪(ETW)。ETW是一种高效的静态日志记录机制。NT内核和许多组件都非常好地被仪器化了。ETW有很多优点:
  • 任何ETW事件提供程序都可以在运行时动态启用/禁用-无需重新启动或进程重启。大多数ETW提供程序提供对单个事件或事件组的细粒度控制。
  • 来自任何提供程序(最重要的是内核)的事件都可以合并到单个跟踪中,以便所有事件都可以相关联。
  • 合并的跟踪可以从计算机上复制并完全处理-带符号。
  • NT内核样本pofile中断可以生成ETW事件-这产生了一个非常轻量级的样本分析器,可以在任何时候使用。
  • 在Vista和Windows Server 2008上,记录事件是无锁的,并且完全支持多核-每个处理器上的线程可以独立记录事件,它们之间不需要同步。

这对我们来说非常有价值,同样适用于你的Windows代码-ETW可被任何组件使用,包括用户模式、驱动程序和其他内核组件。

我们经常做的一件事是编写流式ETW消费者。 我不在代码中放置printf,而是在有趣的地方放置ETW事件。当我的组件运行时,我随时可以运行我的ETW监视器-监视器接收事件并显示它们、计数它们或以其他有趣的方式处理它们。

我非常尊重地不同意tvanfosson的观点。即使最好的代码也可以从良好实施的日志记录中受益。良好实施的静态运行时日志记录可以使查找许多问题变得简单-如果没有它,您对组件中正在发生的情况没有任何可见性。您可以查看输入、输出和猜测-仅此而已。

关键在于术语“良好实施”。仪器必须放置在正确的位置。像任何其他事物一样,这需要一些思考和计划。如果不放置在有用/有趣的位置,则它将无法帮助您在开发、测试或部署场景中找到问题。您还可以过多地进行测试,导致性能问题-甚至在关闭时也会出现问题!

当然,不同的软件产品或组件将有不同的需求。有些东西可能只需要很少的仪器设备。但是广泛部署或关键组件可以从精心设计的仪器设备中获得巨大的好处。

这里有一个场景供您参考(请注意,这可能并不适用于您... :))。假设您在公司的每台桌面上都部署了一款业务应用程序 - 数百或数千个用户。当有人遇到问题时,您会怎么做?您会去他们的办公室并连接调试器吗?如果是这样,您如何知道他们使用的版本?您从哪里获取正确的符号?您如何在他们的系统上安装调试器?如果问题每隔几个小时或几天才发生一次,您会让系统一直连接着调试器运行吗?

正如您所想象的那样,在这种情况下连接调试器是具有破坏性的。

如果您的组件已经使用ETW进行了仪表化,那么您可以要求用户在问题发生时简单地打开跟踪功能,然后继续工作,最后点击"WTF"按钮。更好的是,您的应用程序可能能够自行记录日志-在运行时检测问题并自动打开日志记录。它甚至可以在出现问题时向您发送ETW文件。
这些只是简单的示例-记录可以以许多不同的方式处理。我在这里的主要建议是考虑如何通过记录来帮助您在开发时间、测试时间和部署后找到、调试和修复组件中的问题。

0

我在参与的每个项目中都遇到了同样的问题,因此现在我有一个习惯,从一开始就广泛使用日志记录库(无论语言/平台提供什么)。任何Log4X port对我来说都可以。


0

为自己构建一些适当的调试工具可能非常有价值。例如,在3D环境中,您可能有一个选项来显示八叉树,或者渲染计划的AI路径,或者绘制通常不可见的航点。您可能还希望有一些屏幕显示来辅助分析:当前帧速率、屏幕上的多边形数量、纹理内存使用情况等。

虽然这需要一些时间和精力,但从长远来看,它可以为您节省大量时间和挫折感。


0

在VB6中,你有

Debug.Print

它将输出发送到IDE中的窗口。对于小型项目来说是可以接受的。VB6也有

#If <some var set in the project properties>
'debugging code
#End If

我有一个日志记录类,我在顶部声明它:

Dim Trc as Std.Traces

并在各种地方使用(通常在#If /#End If块内部)

Trc.Tracing = True
Trc.Tracefile = "c:\temp\app.log"
Trc.Trace 'with no argument stores date stamp
Trc.Trace "Var=" & var

是的,这确实会变得混乱,而且我希望有更好的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接