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) {
这让我很困惑——我看不到这里有任何阅读或写作……
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, qoffsetof(ta, m_iCellIndex), H5T_NATIVE_INT);
H5Tinsert(hAgentDataType, AGENT_ID, qoffsetof(ta, m_ulID), H5T_NATIVE_LONG);
...
这些值似乎是有序的,因为程序正确地从文件中读取数据。
当valgrind将函数头指示为无效写入位置时,这意味着什么?
如何解释简单赋值的无效读取?
编辑:
这是函数体:
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 或增大线程堆栈大小或在堆上进行分配,我认为您的问题将会消失。
在我的应用程序中,我有一个从 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) {
这让我很困惑——我看不到这里有任何阅读或写作……
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, qoffsetof(ta, m_iCellIndex), H5T_NATIVE_INT);
H5Tinsert(hAgentDataType, AGENT_ID, qoffsetof(ta, m_ulID), H5T_NATIVE_LONG);
...
这些值似乎是有序的,因为程序正确地从文件中读取数据。
当valgrind将函数头指示为无效写入位置时,这意味着什么?
如何解释简单赋值的无效读取?
编辑: 这是函数体:
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 或增大线程堆栈大小或在堆上进行分配,我认为您的问题将会消失。