Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange valgrind error #1453

Closed
wirepair opened this issue Nov 28, 2024 · 2 comments
Closed

Strange valgrind error #1453

wirepair opened this issue Nov 28, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@wirepair
Copy link

Describe the bug
During testing of my game server I ran valgrind on my tests and the debug binary, both exhibited this valgrind error:

valgrind --leak-check=full --show-leak-kinds=all ./testlib "Repo"

test cases: 1 | 1 passed
assertions: - none -

==21319== 
==21319== HEAP SUMMARY:
==21319==     in use at exit: 7,488 bytes in 1 blocks
==21319==   total heap usage: 7,243 allocs, 7,242 frees, 8,660,661 bytes allocated
==21319== 
==21319== 7,488 bytes in 1 blocks are definitely lost in loss record 1 of 1
==21319==    at 0x483C7F5: malloc (vg_replace_malloc.c:442)
==21319==    by 0x491D450: ecs_os_api_malloc (os_api.c:302)
==21319==    by 0x48F0DED: flecs_stack_alloc (stack_allocator.c:50)
==21319==    by 0x494C0E1: flecs_defer_set (stage.c:397)
==21319==    by 0x4900932: ecs_ensure_modified_id (entity.c:3164)
==21319==    by 0x4684A6: void flecs::set<test::PlayerWorldStates, 0>(ecs_world_t*, unsigned long, test::PlayerWorldStates&&, unsigned long) (world.hpp:25)
==21319==    by 0x468221: void flecs::set<test::PlayerWorldStates, test::PlayerWorldStates>(ecs_world_t*, unsigned long, test::PlayerWorldStates&&) (world.hpp:99)
==21319==    by 0x467387: flecs::entity const& flecs::entity_builder<flecs::entity>::set<test::PlayerWorldStates, 0>(test::PlayerWorldStates&&) const (builder.hpp:699)
==21319==    by 0x4335D4: CATCH2_INTERNAL_TEST_0()::$_0::operator()(flecs::iter&, unsigned long, test::Connected) const (tests/ecs_network.cpp:77)
==21319==    by 0x433787: void flecs::_::each_delegate<CATCH2_INTERNAL_TEST_0()::$_0, test::Connected>::invoke_callback<flecs::_::each_field, flecs::_::field_ptr, CATCH2_INTERNAL_TEST_0()::$_0, 0>(ecs_iter_t*, CATCH2_INTERNAL_TEST_0()::$_0 const&, unsigned long, flecs::_::field_ptr) (delegate.hpp:324)
==21319==    by 0x4336D3: void flecs::_::each_delegate<CATCH2_INTERNAL_TEST_0()::$_0, test::Connected>::invoke_unpack<flecs::_::each_field, flecs::_::field_ptr, 0>(ecs_iter_t*, CATCH2_INTERNAL_TEST_0()::$_0 const&, unsigned long, flecs::array<flecs::_::field_ptr, 1ul, void>&, flecs::_::field_ptr) (delegate.hpp:357)
==21319==    by 0x43330C: void flecs::_::each_delegate<CATCH2_INTERNAL_TEST_0()::$_0, test::Connected>::invoke_unpack<flecs::_::each_field, , 0>(ecs_iter_t*, CATCH2_INTERNAL_TEST_0()::$_0 const&, unsigned long, flecs::array<flecs::_::field_ptr, 1ul, void>&) (delegate.hpp:368)
==21319== 
==21319== LEAK SUMMARY:
==21319==    definitely lost: 7,488 bytes in 1 blocks
==21319==    indirectly lost: 0 bytes in 0 blocks
==21319==      possibly lost: 0 bytes in 0 blocks
==21319==    still reachable: 0 bytes in 0 blocks
==21319==         suppressed: 0 bytes in 0 blocks
==21319== 
==21319== For lists of detected and suppressed errors, rerun with: -s
==21319== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

To Reproduce
Steps to reproduce the behavior:
Compile the following code and run it under valgrind (or just replace the test with a main function):

#include <catch2/catch_all.hpp>
#include <flecs.h>

namespace test
{
    struct Connected 
    {
        int SomeValue = 0;
    };

    struct Player
    {
        int id = 0;
    };

    struct ItemEntity
    {
        uint64_t EntityId; // flecs server entity id
        uint32_t ItemId; // Prefab ID
    };
    

    struct WorldState
    {
        std::array<test::ItemEntity, 16> Equipment{}; // <-- simple types like int will not cause the leak
        std::unordered_set<int> Inventory{};
    };


    struct PlayerWorldStates 
    {
        std::array<test::WorldState, 16> States{}; // if set to a small number, like 4 i did not get the error
    };
    
    struct Level
    {
        Level(flecs::world &GameWorld)
        {
            GameWorld.module<Level>();
            GameWorld.component<Connected>();
            GameWorld.component<PlayerWorldStates>();
        }
    };
}

TEST_CASE("Repo", "[ecs_network]")
{
    flecs::reset();
    flecs::world GameWorld;

    // I suspect this issue only happens with modules/namespaces but could be wrong.
    GameWorld.import<test::Level>();
    
    GameWorld.observer<test::Connected>("OnServerConnected")
        //.write<test::PlayerWorldStates>() // doesn't seem to matter
        .event(flecs::OnAdd)
        .each([&](flecs::iter& It, size_t Index, test::Connected)
        {
            auto CurrentPlayer = It.entity(Index);
            //auto Parent = GameWorld.entity();
            //CurrentPlayer.add(flecs::ChildOf, Parent);
            CurrentPlayer.set<test::Player>({1}); // could be any component here
            CurrentPlayer.set<test::PlayerWorldStates>({});
        });

    GameWorld.entity().set<test::Connected>({});
    GameWorld.progress();
}

Expected behavior
Valgrind should not find any leaks

Additional context
I suspect there's something going on with namespaces and modules, so I left it in, you can probably reduce it further. There seems to be something in particular about std::array. Playing with the array sizes, or removing them from various structs would cause the leak to disappear. It's all very peculiar and I can't really figure out what magic incantation will or will not cause the leak to exhibit itself with out trying various combinations.

Hopefully your knowledge of flecs internals will help you, because I see nothing wrong!

@wirepair wirepair added the bug Something isn't working label Nov 28, 2024
@SanderMertens
Copy link
Owner

SanderMertens commented Dec 17, 2024

Reduced the example to:

struct Connected 
{
    int SomeValue = 0;
};

struct PlayerWorldStates 
{
    char largeArray[4096];
    std::unordered_set<int> Inventory{};
};

int main(int argc, char *argv[]) {
    flecs::world GameWorld;

    GameWorld.defer_begin();

    GameWorld.entity()
        .set<Connected>({})
        .set<PlayerWorldStates>({});

    GameWorld.defer_end();
}

Looks like an issue with batching & deferred setting of non-trivial large components.

@SanderMertens
Copy link
Owner

Fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants