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

Valgrind indicates function header as location of invalid write

在我的应用程序中,我有一个从 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, char const*, 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, long) (SPopulation.cpp:1695)

后面还有70多个无效reads/writes,都是同一个方法。其中一些位于同一位置(在线程 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,  qoffsetof(ta, m_iCellIndex), H5T_NATIVE_INT);
H5Tinsert(hAgentDataType, AGENT_ID,    qoffsetof(ta, m_ulID),       H5T_NATIVE_LONG);
...

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

编辑: 这是函数体:

1694:template<typename T>
1695:int  SPopulation<T>::readAgentDataQDF(hid_t hDataSpace, hid_t hDataSet, 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, &dims, 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, &iCount, &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 { 的行之外,几乎每一行。 =25=]

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, AGENT_DATASET_NAME, 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, AGENT_DATASET_NAME, H5P_DEFAULT), hAgentType);

然后,我怀疑是H5Dopen2写错了,而且我怀疑valgrind没有符号可以更好地了解它。

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

如果这不可能,只需在第 1700 行添加一个 return 0; 并检查 valgrind 是否大喊大叫,如果没有,请在第 1706 行尝试为该方法中调用的每个函数等等,直到它大喊大叫。

此外,您可以在 Valgrind 中选择 start/attach GDB 实例,这样您就可以检查 invalid write 地址指的是什么,但是您'您将需要一个带有调试信息的非优化构建,这才有意义。 运行 在另一个 window 中使用 --vgdb=full --vgdb-error=0 和 运行 gdb /path/to/your/elf/file valgrind,然后在 GDB 控制台中使用 target remote <insert what valgrind tells you here> 附加。

编辑:我已经 re-read 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 或增大线程堆栈大小或在堆上进行分配,我认为您的问题将会消失。