From: jzwiebel@pgl-devsvr.den.mmc.com (John Zwiebel (303)977-1480)
Subject: ODM access gobbles memory
Organization: PAGE @ Martin-Marietta
Date: Sun, 22 Nov 1992 01:23:29 GMT

I have built an SMUX daemon to retrieve kernel information on our in-house 
protocol called RTP (for real-time).  I'm a bit confused on all the calls to
the odm that are required to get information to finally query the kernel.
I've followed the example code in the IBM device drivers manual and I am
successful in retrieving the information I want.  The problem is that
every time through my function to grab the RTP information, calls to the odm
(odm_get_first and odm_lock, odm_terminate, odm_initiate etc. as well as genmajor()) all allocate additional memory which I can't free.  I don't know
what structures are being allocated for these processes and I don't know where
they are being allocated.  Nor can I find any reference in Info or in the device driver manual that discusses freeing this additional memory.

There is a vague reference in odm_get_first that states I have to specifially free memory if I use a NULL pointer and I've tried that option too, still the process starts growing and growing until it uses up all available swap space and then it freezes up the host until it finally can't get any more memory and it dies.

Below is a test application that I've built that anyone should be able to build on any RS6000.  I'd really appreciate it if someone would take the time to look at it and tell me about the 'blahblah()' function that will solve my problems.  Or maybe you can see the big mistake I'm making.  BTW, I tried to run through the ODM calls once and store the devno information as a global.  It seemed to work for a while but it then caused the host to '888' which of course isn't acceptable.

If you do come up with a solution, please mail it to me as well as posting it here.

Thanks a lot.
John Zwiebel

-----------------------------------------------------
First, here's a make file for my test program:

    OBJS        = TestProgram.o
    CFLAGS      = -g -D_BSD -D_BSD_INCLUDES $(INC) -DDEBUG
    LIBS        = -lisode -lsnmp -lodm -lcfg -lcom

rtpd:   $(OBJS)
        $(CC) -o TestProgram $(OBJS) $(LIBS)

clean:
        rm -f *.o TestProgram

--------------------------------------------------------
And TestProgram.c

Some of the calls here are specific to the isode SMUX agent but
don't interfere with the TestProgram.
--------------------------------------------------------
/*  This test program uses certain calls to the odm to obtain information
 *  from the kernel.  It was origonally developed to retrieve information
 *  on the PAGE internally developed RTP (Real-Time Protocol).  The global
 *  "logical_name" determines the object searched for the in CuDv_CLASS of
 *  the odm.  In this test program this has been changed from "PageRtp0" to
 *  "ent0".  Both of these objects are device drivers and both have links to
 *  the PdDv_CLASS objects.  Each call to the odm (using odm_initialize();
 *  odm_get_first(); or odm_terminate()) causes the process to grow in size.
 *  This was determined by loading the program into dbx and stepping through
 *  one line at a time then running "ps aux" to obtain the process size.
 *  Normally this would be expected providing the memory allocated by these
 *  calls is 'freed' after leaving the 'cacheRTP()' function call.  At any 
 *  rate, the process should obtain some sort of an equilibrium in the amount
 *  of memory it requires.  This does not happen.  The process continues to 
 *  run, allocating more memory each time through cacheRTP(), until all of 
 *  the available paging space is used.  When the process can no longer
 *  allocate additional memory it finally dies.
 */

/*****************************
 * The call 'maj = genmajor(preobj.DvDr);' also adds to 
 * the size of the process.  
 *************************/

/* The calls to the odm were origonally based on example code provided
 * in the AIX systems manual.  This included locking and unlocking the database
 * using odm_lock() and odm_unlock() and explicitly opening and closing an
 * object class using the appropriate calls.  When these calls were included  
 * the process grew significantly faster than it does using only the current
 * calls included in this test program.  After closly reading 'info', I
 * determined I could eliminate the extra calls and still obtain the information
 * I required.  The correct information is in fact returned.
 */

/* There are several lines of code that have been commented out in cacheRTP().
 * I was trying to experiment with alternative ways of structuring the odm
 * requests.  The first method was to declare "cusobj" as an automatic and
 * the second was to declare it as a pointer to the structure returned by
 * odm_get_first().  Info states the the use of the second method requires
 * the application program to explicitly free the data structure, which suggests
 * that under the first method there is no requirement for releasing the
 * memory.  cacheRTP() gets information from two odm classes, CuDv_CLASS and
 * PdDv_CLASS.  The odm calls are summarized below:
 *
 *	rc = odm_get_first(CuDv_CLASS,sstring,&cusobj);		Method one
 *	cusobj = odm_get_first(CuDv_CLASS,sstring,NULL);	Method two
 *	rc = odm_get_first(PdDv_CLASS, sstring, &preobj);	Method one
 *	preobj = odm_get_first(PdDv_CLASS, sstring, NULL);	Method two
 */

