Sign in to follow this  

How to locate a memory leak?

This topic is 4207 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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...

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 prototypes
void 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 Procedure
LRESULT 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[i].clientsocket == wParam ) {

ZeroMemory ( &txtOutput, sizeof(txtOutput) );
sprintf ( txtOutput, "[%s]: Disconnected from server.", inet_ntoa ( client[i].sockaddr.sin_addr ) );

shutdown ( client[i].clientsocket, SD_BOTH );
closesocket ( client[i].clientsocket );
client[i].ID = -1;
sprintf ( client[i].nick , "%s", "" );;
client[i].connected = 0;

UpdateOutput ( hOutput, txtOutput );

}

}

} else if ( event == FD_READ ) {

for ( int i = 0; i < MAX_CLIENTS; i++ ) {

if ( wParam == client[i].clientsocket ) {

// Empty the character-buffers
ZeroMemory ( &txtOutput, sizeof(txtOutput) );
ZeroMemory ( &rcvBuffer, sizeof(rcvBuffer) );

int len = sprintf ( txtOutput, "[%s]: ", inet_ntoa ( client[i].sockaddr.sin_addr ) );
int bytesReceived = recv ( wParam, rcvBuffer, 32768, 0 );

strcat ( txtOutput, rcvBuffer );

UpdateOutput ( hOutput, txtOutput );

// Deal with the received string...
HandleRecv ( rcvBuffer, client[i].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 function
void 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[i].clientsocket = INVALID_SOCKET;
client[i].connected = 0;
client[i].ID = -1;
sprintf ( client[i].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[i].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[i].nick ) == 0 && client[i].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[i].connected == 1 ) {

send ( client[i].clientsocket, sndBuffer, strlen(sndBuffer)*sizeof(char), 0 );

}

}

}


}

Share this post


Link to post
Share on other sites

This topic is 4207 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this