我继承了一个用 8051 汇编语言编写的 10K 行程序,需要进行一些更改。不幸的是,它是用意大利面条式代码的最佳传统编写的。该程序(作为单个文件编写)是 CALL 和 LJMP 语句的迷宫(总共约 1200 个),子例程具有多个入口和/或出口点(如果它们可以被识别为子例程)。所有变量都是全局的。有评论;有些是正确的。没有现有的测试,也没有重构的预算。

该应用程序的一些背景知识:该代码控制目前在国际上部署的自动售货应用程序中的通信中心。它同时处理两个串行流(在单独的通信处理器的帮助下),并且可以与最多四个不同的物理设备通信,每个设备都来自不同的供应商。其中一台设备的制造商最近进行了更改(“是的,我们进行了更改,但软件绝对相同!”),这导致某些系统配置不再工作,并且对取消更改不感兴趣(无论它是什么)他们没有改变)。

该程序最初是由另一家公司编写的,转移给我的客户,然后在九年前由另一位顾问修改。原始公司和顾问都无法作为资源提供。

根据对其中一个串行总线上的流量的分析,我想出了一种破解方法,该方法似乎有效,但它很丑陋,并且没有解决根本原因。如果我对程序有更好的理解,我相信我可以解决实际问题。在代码被冻结以支持月底发货日期之前,我还有大约一周的时间。

原问题:我需要充分理解该程序才能在不造成破坏的情况下进行更改。有没有人开发出处理这种混乱的技术?

我在这里看到了一些很好的建议,但受到时间的限制。然而,我将来可能还有另一个机会去采取一些更复杂的行动方案。

有帮助吗?

解决方案

首先,我会尝试取得联系与谁最初开发的代码或谁至少保持它在我面前,希望得到足够的信息来获得一般的代码有基本的了解,这样您就可以开始添加那些人有用的注释。

也许你甚至可以找人来形容最重要的API(包括他们的签名,返回值和目的)的代码。如果全局状态是由函数修改,这也应该明确。类似地,启动功能和过程,以及输入/输出寄存器之间进行区分。

您应该让您的雇主很清楚,这信息是必需的,如果他们不相信你,让他们实际上你坐下来在这段代码之前,而你的描述,你应该做的是什么,以及如何要做到这一点(逆向工程)。在计算和编程具有与背景的雇主实际上是在这种情况下有帮助!

如果你的雇主没有这样的技术背景,让他把另一个程序员/同事解释你的脚步给他,这样做实际上会告诉他,你是认真和诚实的,因为它是一个真正的问题 - 不只是从你的角度来看(确保有谁知道这个“项目”的同事)

如果有效可行,我也非常清楚,即承包(或至少是接触)前开发/维护(如果他们不再为你的公司工作,这是)帮助文档此代码将是一个先决条件,切实提高短的时间跨度内的代码,并确保它能够更容易地保持在将来。

强调,这整个情况是由于以前的软件开发过程中的缺陷,而这些措施将有助于提高代码库。所以,在目前形式的代码库是一个日益严重的问题而无论是现在做处理这个问题是对未来的一种投资。

这本身也很重要,帮助他们评估和了解您的情况:要做到你现在该做的是远离琐碎,他们应该了解它 - 如果只以正自己的预期(例如关于最后期限和任务的复杂性)。

另外,个人我将开始添加单元测试那些我明白不够好,所以我可以慢慢开始重构/重写一些代码部分。

在换句话说,良好的文档和源代码注释是一回事,但是具有综合测试套件是另一个重要的东西,没有人可以被如实预期而不测试关键功能的任何已建立的方法来修改一个不熟悉的代码库。

由于代码是10K,我还要考虑分解出子程序成单独的文件,使组件更易识别,最好使用访问包装,而不是全局变量,也直观的文件名。

除了

,我会研究措施,通过降低复杂性,进一步提高源代码的可读性,具有多个入口点子例程(甚至可能不同的参数签名?)看起来像一个肯定的方式来不必要的混淆代码。

同样,巨大的子例程也被重构到较小的,以帮助提高可读性。

所以,最先的一件事,我会考虑这样做将是与多个入口分裂巨大的子例程来确定,使得它非常复杂神交代码库,然后返工那些部分的那些东西,例如点进去相互调用,而不是不同的子例程。 如果这不能因做性能方面的原因或调用的开销,使用宏来代替。

此外,如果它是一个可行的选择,我会使用更高级语言考虑代码的递增重写部分,或者通过用C的子集,或至少通过使相当过量使用组件宏来利于规范代码的基础上,而且还帮助定位潜在的错误。

如果C中的增量改写是一个可行的选择,一种可能的方式开始将关闭所有功能明显成C函数的尸体被-in /复制的开始 - 从组件文件粘贴,这样就结束了与许多联汇编的C函数。

