#SOLVED: Memory Leak in bufio/reader?

6 messages · Page 1 of 1 (latest)

strong tree
#

Hey all, first time poster...

I'm working on a REPL that parses user commands. Came across a memory leak being reported by the tracking_allocator for when you use bufio.reader_read_string to read an empty line and then trim the resulting line getting a string of length 0.

I've extracted the code out of the application and still does the same:

main :: proc() {
    default := context.allocator
    tracking_allocator: mem.Tracking_Allocator
    mem.tracking_allocator_init(&tracking_allocator, default)
    defer mem.tracking_allocator_destroy(&tracking_allocator)
    context.allocator = mem.tracking_allocator(&tracking_allocator)
    defer print_memory_usage(&tracking_allocator)

    r_buf: [1024]byte
    reader: bufio.Reader
    bufio.reader_init_with_buf(&reader, os.stream_from_handle(os.stdin), r_buf[:])
    defer bufio.reader_destroy(&reader)

    fmt.fprint(os.stdout, "> ")
    line, r_err := bufio.reader_read_string(&reader, '\n', context.allocator)
    if r_err != nil {
        fmt.eprintf("Read Error: %s\n", r_err)
        return
    }
    defer delete(line, context.allocator)

    line = strings.trim_right(line, "\r")
    line = strings.trim_space(line)

    fmt.fprintf(os.stdout, "Echo: %s\n", line)
}

print_memory_usage :: proc(tracking_allocator: ^mem.Tracking_Allocator) {
    if len(tracking_allocator.allocation_map) > 0 {
        fmt.eprintln("Memory Leaks: ")
        for _, entry in tracking_allocator.allocation_map {
            fmt.eprintf(" - Leaked %d @ %v\n", entry.size, entry.location)
        }
    }
}

So if you just press enter at the prompt you get:

>
Echo:
Memory Leaks:
 - Leaked 8 @ /path/to/odin/core/bufio/reader.odin(421:2)

Since this is a REPL won't that add up over time? Does 'delete' ignore empty lines or something?

Thanks

carmine heron
#

You need to preserve the original value of line to delete; in this case it could cause a leak, but it could also cause a bad free (e.g. if you were to start the input with a space, it would delete starting in the middle of the string, which will generally cause issues with allocators

strong tree
#

Riiiight! So strings.trim_* procs give you a slice into the backing string, and delete will only free that area of the original string, leaving the rest of it to be leaked?

carmine heron
#

The trim procs do only give you a view into the original data, they don't allocate a new string. But if the string is all whitespace, then it'll give you back the empty string instead (which points to nothing). So you've replaced the original string in line with one that doesn't point to anything. Calling delete on that doesn't do anything, but you have, in fact, leaked the allocation.

In general, delete will generally only work correctly if you give it the original allocation (or at the very least, don't change where the allocation starts). So, if you were to enter some input starting with a space, the tracking allocator will panic on a bad free (on recent versions of Odin, at least; on older versions it'll protect you but you'd need to check its bad_free_array as you are the allocation_map to see it).

strong tree
#

Cool, that makes complete sense! I've altered the code as you've suggested and looks to be all working as expected! Thanks so much!