这个valgrind错误是什么意思?

6
我有一个基本图书馆,用于绘制OpenGL文本,每当我使用valgrind确保它是空气紧密的时候,我总是会遇到一个异常错误,看起来像是linux c ++库有问题。 我想看看你们能否发现我的错误或者验证我所担心的,那就是我的c ++库有问题,需要替换。 代码非常简单,但它同时使用了OpenGL和FreeImage,因此某些行可能看不懂。
以下是fontsystem.h:
#ifndef FONTSYSTEM_H
#define FONTSYSTEM_H

/*
    The Font System works by loading all the font images (max image size 32px^2) into memory and storing
  the OpenGL texture ID's into an array that can be access at all times. The DrawString() functions will
  search through the string for the specified character requested to draw and then it will draw a quad
  and paint the texture on it. Since all the images are pre-loaded, no loading of the images at load time
  is necessary, this is memory consuming but efficiant for the CPU. ALL functions WILL return a character
  string specifing errors or success. A function will work as long as it can and when an error happens,
  unless the error is fatal, the functions will NOT rollback changes! This ensures that during testing, a
  very certain bug can be spotted.
*/

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <assert.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <FreeImage.h>

#define REPORT(x) (std::cout<<x)
#define TIME clock()

class CFont
{
public:
    CFont();
    ~CFont();

    void DrawString(char *apString, int aiLetterSize, int aiX, int aiY);
    void DrawString(long anNumber, int aiLetterSize, int aiX, int aiY);
    void SetPath(char avPath[]);
    void SetupFont(); // This function will load as many images as possible into memory.

private:
    GLuint *mpTextIDs;
    int *mpDrawIDs;
    char *mpPath;

public:
    CFont(const CFont& a):
        mpTextIDs(a.mpTextIDs),
        mpDrawIDs(a.mpDrawIDs),
        mpPath(a.mpPath)
    {
        std::copy(a.mpTextIDs, a.mpTextIDs+128, mpTextIDs);
        std::copy(a.mpDrawIDs, a.mpDrawIDs+128, mpDrawIDs);
        std::copy(a.mpPath, a.mpPath + strlen(a.mpPath), mpPath);
    }

    CFont& operator=(const CFont& a)
    {
        GLuint *iTmpTex = new GLuint[128];
        int *iTmpDraw = new int[1024];
        char *vTmpPath = new char[4096];

        delete[] mpTextIDs;
        delete[] mpDrawIDs;
        delete[] mpPath;

        std::copy(a.mpTextIDs, a.mpTextIDs+128, iTmpTex);
        std::copy(a.mpDrawIDs, a.mpDrawIDs+128, iTmpDraw);
        std::copy(a.mpPath, a.mpPath + strlen(a.mpPath), vTmpPath);

        mpTextIDs = iTmpTex;
        mpDrawIDs = iTmpDraw;
        mpPath = vTmpPath;

        return *this;
    }
};

#endif // FONTSYSTEM_H

这里是fontsystem.cpp:

#include "fontsystem.h"

CFont::CFont()
{
    mpTextIDs = new GLuint[128];
    mpDrawIDs = new int[1024];
    mpPath = new char[4096];
}

CFont::~CFont()
{
    delete[] mpTextIDs;
    delete[] mpDrawIDs;
    delete[] mpPath;
}

void CFont::DrawString(char *apString, int aiLetterSize, int aiX, int aiY)
{
    // Sanity check!
    if(apString == NULL)
    {
        REPORT("{Gfx}["<< TIME<< "]Error: Drawing string is NULL! <Font System>\n");
        return;
    }

    if(aiLetterSize <= 0)
    {
        REPORT("{Gfx}["<< TIME<< "]Error: Letter size is less than zero! <Font System>\n");
        return;
    }

    // Search the string from most significant character to least significant.
    int iSelectIndex = 0;
    int iNumOfSymb = 0;
    for(size_t i = 0; apString[i] != '\0'; ++i)
    {
        iSelectIndex = apString[i] >= '0' && apString[i] <= '9' ? (apString[i] - '0') :
                       apString[i] >= 'A' && apString[i] <= 'Z' ? (apString[i] - 'A' + 10) :
                       apString[i] >= 'a' && apString[i] <= 'z' ? (apString[i] - 'a' + 10) :
                       apString[i] == ' ' ? 36 : // This is a special case, This see's if the current character is a space or not.
                       -1;

        if(iSelectIndex == -1)
        {
            return;
        }

        // Add the current selected character to the drawing array.
        mpDrawIDs[i] = iSelectIndex;
        ++iNumOfSymb;
    }

    // Go through and draw each and every character.
    for(size_t i = 0; apString[i] != '\0'/*static_cast<size_t>(iNumOfSymb)*/; ++i)
    {
        // Paint each qaud with the X,Y coordinates. After each quad has been successfully drawn,
        // Add the size to the X coordinate. NOTE: Each character is square!!!

        glBindTexture(GL_TEXTURE_2D, mpTextIDs[(uint)apString[i]]);

        int yPos = apString[i] != 'q' || apString[i] != 'j' || apString[i] != 'y' ? aiY : aiY + (aiLetterSize/2);

        glBegin(GL_QUADS);
            glTexCoord2d(0, 0);
            glVertex2d(aiX, yPos);

            glTexCoord2d(1, 0);
            glVertex2d(aiX + aiLetterSize, yPos);

            glTexCoord2d(1, 1);
            glVertex2d(aiX + aiLetterSize, yPos + aiLetterSize);

            glTexCoord2d(0, 1);
            glVertex2d(aiX, yPos + aiLetterSize);
        glEnd();

        // Now, increase the X position by the size.
        aiX += aiLetterSize;
    }
}