/* THE TESTPROGRAM IS CURRENTLY COMMENTED TO USE METHOD TWO */

/* This test program currently loops through cacheRTP once per minute.
 * If you run 'ps aux |grep TestProgram' once every minute, you will see that
 * the process size grows every minute and by the same amount.  Under the 'old'
 * version of cacheRTP(), it did not grow by the same size each loop but it 
 * did develop a pattern in its size growth.  This can be explained by the
 * way the kernel allocates paging space to a process.
 */

/* You may wish to comment out portions of this program as outline below to
 * see where the specific memory leaks are occurring.  For instance, if
 * you leave only the 'odm_' calls in the loop the memory leak is much 
 * much smaller but it still exists
 * # ps aux |grep TestProgram
 * root     11196  0.0  1.0  108  132  pts/1 S    11:26:14  0:00 ./TestProgram
 * root     12925  0.0  1.0   92  136  pts/0 S    12:19:04  0:00 grep TestProgram
 * # date
 * Fri Nov 20 13:08:56 MST 1992
 * # ps aux |grep TestProgram
 * root     11196  0.0  1.0  112  136  pts/1 S    11:26:14  0:01 ./TestProgram
 * root     12832  0.0  1.0   92  136  pts/0 S    13:09:02  0:00 grep TestProgram
 * # date
 * Fri Nov 20 13:22:37 MST 1992
 * # ps aux |grep TestProgram
 * root     11196  0.0  1.0  116  140  pts/1 S    11:26:14  0:01 ./TestProgram
 * root     12954  0.0  1.0   92  136  pts/0 S    13:22:40  0:00 grep TestProgram
 */

/*  Some of the following #include files are probably not required for this
 *  TestProgram (especially the isode includes ) but they do not prevent 
 *  correct operation of the program.
 *  All of these should be available on an RS6000.
 * 
 *  I explicitly added odmi.h but it doesn't seem to make any difference in
 *  compilation nor in process growth.
 */

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <varargs.h>
#include <isode/snmp/smux.h>
#include <isode/snmp/objects.h>
#include <sys/ioctl.h>

#include <odmi.h>

#ifdef	BSD42
#include <sys/file.h>
#endif
#ifdef	SYS5
#include <fcntl.h>
#endif
#include <isode/tailor.h>

/*
 *  Additional PAGE required includes
 */
#include <sys/types.h>
#include <sys/cfgdb.h>
#include <sys/cfgodm.h>
#include <cf.h>
#ifdef	SYS5
#include <fcntl.h>
#endif
#include <sys/sysconfig.h>
#include <sys/mode.h>
#include <sys/device.h>
#include <sys/ldr.h>
#include <sys/stat.h>
#include <sys/errno.h>


/*    DATA */

static	char   *myname = "TestProgram";


#ifdef	DEBUG
#define TRAPTIME	60L
#else
#define	TRAPTIME 	600L			/* how often 'rtp' is queried */
#endif


/* Defined name for the RTP used in cacheRTP */
#define logical_name	"ent0"


/* PAGE CSU's */
extern int cacheRTP ();		/* Read the 'vpdrtp' output */
extern void err_exit ();	/* Used by cacheRTP to report errors */

/* Global Assignments */

/* START OF TEST STUB */
main(){
	printf( " beginning........\n");
	while (1){
		cacheRTP();
		sleep(20);
	}
}

