How to locate a memory leak?

Started by
8 comments, last by rogierpennink 17 years, 10 months ago
Hello, I'm writing a chat protocol, but, having learned everything myself, I generally do not follow any 'universal' guidelines for programming simply because I do not know them. I started off with the server and because I didn't know the possible consequences of allocating memory dynamically I ended up with a server that would crash after 30 minutes simply because the memory would go up with every message sent. I allocated new memory for every message I would send. Of course I figured out this was the example of how not to do things, so I allocated three buffers at the start of the program to contain incoming network traffic, outgoing network traffic, and traffic to the server's output window. This caused, as I suspected, a major decrease in the way my memory usage increased, but still it happens that the memory used by the server slowly goes up... The only three places in my entire program where I use dynamic memory allocation now should be safe, because all the memory is freed after it's been used, so I'm really at a loss of where to find that one place that might cause the nagging memory leak... Is there any recommended way of finding these things, or preferred methods perhaps? I usually try to find any bugs using MessageBox() to output any variables that I'm not sure about, but that doesn't work for possible memory leaks...
Advertisement
Have you tried running it through a debugger? (I have no idea if that would detect it or not)
hippopotomonstrosesquippedaliophobia- the fear of big words
Just tried that, didn't think of it before :S but it didn't return anything. At least, I didn't see any debug-messages which I'm sure would be all flashy and stuff :P
Most memory allocators have functions that help you find the source of memory problems. If you are using Win32, check out this: Visual Studio -- The CRT Debug Heap
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
You mentioned MessageBox, so I'm going to assume that it's a Win32 program you're working on, and that you're using some flavour of Visual Studio. In debug mode, you can instruct Visual Studio to detect memory leaks for you.

Add the following headers to your program:

#include "stdlib.h"
#include "crtdbg.h"

In your WinMain function, add the following before any other code:

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

Now when you close your program after running it in Debug mode, Visual Studio will check for memory which has not been deallocated, and will report it in the Output window. There's only one drawback with this method: it's designed for C functionality. The Visual Studio output will tell you exactly where your memory was allocated, but I believe it only detects memory allocated with malloc and it's variants. That means if you use C++ new to allocate memory, Visual Studio will report that your memory was all allocated in exactly the same place: the implementation's new function, which uses malloc. However, even if Visual Studio can't explicitly locate the leak, knowing there is one should help.

[Edited by - iNsAn1tY on June 11, 2006 12:48:40 PM]
My opinion is a recombination and regurgitation of the opinions of those around me. I bring nothing new to the table, and as such, can be safely ignored.[ Useful things - Firefox | GLee | Boost | DevIL ]
Hmm, thanks for that suggestion... I'll be trying it right after dinner and update you on the results ;)
As an alternative which should give you more meaningful results, you can use a memory manager such as MMGR (there are others, I have just never tried them).
Just hit Ctr-shift-F, and search for all instances in your project you call new. Then verify that each instance has a matching delete at the appropriate place.
Quote:Original post by ph33r
Just hit Ctr-shift-F, and search for all instances in your project you call new. Then verify that each instance has a matching delete at the appropriate place.

There are a couple of problems with that approach:

- You can still forget to delete[] instead of delete
- An external API function might return a pointer that you are supposed to free yourself
I really can't figure out what might cause the problem so I'll post the code of my functions.h file here in the hope that anyone else might locate the leak. Before I'll paste it, here's some more info on the project. It has 3 header files ( sockets.h, functions.h and defines.h ) and one source file ( server.cpp ). sockets.h only contains one function which initizializes winsock and defines.h only defines stuff and has the declaration of the CLIENT struct in it. server.cpp sets up the GUI and does nothing but that so it's not important for this problem as the increase of memory only happens when it receives a lot of data. All data-receiving is handled in functions.h

Another minor note: This is a mix of C, C++ and general rubbish that's probably not common coding practise. Like I said, I learnt it all myself so if there's any comments i'd like to hear them...