void CFont::DrawString(long anNumber, int aiLetterSize, int aiX, int aiY)
{
    // Sanity Check!
    if(aiLetterSize <= 0)
    {
        REPORT("{Gfx}["<< TIME<< "]Error: Letter size is less than zero! <Font System>\n");
        return;
    }

    // Convert the supplied number to a character string via snprintf().
    char *vTempString = new char[1024];
    snprintf(vTempString, 1024, "%ld", anNumber);

    // Next, run DrawString().
    DrawString(vTempString, aiLetterSize, aiX, aiY);
}

void CFont::SetupFont()
{
    // First Load The PNG file holding the font.
    FreeImage_Initialise(false);

    FIBITMAP *spBitmap = FreeImage_Load(FIF_PNG, mpPath, BMP_DEFAULT);

    if(!spBitmap)
    {
        REPORT("{Gfx}["<< TIME<< "]Error: Was Unable to opne/decode font bitmap! <FreeImage>\n");
        return;
    }

    // Do an image sanity check.
    if(!FreeImage_HasPixels(spBitmap))
    {
        REPORT("{Gfx}["<< TIME<< "]Error: The font bitmap contains nothing! <FreeImage>\n");
        return;
    }

    // Retrieve all the image data from FreeImage.
    unsigned char *pData = FreeImage_GetBits(spBitmap);
    int iWidth = FreeImage_GetWidth(spBitmap);
    int iHeight = FreeImage_GetHeight(spBitmap);
    size_t const ciCharWidth = iHeight;
    size_t const ciCharHeight = iHeight;

    // Cutup the PNG.
    int iFontElementSize = (ciCharWidth*ciCharHeight)*4; // The first two numbers, are the dimensions fo the element, the last number (4) is the number of color channels (Red Green Blue and Alpha)
    unsigned char *pElemBuff = new unsigned char[iFontElementSize]; // The temporary element buffer.

    // Create all 37 OpenGL textures. 0-9 and A-Z and finally space (' ')
    glGenTextures(128, mpTextIDs);

    for (size_t iCharIdx = 0; 128 > iCharIdx; ++iCharIdx)
    {
        // Create character texture.
        size_t const ciScanOfst = ciCharWidth * iCharIdx * 4;
        for (size_t iScanLineIdx = 0; ciCharHeight > iScanLineIdx; ++iScanLineIdx)
        {
            memcpy(pElemBuff + ciCharWidth * iScanLineIdx * 4,
                   pData + ciScanOfst + iWidth * (ciCharHeight - iScanLineIdx - 1) * 4,
                   ciCharWidth * 4);
        }

        // Create The OpenGL Texture with the current Element.
        glBindTexture(GL_TEXTURE_2D, mpTextIDs[iCharIdx]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, pElemBuff);

        // Create the correct texture environment to the current texture.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
    }

    // Do a little house cleaning!
    delete[] pElemBuff;
    FreeImage_Unload(spBitmap);
    FreeImage_DeInitialise();

    REPORT("{Gfx}["<< TIME<< "]Information: Font was created succesfully! <Font System>\n");
}

void CFont::SetPath(char avPath[])
{
    mpPath = avPath;
}

Valgrind 报告如下:

