Memory leak with WebRTC streaming

Isaac Sim Version

4.5.0
4.2.0

Operating System

Ubuntu 22.04

GPU Information

  • Model: A10G
  • Driver Version: 535.183.01

Description

We are using “omni.kit.livestream.webrtc” extension to stream to a web client. The issue we have is that when the client is connected to the stream, the memory consumption rises for about 20GB per day until eventually we’re out of memory on the machine. If there’s no clients connected to the stream, the memory stays constant (at about 5GB). We cannot restart it every once in a while as this is supposed to run 24/7. How would we go about diagnosing this, what sort of additional information can we provide?

We’ve tried isaac 4.2.0 & 4.5.0 and the result is the same.

Could you please provide the full logs and detailed steps to help us replicate the issue?

Hi @VickNV

I prepared a minimal reproduction code compilation. It consists of a python file which we use to run our simulations, a sample usda file and a config file.

player.py:

# Python
from typing import Any
import argparse
import os
import yaml

# Isaac
from isaacsim import SimulationApp
import carb


def load_config(config_path: str, default_path: str) -> dict[str, Any]:
    if not config_path.endswith(".yaml"):
        carb.log_warn(f"File {config_path} is not yaml, will use default config")
        config_path = default_path

    with open(config_path, "r") as f:  # pylint: disable=unspecified-encoding
        return yaml.safe_load(f)  # type: ignore


def parse_config() -> dict[str, Any]:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-c",
        "--config",
        required=False,
        help="Include specific config parameters (yaml)",
        default=os.path.join(os.path.dirname(__file__), "config.yaml"),
    )

    args = parser.parse_args()
    args_dict = vars(args)
    config = load_config(args.config, default_path="config.yaml")
    config.update(args_dict)

    return config


def create_simulation_app(config: dict[str, Any]) -> SimulationApp:
    launch_config: dict[str, Any] = config["simulation_app"]["launch_config"]
    experience: str = config["simulation_app"]["experience"]
    return SimulationApp(launch_config, experience)


# Parse config
global_config = parse_config()

# Create simulation app
sim_app = create_simulation_app(global_config)

# Python
import time

# Isaac
from carb.events import IEvent  # pylint: disable=no-name-in-module
from omni.isaac.core import SimulationContext
from omni.isaac.core.utils.stage import get_current_stage
from omni.isaac.core.utils.stage import open_stage
from pxr.Sdf import Layer  # pylint: disable=no-name-in-module
import carb.events
import omni.kit.app
import omni.timeline
from omni.isaac.core.utils.extensions import enable_extension


class SteadyRate:
    """
    Maintains the steady cycle rate provided on initialization by adaptively sleeping an amount
    of time to make up the remaining cycle time after work is done.
    Usage:
    rate = SteadyRate(rate_hz=30.)
    while True:
      do.work()  # Do any work.
      rate.sleep()  # Sleep for the remaining cycle time.
    """

    def __init__(self, rate_hz: float) -> None:
        self.rate_hz = rate_hz
        self.dt = 1.0 / rate_hz
        self.last_sleep_end = time.time()

    def sleep(self) -> None:
        work_elapse = time.time() - self.last_sleep_end
        sleep_time = self.dt - work_elapse
        if sleep_time > 0.0:
            time.sleep(sleep_time)
        self.last_sleep_end = time.time()


