通过DT10检测多任务阻塞死锁问题

创提信息
2021/10/08

分享到

DT10是新一代的动态测试工具,可以长时间跟踪记录目标程序执行情况,获取目标程序动态执行数据,帮助进行难于重现的Bug错误分析,覆盖率检测,性能测试,变量跟踪,多任务程序等等功能。
 
VxWorks在国内航天军工、轨道交通等实时性要求较高的领域应用非常广泛,其提供多任务,中断等机制较好的满足实时嵌入式领域的需求,然而开发VxWorks多任务程序过程中,常常碰到令人头疼的多任务程序阻塞或死锁等问题,例如某个task所占用时间过长导致其他task不能正常执行等等问题。
 
本文介绍如何通过DT10检测和调试VxWorks程序多任务阻塞死锁相关问题。


一、VxWorks环境

 
启动FTP Server,并设置Users/rights…


16.jpg

 
启动VxWorks目标板,如下图所示VxWorks启动成功:


17.jpg

 
启动Target Server和Shell:


18.jpg


19.jpg

 
二、两个简单Tornado事例程序

 
Multi_Task.c 
                       “ 
                        #include "vxworks.h" 
                        #include "semLib.h" 
                        #include "stdio.h"


                        #define TEST_NUM 10


                        SEM_ID semB; 
                        int global; 
                        int isTask1Running=1; 
                        int isTask2Running=1;


                        static void task1(void) 
                        { 
                        int i=0; 
                        while(i<TEST_NUM) 
                               { 
                        printf("<taske1>take sem\n"); 
                        semTake(semB,WAIT_FOREVER); 
                        printf("<taske1>get sem\n"); 
                        global++; 
                        printf("<taske1>global=%d\n",global); 
                        taskDelay(100); 
                        semGive(semB); 
                        printf("<taske1>give sem\n"); 
                        i++; 
                               } 
                               isTask1Running=0; 
                        } 
                        static void task2(void) 
                        { 
                        int i=0; 
                        while(i<TEST_NUM) 
                               { 
                        printf("<taske2>take sem\n"); 
                        semTake(semB,WAIT_FOREVER); 
                        printf("<taske2>get sem\n"); 
                        global--; 
                        printf("<taske2>global=%d\n",global); 
                        taskDelay(200); 
                        semGive(semB); 
                        printf("<taske2>give sem\n"); 
                        i++; 
                               } 
                               isTask2Running=0; 
                        }


                        static void task3(void) 
                        { 
                        while((isTask1Running==1)||(isTask2Running==1)) 
                               { 
                               taskDelay(100); 
                               } 
                               printf("Bye Bye, Both Task1 and Task2 are finished!"); 
                        }


                        void ex3_test() 
                        { 
                        int taskId1,taskId2; 
                        int t1_pri=91; 
                        int t2_pri=90; 
                        semB=semBCreate(SEM_Q_FIFO,SEM_FULL); 
                               taskId1=taskSpawn("t1",t1_pri,0x100,0x1000,task1, 
                                      0,0,0,0,0,0,0,0,0,0); 
                               taskId2=taskSpawn("t2",t2_pri,0x100,0x1000,task2, 
                                      0,0,0,0,0,0,0,0,0,0); 
                               taskId2=taskSpawn("t3",t1_pri+1,0x100,0x1000,task3, 
                                      0,0,0,0,0,0,0,0,0,0); 
                         }

                          ”


Dead_lock.c

                        “

                         #include "vxWorks.h" 
                         #include "semLib.h" 
                         #include "taskLib.h" 
                         #include "stdio.h"


                         voidtaskA(); 
                         voidtaskB(); 


                         SEM_ID sem_1; 
                         SEM_ID sem_2; 


                         voidvxmain() 
                         {

                                sem_1 = semMCreate(SEM_Q_FIFO); 
                                sem_2 = semMCreate(SEM_Q_FIFO); 


                                taskSpawn("tA", 80, 0, 0x1000, (FUNCPTR)taskA, 
                                       0,0,0,0,0,0,0,0,0,0); 


                                taskSpawn("tB", 80, 0, 0x1000, (FUNCPTR)taskB, 
                                       0,0,0,0,0,0,0,0,0,0); 
                          } 


                          /*------------------------------------------------- 
                          the 2 tasks 
                          -------------------------------------------------*/ 
                          voidtaskA() 
                          { 
                                 semTake(sem_1, WAIT_FOREVER); 
                                 printf("Task A took sem_1, try to take sem_2\n"); 


                                 taskDelay(60); /* allow task B to run */


                                 semTake(sem_2, WAIT_FOREVER); 


                                 printf("Task A took sem_1 & sem_2!\n"); /* can never get here */ 
                          } 


                          voidtaskB() 
                          { 
                                 semTake(sem_2, WAIT_FOREVER); 
                                 printf("Task B took sem_2, try to take sem_1\n"); 

 

                                 //semTake(sem_1, WAIT_FOREVER); 


                                 printf("Task B took sem_1 & sem_2!\n"); /* can never get here */ 
                           } 
                            ”

 
