毕业超过十年了,感慨岁月无情。做了若干年后台开发(之前做电信领域),大致说一下常见的开发心得和调试手段。使用互联网这么多年,收获的很多,总结的很少。本着互联网的精神,希望可以帮到互联网另一端的你。由于本人是做 C 语言的开发,陈述的经验也是 C 常用的调试手段。
调试这个蛋疼的事情,困扰这无数程序猿么。很难有人保证自己写的代码一行错误都没有,有问题你就要查。怎么查?高手者,反汇编,看 2 进制;low 一点的就 gdb、看统计;在 low 就加打印。还可以在 low 吗?可以,自己写 bug,别人查。方法林林总总,长期掌握总可以找到适合自己的。
而调试的目的是什么,找到 BUG。想当年一个高手比喻的好:你找 BUG 其实你就是福尔摩斯,(为啥不是警察,因为中国的警察的破案率实在低的可怜)那为啥是福尔莫斯呢?想想你看到 BUG 案发现场--合格的程序都有日志、dump 内存、计数等基本案发现场吧。嗯,什么都没有,找写代码的人自己查。找问题就是在众多信息中,抽丝剥茧,找到疑点、反复推演程序运行的代码,最终找到作案的那一行或者几行代码。
这个过程是折磨人的地方,没有任何眉目时,令人茶不思饭不想。找到问题问题后,如打鸡血般兴奋,自己也会陶醉般飘飘然。真正受过折磨的人,才能体会到修改问题的滋味一二。
开发的程序大致要经过一下两个阶段,最终才可以上线发布。
功能开发阶段
本阶段的主要目标是根据业务要求,开发程序。仅仅是 coder,仅仅是写 if else 吗?写程序真的是这样吗?如果是这样,那么 coder 会更加工厂话,枯燥化。
做事都将就未雨绸缪,做程序更应该这样。大学 C 语言经典教材中定义程序为:程序 = 数据结构 + 算法。而实际生产的过程中,将商业程序做如下的补充定义,我觉得更合适:程序 = 数据结构 + 算法 + 业务逻辑(计算逻辑)+ 框架;
先说说为什么补充业务逻辑,有意义的程序本身就是某种业务逻辑(计算逻辑)的抽象。完成这个业务逻辑才是最终的目的,请不要拿一些算法研究的 code 和我抬杠。
其实作为开发人员,测试驱动开发(TDD)很好思考问题的思路。也许有人听过,也许有同学用过,如果感觉使用不好的兄弟,我可以告诉大家:应该是测试场景 + 场景驱动开发。对,仅仅是里面融入“场景”这个宾语,大家在做开发的时候,就有目的性和针对性。
任何一个业务逻辑,都可以拆分为多个业务场景。场景逐一解决,场景逐一测试,我们开发其实很简单。说的很简单,但是整个过程,需要 50%的时间思考解决问题场景, 20%的编码,30%的测试。其实思考问题的 50%的时间,可以放在任何时间去做(休息时,地铁上,班车上...),只要让自己足够的静,你就会将整个业务逻辑思考的很清楚,分解为多少个业务场景也很明确。对于复杂的业务场景,建议适当的做笔记,多从全局的业务逻辑考虑:自己细化的得到结论是否符合所有的业务场景。反复的修正,直到正确。
HWer 都知道,有几个职级升级需要几个方面的准备。其中一个环节就是机试,要求就是在 4~6 个小时内(多是安排在周六上午),完成一个较大题目的若干需求。你可以一个需求一个需求的 code,也可以思考整个问题后再 code,往往后一种方式更容易得高分。
具体的编码时候,经过我们前面的深思熟虑,每个细节都已经很清楚了,采用迭代的方式,批量的交付小的功能点就可以了哈。
开发阶段的总结两个关键字: TDD + 迭代。需要详情的同学自行 baidu,google。
功能调试阶段
调试的手段很多,走读代码,打日志,gdb,统计,coredump 等,如果有精力也可以搞搞的白盒测试什么的。测试的意图也很明显,确认代码是否按照正确的编码意图在运行!其实自己写的代码,自己还是可以轻松驾驭调试的,原因就是自己清楚代码的本意该如何运行,现在出现了什么问题。
程序猿的三大悲剧之一,就是不知道什么时候需要定位一个其他猿写的 bug。定位前也需要必须要理解另外一位程序猿写这段代码的意图是什么,否则没有办法定位。理解其他的人写的代码途径也就是通过阅读代码了解大致思路,通过日志、gdb、或者统计信息补充代码意图的更多细节,或者修正理解不对的思路。
这个过程可能很枯燥,也可能很有挑战,试图通过种种迹象去了解另外一个猿写代码的初衷和意图,会不会有窥探人家隐私的赶脚!
其实,上面说了这么多只是告诉大家调试好的前提,和调试的初衷。
一个优秀的猿,你会发现他有很多调试技巧,也就是很多调试手段获取自己想得到信息。信息获取的多,自然就很容易清除程序本身的意图。
调试工具的使用细节和说明,同学们可以自行 baidu,google。
我在这里简单的阐述一下自己是怎么调试程序的,怎么理解各种工具的,欢迎大虾门指点交流?
1) 关于日志
如何打好日志绝对是门学问。日志打印多了,自然会影响后台程序的性能;同样打印的少了,没有办法定位问题;更苦逼的是打印到空指针,更有可能 coredunp 掉自己的程序;
所以日志的技巧就是:少,且内容丰富。
如何少,其实就是汇聚。
能不能将表达同一个意思的打印减少?
能不能在关键异常的地方加上统计(输出统计)?
能不能不打?
能不能内存中记录关键信息,在想要的时候,控制其打印时机?
如何丰富,其实就是少打描述性词汇,多打有用的程序运行信息。
方法很多,大家多多思考。并且打印的优化,是反复优化的过程,不是一蹴而就的。曾经遇见一个大牛,测试部提问题了,这哥们从来不去定位。直接告诉测试的兄弟,帮忙执行以下软调,将收集的日志给他分析一下就可以解决问题。
2) 关于 gdb
还有大牛说过:“我就是程序,程序就是我”。我常用 gdb 来检验自己对程序的理解。常用的 gdb 功能就是打印一些程序的运行信息,修改一些内部运行信息,构造复杂场景。
其实很简单,程序在什么场景下应该有什么样的行为,我自己的必须清楚。必须知道关键变量的信息是否正确,周期 gdb 出来,确认变量的信息是否正确,然后决定程序是否符合预期在执行。
可靠性的程序都有类似的保护机制,但是通常需要繁琐的构造测试条件,触发保护机制(如检测到丢包率很高,要告警等)。其实大多数的保护机制都是通过记录一些状态后,程序然后触发的保护机制。
其实,可以 gdb 构造出异常状态,确认告警机制是否生效。gdb 很好的补充这方面的测试和验证工作。
3) 关于统计
统计信息,是关键信息汇集的最好的例子。数据少,切信息明了。
电信软件中,很多模块都通过这样的信息:自证清白。也很容易发现问题出现哪里。
统计的实质,就是通过全局变量,记录一些程序正常,异常点的统计信息。然后通过某种手段输出出来。
4) 关于 coredump
大家看到 coredump 都会头痛,其实 coredump 也是很好的定位手段。
首先程序 coredump 后,会有详细的 coredump 文件,该文件详细的记录了程序在 core 之前的运行信息。gdb 这个 coredump 文件,你想看什么都可以。这只是简单的使用 coredump。
如果碰到复杂的问题,难搞的问题,其实也可以使用 coredump 来定位。
比如程序执行到一个十分不常见的代码分支,然后程序就 core 掉了,但是目前输出信息(日志等),根本没有办法进一步定位问题。
怎么办?有没有想过在复现问题的环节,出个调试版本的程序,在异常分支上主动触发内存异常,产生 coredump,利用 coredump 信息,来确定程序是如何异常的呢 ?
5) 关于代码修改
这个也是我比用的手段之一,反复的对比修改前后的代码,确认修改代码的准确性,全面性,反思自己代码修改是否全面?其实这里面工具就是 beyondcompare
code 数载,偶有所得,记录于此,望猿有所得!