class AnimationPlayer:
    """Application class to play the animation."""

    RATE_HZ = 60.0

    def __init__(self, simulation_app: SimulationApp) -> None:
        """
        Args:
            simulation_app (SimulationApp): SimulationApp instance.
        """
        self.simulation_app = simulation_app
        self._timeline = omni.timeline.get_timeline_interface()
        self._sim_context = SimulationContext(
            stage_units_in_meters=1.0,
            physics_dt=1.0 / self.RATE_HZ,
            rendering_dt=1.0 / self.RATE_HZ,
        )

        self._time_step_index = 0
        self._last_frame_index: int = global_config["last_frame_index"]
        enable_extension("omni.kit.livestream.webrtc")

    @property
    def last_frame_index(self) -> int:
        return self._last_frame_index

    @last_frame_index.setter
    def last_frame_index(self, value: int) -> None:
        self._last_frame_index = value

    def run_loop(self) -> None:
        """
        Run the animation. This method will run the animation until the application is running. Renders
        the world at a fixed rate. Loops the animation if the last frame is reached.
        """
        rate = SteadyRate(self.RATE_HZ)

        is_reset_pending = False
        self.play_from_start()

        while self.simulation_app.is_running():
            # Render world first to get input from user, e.g. Button for stopping animation
            self._sim_context.render()

            # Maintain rate
            rate.sleep()

            if self._timeline.is_stopped() and not is_reset_pending:
                is_reset_pending = True
            if self._timeline.is_playing() and is_reset_pending:
                self.play_from_start()
                is_reset_pending = False

            # If last frame is reached, stop the animation
            if self._time_step_index >= self._last_frame_index:
                self.play_from_start()

            if self._timeline.is_playing():
                self._time_step_index += 1

        # Cleanup
        self.simulation_app.close()

    def play_from_start(self) -> None:
        self._timeline.set_end_time(self.last_frame_index / 60)
        self._timeline.play(start_timecode=0, end_timecode=self._last_frame_index)
        self._time_step_index = 0


def initialize_animation(
    directory_path: str, last_frame_index: int, layer_paths: list[str] | None = None
) -> None:
    main_scene_path: str = directory_path + global_config["scene_name"]
    if not open_stage(main_scene_path):
        carb.log_error(
            f"Could not open stage [{main_scene_path}], closing application ..."
        )
        sim_app.close()
        return

    if layer_paths is not None:
        # By default insert index is 0, reverse the list to insert in the correct order (layers higher in the list have
        # higher priority and will overwrite layers below.)
        layer_paths.reverse()
        for layer_path in layer_paths:
            insert_layer(layer_path)

    app.last_frame_index = last_frame_index
    app.play_from_start()


app = AnimationPlayer(sim_app)


def main() -> None:
    load_usd_type = carb.events.type_from_string(
        "omni.sunrise.LOAD_USD"
    )  # pylint: disable=no-member

    # App provides common event bus. It is event queue which is popped every update (frame).
    bus = omni.kit.app.get_app().get_message_bus_event_stream()

    def on_event(e: IEvent) -> None:
        if e.type == load_usd_type:
            directory_path: str = e.payload.get("directory", None)
            last_frame_index: int = e.payload.get("last_frame_index", None)
            layer_paths_tuple = e.payload.get("layer_paths", None)
            animation_layer_path: list[str] | None = (
                list(layer_paths_tuple) if layer_paths_tuple is not None else None
            )

            initialize_animation(directory_path, last_frame_index, animation_layer_path)

    # Pop is called on next update
    _ = bus.create_subscription_to_pop_by_type(load_usd_type, on_event)

    if global_config.get("usd_directory", None) is not None:
        # And run the full animation
        initialize_animation(
            directory_path=global_config["usd_directory"],
            last_frame_index=global_config["last_frame_index"],
            layer_paths=global_config.get("layer_paths", None),
        )

    # And run the animation loop
    app.run_loop()


def insert_layer(layer_path: str, layer_index_position: int = 0) -> None:
    # Get the root layer
    root_layer: Layer = get_current_stage().GetRootLayer()

    # Create a new sub layer with the given path
    sub_layer: Layer | None = Layer.FindOrOpen(layer_path)

    if sub_layer is None:
        carb.log_error(f"Could not open layer [{layer_path}]")
        return

    # Insert the sub layer into to the first place in the subLayerPaths list
    root_layer.subLayerPaths.insert(layer_index_position, sub_layer.identifier)


if __name__ == "__main__":
    main()

animation.usda:

config.yaml:

simulation_app:
  launch_config:
    renderer: "RayTracedLighting"
    headless: true
  experience: ""

