Search code examples
cmemory-leaksvalgrindgetline

valgrind report memory loss using getline function


I am writing a grep program in C. I am using getline() inside a while loop to get all lines from stream (file or stdin). The lines are stored into char *lineText buffer inside a struct i defined, named lineInText.
unfortunately even though I am freeing this char *lineText at the end of the loop i still get a valgrind report says that there is a memory loss using getline().
What am I doing wrong?

the code:
the struct of line and its related functions:

typedef struct lineInText
{
    char *lineText;
    int indexOfLine;
    int numOfBytesFromStartToHere;
    bool isMatchInLine;
    bool isMatchInLineFromA;
} lineInText;


void initializeCurrentLine(lineInText *currentLine)
{
    currentLine->lineText = NULL;
    currentLine->numOfBytesFromStartToHere=0;
    currentLine->isMatchInLineFromA = false;
    currentLine->isMatchInLine = false;
    currentLine->indexOfLine = 0;
}

void FillLineStruct(lineInText *currentLine, int lineIndex, int numOfBytes) {
    currentLine->indexOfLine = lineIndex;
    currentLine->isMatchInLine = false;
    currentLine ->isMatchInLineFromA = false;
    currentLine->numOfBytesFromStartToHere = numOfBytes;
}

void freeLine(lineInText **line)
{
    free((*line)->lineText);
    free(*line);
}

the main() and the functions calling getline():

void receiveAndExecute(parsedCommandStruct *parsedCommand, FILE **stream)
{
    ssize_t lineSize = ZERO;
    lineInText *currentLine = NULL;
    int lineIndex = 1, counterForC = 0, linesAfterMatchCounter = 0, sumOfBytes = 0;
    currentLine = (lineInText *) malloc(sizeof(lineInText));
    initializeCurrentLine(currentLine);

    while (1)
    {
        readLine(stream, &lineSize, currentLine, lineIndex);
        FillLineStruct(currentLine, lineIndex, sumOfBytes);
        sumOfBytes = (int)lineSize + sumOfBytes;
        lineIndex++;
        if(lineSize<0)
            break;
        reportLineMatch(currentLine, *parsedCommand, &linesAfterMatchCounter);
        printLineToOutput(currentLine, parsedCommand, &counterForC, false);
    }
    printLineToOutput(currentLine, parsedCommand, &counterForC, true);
    freeLine(&currentLine);
}

int main(int argc, char* argv[])
{
    parsedCommandStruct *parsedCommand = NULL;
    FILE *filePtr = NULL;
    bool useFile = false;
    useFile = isUsingFile(argc, argv);
    createAndFillCommand(argc, argv, &parsedCommand);

    if (useFile)
    {
        filePtr = openFile(argv[argc-1]);
        receiveAndExecute(parsedCommand, &filePtr);
        fclose(filePtr);
    }
    else
    {
        receiveAndExecute(parsedCommand, &stdin);
    }
    freeParsedCommandStruct(parsedCommand);
    free(parsedCommand);
    return 0;
}

void readLine(FILE **stream, ssize_t *getLineResult, lineInText *currentLine,  int lineIndex) {
    ssize_t lineSize = ZERO;
    size_t lineBufSize = ZERO;
    lineSize = getline(&(currentLine->lineText), &lineBufSize, *stream);
    *getLineResult = lineSize;
}

the valgrind report:

valgrind --quiet --leak-check=yes ./my_grep bla bla | diff bla -==1878== 120 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1878==    at 0x402C17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==1878==    by 0x40A9FD7: getdelim (iogetdelim.c:62)
==1878==    by 0x40A6EB1: getline (getline.c:32)
==1878==    by 0x8048E79: readLine (in /home/user/Downloads/my_grep/my_grep)
==1878==    by 0x80493EF: receiveAndExecute (in /home/user/Downloads/my_grep/my_grep)
==1878==    by 0x804952A: main (in /home/user/Downloads/my_grep/my_grep)
==1878==

thanks in advance


Solution

  • When you call getline, lineBufSize has a value of 0. This means the function thinks the buffer is 0 bytes in length. This is OK for the first iteration but on subsequent iterations is results in the existing pointer stored in currentLine->lineText getting thrown out, causing the memory leak.

    You need to add a field to your struct to keep track of the current size of the buffer and pass that to getline:

    typedef struct lineInText
    {
        char *lineText;
        int lineTextLen;    // keep track of the size of lineText
        int indexOfLine;
        int numOfBytesFromStartToHere;
        bool isMatchInLine;
        bool isMatchInLineFromA;
    } lineInText;
    
    void initializeCurrentLine(lineInText *currentLine)
    {
        currentLine->lineText = NULL;
        currentLine->lineTextLen = 0;   // initialize buffer length to 0
        currentLine->numOfBytesFromStartToHere=0;
        currentLine->isMatchInLineFromA = false;
        currentLine->isMatchInLine = false;
        currentLine->indexOfLine = 0;
    }
    
    void readLine(FILE **stream, ssize_t *getLineResult, lineInText *currentLine,  int lineIndex) {
        ssize_t lineSize = ZERO;
        lineSize = getline(&(currentLine->lineText), &(currentLine->lineTextLen), *stream);
        *getLineResult = lineSize;
    }