This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Possible memory leak when attempting to connect to a TCP server with NDK

Other Parts Discussed in Thread: SYSBIOS

Hi all,

I am working on a project that will send packets from a Tiva-C board (TM4C129NCPDT) over TCP to a computer. To accomplish this, I am using TI RTOS with the NDK. When the Tiva-C obtains an IP address from a router, the following task is triggered:

void netIPAddrHook(void)
{
	static bool task_created = false;
    Task_Handle taskHandle;
    Task_Params taskParams;
    Error_Block eb;

    if(!task_created)
    {
		Error_init(&eb);
		Task_Params_init(&taskParams);
		taskParams.stackSize = 1024;
		taskParams.priority = 1;
		taskHandle = Task_create((Task_FuncPtr)TcpHandler, &taskParams, &eb);
		if (taskHandle == NULL)
		{
			System_printf("netIPAddrHook: Failed to create TcpHandler Task\n");
		}
		System_flush();
		task_created = true;
    }
}

This task creates another task which will handle the TCP connection. The TCP task should attempt to create the socket, connect to the server running on the computer, and send packets to the server whenever they become available. If something goes wrong, it should keep trying to reconnect.

void TcpHandler(void)
{
	int16_t sockfd;
	struct sockaddr_in server_info;
	struct sPktInfo pkt;

	// Fill structure with info on server to connect to
	memset(&server_info, 0, sizeof(server_info));
	server_info.sin_family = AF_INET;
	inet_pton(AF_INET, SERVER_IP, &(server_info.sin_addr));
	server_info.sin_port = htons(SERVER_PORT);

	for(;;)
	{
		// Create a new socket
		if(sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) != -1)
		{
			// If socket creation is successful attempt to connect to server
			if(connect(sockfd, (struct sockaddr *)&server_info, sizeof(server_info)) != -1)
			{
				// If connection is successful, wait to send data
				for(;;)
				{
					// Wait until data has been received via the radio
					Mailbox_pend(pkt_mbx, &pkt, BIOS_WAIT_FOREVER);
					// Send data, attempt to reconnect if it fails
					if(TcpSendAll(sockfd, pkt.data, PACKET_LEN) == -1) break;
				}
			}
			else
			{
				DbgPrintf(DBG_INFO, "connect failed: error = %d\n", errno);
				System_flush();
			}
			// Catch exception from connection or send fails
			close(sockfd);
		}
		else
		{
			DbgPrintf(DBG_INFO, "socket creation failed: error = %d\n", errno);
			System_flush();
		}
	}
}

So my here's my issue: once it gets to this task, it creates a socket and tries to connect seven times (I haven't figured out why the connect is failing yet... there are a lot of places this could have gone wrong, and not just in the Tiva-C code, but that's an issue for another day). However, after the seventh time, it is unable to create a socket. I start getting heap warnings telling me that it is out of memory - so perhaps there is a memory leak? Does anyone have thoughts on what may be causing this? I'm pretty new to network programming, so I may be doing something blatantly wrong.

Finally, here is the output to the debugger console:

Using MAC address in flash
Service Status: DHCPC : Enabled : : 000
Service Status: DHCPC : Enabled : Running : 000
Network Added: If-1:192.168.1.101
Service Status: DHCPC : Enabled : Running : 017

00003.501 connect failed: error = 9

00003.501 connect failed: error = 9

00003.501 connect failed: error = 9

00003.501 connect failed: error = 9

00003.502 connect failed: error = 9

00003.502 connect failed: error = 9

00003.502 connect failed: error = 9

