Davinci 开发中的“灵异事件”

在进行Codec Engine 服务器端的开发时,有时会出现一些莫名其妙的现象,在模拟器上明明跑得好好地一段程序,移植到开发板上愣是出现一堆错误。这些错误,和GPP端不同:它们没有错误信息、不会自动退出、甚至连Trace信息也不打印。我把这些都称为“灵异事件”。当然,这些事件的产生还是归咎于代码本身的缺陷或不周全。以下问题是我在开发Davinci应用程序中遇见的,摘录下来,供大家参考。

链接时提示符号未定义

这个问题发生在Sever端集成时,无论Lib、还是Codec在各自的机器上都编译通过了,唯独集成在一起时,确出现了以下集成错误信息:

undefined                        first referenced
 symbol                              in file
---------                        ----------------
_LITTLE_B2D                      /prj/common/lib/libaes.a
_LITTLE_D2B                      /prj/common/lib/libaes.a
 
打开libaes项目,发现LITTLE_B2D、LITTLE_D2B定义在头文件中:
 
#undef BIG_ENDIAN
#undef LITTLE_ENDIAN
 
#if defined(USER_BIG_ENDIAN)
    #defineBIG_ENDIAN
#elif defined(USER_LITTLE_ENDIAN)
    #defineLITTLE_ENDIAN
#else
    #if 0
        #define BIG_ENDIAN       //    Big-Endian machine withpointer casting
    #elif defined(_MSC_VER)
        #defineLITTLE_ENDIAN   //   Little-Endian machine with pointer casting
    #else
  //        #error
    #endif
#endif
 
#if defined(BIG_ENDIAN)            Big-Endian machine
    #defineBIG_B2D(B, D)        D = *(XDAS_Int32*)(B)
    #define BIG_D2B(D, B)        *(XDAS_Int32 *)(B) = (XDAS_Int32)(D)
    #define LITTLE_B2D(B, D)    D = ENDIAN_REVERSE_DWORD(*(XDAS_Int32*)(B))
    #define LITTLE_D2B(D, B)    *(XDAS_Int32 *)(B) =ENDIAN_REVERSE_DWORD(D)
#elif defined(LITTLE_ENDIAN)        Little-Endianmachine
    #define BIG_B2D(B, D)         D = ENDIAN_REVERSE_DWORD(*(XDAS_Int32 *)(B))
    #define  BIG_D2B(D, B)        *(XDAS_Int32 *)(B) = ENDIAN_REVERSE_DWORD(D)
    #define  LITTLE_B2D(B, D)     D = *(XDAS_Int32*)(B)
    #define  LITTLE_D2B(D, B)    *(XDAS_Int32 *)(B) = (XDAS_Int32)(D)
#else
    //ERROR()
#endif
 
原来这是因为我们在编译时,忘了添加USER_BIG_ENDIAN或USER_LITTLE_ENDIAN等宏。在CCS的Build Option选项中,加上即可:

没有运行DSP端程序
 
错误描述
 
这个问题发生在GPP和DSP程序联合测试时,当我试图在GPP端调用DSP端算法时,出现如下Trace信息:
 
ti.sdo.ce.image1.IMGENC1- IMGENC1_process> Enter (handle=0x3d9c8, inBufs=…)
ti.sdo.ce.image1.IMGENC1- IMGENC1_process> Exit (handle=0x3d9c8, retVal=0xFFFFFFFD)
 
错误分析
 
显然,在Trace信息中没有看到任何和DSP有关的消息,DSP端没有运行。IMGENC1_process方法直接返回错误:0xFFFFFFFD(实际上就是-3)。打开头文件xdm.h,发现有一个含糊的定义:
 
#define XDM_EUNSUPPORTED        -3          /**< Request is unsupported. */
 
参考一个正确处理过程的Trace信息:
 
