实时嵌入系统的商用系统行为视图工具主要用来查找那些以通常的“查找断点和分析断点”的调试难以发现的问题,这些工具对于查找“Heisenberg 问题”尤其奏效,因为当我们运用普通工具开始查找时,这样的问题难以发现。
记录工具较之于其它的调试工具对系统的影响较小,因其不太可能严重扰乱系统以至于难以查找问题,但这一记录工具会冻结在记录文件中的错误记录,并在以后空闲时间对其进行检查。你可以指定一个用于记录组件的触发点,这一触发点也许是一个总线故障或者是某些系统服务的第N次故障。记录后台程序会截取触发事件附近的事件的记录,并将它们传送到将其形成图形以便分析的工具中去。
运用视图工具的正常周期是从某一问题开始的,假设系统被冻结,标准的调试实用程序就会显示出两个任务没有任何运行,它们最后的行为都是等待锁死。但是,如果存在问题,代码检查就会显示两个任务都在等待锁死,只是等待的时间不同。实际上,该设计要专门调用那两个任务,将这个锁死在两个任务之间来回传递,视图工具就是利用这个javascript:;" onClick="javascript:tagshow(event, '%B9%A4%D7%F7');" target="_self">工作原理来发现软件设计错误的。
视图周期在微调触发点指标和检查记录之间反复,直至问题很清晰地出现在视图中并有充分的行为来表明问题的根本原因。在这个例子当中,视图周期显示某一任务想尽力在某一程序段内获得两次死锁,同时不允许其它任务运行。这时,程序员就能够找到问题所在。
几年前我设计了一种叫做HawkEye的视图工具,其主要两种用途是:(1) 查找在两个任务间的通信过程中由竞争条件所造成的问题;(2) 在短时间间隔上,调整性能。
当我设计Hawkeye的时候,脑海中就浮现出了即时重放的图,这一点也得益于同惠普(HP)逻辑分析仪共事数百个小时得出的灵感。大体上讲,Hawkeye是一个逻辑分析软件。你选择某一个触发点并开始运行它,当触发点事件出现时,Hawkeye会给出一个包含触发点附近信息的时线图。你选择这个触发点,所以线径包含有问题的时间间隔或需要进行优化的时间段。Hawkeye将它所收集的数据以图形表示出来,并使竞争条件和性能问题易于观察。
基于Hawkeye测试版完成测试的基础上,我们决定收集几个实例,目的是进一步完善Hawkeye。
事例1
我曾记录了用以测量上下软件间的切换时间的基准程序,但它在完成几次上下软件间切换后会闭锁起来。分析表明:系统调用本来只需要几毫秒,实际上却花了几秒时间。基准程序在它启动后不久就会死锁,因此我选择基准程序起始分支来触发。
如图1所示,黄线表示正在等待旗语的线,浅黄色的箭头表示未锁的系统调用,黄色的三角形表示锁死的系统调用,黑边框线的图标表示进入系统调用,淡色边框线的图标表示从系统呼叫中退出。在屏幕中间有正在锁死某个旗语并等待的线程11和线程12,这即是死锁现象,从在死锁的线程12的左边你可看到原因。有某一个释放线程11的未锁系统(见交互作用线),然后线程12再次释放旗语。
释放二进制旗语两次与释放一次无异。不知何故,我想到当线程12释放锁的时候,线程11会立刻醒来,并且直到线程11已醒来并重锁旗语,第二个未锁系统调用才会发生。或许这些在线程12的未锁系统间的紫色的图标(表示一套优先系统调用)尚可资研究。我没有记录排除这些死锁的过程,但我做到了。基准程序现在运行很正常。
这是视图工具设计的目的所在,我知道存在某个与系统的多组件的交互作用相关的问题,该工具给出了问题附近的系统行为的抓取记录,这些记录使我找到了问题所在。
事例2
John正在测试一个作为销售工具的产品机顶盒。它不会运行图形Java,如Java无图运行,它会运行得更好,而且不会因脚本或环境变化的干扰而致Java打开一个视窗。用在机顶盒上的软件运行很好,John添加上去的软件在其它平台上也同样运行良好,但当把该软件与机顶盒混合在一起的时候,似乎就出问题了。
John是一个很好的技师,但不是一个好的程序员,但即使是一个好的程序员也会遇到困难。机顶盒中的大部分软件已在工厂中由闪存固化而完成的,因而找不到符号调试所需的信息。John找到的线索是:应当运行window管理器的某个步骤不正常,尽管看起来这个步骤好像启动了,但实际并没有。他将记录后台程序添加到系统中并启动了它们。在与程序员一起检查后,他把触发点设置在记录中间某个特殊系统调用的第一个位置。
问题恰好就在触发点之前。某个低位图形组件正在调用来自图形驱动器的某个服务,该请求返回一个未知服务错误。几微秒之后,那一过程在指令存取上显示存储错误,因而被操作系统终止。问题存在于两点:调用服务的组件应当检查回归码,同时驱动器也应当支持这一服务。John把这个记录文件带给了程序员,程序员立刻查明并解决了这一问题。
在此,关键的问题是John不是程序员,某个系统完成的图形视图将问题报告由“我不能使Java 与图形一起运行,”改为“在这个图形服务实施过程中或应用方式上存在错误。”
事例3图1:黄线表示正在等待旗语的线,浅黄色的箭头表示未锁的系统调用,黄色的三角形表示锁死的系统调用,黑边框线的图标表示进入系统调用,淡色边框线的图标表示从系统呼叫中退出。
某一系统运行正确但执行速度很慢,仿佛CPU的负担很重,但问题诊断不出来。性能良好,系统正常的情况下,造成CPU负担太重的原因很多。
后来,更多的软件被添加到系统中,程序员随意查看和播放。我记录了某个测试程序执行时的某个镜头,看着这个镜头,我的注意力被一个特殊的记录过程所吸引。由于记录程序正在监视的装置没有运行,记录器应当在某个事件等待被屏蔽,但显示器显示正忙。它正在用尽每个可用的CPU周期以便尽可能快的完成事件等待系统调用。所有这些事件等待系统调用均立即返回。它们都在等待同一个事件,而且它们均来自于同一个地址。这个记录程序应当在测试程序的整个执行期间只等待一个事件等待调用,很明显其中存在一些错误。
很快,发现是事件配置不正确。在某一事件等待被返回后,后续的事件等待要无屏蔽地返回。幸亏(或许是不幸)程序进行了检查,以查看它所等待的事件是否发生;如果没有发生,它是否会继续等待。程序员修改了事件配置后,系统的运行速度大大地加快了。
这个错误或许从未被发现过。它不会导致某个软件不适配,也不会产生不正确的结果,或使系统锁死。系统即使通过了它的测试组,但是,只有用视图工具来查看系统,问题才变得一目了然。
事例4
这儿举另外一个不会造成明显故障或性能问题的事例。系统看起来是正常的,但当程序员运行某个视图线迹来查看它是什么问题的时候,线迹表明存在若干非同寻常量的未锁运行,它们当中大部分以紧密组出现,除了第一个返回一个错误信息外,总共有10个快速进行的未锁系统调用。
原因是由于将多用户计算机操作系统(UNIX)接入这一程序的人员没有注意到:他在使用了计数旗语的原程序上使用了二进制旗语。如果在一段程序当中几次出现未锁系统调用,二进制旗语不会有任何释放。程序与二进制旗语一起运行,但是旗语正在管理的队列决不会仅由一个入口填满。系统上下软件之间切换过频,达到大约10次,但仍在性能要求范围内,当二进制旗语被某个计数旗语代替时,系统运行更为良好(至少看起来如此)。
事例5
HawkEye线迹含有我正在寻找的问题的完好信息,而且完全朝向线迹未端,像一个蹩脚楔子,图2将这个楔子放大,我发现它是由均在同一个事件上的约一百个左右的事件设置系统调用组成的,尽管这些事件设置组合释放了一个事件等待。麻烦的是这个事件设置系统调用中的某一个可能足以释放等待任务。这发生于一个到伪-TTY装置的写调用的底部(远程登陆对话应用这些来实现类似终端的槽连接)。这个伪-TTY的终端部分正应用事件来控制装置插槽端的数据搬移,它为每一个特征设置事件,因此,我确信我已经找到了问题所在。
虽然这个线迹显示了问题所在,但实际情况却不是。它是一种相对简单的界面的图形描述,这一界面位于某一个特征装置和某一个数据包装置之间。通过让每一特征上的操作系统上下文切换,通信速度可减慢到约300bps,从而确定问题所在。以每一线而非每一个特征来设置事件听起来不失为一个好主意,但伪-TTY并非以线为导向,一次发送一条线就会像全屏编辑器那样中断程序。
有时候正确的实现方案看起来却很别扭。
经验教训图 2: HawkEye线迹含有正在被寻找的问题的完好信息。
上述事例表明:视图工具能够发现在完全调试过的软件当中所存在的许多问题。当它专用于查找特殊故障的原因时,它从不会出错,此外,它似乎也能够发现像程序员正在寻找的问题。后两类在我们的记录文件中占主要部分。程序员遇到的问题能够全部由软件的单步运行或通过使程序中断来彻底来加以查明。它们不是视图工具要解决的具有奇怪竞争条件的问题,这些问题的发现是由于视图工具使其更加容易地搜集在系统执行过程中的数据,从而使工程师能够利用大脑的图形检测能力对问题进行分析。
我很想知道,上述事例是否代表了嵌入式领域以外的问题。也许我一直在紧张的CPU和存储器预算中工作的太久,以至于丧失了自己的看法,在此,大部分意外事例都同仅遭受性能问题的系统有关。此类例子还很多,例如:
1. 某个程序会一个接一个的分配成打的小的存储块,此后这些存储块将按序释放出来。没有理由说明对所有这些结构,这个程序不会分配某一个邻近的存储块。
2. 某个程序一遍遍分配并立即释放某个存储块,但是它应当只分配和释放一次。
3. 某个程序多次打开并关闭文件。
4. 另一个程序多次打开某个文件却未关闭它。
5. 某个程序反复对信号掩码,然后以相同的次数去掉信号的掩码。
6. 某个程序尝试返回某个未知服务错误的系统调用,并立即再次尝试调用。
上述行为在桌面或主机软件中是可接受的吗?对于嵌入式实时程序员而言,或许视图工具比其它程序员有价值得多。
我一直在设计和使用视图工具,好像他们是软件的逻辑分析仪。这一工具收集并显示围绕某一特定问题执行时的细节。这是一个重要的应用,但工具也显示了在软件执行时的高级图形模式。用户的事例告诉我们:程序员应当用每一种可用的工具来检查他们的产品,而且我们还需要更多和更好的工具来观察软件的运行情况。
我期望用户的事例将揭示:HawkEye需要收集和显示更多的资料,而且它还需要更复杂的触发点。我明白这一点,但是,我不会采取与我的用户相同的方式。他们正应用HawkEye作为观察软件黑盒子内部最为简便的手段,可以自豪地说,HawkEye在查找问题的过程中正在发挥作用。