Want to see memory leak but i couldn't, I intentionally created a memory leak, but i couldn't see the leak, why?

main.zig

const std = @import("std");
const c = @import("c");

const Io = std.Io;
const crypto = std.crypto;

const Parser = @import("bencode.zig").Parser;
const Server = @import("tracker_client.zig").Server;

var global_server_ptr: ?*Server = null;

pub var should_run: std.atomic.Value(bool) = .init(true);

pub fn interrupt_handler(sig: std.os.linux.SIG) callconv(.c) void {
    switch (sig) {
        .INT => {
            should_run.store(false, .release);

            if (global_server_ptr) |srv| {
                // Forcefully stop the server right now!
                srv.listener.socket.close(srv.io);
            }
        },
        else => {},
    }
}

pub fn main(init: std.process.Init) !void {
    // var debug_allocator = std.heap.DebugAllocator(.{ .thread_safe = true }).init;
    // defer _ = debug_allocator.deinit();

    // const allocator = debug_allocator.allocator();
    const allocator = init.gpa;

    const io = init.io;

    const file = try std.Io.Dir.openFile(.cwd(), io, "aapun.txt.torrent", .{});
    defer file.close(io);

    const file_stats = try file.stat(io);
    const file_size: usize = file_stats.size;

    var file_reader_buffer: [512]u8 = undefined;
    var file_reader = file.reader(io, &file_reader_buffer);
    var file_iface = &file_reader.interface;

    const buffer = try allocator.alloc(u8, file_size);
    defer allocator.free(buffer);

    file_iface.readSliceAll(buffer) catch |err| switch (err) {
        error.ReadFailed => {
            return error.ReadFailed;
        },
        error.EndOfStream => {
            return error.EndOfStream;
        },
    };

    std.debug.print("\n\n{s}'\n\n", .{buffer});

    var parser = Parser.init(buffer, allocator);
    var value = try parser.parse_value();
    defer Parser.deinit(&value, allocator);
    Parser.print_value(value);
    if (Parser.get_dict_val(value.data.dict, "info")) |val| {
        const info_bytes = buffer[val.span.start..val.span.end];
        // std.debug.print("\nRaw Info Dictonary: \n{s}\n", .{info_bytes});

        var hash: [32]u8 = undefined;
        crypto.hash.sha2.Sha256.hash(info_bytes, &hash, .{});

        std.debug.print("\nInfo Hash: \n", .{});

        for (hash) |byte| {
            std.debug.print("{x:0>2}", .{byte});
        }

        std.debug.print("\n", .{});
    }

    const sigaction: std.os.linux.Sigaction = .{
        .flags = 0,
        .handler = .{
            .handler = &interrupt_handler,
        },
        .mask = @splat(0),
    };

    switch (std.os.linux.errno(std.os.linux.sigaction(.INT, &sigaction, null))) {
        .SUCCESS => {}, // no error
        else => |err| std.log.err("{t}: Sigaction interrupt handler failure!", .{err}),
    }

    var server = try Server.init("127.0.0.1", 9001, allocator, io);
    defer server.deinit();

    global_server_ptr = &server;
    defer global_server_ptr = null;

    try server.accept_connection();
}

and server module

const std = @import("std");
const c = @import("c");
const main_root = @import("main.zig");

