Valgrind将函数标头指示为无效写入的位置

问题描述

在我的应用程序中,我具有一个从HDF5文件读取数据的功能 valgrind 报告了许多无效的读写操作,其中第一个

 ==17899== Invalid write of size 8
 ==17899==    at 0x6BD617: SPopulation<NPersAgent>::readAgentDataQDF(long,long,long) (SPopulation.cpp:1695)
 ==17899==    by 0x7A02DC: PopReader::read(PopBase*,char const*,int,bool) (PopReader.cpp:164)
 ==17899==    by 0x1B358D: SimParams::setPops(long,bool) (SimParams.cpp:1386)
 ==17899==    by 0x1B3332: SimParams::setPops(char const*) (SimParams.cpp:1351)
 ==17899==    by 0x1B2FFC: SimParams::setPopList(char*) (SimParams.cpp:1294)
 ==17899==    by 0x1B057A: SimParams::readOptions(int,char**) (SimParams.cpp:488)
 ==17899==    by 0x1A02EB: main (QHGMain.cpp:67)
 ==17899==  Address 0x1ffedb5248 is on thread 1's stack
 ==17899==  in frame #0,created by SPopulation<NPersAgent>::readAgentDataQDF(long,long) (SPopulation.cpp:1695)

接下来有70多个其他无效读/写,全部使用同一方法。其中一些位于同一位置(线程1的堆栈上具有不同的地址),另一些位于诸如hsize_t iOffset = 0;之类的赋值行或诸如compactData();之类的具有函数调用的行中。在HDF5函数中的无效读取之后,列出了这些奇怪的位置:

herr_t status = H5Sget_simple_extent_dims(hDataSpace,&dims,NULL);

({dims在此之前已设置为0)

“ SPopulation.cpp:1695”行是方法的开头:

template<typename T>
int  SPopulation<T>::readAgentDataQDF(hid_t hDataSpace,hid_t hDataSet,hid_t hAgentType) {

这完全让我感到难过-我在这里看不到任何阅读或写作...

hDataSpacehDataSet是从打开的HDF5文件创建的:

hid_t hDataSet = H5Dopen2(m_hSpeciesGroup,AGENT_DATASET_NAME,H5P_DEFAULT);
hid_t hDataSpace = H5Dget_space(hDataSet);

hAgentType的创建方式如下:

hid_t hAgentDataType = H5Tcreate (H5T_COMPOUND,agentRealSizeQDF());
T ta;
H5Tinsert(hAgentDataType,LIFE_STATE,qoffsetof(ta,m_iLifeState),H5T_NATIVE_INT);
H5Tinsert(hAgentDataType,CELL_INDEX,m_iCellIndex),AGENT_ID,m_ulID),H5T_NATIVE_LONG);
...

随着程序从文件中正确读取数据,这些值似乎是有序的。

  • valgrind 函数标头指示为无效写入的位置是什么意思?

  • 对于简单的作业,我该如何解释无效的读物?

编辑: 这是函数体:

1694:template<typename T>
1695:int  SPopulation<T>::readAgentDataQDF(hid_t hDataSpace,hid_t hAgentType) {
1696:
1697:    int iResult = 0;
1698:    T aBuf[ABUFSIZE];
1699:    hsize_t dims = 0;
1700:    herr_t status = H5Sget_simple_extent_dims(hDataSpace,NULL);
1701:    hsize_t iOffset = 0;
1702:    hsize_t iCount  = 0;
1703:    hsize_t iStride = 1;
1704:    hsize_t iBlock  = 1;
1705:
1706:    compactData();
1707:
1708:    updatetotal();
1709:
1710:    while ((iResult == 0) && (dims > 0)) {
1711:        if (dims > ABUFSIZE) {
1712:            iCount = ABUFSIZE;
1713:        } else {
1714:            iCount = dims;
1715:        }
1716:
1717:        // read a buffer full
1718:        hid_t hMemSpace = H5Screate_simple (1,&iCount,NULL); 
1719:        status = H5Sselect_hyperslab(hDataSpace,H5S_SELECT_SET,1720:                                     &iOffset,&iStride,&iBlock);
1721:        status = H5Dread(hDataSet,hAgentType,hMemSpace,1722:                     hDataSpace,H5P_DEFAULT,aBuf);
1723:        if (status >= 0) {
1724:
1725:            uint iFirstIndex = m_pAgentController->reserveSpace2((uint)iCount);
1726:            m_aAgents.copyBlock(iFirstIndex,aBuf,(uint)iCount);
1727:            for (uint j =0; j < iCount; j++) {
1728:                if (aBuf[j].m_ulID > m_iMaxID) {
1729:                    m_iMaxID = aBuf[j].m_ulID;
1730:                }
1731:            }
1732: 
1733:            dims -= iCount;
1734:            iOffset += iCount; 
1735:
1736:        } else {
1737:            iResult = -1;
1738:        }
1739:    }
1740:
1741:    updatetotal();
1742:
1743:    updateNumAgentsPerCell();
1744:
1745:    return iResult;
1746:}