usd_directory: "/home/ubuntu/sunrise/robotui_omniverse_app/animation-player/"
scene_name: "animation.usda"

last_frame_index: 7540

With the compilation of these files, one can observe the following memory growth:
Ran the files at 12:03 PM, memory usage was 4.2GB. By 03:44 PM, memory usage rose to 6.7GB. That would bring the memory consumption to over .5GB additional per hour. Note that this requires an active WebRTC client connected to it, one can use your sample repository found at GitHub - NVIDIA-Omniverse/web-viewer-sample: This sample demonstrates how a front-end client can present a streamed Omniverse Kit application and how to send messages between the two apps.. If you need me to compile this into a Github repository, I can make that happen. For these files to run together, it’s enough to put the three into the same folder.

Is there any additional information I can provide?

As for the logs, there is nothing particularly significant in them, but for the test run I described above, it’s this:

I’m using pastebin for these as it’s complaning of message size on the forums.

Please share the exact commands/tools used to measure memory growth and specify the full command used to launch Isaac Sim.

Additionally, does the leak occur when using Isaac Sim’s built-in examples? Have you debugged this with Valgrind?

@VickNV The way you would launch the python file (which in turn launches and imports Isaac in headless mode) is simply by using the isaac’s built in python to run the script I posted above, so e.g.:
$ path-to-omni-python player.py
The way we measure memory is simply by running the top command:
$ top -c -o %MEM
and observing its memory under the “RES” section.

The specific program run I wrote about yesterday has risen to 17.5GB RAM usage overnight.

I’ve tried many different USD files, the one above is a random one I pulled from the internet, and the result is always the same. I’ve also successfully reproduced this on two different machines.

I’ve tried with Valgrind running the process for about two hours, this is its logs (you can see heap usage increased by over twofold, but it does not report a leak):