上述两段程序均使用了VxWorks库函数taskSpawn创建多个task。 multi_task.c文件中创建了三个task,task1, task2会设置标志isTask1Running, isTask2Running,当task1,task2结束后,task3才结束。Dead_lock.c文件中创建taskA, taskB,并通过信号量进行同步。当然,如果你仔细研究代码,这两段代码中,均存在某个task不能正常结束的情况。

 
下面我们将通过DT10提供的相关debug和测试的功能,帮助查找并定位相关问题。

 
三、创建DT10工程

 
DT10中点击File->New创建一个工程,并设定工程名称,指定被测试文件目录和DT10工程存放路径,设定Connection方式(DT10支持多种与目标机的连接方式)为Ethernet(Without Tracer),工程创建好后,如下图:


20.jpg

 
然后点击Plan->New Test Point Insertion…,对test.c文件自动插入FuncIn, FuncOut测试点。测试点插入完毕后,如下图。DT10除了支持FuncIn,FuncOut测试点,还支持CPU压力测试点,变量测试点,Event Trigger测试点等,帮助用户对系统的CPU,任务,变量等进行监控。


21.jpg

 
测试点插入后,我们在VxWorks构建环境Tornado中重新对上述项目进行编译链接,如下图:


22.jpg

 
四、执行程序并收集测试结果

 
1. 启动DT10监听测试结果程序,如下图:


23.jpg

 
2. 在Tornado Shell中启动被测试程序,直接在Shell中键入vxmain即可,如下图:


24.jpg

 
五、分析测试结果

 
通过DT10自动插入测试点,程序在执行过程中,在每个测试点位置,目标程序会发出一个信号告知DT10程序执行到该测试点,这样DT10收集所有测试日志数据后,然后通过DT10的分析功能,可以得到包括每个函数的执行时间,代码的覆盖率信息,以及程序执行路径等图形化报告,下面我们重点介绍通过DT10的哪些功能,帮助我们定位程序死锁或阻塞的问题。

 
1. 通过测试日志了解任务阻塞或死锁情况

 
首先我们看如何通过DT10的测试日志帮我们查找到任务死锁的问题。如下图,我们看到DT10的获取到的测试日志报告:


25.jpg

 
结论:通过DT10测试日志中的FuncIn, FuncOut测试点,很容易找到目标程序运行过程中是否存在某个任务或者函数被阻塞,这对于同时启动很多个task的程序而言,可以了解哪些任务是否存在阻塞情况。

 
2. 通过覆盖率信息查看任务函数执行完整情况

 
另外通过DT10的覆盖率信息,也可以从侧面了解某个函数或任务是否被执行完整,是否发生阻塞导致任务函数未退出。


26.jpg

 
结论:通过覆盖率测试,一方面,使得在软件测试过程中,可以帮助我们了解测试是否完整,测试用例是否存在遗漏,被测试代码是否存在冗余;另外一方面,覆盖率的信息对于我们调试代码也有一定的帮助;

 
3. 通过DT10的函数执行时间了解更多信息

 
通过DT10的函数执行时间报告,可以知道每个函数的执行时间,并且该执行时间精确到纳秒级,同时可以知道每个函数执行的次数。如下图:


27.jpg

 
我们看到该例程中,vxmain和taskB函数执行次数为1次,执行时间为883us和1116us,而taskA的执行次数为0,执行时间为0。这样对于程序员,即可重点关注taskA是否存在问题。除了本例程中这种情况,在实际程序中,程序中的任务可能会执行成千上万次,如果在DT10的函数执行时间报告中,发现某函数或者任务的某次执行时间特别长,举例,比如某个TaskC,执行次数为9999次,其中9998次的执行时间都为1ms左右,另外有一次执行时间却为30s,那么就需要仔细查看这一次30s函数执行过程是什么逻辑路径。通过DT10可以看到每次代码执行路径,从而精确查看为什么本次执行时间为30s,是否存在问题。这种情况在多任务程序中经常碰到,比如某个低优先级的任务可能被其它高优先级的任务阻塞,从而导致该任务执行时间变长。

 
结论:通过DT10了解每个函数执行时间,执行次数,以及每次执行过程中代码执行逻辑路径,从而帮助用户更细致的了解代码执行情况,帮助用户定位和分析代码中隐藏的bug。

 
上面重点对dead_lock.c的结果重点进行分析了,对于multi_task.c的分析类似。有兴趣试用并了解DT10的朋友可以联系我们申请试用。