valgrind 指示为错误的行是1695、1697、1699、1700、1701、1702、1703、1704、1706、1708、1710、1711、1712、1714、1718、1719, 1721、1723、1725、1726、1727、1728、1729、1733、1734、1741、1743和1745。也就是说,除了仅包含方括号或} else {的行以外,几乎每行都是这样。

Edit2:围绕readAGentDataQDF调用的PopReader代码 Edit3:PopReader的正确片段

int PopReader::read(PopBase *pPB,const char *pSpeciesName,int iNumCells,bool bRestore) {
    int iResult = -1;    

    //    printf("reading data for [%s]\n",pSpeciesName);
    m_hSpeciesGroup = qdf_openGroup(m_hPopGroup,pSpeciesName);

    if (iResult == 0) {
        // set the handles
        hid_t hAgentType = pPB->getAgentQDFDataType();
        hid_t hDataSet = H5Dopen2(m_hSpeciesGroup,H5P_DEFAULT);
        hid_t hDataSpace = H5Dget_space(hDataSet);
            

        iResult = pPB->readAgentDataQDF(hDataSpace,hDataSet,hAgentType);
                if (iResult == 0) {
....

 

解决方法

好吧,这是一个假想,而不是一个答复,但无论如何它都不适合发表评论。

看看提供的代码,似乎这些行:

hid_t hDataSet = H5Dopen2(m_hSpeciesGroup,AGENT_DATASET_NAME,H5P_DEFAULT);
hid_t hDataSpace = H5Dget_space(hDataSet);
            

iResult = pPB->readAgentDataQDF(hDataSpace,hDataSet,hAgentType);

由编译器以某种方式实现(优化以删除无用的本地变量):

        iResult = pPB->readAgentDataQDF(H5Dget_space(hDataSet),hDataSet = H5Dopen2(m_hSpeciesGroup,H5P_DEFAULT),hAgentType);

然后,我怀疑H5Dopen2正在执行错误的写操作,并且我怀疑valgrind没有符号可以更好地了解它。

因此,如果我是您,我将尝试使用调试符号(包括H5D库)(可能带有-O0 -ggdb3标志)以尽可能低的优化级别编译此代码。

如果这不可能,只需在第1700行添加一个return 0;并检查valgrind是否大喊大叫,如果不​​是请尝试在1706行,依此类推,直到该方法大喊大叫为止。

此外,在Valgrind中,您可以选择在发生此类错误时启动/附加GDB实例,以便检查无效写入地址所指的内容,但是您需要使用以下非优化的构建调试信息对此毫无意义。 用--vgdb=full --vgdb-error=0运行valgrind并在另一个窗口中运行gdb /path/to/your/elf/file,然后在GDB控制台中附加target remote <insert what valgrind tells you here>

编辑:我已经重新阅读了valgrind的全部信息,并且我想我已经错过了一个非常重要的潜在错误:

==17899==  Address 0x1ffedb5248 is on thread 1's stack
 ==17899==  in frame #0,created by SPopulation<NPersAgent>::readAgentDataQDF(long,long,long) (SPopulation.cpp:1695)

方法中有此行:

1698:    T aBuf[ABUFSIZE];

我很确定ABUFSIZE * sizeof(T)大于您的线程堆栈大小。然后,在进入方法后,编译器将所有方法的本地数据推送到堆栈上,但是由于堆栈太小,最终会导致... ahem 堆栈溢出。

减小ABUFSIZE或增大线程堆栈的大小或在堆上分配,我认为您的问题将不复存在。