// Function prototypesvoid UpdateOutput ( HWND hOutput, char * msg );void StartServer ( HWND hWnd );void StopServer ( HWND hWnd );int AssignClientID ( SOCKET s );void ParseCommands ( HWND hWnd );void HandleRecv ( char * cmd, int cl );// The Window ProcedureLRESULT CALLBACK WindowProcedure ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) {	switch ( msg ) {	case WM_CLOSE:				PostQuitMessage ( 0 );		return 0;		break;	case WM_CTLCOLORSTATIC:		SetBkColor((HDC)wParam, crEditBg); //Make background white				return (LRESULT)hbrEditBg;		break;		case SM_LSOCKETEVENT:	{		int event = WSAGETSELECTEVENT(lParam);		HWND hOutput = GetDlgItem ( hWnd, TXT_OUTPUT );					if ( event == FD_ACCEPT ) {			UpdateOutput ( hOutput, " - A client is connecting." );			SOCKADDR_IN client_addr;            int addrlen = sizeof(client_addr);			SOCKET clientsocket;						clientsocket = accept ( wParam, (SOCKADDR*)&client_addr, &addrlen);						if ( clientsocket == INVALID_SOCKET ) {				ZeroMemory ( &txtOutput, sizeof(txtOutput) );				sprintf ( txtOutput, "Error: %u", WSAGetLastError() );				UpdateOutput ( hOutput, txtOutput );							} else {											// add the client to the clients array				for ( int k = 0; k < MAX_CLIENTS; k++ ) {					if ( client[k].connected == 0 && client[k].ID == -1 ) { 						ZeroMemory ( &txtOutput, sizeof(txtOutput) );						sprintf ( txtOutput, "[server]: Connection from %s established.", inet_ntoa (client_addr.sin_addr) );						UpdateOutput ( hOutput, txtOutput );						client[k].connected = 1;						client[k].clientsocket = clientsocket;						client[k].sockaddr = client_addr;						client[k].ID = AssignClientID ( clientsocket );																								break;					}				}						} 								} else if ( event == FD_CLOSE ) {				// loop through registered clients				for ( int i = 0; i < MAX_CLIENTS; i++ ) {					if ( client.clientsocket == wParam ) {						ZeroMemory ( &txtOutput, sizeof(txtOutput) );						sprintf ( txtOutput, "[%s]: Disconnected from server.", inet_ntoa ( client.sockaddr.sin_addr ) );												shutdown ( client.clientsocket, SD_BOTH );						closesocket ( client.clientsocket );						client.ID = -1;						sprintf ( client.nick , "%s", "" );;						client.connected = 0;						UpdateOutput ( hOutput, txtOutput );											}									}		} else if ( event == FD_READ ) {			for ( int i = 0; i < MAX_CLIENTS; i++ ) {								if ( wParam == client.clientsocket ) {										// Empty the character-buffers					ZeroMemory ( &txtOutput, sizeof(txtOutput) );					ZeroMemory ( &rcvBuffer, sizeof(rcvBuffer) );					int len = sprintf ( txtOutput, "[%s]: ", inet_ntoa ( client.sockaddr.sin_addr ) );										int bytesReceived = recv ( wParam, rcvBuffer, 32768, 0 );					strcat ( txtOutput, rcvBuffer );																	UpdateOutput ( hOutput, txtOutput );														// Deal with the received string...					HandleRecv ( rcvBuffer, client.ID );									}			}		}				return 0;		break;	}			case WM_COMMAND:		switch ( LOWORD ( wParam ) ) {		case CMD_STARTSERVER:			{		    StartServer ( hWnd );			return 0;			break;			}		case CMD_STOPSERVER:			{			StopServer ( hWnd );			break;			}		case CMD_CLEARLOG:			{			HWND hOutput = GetDlgItem ( hWnd, TXT_OUTPUT );			SetWindowText ( hOutput, "========================================================\r\n    Welcome to the MUD 1.0 Server. Press 'start server' to begin!\r\n========================================================\r\n" );			break;			}		}		default:		return DefWindowProc ( hWnd, msg, wParam, lParam );		break;	}}// Add a line to the output window with this functionvoid UpdateOutput ( HWND hOutput, char * msg ){	// Get the time in H:M:S	SYSTEMTIME st;	GetSystemTime ( &st );	char * tBuffer = (char *)malloc(17*sizeof(char));	sprintf ( tBuffer, "[%02d:%02d:%02d] ", st.wHour, st.wMinute, st.wSecond );		int bufferSize = GetWindowTextLength(hOutput) + 1;	// allocate a buffer    char * buffer = (char *)malloc(bufferSize * sizeof(char));    // read into the buffer;    GetWindowText( hOutput, buffer, bufferSize * sizeof(char) );	char * message = (char *)malloc(bufferSize + strlen(tBuffer) + strlen(msg) + strlen("\r\n"));	strcpy(message, buffer);	strcat(message, tBuffer);	strcat(message, msg);	strcat(message, "\r\n");	free(buffer);	SetWindowText ( hOutput, message );	free(message);	free(tBuffer);	int lineCount = SendMessage ( hOutput, EM_GETLINECOUNT, 0, 0 );	SendMessage ( hOutput, EM_LINESCROLL, 0, lineCount );}void StopServer ( HWND hWnd ) {		HWND hOutput = GetDlgItem ( hWnd, TXT_OUTPUT );	if ( serverstarted == 1 ) {		shutdown ( listensocket, SD_BOTH );		closesocket ( listensocket );		WSACleanup();		serverstarted = 0;		// Enable the startbutton, disable the stop button		HWND hStartButton = GetDlgItem ( hWnd, CMD_STARTSERVER );		HWND hStopButton = GetDlgItem ( hWnd, CMD_STOPSERVER );		if ( !IsWindowEnabled ( hStartButton ) )		  EnableWindow ( hStartButton, TRUE );		if ( IsWindowEnabled ( hStopButton ) )		  EnableWindow ( hStopButton, FALSE );		UpdateOutput ( hOutput, "[server]: Server has shut down." );			    	}}void StartServer ( HWND hWnd ) {	// Set all clients to nothing	for ( int i = 0; i < MAX_CLIENTS; i++ ) {		client.clientsocket = INVALID_SOCKET;		client.connected = 0;		client.ID = -1;		sprintf ( client.nick, "%s", "" );			}	if ( serverstarted == 0 ) {		int srvReturn = InitServer ( listensocket, WsaData, localAddress, 6000, hWnd );					HWND hOutput = GetDlgItem ( hWnd, TXT_OUTPUT );					if ( srvReturn == ERROR_NOWINSOCK ) {			UpdateOutput ( hOutput, (char *) "[server]: Could not initialize winsock environment." );			serverstarted = 0;										} else if ( srvReturn == INVALID_SOCKET ) {			UpdateOutput ( hOutput, (char *) "[server]: Could not initialize server socket." );			serverstarted = 0;						} else if ( srvReturn == ERROR_MESSAGEROUTER ) {			UpdateOutput ( hOutput, (char *) "[server]: Could not set up network message router." );			serverstarted = 0;						} else if ( srvReturn == ERROR_NOBIND ) {			UpdateOutput ( hOutput, (char *) "[server]: Could not bind to specified address." );							serverstarted = 0;						} else if ( srvReturn == ERROR_NOLISTEN ) {								UpdateOutput ( hOutput, (char *) "[server]: Could not listen at specified address." );			serverstarted = 0;		} else {							UpdateOutput ( hOutput, (char *) "[server]: Server is now listening at specified port." );						serverstarted = 1;						}			if ( serverstarted == 1 ) {			HWND hStartButton = GetDlgItem ( hWnd, CMD_STARTSERVER );			HWND hStopButton = GetDlgItem ( hWnd, CMD_STOPSERVER );			if ( IsWindowEnabled ( hStartButton ) )			  EnableWindow ( hStartButton, FALSE );			if ( !IsWindowEnabled ( hStopButton ) )			  EnableWindow ( hStopButton, TRUE );		}	}}int AssignClientID ( SOCKET s ){	int ID = -1;	for ( int i = 0; i < MAX_CLIENTS; i++ ) {		if ( client.clientsocket == s ) {			ID = i;		}	}	return ID;}void ParseCommands ( HWND hWnd ){	// Get output window handle	HWND hOutput = GetDlgItem ( hWnd, TXT_OUTPUT );	HWND hInput = GetDlgItem ( hWnd, TXT_INPUT );	}void HandleRecv ( char * cmd, int id ){		int cl;		// Retrieve the correct client by id...	for ( int j = 0; j < MAX_CLIENTS; j++ ) {		if ( client[j].ID == id ) {						cl = j;		}	}    	// This is the heart of the protocol! It deals with all commands!	char * cmdPart = strtok ( cmd, " \0" );		if ( stricmp (cmdPart,"NAME") == 0 ) {		// Further split the string...		cmdPart = strtok ( NULL, " " );			// Is the name taken already?		int iNickFalse = 0;		for ( int i = 0; i < MAX_CLIENTS; i++ ) {				if ( stricmp ( cmdPart, client.nick ) == 0 && client.connected == 1 ) {							iNickFalse = 1;			}		}						if ( iNickFalse == 1 ) {						ZeroMemory ( &sndBuffer, sizeof(sndBuffer) );			sprintf ( sndBuffer, "NAME !INVALIDNAME\0" );			send ( client[cl].clientsocket, sndBuffer, strlen(sndBuffer)*sizeof(char), 0 );		} else {					    sprintf ( client[cl].nick, "%s", cmdPart );						ZeroMemory ( &sndBuffer, sizeof(sndBuffer) );		    		    sprintf ( sndBuffer, "NAME %s\0", cmdPart );						    if ( send ( client[cl].clientsocket, sndBuffer, strlen(sndBuffer)*sizeof(char), 0 ) == SOCKET_ERROR ) {			    MessageBox ( NULL, "cannot send stuff...", "blah", NULL );			}		}	} if ( stricmp ( cmdPart, "MSG" ) == 0 ) {		// When we are giving the chat the feature of 'channels'		// There will have to be checked for in which room the		// Message has been sent.				char * cmdPart1 = strtok ( NULL, "" );				ZeroMemory ( &sndBuffer, sizeof(sndBuffer) );		strcpy ( sndBuffer, cmdPart );		strcat ( sndBuffer, " " );				strcat ( sndBuffer, cmdPart1 );				for ( int i = 0; i < MAX_CLIENTS; i++ ) {			if ( client.connected == 1 ) {								send ( client.clientsocket, sndBuffer, strlen(sndBuffer)*sizeof(char), 0 );			}		}	}}

This topic is closed to new replies.

Advertisement