int cacheRTP () {
	extern long		*genminor();

	char    sstring[256];           /* search criteria string */

	struct Class *cusdev;		/* customized devices class ptr */
	struct Class *predev;		/* predefined devices class ptr */

	struct CuDv *cusobj;		/* customized device object storage */
	struct PdDv *preobj;		/* predefined device object storage */

/*	int	rc;			/* return codes go here */

	struct cfg_load load;		/* code saved that shows how to load */
					/* the device driver a different way */
	struct cfg_dd   dd;		/* used to configure or unconfigure */

	long		maj;		/* major number */
	long		*min;		/* minor number */
	dev_t		devno;		/* device number */
	int		status;		/* return code */


	printf("LOOP\n");

	/* start up odm */
	if (odm_initialize() == NOTOK){
		/* initialization failed */
		err_exit(E_ODMINIT);
		return (NOTOK);
	}


	/* search for customized object with this logical name */
	sprintf(sstring, "name = '%s'", logical_name);
/*
	rc = odm_get_first(cusdev,sstring,&cusobj);
*/
	cusobj = odm_get_first(CuDv_CLASS,sstring,NULL);
/*
	if ((int)rc==0) {
*/
	if ((int)cusobj==0) {
		/* No CuDv object with this name */
		err_exit(E_NOCuDv);
		return (NOTOK);
	}
/*
	else if ((int)rc==NOTOK) {
*/
	else if ((int)cusobj==NOTOK) {
		/* ODM failure */
		err_exit(E_ODMGET);
		return (NOTOK);
	}


	/* get predefined device object for this logical name */
/*
	sprintf(sstring, "uniquetype = '%s'", cusobj.PdDvLn_Lvalue);
*/
	sprintf(sstring, "uniquetype = '%s'", cusobj->PdDvLn_Lvalue);
/*
	rc = odm_get_first(predev, sstring, &preobj);
*/
	preobj = odm_get_first(PdDv_CLASS, sstring, NULL);
/*
	if ((int)rc==0) {
*/
	if ((int)preobj==0) {
		/* No PdDv object for this device */
		err_exit(E_NOPdDv);
		return (NOTOK);
	}
/*
	else if ((int)rc==NOTOK) {
*/
	else if ((int)preobj==NOTOK) {
		/* ODM failure */
		err_exit(E_ODMGET);
		return (NOTOK);
	}

/************************************************************************
 * The code beyond this point uses the information obtained from
 * the odm queries to obtain kernel information.  
 ************************************************************************/
/*
	if (cusobj.status == AVAILABLE) {
*/
	if (cusobj->status == AVAILABLE) {
        	load.path = "/etc/drivers/entdd";
        	load.libpath = "";
        	load.kmid = 0;
        	status = sysconfig(SYS_QUERYLOAD, &load, sizeof(load));
        	if (status == NOTOK)
        	{
 		    advise(LLOG_EXCEPTIONS, NULLCP, 
			"vpdrtp: SYS_QUERYLOAD failed [%s]", strerror(errno));
		    err_exit(errno);
		    return (NOTOK);
        	}
        	if (load.kmid == 0)
        	{
		    advise(LLOG_EXCEPTIONS, NULLCP, 
		    	"vpdrtp: SYS_QUERYLOAD returned a 0 kmid\n");
		    err_exit(errno);
		    return (NOTOK);
		}
/**********************************************************************
 * With the following segment commented out the LOOP does not eat up
 * memory quite as fast.  (See above).  The major device number returned
 * is alway the same.  The 'genmajor' command seems to be the culprit.
 ***********************************************************************/

/*
                maj = genmajor(preobj.DvDr);
*/
                maj = genmajor(preobj->DvDr);
		printf( "The Major Device Driver Number is: %d \n", maj);
                if (maj == NOTOK) {
		    advise(LLOG_EXCEPTIONS, NULLCP, 
			"vpdrtp: genmajor failed\n");
                    err_exit(errno);
		    return (NOTOK);
                }
                min = genminor(logical_name, maj, -1, 1, 1, 1);
		printf( "The Minor Device Driver Number is: %d \n", min);
                if (*min == NOTOK) {
		    advise(LLOG_EXCEPTIONS, NULLCP, 
			"vpdrtp: genminor failed\n");
                    err_exit(errno);
		    return (NOTOK);
                }
                devno = makedev(maj, *min);
		printf( "The devno Number is: %d \n", devno);
  
/*******************************************************
 * Most of the following code is commented out.  It does not affect the
 * size of the process but does show how I wanted to use the information
 * retrieved from the odm.  This code doesn't make the size of the process
 * grow.  (Or it doesn't seem to).  Also I've been happy with the information
 * returned here.
 *******************************************************/

        	/* Fill in device driver structure.*/
/*		dd.devno  = devno;
        	dd.kmid   = load.kmid;
        	dd.cmd    = CFG_QVPD;
        	dd.ddsptr = (caddr_t)&rtpvpd;
        	dd.ddslen = sizeof(RTPVitalProdData_t);
*/
		/*
		 * Get the Vital Product Data from the device driver
		 * using sysconfig and command CFG_QVPD.
		 */
/*		status = sysconfig(SYS_CFGDD, &dd, sizeof(dd));
	        if (status == NOTOK) {
 		    advise(LLOG_EXCEPTIONS, NULLCP, 
			"vpdrtp: SYS_CFGDD failed");
		    err_exit(errno);
		    return (NOTOK);
        	}

*/		
	} 
	else
	{
		/* CODE REMOVED */
	}

/* THESE LINES FREE THE ALLOCATED MEMORY WHEN WE USE METHOD TWO (POINTERS) */ 
	free(cusobj);
	free(preobj);


	if (odm_terminate() == NOTOK)
	    advise(LLOG_EXCEPTIONS, NULLCP,
		"odm_terminate failed: %d", odmerrno);

	return (OK);

}

void err_exit(exitcode)
char    exitcode;

{
	/* Terminate the ODM */
	odm_terminate();

	/* Log the error */
	advise (LLOG_EXCEPTIONS, NULLCP, "cacheRTP() failed: %d [%s]",
		exitcode, strerror(exitcode));
}
Newsgroups: comp.unix.aix