I have an application that by necessity launches a dialogue from a menu item in which the Widgets in the dialogue are created each time, they vary from invocation to invocation.
The application is very long lived and the dialogue used often.
Looking at /proc/$pid/status, VmData is growing.
valgrind indicates the memory leak is with ToolTips.
Below is a test program and valgrind snippet.
On running the program and selecting
Application->Leaky Dialogue->All Done
then
Application->Quit
No leak is detected.
Repeated display of Leaky Dialogue starts the leak and it grows for each invocation.
I don't make use of the ToolTips so if turning them off is a fix that is fine.
I do valgrind --leak-check=yes leak 2>/tmp/trace.txt
to get the snippet below.
Program and Makefile also follow:
==6338====6338== 640 bytes in 80 blocks are definitely lost in loss record 923 of 973==6338== at 0x4022BB5: calloc (vg_replace_malloc.c:418)==6338== by 0x42A6086: XtCalloc (in /usr/lib/libXt.so.6.0.0)==6338== by 0x41F2568: XmSetToolTipString (ToolTip.c:333)==6338== by 0x4172D58: Initialize (Primitive.c:782)==6338== by 0x42AE577: ??? (in /usr/lib/libXt.so.6.0.0)==6338== by 0x42AE537: ??? (in /usr/lib/libXt.so.6.0.0)==6338== by 0x42AE537: ??? (in /usr/lib/libXt.so.6.0.0)==6338== by 0x42AEFFA: ??? (in /usr/lib/libXt.so.6.0.0)==6338== by 0x42AF8BE: _XtCreateWidget (in /usr/lib/libXt.so.6.0.0)==6338== by 0x42AFB75: XtCreateManagedWidget (in /usr/lib/libXt.so.6.0.0)==6338== by 0x80492D0: leakydlg(_WidgetRec*) (leak.cc:183)==6338== by 0x80493BA: app_menu_leak_fn(_WidgetRec*, void*) (leak.cc:79)==6338==
Program/*** Demonstate leak in Tool Tips***/#include <stdio.h>#include <string.h>#include <strings.h>#include <time.h>#include <math.h>#include <stdlib.h>#include <errno.h>#include <ctype.h>#include <limits.h>#include <Xm/Xm.h>#include <Xm/Form.h>#include <Xm/RowColumn.h>#include <Xm/MwmUtil.h>#include <Xm/Protocols.h>#include <Xm/ToggleB.h>#include <Xm/PushB.h>#include <Xm/CascadeB.h>#include <Xm/RowColumn.h>#include <Xm/Separator.h>#include <Xm/MessageB.h>#include <Xm/ToggleB.h>#include <Xm/Frame.h>#include <Xm/Label.h>#include <Xm/TextF.h>#include <X11/xpm.h>#include <unistd.h>
#define LAYOUT_WIDTH 300#define LAYOUT_HEIGHT 300#define FALLBACK_RESOURCES { (String)"*foreground: wheat", \(String)"*background: DarkSlateGray", \(String)"*keyboardFocusPolicy: pointer", \(String)"*highlightOnEnter: TRUE", \NULL }#define DEFAULT_BACKGROUND_COLOUR "DarkSlateGray"#define DEFAULT_FOREGROUND_COLOUR "wheat"#define LAYOUT_ARGS { { XmNwidth, (XtArgVal)LAYOUT_WIDTH }, \{ XmNheight, (XtArgVal)LAYOUT_HEIGHT }, \{ XmNtitle, (XtArgVal)"Leak Test Display" }, \{ XmNiconName, (XtArgVal)"Leak Test Display" }, \{ XmNdeleteResponse, XmDO_NOTHING }, \{ XmNmwmDecorations, MWM_DECOR_ALL | \MWM_DECOR_RESIZEH | \MWM_DECOR_MAXIMIZE } }#define MENU_BAR_ARGS { { XmNtopAttachment, XmATTACH_FORM }, \{ XmNrightAttachment, XmATTACH_FORM }, \{ XmNleftAttachment, XmATTACH_FORM } }#define PULLDOWN_MENU_ARGS { { XmNtearOffModel, XmTEAR_OFF_DISABLED } }#define PULLDOWN_MENU_TEAROFF_ARGS { { XmNtearOffModel, XmTEAR_OFF_ENABLED } }/*** Prototypes*/void app_menu_quit_fn( Widget w, XtPointer pt );void clean_exit( Widget w, XtPointer p1, XtPointer p2 );Widget CreateMenu( Widget parent );void app_menu_leak_fn( Widget w, XtPointer pt );void dialogue_response( Widget w, int *answer, XmPushButtonCallbackStruct *cbs );int leakydlg( Widget parent );void app_menu_quit_fn( Widget w, XtPointer pt ){clean_exit( NULL, NULL, NULL );}void app_menu_leak_fn( Widget w, XtPointer pt ){leakydlg( w );}void clean_exit( Widget w, XtPointer p1, XtPointer p2 ){exit( 0 );}void dialogue_response( Widget w, int *answer, XmPushButtonCallbackStruct *cbs ){switch( cbs->reason){case XmCR_OK:*answer = 1;break;case XmCR_CANCEL:*answer = 2;break;default:break;}}/*** The leaky dialogue follows.** Just create and destroy widgets*/int leakydlg( Widget parent ){int n, nw, block;char temp_string[128];Widget dialogue, blockrc, all, bf, bfl, *bw;XtAppContext context;XmString xmOk, xmtitle;Arg args[32];int answer;context = XtWidgetToApplicationContext( parent );answer = 0;nw = 40;bw = (Widget *)calloc( sizeof( Widget ), nw + 1 );/*** Set up the args and dynamic features*/xmOk = XmStringCreateLocalized( (char *)"All Done" );xmtitle = XmStringCreateLocalized( (char *)"Dynamically created widgets" );n = 0;XtSetArg( args[n], XmNmessageString, xmtitle ); n++;XtSetArg( args[n], XmNokLabelString, xmOk ); n++;XtSetArg( args[n], XmNautoUnmanage, FALSE ); n++;XtSetArg( args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL ); n++;/*** Create the dialogue as a child of the passed parent** as a container for widgets.*/dialogue = XmCreateTemplateDialog( parent, (char *)"leakydlg", args, n );if ( bw == NULL ){XmStringFree( xmOk );xmOk = XmStringCreateLocalized( (char *)"Memory calloc error" );n = 0;XtSetArg( args[n], XmNokLabelString, xmOk ); n++;XtSetValues( dialogue, args, n );}else{/*** Add a rowcolumn widget to be the manager widget for all the others in the dialogue*/n = 0;XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;//XtSetArg( args[n], XmNpacking, XmPACK_COLUMN ); n++;XtSetArg( args[n], XmNpacking, XmPACK_TIGHT ); n++;XtSetArg( args[n], XmNnumColumns, 1 ); n++;XtSetArg( args[n], XmNadjustLast, False ); n++;all = XmCreateRowColumn( dialogue, (char *)"all", args, n );/*** A frame for the block list*/bf = XtCreateManagedWidget( "blockframe", xmFrameWidgetClass, all, NULL, 0 );n = 0;XtSetArg( args[n], XmNchildType, XmFRAME_TITLE_CHILD ); n++;bfl = XtCreateManagedWidget( "Blocks to choose...", xmLabelWidgetClass, bf, args, n );/*** Create a rowcolumn widget attached to the botton of the list** box to hold the block check boxes.*/n = 0;XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;XtSetArg( args[n], XmNpacking, XmPACK_COLUMN ); n++;XtSetArg( args[n], XmNnumColumns, 10 ); n++;blockrc = XmCreateRowColumn( bf, (char *)"blockrc", args, n );n = 0;for ( block = 1; block <= nw; block++ ){sprintf( temp_string, "Block %d", block );bw[block] = XtCreateManagedWidget( temp_string, xmToggleButtonWidgetClass, blockrc, args, n );}XtManageChild( blockrc );XtManageChild( all );}XtAddCallback( dialogue, XmNokCallback, (XtCallbackProc)dialogue_response, &answer );XtManageChild( dialogue );/*** Run a local event loop to simulate a blocking mechanism*/while( answer == 0 || XtAppPending( context ) )XtAppProcessEvent( context, XtIMAll );XmStringFree( xmOk );XmStringFree( xmtitle );XtDestroyWidget( dialogue );free( bw );return answer;}Widget CreateMenu( Widget parent ){Widget menubar, wapplication, w;Arg menu_bar_args[] = MENU_BAR_ARGS, pulldown_menu_args[] = PULLDOWN_MENU_ARGS;Arg arg[20];int n;/*** Menu Bar*/menubar = XmCreateMenuBar( parent, (char *)"menu", menu_bar_args, XtNumber( menu_bar_args ) );/*** Menu Panes*/wapplication = XmCreatePulldownMenu( menubar, (char *)"applicationsubmenu", pulldown_menu_args, XtNumber( pulldown_menu_args ) );/*** Application Sub menu*/n = 0;XtSetArg( arg[n], XmNsubMenuId, wapplication ); n++;w = XmCreateCascadeButton( menubar, (char *)"Application", arg, n );XtManageChild( w );w = XmCreatePushButton( wapplication, (char *)"Quit", NULL, 0 );XtManageChild( w );XtAddCallback( w, XmNactivateCallback, (XtCallbackProc)app_menu_quit_fn, (XtPointer)NULL );w = XmCreatePushButton( wapplication, (char *)"Leaky Dialogue", NULL, 0 );XtManageChild( w );XtAddCallback( w, XmNactivateCallback, (XtCallbackProc)app_menu_leak_fn, (XtPointer)NULL );return menubar;}int main( int argc, char **argv ){Widget top_wid, form, menubar;Arg layout_args[] = LAYOUT_ARGS;String fallback_resources[] = FALLBACK_RESOURCES;Atom wm_atom;XtAppContext app;/*** Creat the application, widgets, menu, and the container** The default args has a window title etc. Change them if one is available*/top_wid = XtAppInitialize( &app, "LEAKDisplay", NULL, 0, &argc, argv, fallback_resources, layout_args, XtNumber( layout_args ) );/*** Add the quit function*/wm_atom = XInternAtom( XtDisplay( top_wid ), "WM_DELETE_WINDOW", FALSE );XmAddWMProtocolCallback( top_wid, wm_atom, (XtCallbackProc)clean_exit, (XtPointer)NULL );form = XmCreateForm( top_wid, (char *)"container", NULL, 0 );menubar = CreateMenu( form );XtManageChild( menubar );XtManageChild( form );XtRealizeWidget( top_wid );XtAppMainLoop( app );return 0;}/*vim:ts=4:sw=4:nowrap:ai:lines=50:co=115:bs=1*/
MakeFileLIBXMU=-lXmuXLDOPTS=-L/usr/X11R6/lib/ -lXm $(LIBXMU) -lXt -lX11 -lXpm
DEBUG=-g#OPT=-O2OPT=-O0SOURCEPATH=.BINPATH=.OBJPATH=.LDOPTS= -L$(OBJPATH) -lmINCL= -I. -I..GUIINCL= -I/usr/X11R6/include -I/usr/X11R6/include/X11ELF= # -fPICCFLAGS= -c -DANSICPP -pedantic -Wall -rdynamic $(CUSTMOPTS)CC= g++ $(DEBUG) $(OPT)VPATH=$(SOURCEPATH):$(OBJPATH):$(BINPATH)ALL= leak.PHONY: all cleanall: $(ALL)leak: leak.o$(CC) $(OBJPATH)/leak.o \$(LDOPTS) $(XLDOPTS) $(DEBUG) -o $(BINPATH)/leakleak.o: leak.cc$(CC) $(CFLAGS) $(INCL) $(GUIINCL) -o $(OBJPATH)/$*.o $(SOURCEPATH)/$*.cc
clean:rm -f $(OBJPATH)/leak*.orm -f $(BINPATH)/leak
The fix I'm trialling is to ToolTip.c to function _XmToolTipRemove().
The new function follow:
#ifdef FIX_1388void _XmToolTipRemove(Widget w){XmToolTipTrait ttp;
_XmToolTipLeave(w, NULL, NULL, NULL);ttp = (XmToolTipTrait) XmeTraitGet(w, XmQTtoolTip);if (ttp != NULL) {XmStringFree(ttp->tool_tip_string);XmeTraitSet(w, XmQTtoolTip, (XtPointer) NULL);
/*** 2010-07-02 A-Harvey Leak fixed.** XmeTraitSet() removes the entry in the hash** table BUT not what entry -> value ->.** Perhaps it should, that is for someone** who knows what they are doing to decide*/XtFree( (char *)ttp );}}#endif /* FIX_1388 */
Can anyone comment on the validity of this patch?
Thanks
Found this fix in 2.3.3
I was looking at 2.3.1.
Sorry for the noise.