一个任务异常挂起(有相关的定位数据),求进一步的定位方法
操作系统:VxWorks,CPU:MPC860,IDE:Tornado2.0
现象:
1.程序(此程序很大有几十个任务运行)运行一段时间后异常复位,每次复位之间的时间间隔无规律,有时半个小时,有时2个小时,有时5,6个小时。
2.通过打印了解到,是由于一个任务tlongtimer被异常挂起(suspended)了,该任务1S执行一次,主要执行两个函数:A(先),B(后),即:
tlongTimer
{
A;
B;
delay(1s);
}
又由于需要,A和B两个函数都用了两个二进制信号量(Mutex1,Mutex2)与其他的函数进行互斥,即
A
{
semtake(Mutex1);
semtake(Mutex2);
..................
semGive(Mutex2);
semGive(Mutex1);
}
B
{
semtake(Mutex1);
semtake(Mutex2);
..................
semGive(Mutex2);
semGive(Mutex1);
}
这里我个人认为有必要介绍一下另外一个任务的情况:tShortTimer,10ms执行一次,它的优先级和tLongTimer一样,它主要执行一个函数C,但C只用了Mutex2与A和B进行互斥,即
C
{
semTake(Mutex2);
....
semGive(Mutex2);
}
3.当出现tlongtimer被挂起后,我用tt命令查看了它的调用栈,发现被挂起之前最后调用的函数是A的semGive,或者B的semGive,至于是semGive哪个信号量,不知道(有方法可以定位出来吗?)
4.再用ti命令查看tLongtimer的TCB信息,打印出N个(大概30多个,有什么R31,R3....,当然有SP和PC)寄存器的值(我不知道绝大部分的寄存器代表什么意思,所以无法定位,有资料可以看吗?),同时还打印出出现异常时该任务的PC地址,我用lkaddr查了一下,该地址对应的函数就是semGive。
我的问题:
收集到这些数据后,我感觉离异常的根因很近了,但又觉得很远。
1.是不是因为采用了二进制信号量?因为我看VxWorkx的帮助文档,说二进制信号量主要用于同步,而互斥信号量用于互斥,但我个人觉得二进制信号量这样用应该也可以起到互斥的作用。
2.是不是tLongtimer的堆栈信息被其他的任务或自身的函数改写了?导致信号量的ID为无效值,所以semGive失败?
定位到这里,我不知道该如何定位下去了,还需要采集其他数据吗?如何采集(用什么命令)?还有什么好的定位策略吗?
这个鸟问题烦了我10多天了,请各位大虾帮忙拯救我
什么时候eeworld也变冷了?还是觉得问题多分太少?那我再加分
感觉LZ的程序逻辑上有问题
从现象上看 是没有规律的 也就是说这种现象是由一种随机出现的事件引起的
看一下两个任务 一个是1S执行一次 一个是10S执行一次
而且 从现象上推断 估计两个任务的执行时间长度肯定是不规律的
出现这种情况通常都是任务间共享资源互斥逻辑上出现问题
不妨在程序中适当输出一些打印信息 例如把每次获取和释放信号量之后的情况打印出来 判断信号量是否获取成功之类的信息
然后在超级终端里捕获一下LOG 对比对比 也许能发现任务死锁的原因
谢谢15楼的兄弟共享的调试方法,非常不错的文章,思路清晰,定位准确,值得学习!
同时,提个问题,这种定位方法是不是需要CPU支持呢?怎么知道哪些CPU支持这种调试方法?
你们说的OCD是集成在CPU内部的吧?
还是说OCD是我需要额外去买的器件?我很想用这种方法去定位我的那个鸟问题!
我非常赞成FireAngel关于去定位寄存器的说法,但是
r0 = 0 sp = 2692420 r2 = 0 r3 = 0
r4 = 0 r5 = 0 r6 = 0 r7 = 0
r8 = 0 r9 = 0 r10 = 0 r11 = 0
r12 = 0 r13 = 0 r14 = 0 r15 = 0
r16 = 0 r17 = 0 r18 = 0 r19 = 0
r20 = 0 r21 = 0 r22 = 0 r23 = 0
r24 = 0 r25 = 0 r26 = 0 r27 = 0
r28 = 0 r29 = ffffffff r30 = b030 r31 = 17e0700
msr = b030 lr = 0 ctr = 0 pc = 1423c0
cr = 20000043 xer = 0
这些寄存器的含义是什么?有没有说明文档?发给小弟,谢谢!
从信号量的使用上来说,不应该用二进制信号量进行代码互斥,而应该使用互斥的信号量来保护。二进制信号量谁都可以take和give,很难保证不出问题。
当然这个一般不会出现导致异常的情况,除非其他任务中有对信号量删除操作之类的。
出异常后,应该会有一段提示出错的信息的,查一下。同时看一下任务堆栈有没有溢出之类的,很多情况下异常都跟堆栈溢出有关
2楼的是说,任务死锁吗?
你的论据呢?
怎么样可以证明是任务死锁呢?
根据异常堆栈,结合addr2lineppc.exe小程序可以定位到哪一行调用被挂起.
4楼兄弟的意思我明白,有一个任务是10ms级的,如果要输出打印的话,似乎太多了吧?
兄弟你说我的程序逻辑似乎有问题,我曾经这么觉得,但有找不出哪里有问题,
或者你能帮我找一个特例说明我的程序逻辑有问题?
addr2lineppc没有用过,5楼的兄弟能不能说得更细些,我只能得到一个任务的调用函数之间的关系,如何能得到函数中局部变量的地址和值,有办法吗?
从逻辑上看来,两个进程之间这样是没问题的,不应该导致死锁。
有没有这样的可能:tShortTimer负载太重,以至于有时候不能在10ms内完成,新的中断又来了,导致自己挂在自己上面。这个时候tShortTimer无法释放mutex,导致两个进程都挂在mutex2上?
我觉得可以如下调试试试看:
1), 将tShortTimer中工作屏蔽掉,只是semTake后semGive,看会不会出现类似错误。
2), 将tlongtimer中A和B的工作屏蔽掉,只是semTake后semGive,看会不会出现类似错误
3), 将tlongtimer和tShortTimer中的工作都屏蔽掉,只是semTake后semGive,看会不会出现类似错误
4), 如果能确定是tLongTimer或tShortTimer引起的,在慢慢往里面回复以前的操作,反复实验,这样应该能找出问题来。
能否尝试将别的任务先不启动,只运行这几个任务,也许问题不一定是在这里。
关于调试查看寄存器,我不知道怎么发给你,所以就贴到我的博客中了,你看看,也许有用。
楼上的兄弟,你说的没错,是需要OCD支持的。事实上,你没有OCD怎么调试呢?
用仿真器当然好啊
不过那东西好贵呀
我们这边一般都是轮着用
所以通常都是通过分析代码和SHELL接口调 其实有很多问题都可以通过分析代码搞定
LZ加油啊
解决了之后定要把分析方法拿来共享一下哈
觉得楼主的这个问题挺有意思的
今天恰好有时间 就在PC上sim了一下
写的代码很简单
除了信号量操作之外只加了空的循环操作
因为要看看输出的结果 所以延时上也比LZ的10ms多了很多
跑了一个下午 没出问题
所以怀疑可能是LZ代码中其他的操作引起的问题或者阻塞
8楼兄弟提出的方法不错 建议楼主试试看 :)
白天忙所以只有晚上看论坛。谢谢各位大侠出的点子,挺感动的,当一个人陷入困境的时候,有其他人的帮助,这种感觉真好。
7楼说的Mark和9楼说的记号,是一种工具还是一种调试手段?能说仔细点吗?
谢谢8楼和11楼的仁兄出点子,现在基本上可以排除是任务死锁造成的了,我怀疑是任务堆栈被异常改写,所以semGvie释放了一个无效的信号量。
10楼的兄弟,麻烦你把资料传给我,我的邮箱是hddream@163.com或者帮你的博客网址给我,谢谢了!
现在最重要的:定位数据堆栈被异常改写有什么好的、快速的调试手段或方法吗?
(数据断点也可以啊,怎么玩?)
Mark的意思就是先做个记号,等会在回来看这个问题。
调试栈被修改是比较麻烦的问题哦。你不是可以用tt打印出来么?应该能看到一层层下来的调用关系啊。作为一个底层的programmer,错误定位到一定的方位后,通过读代码找出代码中的问题也是很重要的能力。
另外,你可以看看异常相关的寄存器,至少你能看出发生了什么异常,另外就是发生地址。
抱歉啊,860太古老了,从我工作开始,就没有机会接触过。但是应该跟其他MPC相似吧?
数据断点,就是硬件断点的一种啦,是硬件提供的一种调试能力,一般是CPU提供1-2套寄存器,将你要观察的地址设置好,下次针对该地址的有读/写操作,就会发生中断,然后你可以看到是谁访问了那里。挺好的方法。
另外还有硬件程序断点,类似上面,将代码的地址设置到寄存器里,下次PC跑到这的时候就断住了。跟软件断点不同的是,软件断点一般是调试器通过修改内存中的代码,插入调试代码后实现的。硬件断点无需这样。
我的博客在eeworld上,你把鼠标移动到我的用户名上,就会显示这个链接的。