==1723818== Memcheck, a memory error detector
==1723818== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1723818== Using Valgrind-3.18.1-42b08ed5bd-20211015 and LibVEX; rerun with -h for copyright info
==1723818== Command: /home/ubuntu/.local/share/ov/pkg/isaac-sim-4.2.0/python.sh /home/ubuntu/sunrise/robotui_omniverse_app/animation-player/animation-player.py
==1723818== Parent PID: 1705233
==1723818== 
--1723818-- 
--1723818-- Valgrind options:
--1723818--    -v
--1723818--    --log-file=valgrind.log
--1723818--    --tool=memcheck
--1723818--    --leak-check=full
--1723818--    --num-callers=40
--1723818-- Contents of /proc/version:
--1723818--   Linux version 6.8.0-1021-aws (buildd@lcy02-amd64-088) (x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #23~22.04.1-Ubuntu SMP Tue Dec 10 16:50:46 UTC 2024
--1723818-- 
--1723818-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-ssse3-avx-avx2-bmi-f16c-rdrand-rdseed
--1723818-- Page sizes: currently 4096, max supported 4096
--1723818-- Valgrind library directory: /usr/libexec/valgrind
--1723818-- Reading syms from /usr/bin/bash
--1723818--    object doesn't have a symbol table
--1723818-- Reading syms from /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
--1723818--   Considering /usr/lib/debug/.build-id/e4/de036b19e4768e7591b596c4be9f9015f2d28a.debug ..
--1723818--   .. build-id is valid
--1723818-- Reading syms from /usr/libexec/valgrind/memcheck-amd64-linux
--1723818--    object doesn't have a symbol table
--1723818--    object doesn't have a dynamic symbol table
--1723818-- Scheduler: using generic scheduler lock implementation.
--1723818-- Reading suppressions file: /usr/libexec/valgrind/default.supp
==1723818== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-1723818-by-ubuntu-on-???
==1723818== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-1723818-by-ubuntu-on-???
==1723818== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-1723818-by-ubuntu-on-???
==1723818== 
==1723818== TO CONTROL THIS PROCESS USING vgdb (which you probably
==1723818== don't want to do, unless you know exactly what you're doing,
==1723818== or are doing some strange experiment):
==1723818==   /usr/bin/vgdb --pid=1723818 ...command...
==1723818== 
==1723818== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==1723818==   /path/to/gdb /home/ubuntu/.local/share/ov/pkg/isaac-sim-4.2.0/python.sh
==1723818== and then give GDB the following command
==1723818==   target remote | /usr/bin/vgdb --pid=1723818
==1723818== --pid is optional if only one valgrind process is running
==1723818== 
--1723818-- REDIR: 0x402aa40 (ld-linux-x86-64.so.2:strlen) redirected to 0x580bcec2 (???)
--1723818-- REDIR: 0x402a810 (ld-linux-x86-64.so.2:index) redirected to 0x580bcedc (???)
--1723818-- Reading syms from /usr/libexec/valgrind/vgpreload_core-amd64-linux.so
--1723818--    object doesn't have a symbol table
--1723818-- Reading syms from /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so
--1723818--    object doesn't have a symbol table
==1723818== WARNING: new redirection conflicts with existing -- ignoring it
--1723818--     old: 0x0402aa40 (strlen              ) R-> (0000.0) 0x580bcec2 ???
--1723818--     new: 0x0402aa40 (strlen              ) R-> (2007.0) 0x0484ee30 strlen
--1723818-- REDIR: 0x4027220 (ld-linux-x86-64.so.2:strcmp) redirected to 0x484fcd0 (strcmp)
--1723818-- REDIR: 0x402afa0 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x4853840 (mempcpy)
--1723818-- Reading syms from /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3
--1723818--    object doesn't have a symbol table
--1723818-- Reading syms from /usr/lib/x86_64-linux-gnu/libc.so.6
--1723818--   Considering /usr/lib/debug/.build-id/cd/410b710f0f094c6832edd95931006d883af48e.debug ..
--1723818--   .. build-id is valid
==1723818== WARNING: new redirection conflicts with existing -- ignoring it
--1723818--     old: 0x04945c60 (memalign            ) R-> (1011.0) 0x0484e080 memalign
--1723818--     new: 0x04945c60 (memalign            ) R-> (1017.0) 0x0484e050 aligned_alloc
==1723818== WARNING: new redirection conflicts with existing -- ignoring it
--1723818--     old: 0x04945c60 (memalign            ) R-> (1011.0) 0x0484e080 memalign
--1723818--     new: 0x04945c60 (memalign            ) R-> (1017.0) 0x0484e020 aligned_alloc
==1723818== WARNING: new redirection conflicts with existing -- ignoring it
--1723818--     old: 0x04945c60 (memalign            ) R-> (1011.0) 0x0484e080 memalign
--1723818--     new: 0x04945c60 (memalign            ) R-> (1017.0) 0x0484e050 aligned_alloc
==1723818== WARNING: new redirection conflicts with existing -- ignoring it
--1723818--     old: 0x04945c60 (memalign            ) R-> (1011.0) 0x0484e080 memalign
--1723818--     new: 0x04945c60 (memalign            ) R-> (1017.0) 0x0484e020 aligned_alloc
--1723818-- REDIR: 0x4948720 (libc.so.6:strnlen) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49489e0 (libc.so.6:strpbrk) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49482e0 (libc.so.6:strcmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4966cd0 (libc.so.6:wcsnlen) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49496d0 (libc.so.6:memset) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4965540 (libc.so.6:wcslen) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949c10 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4965370 (libc.so.6:wcschr) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4948260 (libc.so.6:index) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4948960 (libc.so.6:rindex) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49495a0 (libc.so.6:memmove) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
==1723818== Preferring higher priority redirection:
--1723818--     old: 0x04a407c0 (__memcpy_avx_unalign) R-> (2018.0) 0x04850f90 __memcpy_avx_unaligned_erms
--1723818--     new: 0x04a407c0 (__memcpy_avx_unalign) R-> (2018.1) 0x04852880 memmove
--1723818-- REDIR: 0x49653f0 (libc.so.6:wcscmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49499e0 (libc.so.6:stpncpy) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49659b0 (libc.so.6:wmemchr) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4948830 (libc.so.6:strncmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949a70 (libc.so.6:strcasecmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4948420 (libc.so.6:strcspn) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4965470 (libc.so.6:wcscpy) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49481d0 (libc.so.6:strcat) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949b60 (libc.so.6:strncasecmp_l) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949480 (libc.so.6:bcmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4951570 (libc.so.6:memrchr) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x494b010 (libc.so.6:strchrnul) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4948390 (libc.so.6:strcpy) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949b10 (libc.so.6:strcasecmp_l) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49486a0 (libc.so.6:strlen) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49488d0 (libc.so.6:strncpy) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949950 (libc.so.6:stpcpy) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49493f0 (libc.so.6:memchr) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4948b00 (libc.so.6:strspn) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49497d0 (libc.so.6:mempcpy) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949ac0 (libc.so.6:strncasecmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x494af80 (libc.so.6:rawmemchr) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49487a0 (libc.so.6:strncat) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49d4280 (libc.so.6:__memcpy_chk) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4949310 (libc.so.6:strstr) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x49d43b0 (libc.so.6:__memmove_chk) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
==1723818== WARNING: new redirection conflicts with existing -- ignoring it
--1723818--     old: 0x04a40790 (__memcpy_chk_avx_una) R-> (2030.0) 0x04853940 __memcpy_chk
--1723818--     new: 0x04a40790 (__memcpy_chk_avx_una) R-> (2024.0) 0x048532c0 __memmove_chk
--1723818-- REDIR: 0x4965620 (libc.so.6:wcsncmp) redirected to 0x483f220 (_vgnU_ifunc_wrapper)
--1723818-- REDIR: 0x4a3d610 (libc.so.6:__strrchr_avx2) redirected to 0x484e810 (rindex)
--1723818-- REDIR: 0x4a38940 (libc.so.6:__strcmp_avx2) redirected to 0x484fbd0 (strcmp)
--1723818-- REDIR: 0x4a3d7e0 (libc.so.6:__strlen_avx2) redirected to 0x484ed10 (strlen)
--1723818-- REDIR: 0x4a38d80 (libc.so.6:__strncmp_avx2) redirected to 0x484f3e0 (strncmp)
--1723818-- REDIR: 0x49450a0 (libc.so.6:malloc) redirected to 0x4848820 (malloc)
--1723818-- REDIR: 0x4945740 (libc.so.6:realloc) redirected to 0x484dc50 (realloc)
--1723818-- REDIR: 0x4a3d180 (libc.so.6:__strchr_avx2) redirected to 0x484e9f0 (index)
--1723818-- REDIR: 0x4a392c0 (libc.so.6:__memchr_avx2) redirected to 0x484fd50 (memchr)
--1723818-- REDIR: 0x4a40780 (libc.so.6:__mempcpy_avx_unaligned_erms) redirected to 0x4853440 (mempcpy)
--1723818-- REDIR: 0x4948fc0 (libc.so.6:__GI_strstr) redirected to 0x4853ae0 (__strstr_sse2)
--1723818-- REDIR: 0x4a407c0 (libc.so.6:__memcpy_avx_unaligned_erms) redirected to 0x4852880 (memmove)
--1723818-- REDIR: 0x49453e0 (libc.so.6:free) redirected to 0x484b210 (free)
--1723818-- REDIR: 0x4a3a144 (libc.so.6:__strcasecmp_l_avx) redirected to 0x484f7e0 (strcasecmp_l)
--1723818-- REDIR: 0x4a3f9a0 (libc.so.6:__stpcpy_avx2) redirected to 0x4852130 (stpcpy)
--1723818-- REDIR: 0x4a40f80 (libc.so.6:__memset_avx2_unaligned_erms) redirected to 0x4852770 (memset)
--1723818-- REDIR: 0x4a3d960 (libc.so.6:__strnlen_avx2) redirected to 0x484ecb0 (strnlen)
--1723818-- REDIR: 0x4946520 (libc.so.6:calloc) redirected to 0x484d9d0 (calloc)
--1723818-- REDIR: 0x4a3ecb0 (libc.so.6:__strcpy_avx2) redirected to 0x484ee60 (strcpy)
--1723818-- REDIR: 0x4a3d400 (libc.so.6:__strchrnul_avx2) redirected to 0x4853330 (strchrnul)
--1723818-- REDIR: 0x4a386e0 (libc.so.6:__strpbrk_sse42) redirected to 0x4853c00 (strpbrk)
--1723823-- REDIR: 0x4a3f040 (libc.so.6:__strncpy_avx2) redirected to 0x484efe0 (strncpy)
==1723823== 
==1723823== HEAP SUMMARY:
==1723823==     in use at exit: 76,820 bytes in 1,047 blocks
==1723823==   total heap usage: 1,911 allocs, 864 frees, 143,550 bytes allocated
==1723823== 
==1723823== Searching for pointers to 1,047 not-freed blocks
==1723823== Checked 259,648 bytes
==1723823== 
==1723823== LEAK SUMMARY:
==1723823==    definitely lost: 0 bytes in 0 blocks
==1723823==    indirectly lost: 0 bytes in 0 blocks
==1723823==      possibly lost: 0 bytes in 0 blocks
==1723823==    still reachable: 76,820 bytes in 1,047 blocks
==1723823==         suppressed: 0 bytes in 0 blocks
==1723823== Reachable blocks (those to which a pointer was found) are not shown.
==1723823== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==1723823== 
==1723823== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
--1723818-- REDIR: 0x4a3f040 (libc.so.6:__strncpy_avx2) redirected to 0x484efe0 (strncpy)
==1723825== 
==1723825== HEAP SUMMARY:
==1723825==     in use at exit: 83,678 bytes in 1,155 blocks
==1723825==   total heap usage: 2,363 allocs, 1,208 frees, 166,772 bytes allocated
==1723825== 
==1723825== Searching for pointers to 1,155 not-freed blocks
==1723825== Checked 267,224 bytes
==1723825== 
==1723825== LEAK SUMMARY:
==1723825==    definitely lost: 0 bytes in 0 blocks
==1723825==    indirectly lost: 0 bytes in 0 blocks
==1723825==      possibly lost: 0 bytes in 0 blocks
==1723825==    still reachable: 83,678 bytes in 1,155 blocks
==1723825==         suppressed: 0 bytes in 0 blocks
==1723825== Reachable blocks (those to which a pointer was found) are not shown.
==1723825== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==1723825== 
==1723825== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
--1723818-- REDIR: 0x4a40790 (libc.so.6:__memcpy_chk_avx_unaligned_erms) redirected to 0x4853940 (__memcpy_chk)
==1723818== 
==1723818== HEAP SUMMARY:
==1723818==     in use at exit: 173,445 bytes in 1,157 blocks
==1723818==   total heap usage: 3,682 allocs, 2,525 frees, 309,657 bytes allocated
==1723818== 
==1723818== Searching for pointers to 1,157 not-freed blocks
==1723818== Checked 320,728 bytes
==1723818== 
==1723818== LEAK SUMMARY:
==1723818==    definitely lost: 0 bytes in 0 blocks
==1723818==    indirectly lost: 0 bytes in 0 blocks
==1723818==      possibly lost: 0 bytes in 0 blocks
==1723818==    still reachable: 173,445 bytes in 1,157 blocks
==1723818==         suppressed: 0 bytes in 0 blocks
==1723818== Reachable blocks (those to which a pointer was found) are not shown.
==1723818== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==1723818== 
==1723818== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

@VickNV I also added the full code to github for ease of access:

Any additional info on your part? Any other info I can provide?