ti.sysbios.heaps.HeapMem: line 307: out of memory: handle=0x2001154c, size=1032
00003.503 mmBulkAlloc(): could not allocate memory.
00003.503 out of memory: handle=0x0, size=536873180
00003.503 SBNew: Buffer OOM
00003.503 socket creation failed: error = 12

  • Hi Tyler,

    Whenever a TCP socket is opened, a send and receive buffer is allocated. The size of the buffer depends on the target and configuration (2K for Tiva devices by default). If the TCP sockets are created and closed very quickly, many sockets will be put in a timedwait state and the scratchpad memory will be exhausted (buffers linger around for a while after closing socket) resulting in out of memory errors. A solution to this problem is to set the SO_LINGER option. You can checkout this thread for info on how to use this option:

    https://e2e.ti.com/support/embedded/tirtos/f/355/t/176347

    Alternatively, you can look at the NDK API guide.

    Best,

    Ashish

  • I added

    linger_opts.l_onoff = 1;
    linger_opts.l_linger = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &linger_opts, sizeof(linger_opts));

    to right after the socket creation (and of course "struct linger linger_opts;" to the top of the task). However, I am still seeing the same behavior. So unless I am setting this option incorrectly, it seems there is a different issue.

  • Tyler,

    Can you print the memory manager report each time a connect fails ? The mem manager report will confirm a memory leak.

    Look for mmCheck function in the NDK user's guide for more info on how to generate the report:
    www.ti.com/.../spru523j.pdf

    Another thing you can try is call Task_sleep(1) whenever connect fails. This will idle thread an opportunity to run and cleanup any terminated tasks. If adding sleep helps then it will serve as a hint to the real problem.

    Best,
    Ashish
  • I have tried calling Task_sleep. I even tried having the task sleep for up to a second with no success.

    Regarding the mmCheck function, I am actually having a hard time including it in my project. I'm not sure, but I think it might be because in my project the NDK is included as part of TI-RTOS for TivaC (2.14.0.10) and so the configuration seems different than what's assumed in the user's guide. I tried including the oskern.h file that _mmCheck is defined in directly, but that threw up a bunch of errors.

    Any suggestions would be appreciated.

  • What error do you get when using mmCheck ? Also, can you share your application's cfg file ?

    Best,
    Ashish
  • I get errors related to oskern.h when I try to build the project. It gives me errors like "#20 identifier 'uint' is undefined" and "#66 expected a ';' " for the header file. However, if I hover over what it claims are undefined types, code composer shows me their definition. Also, on the lines where it says it expects a semicolon, there are clearly semicolons already there. And then in my file, it tells that _mmCheck is declared implicitly even though I can hit F3 and go to the function declaration.

    Here is my config:

    /* ================ General configuration ================ */
    var Defaults = xdc.useModule('xdc.runtime.Defaults');
    var Diags = xdc.useModule('xdc.runtime.Diags');
    var Error = xdc.useModule('xdc.runtime.Error');
    var Log = xdc.useModule('xdc.runtime.Log');
    var Main = xdc.useModule('xdc.runtime.Main');
    var Memory = xdc.useModule('xdc.runtime.Memory');
    var System = xdc.useModule('xdc.runtime.System');
    var Text = xdc.useModule('xdc.runtime.Text');
    
    var BIOS = xdc.useModule('ti.sysbios.BIOS');
    var Task = xdc.useModule('ti.sysbios.knl.Task');
    var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore');
    var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
    var Hwi = xdc.useModule('ti.sysbios.hal.Hwi');
    var Swi = xdc.useModule('ti.sysbios.knl.Swi');
    var Clock = xdc.useModule('ti.sysbios.knl.Clock');
    var Timer = xdc.useModule('ti.sysbios.hal.Timer');
    var Mailbox = xdc.useModule('ti.sysbios.knl.Mailbox');
    
    BIOS.heapSize = 20480;
    Task.idleTaskStackSize = 768;
    
    /*
     *  Program.stack is ignored with IAR. Use the project options in
     *  IAR Embedded Workbench to alter the system stack size.
     */
    if (!Program.build.target.$name.match(/iar/)) {
        /*
         *  Reducing the system stack size (used by ISRs and Swis) to reduce
         *  RAM usage.
         */
        Program.stack = 2048;
    }
    
    /* ================ System configuration ================ */
    var SysMin = xdc.useModule('xdc.runtime.SysMin');
    System.SupportProxy = SysMin;
    
    /* Runtime stack checking is performed */
    Task.checkStackFlag = true;
    Hwi.checkStackFlag = true;
    
    /* Enable Semihosting for GNU targets to print to CCS console */
    if (Program.build.target.$name.match(/gnu/)) {
        var SemiHost = xdc.useModule('ti.sysbios.rts.gnu.SemiHostSupport');
    }
    
    /* ================ Logging configuration ================ */
    var LoggingSetup = xdc.useModule('ti.uia.sysbios.LoggingSetup');
    
    /* ================ Kernel configuration ================ */
    /* Use Custom library */
    var BIOS = xdc.useModule('ti.sysbios.BIOS');
    BIOS.libType = BIOS.LibType_Custom;
    BIOS.logsEnabled = true;
    BIOS.assertsEnabled = true;
    
    /* ================ NDK configuration ================ */
    var Ndk       = xdc.loadPackage('ti.ndk.config');
    var Global    = xdc.useModule('ti.ndk.config.Global');
    var Ip        = xdc.useModule('ti.ndk.config.Ip');
    var Tcp       = xdc.useModule('ti.ndk.config.Tcp');
    
    Global.IPv6 = false;
    Global.stackLibType = Global.MIN;
    Global.networkOpenHook = null;
    Global.networkIPAddrHook = "&netIPAddrHook";
    
    /* automatically call fdOpen/CloseSession for our sockets Task */
    Global.autoOpenCloseFD = true;
    
    Global.pktNumFrameBufs = 10;
    Global.memRawPageCount = 6;
    Global.ndkThreadStackSize = 1536;
    Global.lowTaskStackSize = 1024;
    Global.normTaskStackSize = 1024;
    Global.highTaskStackSize = 1024;
    Tcp.transmitBufSize = 1024;
    Tcp.receiveBufSize = 1024;
    
    /* ================ Driver configuration ================ */
    var TIRTOS = xdc.useModule('ti.tirtos.TIRTOS');
    TIRTOS.useGPIO = true;
    TIRTOS.libType = TIRTOS.LibType_NonInstrumented;
    TIRTOS.useSPI = true;
    TIRTOS.useUART = false;
    
    /* Added for TCP */
    TIRTOS.useEMAC = true;
    TIRTOS.useGPIO = true;
    Ip.autoIp = true;
    Ip.address = "";
    
    var swi0Params = new Swi.Params();
    swi0Params.instance.name = "gdo0_swi";
    swi0Params.priority = 15;
    Program.global.gdo0_swi = Swi.create("&ReceivePacket", swi0Params);
    Clock.tickPeriod = 1000;
    var clock0Params = new Clock.Params();
    clock0Params.instance.name = "channel_timeout";
    clock0Params.startFlag = true;
    Program.global.channel_timeout = Clock.create("&ChannelTimeout", 1500, clock0Params);
    var task1Params = new Task.Params();
    task1Params.instance.name = "usb_tx_task";
    Program.global.usb_tx_task = Task.create("&UsbTransmit", task1Params);
    var task2Params = new Task.Params();
    task2Params.instance.name = "change_channel_task";
    Program.global.change_channel_task = Task.create("&ChangeChannel", task2Params);
    var semaphore2Params = new Semaphore.Params();
    semaphore2Params.instance.name = "change_channel_sem";
    semaphore2Params.mode = Semaphore.Mode_BINARY;
    Program.global.change_channel_sem = Semaphore.create(null, semaphore2Params);
    var mailbox0Params = new Mailbox.Params();
    mailbox0Params.instance.name = "pkt_mbx";
    Program.global.pkt_mbx = Mailbox.create(32, 5, mailbox0Params);

  • Hi Tyler,

    We are looking into the problem and will get back to you soon.

    Best,
    Ashish
  • Ashish,

    I recently resolved the issues I was having with _mmCheck. I had to include netmain.h in my project and then include oskern.h after netmain.h. In making this change, I also transitioned from using the BSD style sockets to the non-BSD API. Somehow, this seems to have fixed (or at least avoided) my memory problem - I'm not seeing any errors on this for a least a couple hours of run time now. And mmCheck seems to confirm this as the memory report doesn't change after each connect attempt. Also, now my connect error is a timeout error instead of a bad file descriptor error. Here's a sample of the debugger console:

    Using MAC address in flash
    Service Status: DHCPC : Enabled : : 000
    Service Status: DHCPC : Enabled : Running : 000
    Network Added: If-1:192.168.1.102
    Service Status: DHCPC : Enabled : Running : 017
    00009.400 TcpTimeoutRexmt: Retransmit Timeout
    00033.400 TcpTimeoutRexmt: Retransmit Timeout
    00078.400 TcpTimeoutKeep: Keep Timeout
    00078.400 connect failed: error = 60

    24:48 ( 37%) 17:96 ( 53%) 1:128 ( 4%) 2:256 ( 16%)
    1:512 ( 16%) 0:1536 0:3072
    (15360/18432 mmAlloc: 50/0/9, mmBulk: 3/0/2)

    1 blocks alloced in 512 byte page
    22 blocks alloced in 48 byte page
    17 blocks alloced in 96 byte page
    1 blocks alloced in 128 byte page

    00084.400 TcpTimeoutRexmt: Retransmit Timeout
    00108.400 TcpTimeoutRexmt: Retransmit Timeout
    00153.400 TcpTimeoutKeep: Keep Timeout
    00153.400 connect failed: error = 60

    24:48 ( 37%) 19:96 ( 59%) 1:128 ( 4%) 2:256 ( 16%)
    1:512 ( 16%) 0:1536 0:3072
    (15360/18432 mmAlloc: 58/0/17, mmBulk: 5/0/4)

    1 blocks alloced in 512 byte page
    22 blocks alloced in 48 byte page
    17 blocks alloced in 96 byte page
    1 blocks alloced in 128 byte page

    00159.400 TcpTimeoutRexmt: Retransmit Timeout
    00183.400 TcpTimeoutRexmt: Retransmit Timeout
    00228.400 TcpTimeoutKeep: Keep Timeout
    00228.400 connect failed: error = 60

    24:48 ( 37%) 19:96 ( 59%) 1:128 ( 4%) 2:256 ( 16%)
    1:512 ( 16%) 0:1536 0:3072
    (15360/18432 mmAlloc: 62/0/21, mmBulk: 7/0/6)

    1 blocks alloced in 512 byte page
    22 blocks alloced in 48 byte page
    17 blocks alloced in 96 byte page
    1 blocks alloced in 128 byte page

    00234.400 TcpTimeoutRexmt: Retransmit Timeout
    00258.400 TcpTimeoutRexmt: Retransmit Timeout
    00303.400 TcpTimeoutKeep: Keep Timeout
    00303.400 connect failed: error = 60

    24:48 ( 37%) 19:96 ( 59%) 1:128 ( 4%) 2:256 ( 16%)
    1:512 ( 16%) 0:1536 0:3072
    (15360/18432 mmAlloc: 70/0/29, mmBulk: 9/0/8)

    1 blocks alloced in 512 byte page
    22 blocks alloced in 48 byte page
    17 blocks alloced in 96 byte page
    1 blocks alloced in 128 byte page

    Also, here is my updated TCP handler:

    void TcpHandler(void)
    {
    	SOCKET sockfd;
    	struct sockaddr_in server_info;
    	struct sPktInfo pkt;
    
    	// Fill structure with info on server to connect to
    	memset(&server_info, '0', sizeof(server_info));
    	server_info.sin_family = AF_INET;
    	if(inet_pton(AF_INET, SERVER_IP, &server_info.sin_addr) <= 0) System_printf("inet_pton error\n");
    	server_info.sin_port = htons(SERVER_PORT);
    
    	for(;;)
    	{
    		// Create a new socket
    		sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    		if(sockfd != INVALID_SOCKET)
    		{
    			// If socket creation is successful attempt to connect to server
    			if(connect(sockfd, (PSA)&server_info, sizeof(server_info)) != -1)
    			{
    				// If connection is successful, wait to send data
    				for(;;)
    				{
    					// Wait until data has been received via the radio
    					Mailbox_pend(pkt_mbx, &pkt, BIOS_WAIT_FOREVER);
    					// Send data, attempt to reconnect if it fails
    					if(TcpSendAll(sockfd, pkt.data, PACKET_LEN) == -1) break;
    				}
    			}
    			else DbgPrintf(DBG_INFO, "connect failed: error = %d\n", fdError());
    			// Catch exception from connection or send fails
    			if(fdClose(sockfd) == -1) DbgPrintf(DBG_INFO, "socket close failed");
    			_mmCheck(MMCHECK_MAP, &System_printf);
    		}
    		else DbgPrintf(DBG_INFO, "socket creation failed: error = %d\n", fdError());
    		System_flush();
    	}
    }

    I can't figure out what the difference is. I don't think any of the changes I made to the TCP handler would have changed the functionality. Any thoughts on why I am no longer seeing the memory issue?

  • Hi Tyler,

    Tyler Horton said:
    		// Create a new socket
    		if(sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) != -1)
    		{
    

    The above code is from your original post and has an issue in the ordering of operations. sockfd is being set to the boolean result of the following comparison operation:

    socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) != -1

    In your more recent code, this has been fixed and therefore you don't see a problem:

    // Create a new socket
            sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(sockfd != INVALID_SOCKET)
            {

    Regarding the timeout issue, is your app using the loopback ip address as the server IP ? If so, then you need to have a socket listening for the connection otherwise the client connection will fail.

    Best,

    Ashish

  • That makes sense.

    I ended up resolving my timeout issue as well. My best guess is that the incoming connections to the computer I was running my TCP server on were getting blocked by some sort of enterprise firewall. I ran my server on a different computer that didn't have the enterprise configuration and I was able to successfully connect.

    Thanks for the help.