就个人而言,我也尝试在模拟器/仿真器运行代码容易地通过代码和希望开始了解的最重要的组成部分(在检查寄存器堆栈使用),良好的8051模拟器内置调试器应提供给你,如果你真的要做到这一点主要是你自己的。

这也将有助于你想出的初始化序列和主回路结构,以及一个调用图。

也许,你甚至可以找到一个很好的开源80851模拟器,可以很容易地修改,还可以自动提供一个完整的调用图,只是做了快速搜索,我发现的 gsim51 ,但也有明显的其他几种选择,各种专有的以及。

如果我是你的情况,我甚至会考虑外包修改我的工具,以简化与此源代码工作的努力,即许多SourceForge上的项目接受捐赠,也许你可以跟你的雇主为赞助这样的修改。

如果没有财政,也许是你提供相应的补丁呢?

如果您已经使用了专有的产品,你甚至可以用这个软件,并详细说明您的要求,制造商交谈,问他们是否愿意改善该产品的任何方式或如果他们能至少暴露了接口允许客户做出这样的定制(某种形式的内部API,或者甚至简单的胶合脚本)。

如果他们不回应,表明你的雇主已经在想现在用了一段时间不同的产品,并且您是唯一一个坚持特定的产品使用...; - )

如果软件所需的某些I / O硬件和外设,你甚至可能要考虑编写相应的硬件仿真循环在模拟器上运行的软件。

最后,我知道一个事实,我个人更喜欢自定义其他软件来帮助我理解这种面条代码的怪物,不是通过代码手动逐步和玩模拟器自己,不管有多少加仑的过程咖啡我可以得到的。

获取可用的调用图了一个开源的8051模拟器不应该远远长于说,利用周末(最多),因为它主要是指以寻找CALL操作码和记录他们的地址(位置和目标),让一切的转储到供以后检查的文件。

能够直接访问一个仿真器的内部实际上也是巨大的方式来进一步检查代码,例如,以便找到操作码的重复图形(比方说20-50 +),其可以被分解成单独的功能/过程,这实际上可能帮助甚至进一步减少代码库的大小和复杂性。

在接下来的步骤很可能会检查堆栈和寄存器使用。并确定所使用的函数的参数类型/尺寸,以及它们的值的范围 - 。这样就可以设想相应的单元测试

使用像点/ graphviz的工具,以可视化的初始化序列和主回路本身的结构,将是一个纯粹的喜悦相比人工手动操作所有这些东西。

此外,你会真正有用的数据和文件,可以作为基础在LO更好的文档结束纳克运行。

其他提示

恐怕没有灵丹妙药这样那样的问题。我找到的唯一的解决办法是打印出来的文件ASM然后找个安静的地方,并模拟运行在你脑中线的程序行(同时写在记事本的寄存器和存储器中的内容)。过了一会儿,你觉得这不,只要你会想到服用。 准备花很多时间做这个,喝咖啡加仑。不久后,你将拥有它是做什么的理解,你可以考虑改变。

是否有8051任何未使用的IO端口?如果它不,你不能工作了,当被称为某些例程,然后添加代码来发送这些备用端口高或低。然后 当程序正在运行观看这些端口用示波器。

好运

