Logfind Attempt, Memory Leaks?

Below is my attempt at the logfind project from exercise number 26. It seems to be working, but I’m wondering if there’s any way to make it smaller or more efficient… I’d really appreciate any suggestions.

The other problem is that Valgrind says there are two memory leaks, but I’m pretty sure I freed everything. Any ideas where this is occurring?

==16550== Memcheck, a memory error detector
==16550== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16550== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16550== Command: ./logfind felix
==16550== 
[/etc/testlogs/logwithfelix.log] [010101] Felix is terrible
[/etc/testlogs/logwithfelix.log] [020202] Felix is awesome
==16550== 
==16550== HEAP SUMMARY:
==16550==     in use at exit: 134 bytes in 3 blocks
==16550==   total heap usage: 16 allocs, 13 frees, 48,030 bytes allocated
==16550== 
==16550== 21 bytes in 1 blocks are definitely lost in loss record 1 of 2
==16550==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16550==    by 0x108E8D: get_file (logfind.c:32)
==16550==    by 0x1093E1: main (logfind.c:125)
==16550== 
==16550== 113 bytes in 2 blocks are definitely lost in loss record 2 of 2
==16550==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16550==    by 0x108E8D: get_file (logfind.c:32)
==16550==    by 0x109079: do_finding (logfind.c:68)
==16550==    by 0x109308: do_globbing (logfind.c:113)
==16550==    by 0x1093FC: main (logfind.c:127)
==16550== 
==16550== LEAK SUMMARY:
==16550==    definitely lost: 134 bytes in 3 blocks
==16550==    indirectly lost: 0 bytes in 0 blocks
==16550==      possibly lost: 0 bytes in 0 blocks
==16550==    still reachable: 0 bytes in 0 blocks
==16550==         suppressed: 0 bytes in 0 blocks
==16550== 
==16550== For counts of detected and suppressed errors, rerun with: -v
==16550== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
#define _GNU_SOURCE // Needed to get strcasestr()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <glob.h>
#include "dbg.h"

#define HOME "/home/felix"

int is_empty(const char *string) {
    int i = 0;

    for (i = 0; i < strlen(string); i++) {
        if (!isspace(string[i])) return 0;
    }

    return 1;
}

char *get_file(char *location) {
    if (is_empty(location)) return "";

    FILE *file = fopen(location, "rb");
    check(file, "The file %s couldn't be opened", location);

    fseek(file, 0, SEEK_END);
    long size = ftell(file); // Get size of memory needed to store file content
    rewind(file);

    char *content = malloc(size + 1); // Allocate memory for file content
    check_mem(content);

    fread(content, size, 1, file); // Read file into memory buffer
    fclose(file);
    content[size] = '\0';

    return content;
error:
    exit(1);
}

int has_and_or(char *string, int argc, char *argv[]) {
    int i = 0;
    int found = -1;
    int or = 0;

    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-o") == 0) {
            or = 1;
            continue;
        }
        int found_this_time = strcasestr(string, argv[i]) ? 1 : 0;
        if (or) {
            found = found == -1 ? 0 : found;
            found = found || found_this_time;
        } else {
            found = found == -1 ? 1 : found;
            found = found && found_this_time;
        }
    }

    return found;
}

void do_finding(char *file, int argc, char *argv[]) {
    char *logs = get_file(file);
    char *line;

    while ((line = strsep(&logs, "\n"))) {
        if (is_empty(line)) continue;
        if (has_and_or(line, argc, argv)) {
            printf("[");
            printf("\x1B[31m"); // Color red
            printf("%s", file);
            printf("\x1B[0m"); // Color normal
            printf("] ");
            printf("%s\n", line);
        }
    }

    free(logs);
}

char *clean_glob_err(int rc) {
    switch(rc) {
        case GLOB_ABORTED:
            return "Filesystem problem";
        case GLOB_NOMATCH:
            return "No matches";
        case GLOB_NOSPACE:
            return "Out of memory";
        default:
            return "Unknown error";
    }
}

int glob_warn(const char *path, int _) {
    log_warn("Trouble with glob: %s", path);
    return 0;
}

void do_globbing(char *glob_in, int argc, char *argv[]) {
    if (is_empty(glob_in)) return;

    glob_t results;
    int i = 0;
    int rc = glob(glob_in, 0, glob_warn, &results);
    check(rc == 0, "Bad glob: %s (%s)", glob_in, clean_glob_err(rc));

    for (i = 0; i < results.gl_pathc; i++) {
        do_finding(results.gl_pathv[i], argc, argv);
    }

    globfree(&results);
error:
    globfree(&results); // Free the glob results
    return;
}

int main(int argc, char *argv[]) {
    check(argc >= 2, "You must specify at least one search term");

    char *config = get_file(HOME "/.logfind");
    char *glob_in;
    while ((glob_in = strsep(&config, "\n"))) do_globbing(glob_in, argc, argv);
    free(config);

    return 0;
error:
    if (config) free(config);
    return 1;
}

I think that’s pretty decent for now. If you want to try and improve it try handing it garbage. You can use /dev/random to create a garbage file and see what it does.

It actually seems to take garbage surprisingly well:

I didn’t expect that. I’m still trying to figure out the memory leak, though… I’m not totally sure why it’s happening.

Valgrind with lots of options is going to help you find that. I don’t remember them off my head but it should be in the docs.

I tried running with a bunch of options, although the documentation isn’t seeming to help very much. It still reports the memory leaks. I don’t get how I can allocate memory, free it (without any errors, mind you), and then still have a memory leak!

Welp! If you can’t figure out the exact line number in your code where you allocated the RAM then I’m at a loss as to where it’s from. I’d say, unless you can run the offending lines in a massive loop to see if you run out then it’s probably not an important amount of memory and just gets cleaned up when you exit. Try moving on and come back to this later.

I know exactly exactly where I allocate the memory (line 32), It’s just that I also free it on lines 83 and 128, but the memory is still apparently leaking. I’m confuddled.

Yeah, I guess I’ll just move on and come back to this…

I’m way late to this, but I meant to look into it when I completed Logfind myself. I played with the code you posted. Something notable I did: instead of declaring the content variable in get_file I changed it so the variable was created in the block calling get_file then passed in to the function to be allocated/populated. It seemed to better allow each function to control & free it’s own content variable and definitely took care of most of the memory leaks. But in the process I broke something else and the results stopped showing up. Figured instead of showing you my broken code I’d just tell you what I figured out so far. I think it’s possible the pointer to each file string is being lost somewhere and doesn’t reach the free functions. But even I’m having a hell of a time seeing where.

1 Like