Starting the FontSystem...
FontSystem Started!
==5058== Invalid free() / delete / delete[] / realloc()
==5058==    at 0x402A8DC: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5058==    by 0x8048F03: CFont::~CFont() (fontsystem.cpp:14)
==5058==    by 0x8048DE0: main (main.cpp:13)
==5058==  Address 0x804972f is not stack'd, malloc'd or (recently) free'd
==5058== 
==5058== 
==5058== HEAP SUMMARY:
==5058==     in use at exit: 4,172 bytes in 3 blocks
==5058==   total heap usage: 153 allocs, 151 frees, 135,457 bytes allocated
==5058== 
==5058== 20 bytes in 1 blocks are still reachable in loss record 1 of 3
==5058==    at 0x402A5E6: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5058==    by 0x64DB3AD: _dlerror_run (dlerror.c:142)
==5058==    by 0x444EC64: ??? (in /usr/lib/libGL.so.295.59)
==5058== 
==5058== 56 bytes in 1 blocks are still reachable in loss record 2 of 3
==5058==    at 0x402BE68: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5058==    by 0x442860E: ??? (in /usr/lib/libGL.so.295.59)
==5058==    by 0xBE872481: ???
==5058==    by 0x4E45504E: ???
==5058== 
==5058== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==5058==    at 0x402B454: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5058==    by 0x8048EAC: CFont::CFont() (fontsystem.cpp:7)
==5058==    by 0x8048D68: main (main.cpp:7)
==5058== 
==5058== LEAK SUMMARY:
==5058==    definitely lost: 4,096 bytes in 1 blocks
==5058==    indirectly lost: 0 bytes in 0 blocks
==5058==      possibly lost: 0 bytes in 0 blocks
==5058==    still reachable: 76 bytes in 2 blocks
==5058==         suppressed: 0 bytes in 0 blocks
==5058== 
==5058== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

我希望你们能帮助我解决这个问题,因为这些错误似乎在我所有的程序中都很常见。作为一名程序员,我不喜欢那些微妙但又讨厌的错误。

2个回答

7
最显著的问题是这个:
==5058== Invalid free() / delete / delete[] / realloc()
==5058==    at 0x402A8DC: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5058==    by 0x8048F03: CFont::~CFont() (fontsystem.cpp:14)
==5058==    by 0x8048DE0: main (main.cpp:13)
==5058==  Address 0x804972f is not stack'd, malloc'd or (recently) free'd
==5058== 

正如您所看到的,它是指fontsystem.cpp的第14行,在CFont的析构函数中,如下:

delete[] mpPath;

看起来你试图 delete [] 一个从未被动态分配过的变量,或者没有按正确的方式分配。这怎么可能呢?构造函数中对应的语句看起来很好:

mpPath = new char[4096];

因此,在构造函数调用后,mpPath的值必须在某处更改过。确实,有这个函数:

void CFont::SetPath(char avPath[])
{
    mpPath = avPath;
}

您可能在某个地方调用了这个功能,试图将一个栈分配的数组传递给它。您不应该这样做。如果 CFont 负责分配和释放内存,那么就不应该有一个函数试图接受一个外部数组并将成员指针设置为它。请考虑其他替代方案,例如将 avPath 数组的副本制作到新分配的堆数组中,并释放先前分配的数组。更好的方法是使用 std::vector 或智能指针。

另一个问题与此相关:

==5058== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==5058==    at 0x402B454: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5058==    by 0x8048EAC: CFont::CFont() (fontsystem.cpp:7)
==5058==    by 0x8048D68: main (main.cpp:7)

这是与同一成员变量mpPath相关的内存泄漏问题。当然,这个泄漏是因为setPath在重置指针时没有释放现有数组而发生的。和上面的问题一样。

其余的消息都与各种外部库调用有关。小的内存泄漏可能不太需要您进行处理。


原来是函数 void CFont::SetPath(char*) 导致了错误。有趣的是,这样微小的事情竟然会导致如此奇怪和难以理解的错误。我现在已经将其更改为:`void CFont::SetPath(char *avPath) { assert(avPath != NULL);if(mpPath == NULL) { mpPath = new char[4096]; } std::copy(avPath, avPath+strlen(avPath), mpPath);}` - user1787379
是的,这听起来是正确的(假设avPath字符串永远不会超过4096个字符,包括终端\x0)。也许不用说,最好的解决方案是放弃字符数组,改用std::string - jogojapan

2
你所丢失的4096字节可能来自这里:
void CFont::SetPath(char avPath[])
{
    mpPath = avPath;
}

你只是在复制指针(并覆盖旧的指针);你需要使用strcpy或者在代码的其他部分中使用std::copy。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接