问题描述
在我的应用程序中,我具有一个从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) {
这完全让我感到难过-我在这里看不到任何阅读或写作...
hDataSpace
和hDataSet
是从打开的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或增大线程堆栈的大小或在堆上分配,我认为您的问题将不复存在。