pub const Server = struct {
    allocator: std.mem.Allocator,
    io: std.Io,
    listener: std.Io.net.Server,
    group: std.Io.Group,

    pub fn init(server_url: []const u8, port: u16, allocator: std.mem.Allocator, io: std.Io) !Server {
        const ip_addr = try std.Io.net.IpAddress.parse(server_url, port);

        return .{
            .allocator = allocator,
            .io = io,
            .listener = try ip_addr.listen(io, .{ .reuse_address = true }),
            .group = .init,
        };
    }

    pub fn deinit(self: *Server) void {
        std.debug.print("\n\nServer Shutdown Gracefully.\n\n", .{});

        self.group.cancel(self.io);
        self.group.await(self.io) catch |err| {
            std.debug.print("Group await error: {}\n", .{err});
        };
    }

    pub fn accept_connection(self: *Server) !void {
        while (main_root.should_run.load(.acquire)) {
            const client_stream = self.listener.accept(self.io) catch |err| {
                if (!main_root.should_run.load(.monotonic)) break;
                std.debug.print("Accept error: {}\n", .{err});
                continue;
            };

            self.group.concurrent(self.io, handle_client, .{ self.io, self.allocator, client_stream }) catch |err| {
                std.debug.print("Failed to spawn background task: {}\n", .{err});
                client_stream.close(self.io);
                continue;
            };
        }

        self.listener.socket.close(self.io);
        std.debug.print("\nServer loop exited.", .{});
    }

    fn handle_client(io: std.Io, allocator: std.mem.Allocator, stream: std.Io.net.Stream) std.Io.Cancelable!void {
        defer stream.close(io);

        _ = allocator.alloc(u8, 4096) catch |err| {
            std.debug.print("err: {any}", .{err});
            return std.Io.Cancelable.Canceled;
        };
        _ = allocator.alloc(u8, 4096) catch |err| {
            std.debug.print("err: {any}", .{err});
            return std.Io.Cancelable.Canceled;
        };
        // _ = allocator.alloc(u8, 4096) catch |err| {
        //     std.debug.print("err: {any}", .{err});
        //     return std.Io.Cancelable.Canceled;
        // };
        // _ = allocator.alloc(u8, 4096) catch |err| {
        //     std.debug.print("err: {any}", .{err});
        //     return std.Io.Cancelable.Canceled;
        // };
        // _ = allocator.alloc(u8, 4096) catch |err| {
        //     std.debug.print("err: {any}", .{err});
        //     return std.Io.Cancelable.Canceled;
        // };

        std.debug.print("New Client Connectedddd {any}:{d}\n", .{
            stream.socket.address.ip4.bytes, stream.socket.address.ip4.port,
        });

        // TODO:
        // Read request
        // Process request
        // Write response

        io.sleep(std.Io.Duration.fromSeconds(5), .awake) catch |err| {
            std.debug.print("Task sleep interrupted or canceled: {}\n", .{err});
            return;
        };

        std.debug.print("Client Disconnected\n", .{});
        std.debug.print("Client Disconnected cleanly\n", .{});
    }
};

Where is the leak meant to be, and how are you intending to observe it?

If you’re just expecting gpa to print leaks - you have to call gpa.deinit() at exit

1 Like

They are using the allocator from juicy main which does that for them, they can’t do it themselves anyway as they only get the interface.

The leak seems to be in server.handle_client, which is called by sever.accept_connection.

2 reasons I can think of that would result in no leak being logged.

  1. you are not trying to connect to the server, so handle_client never gets called so no memory gets leaked
  2. you are using an unsafe build mode, where juicy main chooses an allocator that does not detect leaks.

I use this to run the program:

zig build -Doptimize=ReleaseSafe run

otherwise i am getting .sframe register error

release safe still has leak checking.

Post the full error, this looks like a more complicated problem

Wait a sec..

torrent main  ❯ zig build run
run
└─ run exe torrent
   └─ install
      └─ install torrent
         └─ compile exe torrent Debug native 2 errors
error: fatal linker error: unhandled relocation type R_X86_64_PC64 at offset 0x1c
    note: in /usr/lib/gcc/x86_64-pc-linux-gnu/16.1.1/../../../../lib/crt1.o:.sframe
error: fatal linker error: unhandled relocation type R_X86_64_PC64 at offset 0x2c
    note: in /usr/lib/gcc/x86_64-pc-linux-gnu/16.1.1/../../../../lib/crt1.o:.sframe
error: 2 compilation errors
failed command: /home/itsmonday/.local/share/mise/installs/zig/0.16.0/zig build-exe -ODebug --dep torrent --dep c "-Mroot=/run/media/itsmonday/Queenines reveng/torrent/src/main.zig" "-Mtorrent=/run/media/itsmonday/Queenines reveng/torrent/src/main.zig" -ODebug -Mc=.zig-cache/o/2e24a39d0c683e55f77845d377b054a9/c.zig -lc --cache-dir .zig-cache --global-cache-dir /home/itsmonday/.cache/zig --name torrent --zig-lib-dir /home/itsmonday/.local/share/mise/installs/zig/0.16.0/lib/ --listen=-

See i pasted the error

this is a bug in the compiler, it has already been fixed on master, but you should be able to get around it by forcing llvm with .use_llvm = true in the exe options in your build.zig

I used this, but then i get differnet error

send the error..

1 Like

See This

in the terminal

New Client Connectedddd { 127, 0, 0, 1 }:37984
Client Disconnected
Client Disconnected cleanly

and from another terminal

torrent main  ✗ telnet localhost 9001
Trying ::1...
Connection failed: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
^C^]
telnet> quit
Connection closed.

The leak the i produced intentionally i wanted to see this

This is the repo:

You can verify by running it, in your own

Did you check it?