Hello! 欢迎来到小浪云!


Linux后台开发调试经验分享


在这篇文章中,beck分享了他在linux后台开发和调试领域的丰富经验。作为一名从事c语言开发超过十年的从业者,他详细介绍了调试的挑战和方法,并强调了开发过程中的关键阶段。

Linux后台开发调试经验分享作者:beck

毕业超过十年了,感慨岁月无情。作为一名从事后台开发多年的从业者(之前在电信领域工作),我将分享一些常见的开发心得和调试手段。多年使用互联网的经历让我收获颇丰,但总结的很少。秉承互联网精神,希望我的经验能帮助到互联网另一端的你。由于我主要从事c语言开发,以下经验也是基于C语言的调试手段。

调试是一个复杂而令人困扰的问题,很少有人能保证自己的代码完全没有错误。一旦发现问题,就需要进行调试。调试方法多种多样,从反汇编查看二进制代码,到使用gdb查看统计信息,再到简单的打印日志,甚至是自己制造bug让别人来查。掌握这些方法后,总能找到适合自己的调试方式。

调试的最终目的是找到bug。一个高手曾打过一个有趣的比喻:你找BUG其实就像是福尔摩斯。你在BUG的“案发现场”寻找线索——合格的程序应该有日志、dump内存、计数等基本信息。如果什么都没有,只能找写代码的人自己查。调试就是在众多信息中抽丝剥茧,找到疑点,反复推演程序运行的代码,最终定位到那几行“作案”的代码。

这个过程非常折磨人,没有任何眉目时,令人茶饭不思。但一旦找到问题,就像打了鸡血般兴奋,甚至会陶醉其中。只有真正经历过这种折磨的人,才能体会到修改问题的快感。

开发的程序通常要经过两个阶段,最终才能上线发布。

在功能开发阶段,主要目标是根据业务需求开发程序。仅仅是写if else吗?写程序绝不仅仅如此。如果只是这样,开发人员的工作将变得更加枯燥和机械化。

做事都讲究未雨绸缪,开发程序更应该如此。大学C语言经典教材中定义程序为:程序 = 数据结构 + 算法。但在实际生产过程中,我认为更合适的定义是:程序 = 数据结构 + 算法 + 业务逻辑(计算逻辑)+ 框架。

补充业务逻辑的原因是有意义的程序本身就是某种业务逻辑(计算逻辑)的抽象。完成这个业务逻辑才是最终目的,不要拿一些算法研究的代码与我争论。

作为开发人员,测试驱动开发(tdd)是一种很好的思考问题的方式。也许有人听说过,也许有人用过,如果你觉得使用效果不佳,我可以告诉大家:应该采用测试场景 + 场景驱动开发。是的,仅仅是加入“场景”这个宾语,就能让开发更有目的性和针对性。

任何一个业务逻辑都可以拆分为多个业务场景。逐一解决和测试这些场景,开发过程其实很简单。虽然听起来简单,但整个过程需要50%的时间思考解决问题场景,20%的时间编码,30%的时间测试。思考问题的50%的时间,可以在任何时间进行(休息时,地铁上,班车上…),只要让自己足够静,你就能将整个业务逻辑思考得非常清楚,分解为多个业务场景。对于复杂的业务场景,建议适当做笔记,从全局的业务逻辑考虑:自己细化的结论是否符合所有的业务场景。反复修正,直到正确。

具体编码时,经过前面的深思熟虑,每个细节都已经很清楚了,可以采用迭代的方式,批量交付小的功能点。

开发阶段的关键词总结为:TDD + 迭代。需要更多详情的同学可以自行百度谷歌

Linux后台开发调试经验分享在功能调试阶段,调试手段有很多,包括走读代码、打日志、使用gdb、统计、coredump等,如果有精力也可以进行白盒测试。测试的意图很明确,就是确认代码是否按照正确的编码意图运行。自己写的代码,调试起来相对容易,因为你清楚代码的本意该如何运行,现在出现了什么问题。

程序员的三大悲剧之一,就是不知道什么时候需要定位其他人写的bug。定位前必须理解另一位程序员写这段代码的意图,否则无法进行定位。理解其他人的代码可以通过阅读代码了解大致思路,通过日志、gdb或统计信息补充代码意图的更多细节,或者修正理解不对的思路。

这个过程可能很枯燥,也可能很有挑战,试图通过种种迹象去了解另一位程序员写代码的初衷和意图,有点像窥探人家的隐私!

以上说的很多只是为了说明调试的前提和初衷。一个优秀的程序员会掌握很多调试技巧,也就是很多调试手段来获取自己想要的信息。获取的信息越多,就越容易理解程序本身的意图。

调试工具的使用细节和说明,读者可以自行百度谷歌

以下是我简单阐述自己是如何调试程序的,以及如何理解各种工具的,欢迎各位大虾指点交流:

1) 关于日志如何打好日志绝对是一门学问。日志打印太多会影响后台程序的性能,打印太少则无法定位问题。更糟糕的是打印到空指针,可能导致程序coredump。

所以日志的技巧是:少,且内容丰富。

如何做到少,就是汇聚。

能否将表达同一个意思的打印减少?

能否在关键异常的地方加上统计(输出统计)?

能否不打?

能否在内存中记录关键信息,在需要时控制其打印时机?

如何做到内容丰富,就是少打描述性词汇,多打有用的程序运行信息。

方法很多,大家多多思考。并且打印的优化是一个反复优化的过程,不是马上就能完成的。曾经遇到过一个大牛,测试部提出问题时,他从不亲自定位,而是直接让测试的兄弟执行软调,将收集的日志给他分析就能解决问题。

2) 关于gdb有大牛说过:“我就是程序,程序就是我”。我常用gdb来检验自己对程序的理解。常用的gdb功能包括打印程序运行信息,修改一些内部运行信息,构造复杂场景。

其实很简单,程序在什么场景下应该有什么样的行为,我自己必须清楚。必须知道关键变量的信息是否正确,周期性地使用gdb确认变量的信息是否正确,然后决定程序是否符合预期在执行。

可靠的程序都有类似的保护机制,但通常需要繁琐地构造测试条件来触发这些机制(如检测到丢包率很高,要告警等)。大多数保护机制都是通过记录一些状态后触发的。

其实,可以使用gdb构造出异常状态,确认告警机制是否生效。gdb很好地补充了这方面的测试和验证工作。

3) 关于统计统计信息是关键信息汇集的最好例子。数据少,信息明确。

在电信软件中,很多模块都通过这样的信息来“自证清白”,也很容易发现问题出现在哪里。

统计的实质是通过全局变量记录程序正常和异常点的统计信息,然后通过某种手段输出出来。

4) 关于coredump大家看到coredump都会头痛,但coredump也是很好的定位手段。

首先,程序coredump后,会生成详细的coredump文件,该文件详细记录了程序在core之前的运行信息。使用gdb加载这个coredump文件,你想看什么都可以。这只是简单地使用coredump。

如果遇到复杂的问题,难搞的问题,也可以使用coredump来定位。

比如程序执行到一个十分不常见的代码分支,然后就core掉了,但目前的输出信息(如日志等)无法进一步定位问题。

怎么办?有没有想过在复现问题的环节,出一个调试版本的程序,在异常分支上主动触发内存异常,产生coredump,利用coredump信息来确定程序是如何异常的。

5) 关于代码修改这也是我常用的手段之一,反复对比修改前后的代码,确认修改代码的准确性和全面性,反思自己代码修改是否全面?这里面用到的工具就是beyondcompare。

从事编程多年,偶有所得,记录于此,希望各位有所收获!

相关阅读