ti.sdo.ce.image1.IMGENC1- IMGENC1_process> Enter (handle=0x3d9c8, inBufs=……)
CV -VISA_allocMsg> Allocating message for messageId=0x000600b2
OM -Memory_getBufferPhysicalAddress> Enter(virtAddr=0x41209000, size=16641)
OM -Memory__getPhysicalAddress> Enter(virtAddr=0x41209000, size=16641)
OM -Memory__getPhysicalAddress> found in cb(Sc=0x41209000, Ec=……)
OM -Memory__getPhysicalAddress> returning physAddr=0x837ac000
OM -Memory_getBufferPhysicalAddress> return (0x837ac000)
OM -Memory_getBufferPhysicalAddress> Enter(virtAddr=0x41229000, size=262144)
@13,515,611us:[+0 T:0x42ac9b60 S:0x42a88eac] CV - VISA_call(visa=0x3d9c8, msg=……
 
说明,这个错误时在stub中返回的。
 
修改IIMGENC1
 
到这一步,不禁想,要是TI提供的imgenc库能打印Trace信息就好了。幸好Codec Engine里,为我们提供了IMGENC的源代码,因此我需要修改源文件,加入Trace模块,重新编译,替换掉TI原有的库就可以。 IMGENC源文件存放在目录:($codecengine安装目录)\packages\ti\sdo\ce\image1文件夹中,这里我需要修改imgenc1_stubs.c。在Visual Studio中打开文件,加入Trace的代码:
 
GT_Maskti_sdo_ce_image1_IMGENC1_curTrace;
BOOLti_sdo_ce_image1_IMGENC1_curTrace_init=FALSE;
 
以及初始化函数:
 
if(!ti_sdo_ce_image1_IMGENC1_curTrace_init)
{
    GT_create(&ti_sdo_ce_image1_IMGENC1_curTrace,"ti.sdo.ce.imgenc1.IMGENC1_STUBS");
    GT_set("ti.sdo.ce.imgenc1.IMGENC1_STUBS=01234567");
    ti_sdo_ce_image1_IMGENC1_curTrace_init=TRUE;
}
 
接下来就可以在需要加入Trace的地方添加命令了。
GT_Trace使用方法可以参考我的文章:使用Generic Trace Support打印调试信息
 
编译
 
由于源文件目录下没有makefile、package.bld文件,我们需要手动添加:
makefile文件,主要是载入xdcpaths.mak、xdcrules.mak等文件
 
PROJECT_ROOTDIR:= $(CURDIR)/../../../..
 
include$(PROJECT_ROOTDIR)/xdcpaths.mak
 
XDC_PATH :=$(PROJECT_ROOTDIR);$(XDC_PATH)
 
include$(PROJECT_ROOTDIR)/buildutils/xdcrules.mak
 
package.bld文件,主要是指定哪些文件需要被编译:
 
Pkg.attrs.exportAll= true;
 
var SRCS =["imgenc1.c", "imgenc1_skel.c","imgenc1_stubs.c"];
 
Pkg.otherFiles= [
    "imgenc1.h"
];
 
for (var i =0; i < Build.targets.length; i++) {
    var targ = Build.targets[i];
 
    Pkg.addLibrary("lib/imgenc1",targ, {
    }).addObjects(SRCS);
 
   Pkg.addLibrary("lib/imgenc1_debug", targ, {
        profile : "debug"
    }).addObjects(SRCS);
}
 
发现错误
 
最后,我发现,原来错误来自于:
 
if ((sizeof(VISA_MsgHeader) + sizeof(*inBufs) + sizeof(*outBufs) +
        inArgs->size +outArgs->size) > sizeof(_IMGENC1_Msg)) {
    return  (IIMGENC1_EUNSUPPORTED);
}
 
原来,我忘记在GPP端为outArgs指定size的值了:
 
outArgs.size =sizeof(outArgs);
 
加上后,错误解决。
莫名奇妙死机
 
问题描述
 
这下程序应该能完美运行了吧,我兴冲冲地传入测试参数,泡杯茶(第一次运行,不知道需要多久),开始等待……
五分钟过去了……怎么还没有结果?应该是计算量大的问题,因为我的程序里用了大量的递归,先去打会儿网球吧
不是吧,都过去了两小时,怎么还是停留在这里?
 
VISA_call(visa=0x3d9c8,msg=0x4115ec80): ……
Comm_put>Enter(queue=0x6, msg=0x4115ec80)
Comm_put>return (0)
Comm_get>Enter(queue=0x10005, msg=0x42a88f14, timeout=-1)
Comm_get>return (0)
Comm_put>Enter(queue=0x0, msg=0x41159c80)
 
强制退出后,DSP居然死机了……
 
问题分析
 
我们知道,DM6446是双核的,GPP和DSP。一个算法接口,在GPP和DSP端分别对应Stub和Skelton。GPP端在Stub中通过调用VISA_call方法来执行DSP端的代码。DSP端完成操作后,在返回。整个过程,是基于一种“消息”传递机制的。
从上面的Trace信息中可以看出:代码已经执行到VISA_call,但是,却没有等到DSP端返回结果,因此处于一种“死机”状态。只是这种状态,也太令人困混了:由于DSP端没有返回任何信息,因此,我们根本看不到DSP端的Trace信息,无法判断到底是哪里出了问题。
我编写代码有个习惯,现在VC上将代码调试通过,然后移植到DSP上。既然代码在VC上能调试成功,说明不是程序的问题。最大的可能是:内存分配错误导致DSP端代码崩溃!基于这点,我检查了代码,发现最有可能的问题是:递归太多。
接下来,我减少了递归的数量,重新编译后,程序通过。
 
修改栈大小
 
在C、C++编程中,堆(Heap)和栈(Stack)是分配内存空间,存放数据的两个重要地点。一般的,使用malloc或new关键字分配的内存空间保存在堆中,而局部变量、或者函数的参数,都是保存在栈中。因此,当一个程序中递归的数量很多时,栈空间往往不够,最终造成内存溢出。
打开项目中的xs文件,修改getStackSize方法,增大栈空间(我这里将原来的1024增加为4096):
 
functiongetStackSize(prog)
{
    if(verbose) {
        print("getting stack size for" + this.$name
            + " built for the target" + prog.build.target.$name
            + ", running on platform" +  prog.platformName);
    }
 
    return (4096);
}
 
或者也可以修改server端的cfg文件:
 
Server.algs =[
    {   name: "enctriangle",
        mod: ENCTRIANGLE,
        groupId: 0,
        threadAttrs: {
            stackSize: 4096,
            stackMemId: 0,
            priority: Server.MINPRI + 1
        }
    },
];
 
无法分配内存
 
又出问题了
 
解决完上述问题,似乎可以松口气,运行程序,编码,一切正常!
怀着期盼的心情,我点击了解码按钮,结果……怎么我的图片少了一大块?经过检查,确定不是解码的问题,看来编码这块儿还是不能让人放心啊!
 
继续分析问题
 
这次比较简单,在编码的关键点加入Trace后,清楚地发现,当系统运行到某个阶段时,
 
pPerson person=(pTFlat)malloc(sizeof(Person));
 
返回NULL指针!
想必是堆空间不够了!
 
堆不够?那就弄大点吧!
 
打开server工程的.cfg文件,睁大眼睛,仔细检查空间分配问题:
 
var mem_ext  = [
{
    comment: "DDRALGHEAP: off-chip memory for dynamic algmemallocation",
    name: "DDRALGHEAP",
    base: 0x82E00000,   // 46MB
    len: 0x00a00000,    // 10MB
    space: "code/data"
},
{
    comment: "DDR2: off-chip memory forapplication code and data",
    name: "DDR2",
    base: 0x83800000,   // 56MB
    len: 0x00600000,    // 6MB
    space: "code/data"
},
{
    comment: "DSPLINK: off-chip memoryreserved for DSPLINK code and data",
    name: "DSPLINKMEM",
    base: 0x83E00000,   // 62MB
    len: 0x00100000,    // 1MB
    space: "code/data"
},
{
    comment: "RESET_VECTOR: off-chipmemory for the reset vector table",
    name: "RESET_VECTOR",
    base: 0x83F00000,   // 63MB
    len: 0x00000080,
    space: "code/data"
},
];
 
怎么可能,我们明明分配了10MB的空间给DDRALGHEAP。继续往下看吧:
 
bios.DDR2.createHeap = true;
bios.DDR2.heapSize = 0x20000; // 128K
 
bios.DDRALGHEAP.createHeap = true;
bios.DDRALGHEAP.heapSize =bios.DDRALGHEAP.len;
 
试着修改bios.DDR2.heapSize的值,改成0×40000。运行程序,错误解决了。
 
为什么是DDR2
 
根据上面的内存分配表,DDR2区域应该是存放算法代码、数据的,不应该具备“堆”的功能,为什么这里修改了bios.DDR2.heapSize却可以回避异常呢?按理来说,应该是DDRALGHEAP才对。再说,当前程序中将bios.DDR2.heapSize设置成0×40000可以,如果将来运算量多,区区256K的堆空间,不一定能用。
继续查看代码,发现cfg中设置如下:
 
bios.setMemCodeSections(prog,bios.DDR2);
bios.setMemDataNoHeapSections(prog,bios.DDR2);
bios.setMemDataHeapSections(prog,bios.DDR2);
 
修改如下:
 
bios.setMemCodeSections(prog,bios.DDR2);
bios.setMemDataNoHeapSections(prog,bios.DDR2);
bios.setMemDataHeapSections(prog,bios.DDRALGHEAP);
 
OK了!
无法更新outbuf
 
问题描述
 
这个问题出现在当对第二幅图像编码完成后,从DSP端传出的数值仍然是第一幅图像的编码结果。
 
分析
 
DM6446是双核处理器,GPP端和DSP端通过CMEM共享内存来传递数据。GPP端是相对地址,DSP端是绝对地址。二者实际上是对应不同的内存块,通过Codec Engine API中的Memory_cacheInv、Memory_cacheWb等函数来转换数据(相当于memcpy)。
因此,如果出现上述问题,肯定是由于在skel层没有执行相应的操作。打开imgenc1_skel.c。找到以下代码:
 
if ((pStatus->data.buf!= NULL) &&
    XDM_ISACCESSMODE_WRITE(pStatus->data.accessMask)) {
 
    Memory_cacheWb(pStatus->data.buf, pStatus->data.bufSize);
 
    /*
     * Since we've cacheWb this buffer, wearguably should
     * reflect this cache state and clear theWRITE bit in
     * the .accessMask field.  However, we know the stub
     * doesn't propogate this field to thecalling app, so
     * this extra buffer management detailisn't necessary:
     *
     *XDM_CLEARACCESSMODE_WRITE(pStatus->data.accessMask);
     */
}
 
不难判断,问题出现在XDM_ISACCESSMODE_WRITE(data.accessMask)这里。
 
解决问题
 
原来,在DSP端的Skel中,Memory API通过查看data.accessMask来判断该段内存是否被写过,如果写过,才将它转化成GPP端的内存,否则,跳过。
打开Codec实现文件,在更新完outBufs数据后,修改outBufs的accessMask:
 
/* report _how_we accessed the 2 data buffers */
XDM_CLEARACCESSMODE_WRITE(inBufs->descs[0].accessMask);
XDM_SETACCESSMODE_READ(inBufs->descs[0].accessMask);
 
XDM_CLEARACCESSMODE_READ(outBufs->descs[0].accessMask);
XDM_SETACCESSMODE_WRITE(outBufs->descs[0].accessMask);

http://newinfo.sysu.edu.cn/Snowwaft/?p=272