Zen
April 13, 2025, 11:02pm
1
const allocator = std.heap.c_allocator;
$zig test tests-1.zig -target aarch64-linux-musl -lc
works without any error !
However, after change allocator to
const allocator = std.testing.allocator;
$zig test tests-1.zig -target aarch64-linux-musl -lc
[gpa] (err): memory address 0x7680480000 leaked:
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1477:53: 0x1128b9f in allocate (test)
const slice = try allocator.alignedAlloc(u8, max_align, total_size);
^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1434:29: 0x1115bc3 in grow (test)
try map.allocate(allocator, new_cap);
^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1295:30: 0x10fca3f in growIfNeeded (test)
...
Regards!
Sze
April 13, 2025, 11:06pm
2
std.heap.c_allocator
doesn’t have leak detection, so the leak exists it just isn’t noticed.
With the std.testing.allocator
the error becomes visible.
14 Likes
Zen
April 14, 2025, 9:07pm
3
$./test
[gpa] (erro): memory address 0x77a9fe0000 leaked:
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1477:53: 0x111f35b in allocate (test) const slice = try allocator.alignedAlloc(u8, max_align, total_size); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1434:29: 0x110db77 in grow (test)
try map.allocate(allocator, new_cap); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1295:30: 0x10f4fdb in growIfNeeded (test)
try self.grow(allocator, capacityForSize(self.load() + new_count), ctx); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1114:34: 0x10d6f6b in getOrPutContextAdapted__anon_12385 (test)
self.growIfNeeded(allocator, 1, ctx) catch |err| { ^ /data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1099:56: 0x10bce0f in getOrPutContext (test) const gop = try self.getOrPutContextAdapted(allocator, key, ctx, ctx); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1025:52: 0x10b43eb in putContext (test) const result = try self.getOrPutContext(allocator, key, ctx);
...```
...
$valgrind --leak-check=full test
...
…
==13379== HEAP SUMMARY: ==13379== in use at exit: 0 bytes in 0 blocks
==13379== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==13379==
==13379== All heap blocks were freed – no leaks are possible ==13379==
==13379== For lists of detected and suppressed errors, rerun with: -s ==13379== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Aborted
It is hard for me to figure the leak out. Any info is appreciated !
Sze
April 14, 2025, 9:17pm
4
I have no idea how to help you unless you show the code of the actual test, or describe the problem in more detail.
1 Like
Zen
April 14, 2025, 9:26pm
5
const std = @import("std");
const expectEqual = std.testing.expectEqual;
const expectEqualSlices = std.testing.expectEqualSlices;
const allocator = std.testing.allocator; //const allocator = std.heap.c_allocator;
const stdout = std.io.getStdOut().writer();
const print = std.debug.print;
test "Search" {
tables.initAll(allocator);
defer tables.deinitAll(allocator);
var s: position.State = position.State{};
var pos: position.Position = try position.Position.setFen(&s, position.start_fen);
var move: types.Move = .none;
var limits = interface.limits;
limits.depth = 7;
move = try search.iterativeDeepening(allocator, stdout, &pos, limits, evaluate.evaluateShannon);
//move = try search.iterativeDeepening(allocator, stdout, &pos, limits, evaluate.evaluateTable); try stdout.print("bestmove ", .{});
try move.printUCI(stdout);
try stdout.print("\n", .{});
}
The code is from a chess engine i used to study Zig!
Sze
April 14, 2025, 9:35pm
6
What are you doing, why do you mess up the formatting I just fixed?
Do you write from some tiny screen phone, or what is going on?
It looks like it’s from a phone or tablet using termux.
1 Like
Zen
April 14, 2025, 9:38pm
8
I donot know you are helping me to format it. I am using a phone to input it. please reformat it again, thank you!
1 Like
If I would have to guess, you are using some hashmap in this function that is not being deinitialized.
1 Like
Zen:
==13379== HEAP SUMMARY: ==13379== in use at exit: 0 bytes in 0 blocks
==13379== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==13379==
==13379== All heap blocks were freed – no leaks are possible ==13379==
==13379== For lists of detected and suppressed errors, rerun with: -s ==13379== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Aborted
In this case, I think valgrind is reporting on the test runner, not the test code. I don’t know for sure, but I would assume that the test runner correctly manages the memory and so Valgrind will not find any issues with the test runner.
1 Like
Sze
April 14, 2025, 9:50pm
11
Zen:
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1477:53: 0x111f35b in allocate (test) const slice = try allocator.alignedAlloc(u8, max_align, total_size); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1434:29: 0x110db77 in grow (test)
try map.allocate(allocator, new_cap); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1295:30: 0x10f4fdb in growIfNeeded (test)
try self.grow(allocator, capacityForSize(self.load() + new_count), ctx); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1114:34: 0x10d6f6b in getOrPutContextAdapted__anon_12385 (test)
self.growIfNeeded(allocator, 1, ctx) catch |err| { ^ /data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1099:56: 0x10bce0f in getOrPutContext (test) const gop = try self.getOrPutContextAdapted(allocator, key, ctx, ctx); ^
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1025:52: 0x10b43eb in putContext (test) const result = try self.getOrPutContext(allocator, key, ctx);
This looks like you have only posted part of the error message, there also should be stackframes about which allocation in your code was leaked.
1 Like
Zen
April 14, 2025, 9:52pm
12
The original code is a bit longer. I choose what i think relative parts.
var root_moves: std.ArrayListUnmanaged(RootMove) = .empty;
const RootMove = struct {
// ...
pv: std.ArrayListUnmanaged(types.Move) = .empty,
};
var move_list: std.ArrayListUnmanaged(types.Move) = .empty;
defer move_list.deinit(allocator); // ...
for (root_moves.items) |*root_move| {
root_move.pv.deinit(allocator);
}
root_moves.deinit(allocator); // <- added by me
Regards!
So those snippets you added don’t use a hashmap, they use ArrayLists which is a different data structure. Somewhere you are using a hashmap and that is the issue.
As a note, you can put your code as ```zig block it will do syntax highlighting.
Zen
April 14, 2025, 10:12pm
14
bestmove a2a3 Black to move fen: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 1 1 zobrist: 0
[gpa] (err): memory address 0x77bf740000 leaked:
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1477:53: 0x111f35b in allocate (test)
const slice = try allocator.alignedAlloc(u8, max_align, total_size); /data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1434:29: 0x110db77 in grow (test)try map.allocate(allocator, new_cap); /data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1295:30: 0x10f4fdb in growIfNeeded (test)
try self.grow(allocator, capacityForSize(self.load() + new_count), ctx);
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1114:34: 0x10d6f6b in getOrPutContextAdapted__anon_12385 (test) self.growIfNeeded(allocator, 1, ctx) catch |err| {
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1099:56: 0x10bce0f in getOrPutContext (test) const gop = try self.getOrPutContextAdapted(allocator, key, ctx, ctx);
/data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1025:52: 0x10b43eb in putContext (test)
const result = try self.getOrPutContext(allocator, key, ctx); /data/data/com.termux/files/usr/lib/zig/lib/std/hash_map.zig:1022:35: 0x10a10f7 in put (test)
return self.putContext(allocator, key, value, undefined);
/data/data/com.termux/files/home/z/chess/search.zig:392:63: 0x10a3c3b in abSearch__anon_4517 (test )
try tables.transposition_table.put(allocator, key, .{ score, current_depth - 1, move }) /data/data/com.termux/files/home/z/chess/search.zig:388:42: 0x10a3aaf in abSearch__anon_4517 (test)
score = -try abSearch(allocator, NodeType.pv, ss + 1, pos, limits, eval, -beta, -alpha, current_depth - 1 + @intFromBool(pos.state.checkers != 0), false);
/data/data/com.termux/files/home/z/chess/search.zig:388:42: 0x10a3aaf in abSearch__anon_4517 (test)
score = -try abSearch(allocator, NodeType.pv, ss + 1, pos, limits, eval, -beta, -alpha, current_depth - 1 + @intFromBool(pos.state.checkers != 0), false);
All 1 tests passed.
1 errors were logged.
1 tests leaked memory.
Regards
Sze
April 14, 2025, 10:25pm
15
Seems like your deinitAll
function doesn’t clear tables.transposition_table
try adding self.transposition_table.clearAndFree(allocator);
to that function.
Or post the code of the deinitAll
function.
1 Like
Zen
April 14, 2025, 10:36pm
16
pub fn deinitAll(allocator: std.mem.Allocator) void {
var sq: u8 = Square.a1.index();
while (sq <= Square.h8.index()) : (sq += 1) {
moves_bishop[sq].deinit(allocator);
moves_rook[sq].deinit(allocator);
}
} transposition_table.clearAndFree(allocator); // added as you suggested!
It seems transposition_table.deinit(allocator)
works too ?
Great! memory leak disappear !
Sze
April 15, 2025, 1:17am
17
Where do you create/declare the transposition_table
?
Maybe it would make more sense to put the call to transposition_table.deinit(allocator)
somewhere else if it is a global variable (for example using defer), the benefit of clearAndFree
is that it should reset the hash table to the empty state without making the hash table unusable, so if it is used as a caching table it just resets the cache without making it unusable (needing to create a new cache instance), and resetting the cache to empty should be enough to avoid memory leaks because empty hash tables don’t own any dynamically allocated memory.
So basically you have to decide where and why you want to deinit
or clearAndFree
the memory, it depends a bit on your api/implementation design.
2 Likes
sbrow
April 15, 2025, 1:43am
18
Generally, when asking for help, you should distill the problem down to the smallest amount of code that generates the error, and then post the entire code sample and error trace- not just the part you think is relevant. If you knew what the problem was, you likely wouldn’t need to ask for help
4 Likes