我知道这听起来很疯狂....但我失业了(我选错了时间告诉marjority伙伴去地狱),并有空闲的时间。我很愿意看看它。我以前写为苹果组装] [和原来的PC。如果我能与你的代码在模拟器上玩了几个小时,我可以给你一个想法,如果我有记录它给你的机会(无乳宁我的计划外休假)。因为我什么都不知道8051对于像我这样也许是不可能的,但模拟器看起来前途无量。我不希望任何钱这样做。它足够的只是为了让接触到8051嵌入式开发。我告诉你,这会听起来很疯狂。

查找另一求职认真!如果做不到这一点的书“与遗留代码有效地工作”可能会帮助 - 但我认为它指的是旧的代码代码,而单元测试。

我已经做过几次这样的事情了。一些建议:

  • 首先查看原理图,这应该有助于您了解哪些端口和销钉所需的更改影响。
  • 使用GREP查找所有呼叫,分支,跳跃和返回。这可以帮助理解流程并确定代码的块。
  • 查看重置向量和中断表以识别主线。
  • 使用GREP为所有代码标签和数据参考创建一个交叉引用(如果您的汇编工具无法为您执行此操作)。

请记住霍夫施塔特定律:即使考虑到霍夫施塔特定律,它所花费的时间总是比您预期的要长.

祝你好运。

您对这段代码运行的硬件平台了解多少?

  • 是否已将其放入电源模式(PCON = 2),以节省电源,如果这样,它如何被唤醒。(复位或硬件中断)

  • 上电后是否必须等待振荡器稳定后再进行串行通信

  • 是否进入睡眠模式(Pcon=1)

现场是否有不同版本的硬件?

确保您有所有不同的硬件版本可供测试。

不要在模拟器上浪费时间——它很难使用,而且你必须对硬件做出很多假设。给自己一个 在线仿真器 (ICE) 并在硬件上运行。

该软件是用汇编语言编写的,您需要找出原因。IE。- 内存约束 - 速度约束

这段代码混乱可能是有原因的

查看链接文件:

XDATA 空间、IDATA 空间和代码空间:

如果没有空闲代码空间或者Xdata或者Idata?

原作者可能对其进行了优化以适应可用的内存空间。

如果是这样的话 您需要与原始开发人员交谈以了解他做了什么.

您不需要为重构和测试特别预算 - 他们为你省钱,让您提高工作效率 - 得到它。这是你应该使用的变化添加到传统的技术,继承代码,因为它是这样做没有“没有破损”最便宜的方式。

大多数时候,我觉得有一个权衡,你换来花更多的时间获得更多的质量,但与传统的代码,你不熟悉的,我觉得它的速度更快,使测试 - 你必须运行代码之前出货,对吧?

这是几次我要去,建议你把你的软技能的工作,并提出您的PM /经理/ CXO了重新写你的背后理由之一,并参与了这种节省时间/成本承诺书

它切成片。

我有一些非常类似的问题与8052软件。因此,该公司继承这样的野兽,代码ROM满(64K字节),约1.5兆的组件面条模块加上由该编码怪物2 3000行PL / M模块。该软件的原始开发者早就死了(这并不意味着有任何人,但确实没有人谁也把它理解为一个整体),编译器编译这些都是从一个MDS-70模拟器中运行的中间80年代,和几个关键模块分别在这些编译器的极限。像添加一个全局符号,并且链接器将崩溃。一个以上符号添加到ASM文件,编译器会崩溃。

那么,如何人们可以开始削减这个吗?

首先,你需要的工具。记事本+ +例如是一个非常好的事情,因为它可用于在一次穿越搜索沿着几个文件,理想找到哪些模块是指一个全球性的象征。这可能是最关键的元件。

如果可能的话,让你可以找到任何软件的文件。与这些野兽,解决最紧迫的问题是要了解它们是如何大致包括,什么是他们的建筑。这通常是不包括在软件本身,而不是即使它被适当地另有注释。

要得到自己的架构,首先你可以尝试为建立调用图。因为通常有横档电话和跳得比全局变量少,是比简单数据流图的事情。对于这个调用图只考虑全局符号假设的源文件应该是模块(其不一定是真实的,但通常它们应该是)。

要做到这一点,使用您的工具进行交叉文件搜索,创建一个大名单(例如OpenOffice的计算器),您收集其符号在文件中定义的,以及哪些文件是指这个符号调用它。

然后,从绘图机窃取一些大(!)片材,并开始素描。如果你是在一些图形软件很精通,你可以使用它,但除非它是如此,它更可能将你带回过去。所以SKETCH UP调用图显示哪些文件有到其它文件的调用(不显示的符号本身,有50个左右的文件,您将无法对其进行管理)。

最有可能这样做的结果将是一个意大利面条。我们的目标是理顺了这一点得到它的分层树与根(这将是包含程序的入口点的文件)没有循环。您可以在此过程中反复的弯曲矫正猛兽吞吃了几张。你也可能会发现某些文件是这么多相互纠结,他们不能没有循环表示。这种情况下,最有可能是一个“模块”两个文件,或更多概念性的模块得到了某种方式被分离纠缠不清。回到您的通话清单和组符号,从而削减了有问题的文件,在较小的独立单元(你将需要检查的文件本身也为当地在这里跳跃看到你的假设切割是可能的)。

要结束,除非你已经工作了别的地方为了你自己好,你会得到与概念上的模块层次调用关系图。从这一点可以扣除软件的故意架构和进一步的工作。

下一个目标是在架构即可。通过你以前所做的地图,你将需要沿该软件浏览,找出它的线程(中断和主程序任务),并且每个模块/源文件的大致用途。你如何能做到这一点,你在这里买到更多地取决于应用领域。

当这两个完成后,在“休息”是相当简单的。通过这些,你应该基本了解的事情每一部分都应该做的,所以你知道你有可能打交道时,你开始对源文件的工作有。无论何时你发现在源东西“腥”这是尽管这很重要,那该方案似乎做什么无关,回到你的架构和调用图,并在必要时进行更正。

要的其余部分中提到的方法而其他则应用好。我刚才概述这些给什么可以真正可怕的情况下做了一些见解。我希望我刚刚10K行代码来处理当时...

我说IanW的回答(只是把它打印出来,并保持跟踪)可能是最好的。这就是说,我有一个稍微偏离壁想法:

尝试运行通过一个伪君子,可重构的C代码的代码(可能是二进制)(如果你能找到一个为8051)。也许这将确定一些程序,你不能(容易)。

也许这会帮助你。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top