2001-11-16 01:40:53 +03:00
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS
2002-03-19 07:30:17 +03:00
* IS " basis, WITHOUT WARRANTY OF ANY KIND, either express or
2001-11-16 01:40:53 +03:00
* implied . See the License for the specific language governing
* rights and limitations under the License .
*
* The Original Code is spacetrace . h / spacetrace . c code , released
* Nov 6 , 2001.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation . Portions created by Netscape are
* Copyright ( C ) 2001 Netscape Communications Corporation . All
* Rights Reserved .
*
* Contributor ( s ) :
* Garrett Arch Blythe , 31 - October - 2001
*
* Alternatively , the contents of this file may be used under the
* terms of the GNU Public License ( the " GPL " ) , in which case the
* provisions of the GPL are applicable instead of those above .
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the MPL , indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL . If you do not delete
* the provisions above , a recipient may use your version of this
* file under either the MPL or the GPL .
*/
/*
* * spacetrace . c
* *
* * SpaceTrace is meant to take the output of trace - malloc and present
* * a picture of allocations over the run of the application .
*/
/*
* * Required include files .
*/
# include "spacetrace.h"
# include <ctype.h>
# include <math.h>
# include <string.h>
# include <time.h>
2001-11-29 04:29:26 +03:00
# if defined(XP_WIN32)
# include <malloc.h> /* _heapMin */
# endif
2001-11-16 01:40:53 +03:00
# if defined(HAVE_BOUTELL_GD)
/*
* * See http : //www.boutell.com/gd for the GD graphics library.
* * Ports for many platorms exist .
* * Your box may already have the lib ( mine did , redhat 7.1 workstation ) .
*/
# include <gd.h>
# include <gdfontt.h>
# include <gdfonts.h>
# include <gdfontmb.h>
# endif /* HAVE_BOUTELL_GD */
2001-12-18 04:00:36 +03:00
/*
* * Ugh , MSVC6 ' s qsort is too slow . . .
*/
# include "nsQuickSort.h"
2001-11-16 01:40:53 +03:00
2002-05-09 01:32:24 +04:00
/*
* * strcasecmp API please .
*/
# if defined(_MSC_VER)
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
# endif
2001-11-16 01:40:53 +03:00
/*
* * the globals variables . happy joy .
*/
2002-04-17 04:12:44 +04:00
STGlobals globals ;
2001-11-16 01:40:53 +03:00
2001-11-29 04:29:26 +03:00
/*
* * have the heap cleanup at opportune times , if possible .
*/
void heapCompact ( void )
{
# if defined(XP_WIN32)
_heapmin ( ) ;
# endif
}
2001-11-16 01:40:53 +03:00
/*
* * showHelp
* *
* * Give simple command line help .
* * Returns ! 0 if the help was showed .
*/
int showHelp ( void )
{
int retval = 0 ;
2002-05-09 01:32:24 +04:00
if ( PR_FALSE ! = globals . mOptions . mHelp )
2001-11-16 01:40:53 +03:00
{
2002-05-09 01:32:24 +04:00
PR_fprintf ( PR_STDOUT , " Usage: \t %s [OPTION]... [-|filename] \n \n " , globals . mProgramName ) ;
# define ST_CMD_OPTION_BOOL(option_name, option_help) \
PR_fprintf ( PR_STDOUT , " --%s \n Disabled by default. \n %s \n " , # option_name , option_help ) ;
# define ST_CMD_OPTION_STRING(option_name, default_value, option_help) \
PR_fprintf ( PR_STDOUT , " --%s=<value> \n Default value is \" %s \" . \n %s \n " , # option_name , default_value , option_help ) ;
# define ST_CMD_OPTION_STRING_ARRAY(option_name, array_size, option_help) \
PR_fprintf ( PR_STDOUT , " --%s=<value> \n Up to %u occurrences allowed. \n %s \n " , # option_name , array_size , option_help ) ;
# define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_help) \
PR_fprintf ( PR_STDOUT , " --%s=<value> \n Unlimited occurrences allowed. \n %s \n " , # option_name , option_help ) ;
# define ST_CMD_OPTION_UINT32(option_name, default_value, multiplier, option_help) \
PR_fprintf ( PR_STDOUT , " --%s=<value> \n Default value is %u. \n %s \n " , # option_name , default_value , option_help ) ;
# define ST_CMD_OPTION_UINT64(option_name, default_value, multiplier, option_help) \
PR_fprintf ( PR_STDOUT , " --%s=<value> \n Default value is %llu. \n %s \n " , # option_name , default_value , option_help ) ;
2001-11-16 01:40:53 +03:00
2002-05-09 01:32:24 +04:00
# include "stoptions.h"
2002-04-20 01:49:55 +04:00
2001-11-16 01:40:53 +03:00
/*
* * Showed something .
*/
retval = __LINE__ ;
}
return retval ;
}
2001-12-15 03:27:15 +03:00
/*
2001-12-18 04:00:36 +03:00
* * ticks2xsec
2001-12-15 03:27:15 +03:00
* *
2001-12-18 04:00:36 +03:00
* * Convert platform specific ticks to second units
2001-12-15 03:27:15 +03:00
* * Returns 0 on success .
*/
2001-12-18 04:00:36 +03:00
PRUint32 ticks2xsec ( tmreader * aReader , PRUint32 aTicks , PRUint32 aResolution )
2001-12-15 03:27:15 +03:00
{
PRUint32 retval = 0 ;
2001-12-18 04:00:36 +03:00
PRUint64 bigone ;
2001-12-15 03:27:15 +03:00
PRUint64 tmp64 ;
2001-12-18 04:00:36 +03:00
LL_UI2L ( bigone , aResolution ) ;
2001-12-15 03:27:15 +03:00
LL_UI2L ( tmp64 , aTicks ) ;
LL_MUL ( bigone , bigone , tmp64 ) ;
LL_UI2L ( tmp64 , aReader - > ticksPerSec ) ;
LL_DIV ( bigone , bigone , tmp64 ) ;
LL_L2UI ( retval , bigone ) ;
return retval ;
}
2001-12-18 04:00:36 +03:00
# define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
# define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000)
2001-12-15 03:27:15 +03:00
2001-11-16 01:40:53 +03:00
/*
* * initOptions
* *
* * Determine global settings for the application .
* * Returns 0 on success .
*/
int initOptions ( int aArgCount , char * * aArgArray )
{
int retval = 0 ;
int traverse = 0 ;
/*
2002-05-08 04:16:20 +04:00
* * Set the initial global default options .
2001-11-16 01:40:53 +03:00
*/
2002-05-08 04:16:20 +04:00
# define ST_CMD_OPTION_BOOL(option_name, option_help) globals.mOptions.m##option_name = PR_FALSE;
# define ST_CMD_OPTION_STRING(option_name, default_value, option_help) PR_snprintf(globals.mOptions.m##option_name, sizeof(globals.mOptions.m##option_name), "%s", default_value);
# define ST_CMD_OPTION_STRING_ARRAY(option_name, array_size, option_help) { int loop; for(loop = 0; loop < array_size; loop++) { globals.mOptions.m##option_name[loop][0] = '\0'; } }
# define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_help) globals.mOptions.m##option_name = NULL; globals.mOptions.m##option_name##Count = 0;
# define ST_CMD_OPTION_UINT32(option_name, default_value, multiplier, option_help) globals.mOptions.m##option_name = default_value * multiplier;
# define ST_CMD_OPTION_UINT64(option_name, default_value, multiplier, option_help) { PRUint64 def64 = default_value; PRUint64 mul64 = multiplier; LL_MUL(globals.mOptions.m##option_name##64, def64, mul64); }
# include "stoptions.h"
2002-04-17 03:36:47 +04:00
2001-11-16 01:40:53 +03:00
/*
* * Go through all arguments .
2002-05-09 01:32:24 +04:00
* * Two dashes lead off an option .
* * Any single dash leads off help , unless it is a lone dash ( stdin ) .
* * Anything else will be attempted as a file to be processed .
2001-11-16 01:40:53 +03:00
*/
for ( traverse = 1 ; traverse < aArgCount ; traverse + + )
{
2002-05-09 01:32:24 +04:00
if ( ' - ' = = aArgArray [ traverse ] [ 0 ] & & ' - ' = = aArgArray [ traverse ] [ 1 ] )
2001-11-16 01:40:53 +03:00
{
2002-05-09 01:32:24 +04:00
const char * option = & aArgArray [ traverse ] [ 2 ] ;
2001-11-16 01:40:53 +03:00
/*
2002-05-09 01:32:24 +04:00
* * Initial if ( 0 ) needed to make " else if " s valid .
2001-11-16 01:40:53 +03:00
*/
2002-05-09 01:32:24 +04:00
if ( 0 )
2001-11-16 01:40:53 +03:00
{
2002-05-09 01:32:24 +04:00
}
2002-04-20 01:49:55 +04:00
2002-05-09 01:32:24 +04:00
# define ST_CMD_OPTION_BOOL(option_name, option_help) \
else if ( 0 = = strcasecmp ( option , # option_name ) ) \
{ \
globals . mOptions . m # # option_name = PR_TRUE ; \
}
# define ST_CMD_OPTION_STRING(option_name, default_value, option_help) \
else if ( 0 = = strncasecmp ( option , # option_name " = " , strlen ( # option_name " = " ) ) ) \
{ \
PR_snprintf ( globals . mOptions . m # # option_name , sizeof ( globals . mOptions . m # # option_name ) , " %s " , option + strlen ( # option_name " = " ) ) ; \
}
# define ST_CMD_OPTION_STRING_ARRAY(option_name, array_size, option_help) \
else if ( 0 = = strncasecmp ( option , # option_name " = " , strlen ( # option_name " = " ) ) ) \
{ \
int arrLoop = 0 ; \
\
for ( arrLoop = 0 ; arrLoop < array_size ; arrLoop + + ) \
{ \
if ( ' \0 ' = = globals . mOptions . m # # option_name [ arrLoop ] [ 0 ] ) \
{ \
break ; \
} \
} \
\
if ( arrLoop ! = array_size ) \
{ \
PR_snprintf ( globals . mOptions . m # # option_name [ arrLoop ] , sizeof ( globals . mOptions . m # # option_name [ arrLoop ] ) , " %s " , option + strlen ( # option_name " = " ) ) ; \
} \
else \
{ \
REPORT_ERROR_MSG ( __LINE__ , option ) ; \
retval = __LINE__ ; \
globals . mOptions . mHelp = PR_TRUE ; \
} \
}
# define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_help) \
else if ( 0 = = strncasecmp ( option , # option_name " = " , strlen ( # option_name " = " ) ) ) \
{ \
const char * * expand = NULL ; \
\
expand = ( const char * * ) realloc ( ( void * ) globals . mOptions . m # # option_name , sizeof ( const char * ) * ( globals . mOptions . m # # option_name # # Count + 1 ) ) ; \
if ( NULL ! = expand ) \
{ \
globals . mOptions . m # # option_name = expand ; \
globals . mOptions . m # # option_name [ globals . mOptions . m # # option_name # # Count ] = option + strlen ( # option_name " = " ) ; \
globals . mOptions . m # # option_name # # Count + + ; \
} \
else \
{ \
retval = __LINE__ ; \
globals . mOptions . mHelp = PR_TRUE ; \
} \
}
# define ST_CMD_OPTION_UINT32(option_name, default_value, multiplier, option_help) \
else if ( 0 = = strncasecmp ( option , # option_name " = " , strlen ( # option_name " = " ) ) ) \
{ \
PRInt32 scanRes = 0 ; \
\
scanRes = PR_sscanf ( option + strlen ( # option_name " = " ) , " %u " , & globals . mOptions . m # # option_name ) ; \
if ( 1 ! = scanRes ) \
{ \
REPORT_ERROR_MSG ( __LINE__ , option ) ; \
retval = __LINE__ ; \
globals . mOptions . mHelp = PR_TRUE ; \
} \
}
# define ST_CMD_OPTION_UINT64(option_name, default_value, multiplier, option_help) \
else if ( 0 = = strncasecmp ( option , # option_name " = " , strlen ( # option_name " = " ) ) ) \
{ \
PRInt32 scanRes = 0 ; \
\
scanRes = PR_sscanf ( option + strlen ( # option_name " = " ) , " %llu " , & globals . mOptions . m # # option_name # # 64 ) ; \
if ( 1 ! = scanRes ) \
{ \
REPORT_ERROR_MSG ( __LINE__ , option ) ; \
retval = __LINE__ ; \
globals . mOptions . mHelp = PR_TRUE ; \
} \
}
2002-04-20 01:49:55 +04:00
2002-05-09 01:32:24 +04:00
/*
* * Expands to a bunch of " else if " s .
*/
# include "stoptions.h"
2001-11-16 01:40:53 +03:00
/*
2002-05-09 01:32:24 +04:00
* * If no match on options , this else will get hit .
2001-11-16 01:40:53 +03:00
*/
2002-05-09 01:32:24 +04:00
else
2001-11-16 01:40:53 +03:00
{
2002-05-09 01:32:24 +04:00
REPORT_ERROR_MSG ( __LINE__ , option ) ;
retval = __LINE__ ;
globals . mOptions . mHelp = PR_TRUE ;
2001-11-16 01:40:53 +03:00
}
}
2002-05-09 01:32:24 +04:00
else if ( ' - ' = = aArgArray [ traverse ] [ 0 ] & & ' \0 ' ! = aArgArray [ traverse ] [ 1 ] )
{
/*
* * Show help , bad / legacy option .
*/
REPORT_ERROR_MSG ( __LINE__ , aArgArray [ traverse ] ) ;
retval = __LINE__ ;
globals . mOptions . mHelp = PR_TRUE ;
}
2001-11-16 01:40:53 +03:00
else
{
/*
2002-05-09 01:32:24 +04:00
* * Default is same as FileName option , the file to process .
2001-11-16 01:40:53 +03:00
*/
2002-05-03 04:32:23 +04:00
PR_snprintf ( globals . mOptions . mFileName , sizeof ( globals . mOptions . mFileName ) , " %s " , aArgArray [ traverse ] ) ;
2001-11-16 01:40:53 +03:00
}
}
2002-04-17 03:36:47 +04:00
/*
* * initalize the categories
*/
initCategories ( & globals ) ;
2001-11-16 01:40:53 +03:00
return retval ;
}
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
* * createGraph
* *
* * Create a GD image with the common properties of a graph .
* * Upon return , you normally allocate legend colors ,
* * draw your graph inside the region
* * STGD_MARGIN , STGD_MARGIN , STGD_WIDTH - STGD_MARGIN , STGD_HEIGH - STGD_MARGIN ,
* * and then call drawGraph to format the surrounding information .
* *
* * You should use the normal GD image release function , gdImageDestroy
* * when done with it .
* *
* * Image attributes :
* * STGD_WIDTHxSTGD_HEIGHT
* * trasparent ( white ) background
* * incremental display
*/
gdImagePtr createGraph ( int * aTransparencyColor )
{
gdImagePtr retval = NULL ;
if ( NULL ! = aTransparencyColor )
{
* aTransparencyColor = - 1 ;
retval = gdImageCreate ( STGD_WIDTH , STGD_HEIGHT ) ;
if ( NULL ! = retval )
{
/*
* * Background color ( first one ) .
*/
* aTransparencyColor = gdImageColorAllocate ( retval , 255 , 255 , 255 ) ;
if ( - 1 ! = * aTransparencyColor )
{
/*
* * As transparency .
*/
gdImageColorTransparent ( retval , * aTransparencyColor ) ;
}
/*
* * And to set interlacing .
*/
gdImageInterlace ( retval , 1 ) ;
}
else
{
REPORT_ERROR ( __LINE__ , gdImageCreate ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
return retval ;
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
* * drawGraph
* *
* * This function mainly exists to simplify putitng all the pretty lace
* * around a home made graph .
*/
void drawGraph ( gdImagePtr aImage , int aColor , const char * aGraphTitle , const char * aXAxisTitle , const char * aYAxisTitle , PRUint32 aXMarkCount , PRUint32 * aXMarkPercents , const char * * aXMarkTexts , PRUint32 aYMarkCount , PRUint32 * aYMarkPercents , const char * * aYMarkTexts , PRUint32 aLegendCount , int * aLegendColors , const char * * aLegendTexts )
{
if ( NULL ! = aImage & & NULL ! = aGraphTitle & & NULL ! = aXAxisTitle & & NULL ! = aYAxisTitle & & ( 0 = = aXMarkCount | | ( NULL ! = aXMarkPercents & & NULL ! = aXMarkTexts ) ) & & ( 0 = = aYMarkCount | | ( NULL ! = aYMarkPercents & & NULL ! = aYMarkTexts ) ) & & ( 0 = = aLegendCount | | ( NULL ! = aLegendColors & & NULL ! = aLegendTexts ) ) )
{
int margin = 1 ;
PRUint32 traverse = 0 ;
PRUint32 target = 0 ;
const int markSize = 2 ;
int x1 = 0 ;
int y1 = 0 ;
int x2 = 0 ;
int y2 = 0 ;
time_t theTimeT = time ( NULL ) ;
char * theTime = ctime ( & theTimeT ) ;
const char * logo = " SpaceTrace " ;
gdFontPtr titleFont = gdFontMediumBold ;
gdFontPtr markFont = gdFontTiny ;
gdFontPtr dateFont = gdFontTiny ;
gdFontPtr axisFont = gdFontSmall ;
gdFontPtr legendFont = gdFontTiny ;
gdFontPtr logoFont = gdFontTiny ;
/*
* * Fixup the color .
* * Black by default .
*/
if ( - 1 = = aColor )
{
aColor = gdImageColorAllocate ( aImage , 0 , 0 , 0 ) ;
}
if ( - 1 = = aColor )
{
aColor = gdImageColorClosest ( aImage , 0 , 0 , 0 ) ;
}
/*
* * Output the box .
*/
x1 = STGD_MARGIN - margin ;
y1 = STGD_MARGIN - margin ;
x2 = STGD_WIDTH - x1 ;
y2 = STGD_HEIGHT - y1 ;
gdImageRectangle ( aImage , x1 , y1 , x2 , y2 , aColor ) ;
margin + + ;
/*
* * Need to make small markings on the graph to indicate where the
* * labels line up exactly .
* * While we ' re at it , draw the label text .
*/
for ( traverse = 0 ; traverse < aXMarkCount ; traverse + + )
{
target = ( ( STGD_WIDTH - ( STGD_MARGIN * 2 ) ) * aXMarkPercents [ traverse ] ) / 100 ;
x1 = STGD_MARGIN + target ;
y1 = STGD_MARGIN - margin ;
x2 = x1 ;
y2 = y1 - markSize ;
gdImageLine ( aImage , x1 , y1 , x2 , y2 , aColor ) ;
y1 = STGD_HEIGHT - y1 ;
y2 = STGD_HEIGHT - y2 ;
gdImageLine ( aImage , x1 , y1 , x2 , y2 , aColor ) ;
if ( NULL ! = aXMarkTexts [ traverse ] )
{
x1 = STGD_MARGIN + target - ( markFont - > h / 2 ) ;
y1 = STGD_HEIGHT - STGD_MARGIN + margin + markSize + ( strlen ( aXMarkTexts [ traverse ] ) * markFont - > w ) ;
gdImageStringUp ( aImage , markFont , x1 , y1 , ( unsigned char * ) aXMarkTexts [ traverse ] , aColor ) ;
}
}
for ( traverse = 0 ; traverse < aYMarkCount ; traverse + + )
{
target = ( ( STGD_HEIGHT - ( STGD_MARGIN * 2 ) ) * ( 100 - aYMarkPercents [ traverse ] ) ) / 100 ;
x1 = STGD_MARGIN - margin ;
y1 = STGD_MARGIN + target ;
x2 = x1 - markSize ;
y2 = y1 ;
gdImageLine ( aImage , x1 , y1 , x2 , y2 , aColor ) ;
x1 = STGD_WIDTH - x1 ;
x2 = STGD_WIDTH - x2 ;
gdImageLine ( aImage , x1 , y1 , x2 , y2 , aColor ) ;
if ( NULL ! = aYMarkTexts [ traverse ] )
{
x1 = STGD_MARGIN - margin - markSize - ( strlen ( aYMarkTexts [ traverse ] ) * markFont - > w ) ;
y1 = STGD_MARGIN + target - ( markFont - > h / 2 ) ;
gdImageString ( aImage , markFont , x1 , y1 , ( unsigned char * ) aYMarkTexts [ traverse ] , aColor ) ;
}
}
margin + = markSize ;
/*
* * Title will be centered above the image .
*/
x1 = ( STGD_WIDTH / 2 ) - ( ( strlen ( aGraphTitle ) * titleFont - > w ) / 2 ) ;
y1 = ( ( STGD_MARGIN - margin ) / 2 ) - ( titleFont - > h / 2 ) ;
gdImageString ( aImage , titleFont , x1 , y1 , ( unsigned char * ) aGraphTitle , aColor ) ;
/*
* * Upper left will be the date .
*/
x1 = 0 ;
y1 = 0 ;
traverse = strlen ( theTime ) - 1 ;
if ( isspace ( theTime [ traverse ] ) )
{
theTime [ traverse ] = ' \0 ' ;
}
gdImageString ( aImage , dateFont , x1 , y1 , ( unsigned char * ) theTime , aColor ) ;
/*
* * Lower right will be the logo .
*/
x1 = STGD_WIDTH - ( strlen ( logo ) * logoFont - > w ) ;
y1 = STGD_HEIGHT - logoFont - > h ;
gdImageString ( aImage , logoFont , x1 , y1 , ( unsigned char * ) logo , aColor ) ;
/*
* * X and Y axis titles
*/
x1 = ( STGD_WIDTH / 2 ) - ( ( strlen ( aXAxisTitle ) * axisFont - > w ) / 2 ) ;
y1 = STGD_HEIGHT - axisFont - > h ;
gdImageString ( aImage , axisFont , x1 , y1 , ( unsigned char * ) aXAxisTitle , aColor ) ;
x1 = 0 ;
y1 = ( STGD_HEIGHT / 2 ) + ( ( strlen ( aYAxisTitle ) * axisFont - > w ) / 2 ) ;
gdImageStringUp ( aImage , axisFont , x1 , y1 , ( unsigned char * ) aYAxisTitle , aColor ) ;
/*
* * The legend .
* * Centered on the right hand side , going up .
*/
x1 = STGD_WIDTH - STGD_MARGIN + margin + ( aLegendCount * legendFont - > h ) / 2 ;
x2 = STGD_WIDTH - ( aLegendCount * legendFont - > h ) ;
if ( x1 > x2 )
{
x1 = x2 ;
}
y1 = 0 ;
for ( traverse = 0 ; traverse < aLegendCount ; traverse + + )
{
y2 = ( STGD_HEIGHT / 2 ) + ( ( strlen ( aLegendTexts [ traverse ] ) * legendFont - > w ) / 2 ) ;
if ( y2 > y1 )
{
y1 = y2 ;
}
}
for ( traverse = 0 ; traverse < aLegendCount ; traverse + + )
{
gdImageStringUp ( aImage , legendFont , x1 , y1 , ( unsigned char * ) aLegendTexts [ traverse ] , aLegendColors [ traverse ] ) ;
x1 + = legendFont - > h ;
}
}
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
# if defined(HAVE_BOUTELL_GD)
/*
* * pngSink
* *
* * GD callback , used to write out the png .
*/
int pngSink ( void * aContext , const char * aBuffer , int aLen )
{
return PR_Write ( ( PRFileDesc * ) aContext , aBuffer , aLen ) ;
}
# endif /* HAVE_BOUTELL_GD */
/*
2002-01-09 02:20:29 +03:00
* * FormatNumber
2001-11-16 01:40:53 +03:00
* *
2002-01-09 02:20:29 +03:00
* * Formats a number with thousands separator . Dont free the result . Returns
* * static data .
2001-11-16 01:40:53 +03:00
*/
2002-01-09 02:20:29 +03:00
char * FormatNumber ( PRInt32 num )
2001-11-16 01:40:53 +03:00
{
2002-01-09 02:20:29 +03:00
static char buf [ 64 ] ;
char tmpbuf [ 64 ] ;
int len = 0 ;
int bufindex = sizeof ( buf ) - 1 ;
int mod3 ;
PR_snprintf ( tmpbuf , sizeof ( tmpbuf ) , " %d " , num ) ;
/* now insert the thousands separator */
mod3 = 0 ;
len = strlen ( tmpbuf ) ;
while ( len > = 0 )
2001-11-16 01:40:53 +03:00
{
2002-01-09 02:20:29 +03:00
if ( tmpbuf [ len ] > = ' 0 ' & & tmpbuf [ len ] < = ' 9 ' )
2001-11-16 01:40:53 +03:00
{
2002-01-09 02:20:29 +03:00
if ( mod3 = = 3 )
{
buf [ bufindex - - ] = ' , ' ;
mod3 = 0 ;
}
mod3 + + ;
2001-11-16 01:40:53 +03:00
}
2002-01-09 02:20:29 +03:00
buf [ bufindex - - ] = tmpbuf [ len - - ] ;
2001-11-16 01:40:53 +03:00
}
2002-01-09 02:20:29 +03:00
return buf + bufindex + 1 ;
}
2001-11-16 01:40:53 +03:00
2002-01-09 02:20:29 +03:00
/*
* * actualByteSize
* *
* * Apply alignment and overhead to size to figure out actual byte size
*/
PRUint32 actualByteSize ( PRUint32 retval )
{
2001-11-29 04:29:26 +03:00
/*
2001-12-26 21:56:01 +03:00
* * Need to bump the result by our alignment and overhead .
2001-11-29 04:29:26 +03:00
* * The idea here is that an allocation actually costs you more than you
2001-12-26 21:56:01 +03:00
* * thought .
* *
* * The msvcrt malloc has an alignment of 16 with an overhead of 8.
* * The win32 HeapAlloc has an alignment of 8 with an overhead of 8.
2001-11-29 04:29:26 +03:00
*/
2001-12-26 21:56:01 +03:00
if ( 0 ! = retval )
2001-11-29 04:29:26 +03:00
{
2001-12-26 21:56:01 +03:00
PRUint32 eval = 0 ;
PRUint32 over = 0 ;
2001-11-29 04:29:26 +03:00
2001-12-26 21:56:01 +03:00
eval = retval - 1 ;
if ( 0 ! = globals . mOptions . mAlignBy )
2001-11-29 04:29:26 +03:00
{
2001-12-26 21:56:01 +03:00
over = eval % globals . mOptions . mAlignBy ;
2001-11-29 04:29:26 +03:00
}
2001-12-26 21:56:01 +03:00
retval = eval + globals . mOptions . mOverhead + globals . mOptions . mAlignBy - over ;
2001-11-29 04:29:26 +03:00
}
2001-11-16 01:40:53 +03:00
return retval ;
}
2002-01-09 02:20:29 +03:00
/*
* * byteSize
* *
* * Figuring the byte size of an allocation .
* * Might expand in the future to report size at a given time .
* * For now , just use last relevant event .
*/
PRUint32 byteSize ( STAllocation * aAlloc )
{
PRUint32 retval = 0 ;
if ( NULL ! = aAlloc & & 0 ! = aAlloc - > mEventCount )
{
PRUint32 index = aAlloc - > mEventCount ;
/*
* * Generally , the size is the last event ' s size .
*/
do
{
index - - ;
retval = aAlloc - > mEvents [ index ] . mHeapSize ;
}
while ( 0 = = retval & & 0 ! = index ) ;
}
return actualByteSize ( retval ) ;
}
2002-04-17 03:36:47 +04:00
/*
* * recalculateAllocationCost
* *
* * Given an allocation , does a recalculation of Cost - weight , heapcount etc .
* * and does the right thing to propogate the cost upwards .
*/
2002-04-19 00:11:45 +04:00
int recalculateAllocationCost ( STRun * aRun , STAllocation * aAllocation , PRBool updateParent )
2002-04-17 03:36:47 +04:00
{
/*
* * Now , see if they desire a callsite update .
* * As mentioned previously , we decide if the run desires us to
* * manipulate the callsite data only if it ' s stamp is set .
* * We change all callsites and parent callsites to have that
* * stamp as well , so as to mark them as being relevant to
* * the current run in question .
*/
if ( 0 ! = aRun - > mStats . mStamp )
{
PRUint32 timeval = aAllocation - > mMaxTimeval - aAllocation - > mMinTimeval ;
PRUint32 size = byteSize ( aAllocation ) ;
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
PRUint32 heapCost = aAllocation - > mHeapRuntimeCost ;
PRUint64 timeval64 = LL_INIT ( 0 , 0 ) ;
PRUint64 size64 = LL_INIT ( 0 , 0 ) ;
LL_UI2L ( timeval64 , timeval ) ;
LL_UI2L ( size64 , size ) ;
LL_MUL ( weight64 , timeval64 , size64 ) ;
/*
* * First , update this run .
*/
aRun - > mStats . mCompositeCount + + ;
aRun - > mStats . mHeapRuntimeCost + = heapCost ;
aRun - > mStats . mSize + = size ;
LL_ADD ( aRun - > mStats . mTimeval64 , aRun - > mStats . mTimeval64 , timeval64 ) ;
LL_ADD ( aRun - > mStats . mWeight64 , aRun - > mStats . mWeight64 , weight64 ) ;
/*
* * Use the first event of the allocation to update the parent
* * callsites .
* * This has positive effect of not updating realloc callsites
* * with the same data over and over again .
*/
2002-04-19 00:11:45 +04:00
if ( updateParent & & 0 < aAllocation - > mEventCount )
2002-04-17 03:36:47 +04:00
{
tmcallsite * callsite = aAllocation - > mEvents [ 0 ] . mCallsite ;
STRun * callsiteRun = NULL ;
/*
* * Go up parents till we drop .
*/
while ( NULL ! = callsite & & NULL ! = callsite - > method )
{
callsiteRun = CALLSITE_RUN ( callsite ) ;
if ( NULL ! = callsiteRun )
{
/*
* * Do we init it ?
*/
if ( callsiteRun - > mStats . mStamp ! = aRun - > mStats . mStamp )
{
memset ( & callsiteRun - > mStats , 0 , sizeof ( STCallsiteStats ) ) ;
callsiteRun - > mStats . mStamp = aRun - > mStats . mStamp ;
}
/*
* * Add the values .
* * Note that if the allocation was ever realloced ,
* * we are actually recording the final size .
* * Also , the composite count does not include
* * calls to realloc ( or free for that matter ) ,
* * but rather is simply a count of actual heap
* * allocation objects , from which someone will
* * draw conclusions regarding number of malloc
* * and free calls .
* * It is possible to generate the exact number
* * of calls to free / malloc / realloc should the
* * absolute need arise to count them individually ,
* * but I fear it will take mucho memory and this
* * is perhaps good enough for now .
*/
callsiteRun - > mStats . mCompositeCount + + ;
callsiteRun - > mStats . mHeapRuntimeCost + = heapCost ;
callsiteRun - > mStats . mSize + = size ;
LL_ADD ( callsiteRun - > mStats . mTimeval64 , callsiteRun - > mStats . mTimeval64 , timeval64 ) ;
LL_ADD ( callsiteRun - > mStats . mWeight64 , callsiteRun - > mStats . mWeight64 , weight64 ) ;
}
callsite = callsite - > parent ;
}
}
}
return 0 ;
}
2001-11-16 01:40:53 +03:00
/*
* * appendAllocation
* *
* * Given a run , append the allocation to it .
* * No DUP checks are done .
* * Also , we might want to update the parent callsites with stats .
* * We decide to do this heavy duty work only if the run we are appending
* * to has a non ZERO mStats . mStamp , meaning that it is asking to track
* * such information when it was created .
* * Returns ! 0 on success .
*/
int appendAllocation ( STRun * aRun , STAllocation * aAllocation )
{
int retval = 0 ;
if ( NULL ! = aRun & & NULL ! = aAllocation )
{
STAllocation * * expand = NULL ;
/*
* * Expand the size of the array if needed .
*/
expand = ( STAllocation * * ) realloc ( aRun - > mAllocations , sizeof ( STAllocation * ) * ( aRun - > mAllocationCount + 1 ) ) ;
if ( NULL ! = expand )
{
/*
* * Reassign in case of pointer move .
*/
aRun - > mAllocations = expand ;
/*
* * Stick the allocation in .
*/
aRun - > mAllocations [ aRun - > mAllocationCount ] = aAllocation ;
/*
* * If this is the global run , we need to let the allocation
* * track the index back to us .
*/
if ( & globals . mRun = = aRun )
{
aAllocation - > mRunIndex = aRun - > mAllocationCount ;
}
/*
* * Increase the count .
*/
aRun - > mAllocationCount + + ;
/*
* * We ' re good .
*/
retval = __LINE__ ;
/*
2002-04-17 03:36:47 +04:00
* * update allocation cost
2001-11-16 01:40:53 +03:00
*/
2002-04-19 00:11:45 +04:00
recalculateAllocationCost ( aRun , aAllocation , PR_TRUE ) ;
2001-11-16 01:40:53 +03:00
}
else
{
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
return retval ;
}
/*
* * hasCallsiteMatch
* *
* * Determine if the callsite or the other callsites has the matching text .
* *
* * Returns 0 if there is no match .
*/
int hasCallsiteMatch ( tmcallsite * aCallsite , const char * aMatch , int aDirection )
{
int retval = 0 ;
if ( NULL ! = aCallsite & & NULL ! = aCallsite - > method & & NULL ! = aMatch & & ' \0 ' ! = * aMatch )
{
const char * methodName = NULL ;
do
{
2002-01-09 22:03:01 +03:00
methodName = tmmethodnode_name ( aCallsite - > method ) ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = methodName & & NULL ! = strstr ( methodName , aMatch ) )
{
/*
* * Contains the text .
*/
retval = __LINE__ ;
break ;
}
else
{
switch ( aDirection )
{
case ST_FOLLOW_SIBLINGS :
aCallsite = aCallsite - > siblings ;
break ;
case ST_FOLLOW_PARENTS :
aCallsite = aCallsite - > parent ;
break ;
default :
aCallsite = NULL ;
REPORT_ERROR ( __LINE__ , hasCallsiteMatch ) ;
break ;
}
}
}
while ( NULL ! = aCallsite & & NULL ! = aCallsite - > method ) ;
}
else
{
REPORT_ERROR ( __LINE__ , hasCallsiteMatch ) ;
}
return retval ;
}
/*
* * harvestRun
* *
* * Provide a simply way to go over a run , and yield the relevant allocations .
* * The restrictions are easily set via the options page or the command
* * line switches .
* *
* * On any match , add the allocation to the provided run .
* *
* * This makes it much easier for all the code to respect the options in
* * force .
* *
* * You can override the global options by passing in your own options
* * pointer if you need a custom harvest .
* *
* * Returns ! 0 on error , though aOutRun may contain a partial data set .
*/
int harvestRun ( const STRun * aInRun , STRun * aOutRun , STOptions * aOptions )
{
int retval = 0 ;
2002-04-17 03:36:47 +04:00
# if defined(DEBUG_dp)
PRIntervalTime start = PR_IntervalNow ( ) ;
fprintf ( stderr , " DEBUG: harvesting run... \n " ) ;
# endif
2001-11-16 01:40:53 +03:00
if ( NULL ! = aInRun & & NULL ! = aOutRun & & aInRun ! = aOutRun )
{
PRUint32 traverse = 0 ;
STAllocation * current = NULL ;
/*
* * Fixup options if not provided .
*/
if ( NULL = = aOptions )
{
aOptions = & globals . mOptions ;
}
for ( traverse = 0 ; 0 = = retval & & traverse < aInRun - > mAllocationCount ; traverse + + )
{
current = aInRun - > mAllocations [ traverse ] ;
if ( NULL ! = current )
{
PRUint32 lifetime = 0 ;
PRUint32 bytesize = 0 ;
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
2001-12-29 01:27:20 +03:00
PRUint64 bytesize64 = LL_INIT ( 0 , 0 ) ;
PRUint64 lifetime64 = LL_INIT ( 0 , 0 ) ;
2001-11-16 01:40:53 +03:00
int appendRes = 0 ;
2002-01-24 03:58:33 +03:00
int looper = 0 ;
2002-01-25 01:41:08 +03:00
PRBool matched = PR_FALSE ;
2001-11-16 01:40:53 +03:00
/*
* * Use this as an opportune time to fixup a memory
2001-11-29 04:29:26 +03:00
* * leaked timeval , so as to not completely skew
2001-11-16 01:40:53 +03:00
* * the weights .
*/
2001-11-29 04:29:26 +03:00
if ( ST_TIMEVAL_MAX = = current - > mMaxTimeval )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
current - > mMaxTimeval = globals . mMaxTimeval ;
2001-11-16 01:40:53 +03:00
}
/*
2001-11-29 04:29:26 +03:00
* * Check allocation timeval restrictions .
* * We have to slide the recorded timevals to be zero
2001-11-16 01:40:53 +03:00
* * based , so that the comparisons make sense .
*/
2001-11-29 04:29:26 +03:00
if ( aOptions - > mAllocationTimevalMin > ( current - > mMinTimeval - globals . mMinTimeval ) )
2001-11-16 01:40:53 +03:00
{
continue ;
}
2001-11-29 04:29:26 +03:00
else if ( aOptions - > mAllocationTimevalMax < ( current - > mMinTimeval - globals . mMinTimeval ) )
2001-11-16 01:40:53 +03:00
{
continue ;
}
/*
2001-11-29 04:29:26 +03:00
* * Check timeval restrictions .
* * We have to slide the recorded timevals to be zero
2001-11-16 01:40:53 +03:00
* * based , so that the comparisons make sense .
*/
2001-11-29 04:29:26 +03:00
if ( aOptions - > mTimevalMin > ( current - > mMaxTimeval - globals . mMinTimeval ) )
2001-11-16 01:40:53 +03:00
{
continue ;
}
2001-11-29 04:29:26 +03:00
else if ( aOptions - > mTimevalMax < ( current - > mMinTimeval - globals . mMinTimeval ) )
2001-11-16 01:40:53 +03:00
{
continue ;
}
/*
* * Check lifetime restrictions .
*/
2001-11-29 04:29:26 +03:00
lifetime = current - > mMaxTimeval - current - > mMinTimeval ;
2001-11-16 01:40:53 +03:00
if ( lifetime < aOptions - > mLifetimeMin )
{
continue ;
}
else if ( lifetime > aOptions - > mLifetimeMax )
{
continue ;
}
/*
* * Check byte size restrictions .
*/
bytesize = byteSize ( current ) ;
if ( bytesize < aOptions - > mSizeMin )
{
continue ;
}
else if ( bytesize > aOptions - > mSizeMax )
{
continue ;
}
/*
* * Check weight restrictions .
*/
2001-12-29 01:27:20 +03:00
LL_UI2L ( bytesize64 , bytesize ) ;
LL_UI2L ( lifetime64 , lifetime ) ;
LL_MUL ( weight64 , bytesize64 , lifetime64 ) ;
2001-11-16 01:40:53 +03:00
if ( LL_UCMP ( weight64 , < , aOptions - > mWeightMin64 ) )
{
continue ;
}
else if ( LL_UCMP ( weight64 , > , aOptions - > mWeightMax64 ) )
{
continue ;
}
/*
* * Possibly restrict the callsite by text .
* * Do this last , as it is a heavier check .
* *
* * One day , we may need to expand the logic to check for
* * events beyond the initial allocation event .
*/
2002-01-24 03:58:33 +03:00
for ( looper = 0 ; ST_SUBSTRING_MATCH_MAX > looper ; looper + + )
2001-11-16 01:40:53 +03:00
{
2002-05-03 04:32:23 +04:00
if ( ' \0 ' ! = globals . mOptions . mRestrictText [ looper ] [ 0 ] )
2001-11-16 01:40:53 +03:00
{
2002-01-24 03:58:33 +03:00
if ( 0 = = hasCallsiteMatch ( current - > mEvents [ 0 ] . mCallsite , globals . mOptions . mRestrictText [ looper ] , ST_FOLLOW_PARENTS ) )
{
break ;
}
2001-11-16 01:40:53 +03:00
}
2002-01-25 01:41:08 +03:00
else
{
matched = PR_TRUE ;
break ;
}
}
if ( ST_SUBSTRING_MATCH_MAX = = looper )
{
matched = PR_TRUE ;
2001-11-16 01:40:53 +03:00
}
2002-01-25 01:41:08 +03:00
if ( PR_FALSE = = matched )
2002-01-24 03:58:33 +03:00
{
continue ;
}
2001-11-16 01:40:53 +03:00
/*
* * You get here , we add to the run .
*/
appendRes = appendAllocation ( aOutRun , current ) ;
if ( 0 = = appendRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
}
}
}
2002-04-17 03:36:47 +04:00
# if defined(DEBUG_dp)
fprintf ( stderr , " DEBUG: harvesting ends: %dms [%d allocations] \n " ,
PR_IntervalToMilliseconds ( PR_IntervalNow ( ) - start ) , aInRun - > mAllocationCount ) ;
# endif
2001-11-16 01:40:53 +03:00
return retval ;
}
2002-04-17 03:36:47 +04:00
/*
* * recalculateRunCost
* *
* * Goes over all allocations of a run and recalculates and propogates
* * the allocation costs - weight , heapcount , size
*/
int recalculateRunCost ( STRun * aRun )
{
PRUint32 traverse = 0 ;
STAllocation * current = NULL ;
# if defined(DEBUG_dp)
PRIntervalTime start = PR_IntervalNow ( ) ;
fprintf ( stderr , " DEBUG: recalculateRunCost... \n " ) ;
# endif
if ( NULL = = aRun )
return - 1 ;
/* reset stats of this run to 0 to begin recalculation */
memset ( & aRun - > mStats , 0 , sizeof ( STCallsiteStats ) ) ;
/* reset timestamp to force propogation of cost */
aRun - > mStats . mStamp = PR_IntervalNow ( ) ;
for ( traverse = 0 ; traverse < aRun - > mAllocationCount ; traverse + + )
{
current = aRun - > mAllocations [ traverse ] ;
if ( NULL ! = current )
{
2002-04-19 00:11:45 +04:00
recalculateAllocationCost ( aRun , current , PR_TRUE ) ;
2002-04-17 03:36:47 +04:00
}
}
# if defined(DEBUG_dp)
fprintf ( stderr , " DEBUG: recalculateRunCost ends: %dms [%d allocations] \n " ,
PR_IntervalToMilliseconds ( PR_IntervalNow ( ) - start ) , aRun - > mAllocationCount ) ;
# endif
2002-05-04 06:06:52 +04:00
/*
* * Cause the graphs to be recalculated as well .
*/
globals . mCache . mFootprintCached = 0 ;
globals . mCache . mTimevalCached = 0 ;
globals . mCache . mLifespanCached = 0 ;
globals . mCache . mWeightCached = 0 ;
2002-04-17 03:36:47 +04:00
return 0 ;
}
2001-11-16 01:40:53 +03:00
/*
* * compareAllocations
* *
* * qsort callback .
* * Compare the allocations as specified by the options .
*/
2001-12-18 04:00:36 +03:00
int compareAllocations ( const void * aAlloc1 , const void * aAlloc2 , void * aContext )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aAlloc1 & & NULL ! = aAlloc2 )
{
STAllocation * alloc1 = * ( ( STAllocation * * ) aAlloc1 ) ;
STAllocation * alloc2 = * ( ( STAllocation * * ) aAlloc2 ) ;
if ( NULL ! = alloc1 & & NULL ! = alloc2 )
{
/*
* * Logic determined by pref / option .
*/
switch ( globals . mOptions . mOrderBy )
{
2001-12-18 04:00:36 +03:00
case ST_COUNT :
/*
2001-12-29 01:27:20 +03:00
* * " By count " on a single allocation means nothing ,
2001-12-18 04:00:36 +03:00
* * fall through to weight .
*/
2001-11-16 01:40:53 +03:00
case ST_WEIGHT :
{
PRUint64 weight164 = LL_INIT ( 0 , 0 ) ;
PRUint64 weight264 = LL_INIT ( 0 , 0 ) ;
2001-12-29 01:27:20 +03:00
PRUint64 bytesize164 = LL_INIT ( 0 , 0 ) ;
PRUint64 bytesize264 = LL_INIT ( 0 , 0 ) ;
PRUint64 timeval164 = LL_INIT ( 0 , 0 ) ;
PRUint64 timeval264 = LL_INIT ( 0 , 0 ) ;
LL_UI2L ( bytesize164 , byteSize ( alloc1 ) ) ;
LL_UI2L ( timeval164 , ( alloc1 - > mMaxTimeval - alloc1 - > mMinTimeval ) ) ;
LL_MUL ( weight164 , bytesize164 , timeval164 ) ;
LL_UI2L ( bytesize264 , byteSize ( alloc2 ) ) ;
LL_UI2L ( timeval264 , ( alloc2 - > mMaxTimeval - alloc2 - > mMinTimeval ) ) ;
LL_MUL ( weight264 , bytesize264 , timeval264 ) ;
2001-11-16 01:40:53 +03:00
if ( LL_UCMP ( weight164 , < , weight264 ) )
{
retval = __LINE__ ;
}
else if ( LL_UCMP ( weight164 , > , weight264 ) )
{
retval = - __LINE__ ;
}
}
break ;
case ST_SIZE :
{
2001-12-18 04:00:36 +03:00
PRUint32 size1 = byteSize ( alloc1 ) ;
PRUint32 size2 = byteSize ( alloc2 ) ;
2001-11-16 01:40:53 +03:00
if ( size1 < size2 )
{
retval = __LINE__ ;
}
else if ( size1 > size2 )
{
retval = - __LINE__ ;
}
}
break ;
2001-11-29 04:29:26 +03:00
case ST_TIMEVAL :
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
PRUint32 timeval1 = ( alloc1 - > mMaxTimeval - alloc1 - > mMinTimeval ) ;
PRUint32 timeval2 = ( alloc2 - > mMaxTimeval - alloc2 - > mMinTimeval ) ;
2001-11-16 01:40:53 +03:00
2001-11-29 04:29:26 +03:00
if ( timeval1 < timeval2 )
2001-11-16 01:40:53 +03:00
{
retval = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else if ( timeval1 > timeval2 )
2001-11-16 01:40:53 +03:00
{
retval = - __LINE__ ;
}
}
break ;
2001-12-18 04:00:36 +03:00
case ST_HEAPCOST :
{
PRUint32 cost1 = alloc1 - > mHeapRuntimeCost ;
PRUint32 cost2 = alloc2 - > mHeapRuntimeCost ;
if ( cost1 < cost2 )
{
retval = __LINE__ ;
}
else if ( cost1 > cost2 )
{
retval = - __LINE__ ;
}
}
break ;
2001-11-16 01:40:53 +03:00
default :
{
REPORT_ERROR ( __LINE__ , compareAllocations ) ;
}
break ;
}
}
}
return retval ;
}
/*
* * sortRun
* *
* * Given a run , sort it in the manner specified by the options .
* * Returns ! 0 on failure .
*/
int sortRun ( STRun * aRun )
{
int retval = 0 ;
if ( NULL ! = aRun )
{
if ( NULL ! = aRun - > mAllocations & & 0 < aRun - > mAllocationCount )
{
2001-12-18 04:00:36 +03:00
NS_QuickSort ( aRun - > mAllocations , aRun - > mAllocationCount , sizeof ( STAllocation * ) , compareAllocations , NULL ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , sortRun ) ;
}
return retval ;
}
/*
* * createRun
* *
* * Returns a newly allocated run , properly initialized .
* * Must call freeRun ( ) with the new STRun .
* *
* * ONLY PASS IN A NON_ZERO STAMP IF YOU KNOW WHAT YOU ARE DOING ! ! !
* * A non zero stamp in a run has side effects all over the
* * callsites of the allocations added to the run and their
* * parents .
* *
* * Returns NULL on failure .
*/
STRun * createRun ( PRUint32 aStamp )
{
STRun * retval = NULL ;
retval = ( STRun * ) calloc ( 1 , sizeof ( STRun ) ) ;
if ( NULL ! = retval )
{
retval - > mStats . mStamp = aStamp ;
}
return retval ;
}
/*
* * freeRun
* *
* * Free off the run and the associated data .
*/
void freeRun ( STRun * aRun )
{
if ( NULL ! = aRun )
{
if ( NULL ! = aRun - > mAllocations )
{
/*
* * We do not free the allocations themselves .
* * They are likely pointed to by at least 2 other existing
* * runs .
*/
free ( aRun - > mAllocations ) ;
aRun - > mAllocations = NULL ;
}
free ( aRun ) ;
aRun = NULL ;
}
}
/*
* * createRunFromGlobal
* *
* * Harvest the global run , then sort it .
* * Returns NULL on failure .
* * Must call freeRun ( ) with the new STRun .
*/
STRun * createRunFromGlobal ( void )
{
STRun * retval = NULL ;
/*
* * We stamp the run .
* * As things are appended to it , it realizes that it should stamp the
* * callsite backtrace with the information as well .
* * In this manner , we can provide meaningful callsite data .
*/
retval = createRun ( PR_IntervalNow ( ) ) ;
if ( NULL ! = retval )
{
2002-05-03 04:32:23 +04:00
STCategoryNode * node = NULL ;
2001-11-16 01:40:53 +03:00
int failure = 0 ;
int harvestRes = harvestRun ( & globals . mRun , retval , NULL ) ;
if ( 0 = = harvestRes )
{
int sortRes = sortRun ( retval ) ;
if ( 0 ! = sortRes )
{
failure = __LINE__ ;
}
}
else
{
failure = __LINE__ ;
}
2002-04-17 03:36:47 +04:00
2001-11-16 01:40:53 +03:00
if ( 0 ! = failure )
{
freeRun ( retval ) ;
retval = NULL ;
REPORT_ERROR ( failure , createRunFromGlobal ) ;
}
2002-04-17 03:36:47 +04:00
/*
* * Categorize the run
*/
failure = categorizeRun ( retval , & globals ) ;
if ( 0 ! = failure )
{
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
/*
* * if we are focussing on a category , return that run instead of
* * the harvested run . Make sure to recalculate cost .
*/
2002-05-03 04:32:23 +04:00
node = findCategoryNode ( globals . mOptions . mCategoryName , & globals ) ;
if ( node )
2002-04-17 03:36:47 +04:00
{
2002-05-03 04:32:23 +04:00
/* Recalculate cost of run */
recalculateRunCost ( node - > run ) ;
retval = node - > run ;
2002-04-17 03:36:47 +04:00
}
2001-11-16 01:40:53 +03:00
}
return retval ;
}
/*
* * getLiveAllocationByHeapID
* *
* * Go through a run and find the right heap ID .
* * At the time of the call to this function , the allocation must be LIVE ,
* * meaning that it can not be freed .
* * Go through the run backwards , in hopes of finding it near the end .
* *
* * Returns the allocation on success , otherwise NULL .
*/
STAllocation * getLiveAllocationByHeapID ( STRun * aRun , PRUint32 aHeapID )
{
STAllocation * retval = NULL ;
if ( NULL ! = aRun & & 0 ! = aHeapID )
{
PRUint32 traverse = aRun - > mAllocationCount ;
STAllocation * eval = NULL ;
/*
* * Go through in reverse order .
* * Stop when we have a return value .
*/
while ( 0 < traverse & & NULL = = retval )
{
/*
* * Back up one to align with zero based index .
*/
traverse - - ;
/*
* * Take the pointer math out of further operations .
*/
eval = aRun - > mAllocations [ traverse ] ;
/*
* * Take a look at the events in reverse order .
* * Basically the last event must NOT be a free .
* * The last event must NOT be a realloc of size zero ( free ) .
* * Otherwise , try to match up the heapID of the event .
*/
if ( 0 ! = eval - > mEventCount )
{
STAllocEvent * event = eval - > mEvents + ( eval - > mEventCount - 1 ) ;
switch ( event - > mEventType )
{
case TM_EVENT_FREE :
{
/*
* * No freed allocation can match .
*/
}
break ;
case TM_EVENT_REALLOC :
case TM_EVENT_CALLOC :
case TM_EVENT_MALLOC :
{
/*
* * Heap IDs must match .
*/
if ( aHeapID = = event - > mHeapID )
{
retval = eval ;
}
}
break ;
default :
{
REPORT_ERROR ( __LINE__ , getAllocationByHeapID ) ;
}
break ;
}
}
}
}
else
{
REPORT_ERROR ( __LINE__ , getAllocationByHeapID ) ;
}
return retval ;
}
/*
* * appendEvent
* *
* * Given an allocation , append a new event to it ' s lifetime .
* * Returns the new event on success , otherwise NULL .
*/
2001-11-29 04:29:26 +03:00
STAllocEvent * appendEvent ( STAllocation * aAllocation , PRUint32 aTimeval , char aEventType , PRUint32 aHeapID , PRUint32 aHeapSize , tmcallsite * aCallsite )
2001-11-16 01:40:53 +03:00
{
STAllocEvent * retval = NULL ;
if ( NULL ! = aAllocation & & NULL ! = aCallsite )
{
STAllocEvent * expand = NULL ;
/*
* * Expand the allocation ' s event array .
*/
expand = ( STAllocEvent * ) realloc ( aAllocation - > mEvents , sizeof ( STAllocEvent ) * ( aAllocation - > mEventCount + 1 ) ) ;
if ( NULL ! = expand )
{
/*
* * Reassign in case of pointer move .
*/
aAllocation - > mEvents = expand ;
/*
* * Remove the pointer math from rest of code .
*/
retval = aAllocation - > mEvents + aAllocation - > mEventCount ;
/*
* * Increase event array count .
*/
aAllocation - > mEventCount + + ;
/*
* * Fill in the event .
*/
2001-11-29 04:29:26 +03:00
retval - > mTimeval = aTimeval ;
2001-11-16 01:40:53 +03:00
retval - > mEventType = aEventType ;
retval - > mHeapID = aHeapID ;
retval - > mHeapSize = aHeapSize ;
retval - > mCallsite = aCallsite ;
/*
* * Allocation may need to update idea of lifetime .
2001-11-29 04:29:26 +03:00
* * See allocationTracker to see mMinTimeval inited to ST_TIMEVAL_MAX .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
if ( aAllocation - > mMinTimeval > aTimeval )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
aAllocation - > mMinTimeval = aTimeval ;
2001-11-16 01:40:53 +03:00
}
/*
* * This a free event ?
2001-11-29 04:29:26 +03:00
* * Can only set max timeval on a free .
* * Otherwise , mMaxTimeval remains ST_TIMEVAL_MAX .
2001-11-16 01:40:53 +03:00
* * Set in allocationTracker .
*/
2001-11-29 21:44:15 +03:00
if ( TM_EVENT_FREE = = aEventType )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
aAllocation - > mMaxTimeval = aTimeval ;
2001-11-16 01:40:53 +03:00
}
}
else
{
REPORT_ERROR ( __LINE__ , appendEvent ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , appendEvent ) ;
}
return retval ;
}
/*
* * hasAllocation
* *
* * Determine if a given run has an allocation .
* * This is really nothing more than a pointer comparison loop .
* * Returns ! 0 if the run has the allocation .
*/
int hasAllocation ( STRun * aRun , STAllocation * aTestFor )
{
int retval = 0 ;
if ( NULL ! = aRun & & NULL ! = aTestFor )
{
PRUint32 traverse = aRun - > mAllocationCount ;
/*
* * Go through reverse , in the hopes it exists nearer the end .
*/
while ( 0 < traverse )
{
/*
* * Back up .
*/
traverse - - ;
if ( aTestFor = = aRun - > mAllocations [ traverse ] )
{
retval = __LINE__ ;
break ;
}
}
}
else
{
REPORT_ERROR ( __LINE__ , hasAllocation ) ;
}
return retval ;
}
/*
* * allocationTracker
* *
* * Important to keep track of all allocations unique so as to determine
* * their lifetimes .
* *
* * Returns a pointer to the allocation on success .
* * Return NULL on failure .
*/
2001-12-18 04:00:36 +03:00
STAllocation * allocationTracker ( PRUint32 aTimeval , char aType , PRUint32 aHeapRuntimeCost , tmcallsite * aCallsite , PRUint32 aHeapID , PRUint32 aSize , tmcallsite * aOldCallsite , PRUint32 aOldHeapID , PRUint32 aOldSize )
2001-11-16 01:40:53 +03:00
{
STAllocation * retval = NULL ;
2001-11-29 04:29:26 +03:00
static int compactor = 1 ;
const int frequency = 10000 ;
2002-01-09 02:20:29 +03:00
PRUint32 actualSize , actualOldSize = 0 ;
actualSize = actualByteSize ( aSize ) ;
if ( aOldSize )
actualOldSize = actualByteSize ( aOldSize ) ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = aCallsite )
{
int newAllocation = 0 ;
tmcallsite * searchCallsite = NULL ;
PRUint32 searchHeapID = 0 ;
STAllocation * allocation = NULL ;
/*
* * Global operation ID increases .
*/
globals . mOperationCount + + ;
/*
2001-11-29 04:29:26 +03:00
* * Fix up the timevals if needed .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
if ( aTimeval < globals . mMinTimeval )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
globals . mMinTimeval = aTimeval ;
2001-11-16 01:40:53 +03:00
}
2001-11-29 04:29:26 +03:00
if ( aTimeval > globals . mMaxTimeval )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
globals . mMaxTimeval = aTimeval ;
2001-11-16 01:40:53 +03:00
}
switch ( aType )
{
case TM_EVENT_FREE :
{
/*
* * Update the global counter .
*/
globals . mFreeCount + + ;
2002-01-09 02:20:29 +03:00
/*
* * Update our peak memory used counter
*/
globals . mMemoryUsed - = actualSize ;
2001-11-16 01:40:53 +03:00
/*
* * Not a new allocation , will need to search passed in site
* * for the original allocation .
*/
searchCallsite = aCallsite ;
searchHeapID = aHeapID ;
}
break ;
case TM_EVENT_MALLOC :
{
/*
* * Update the global counter .
*/
globals . mMallocCount + + ;
2002-01-09 02:20:29 +03:00
/*
* * Update our peak memory used counter
*/
globals . mMemoryUsed + = actualSize ;
if ( globals . mMemoryUsed > globals . mPeakMemoryUsed )
{
globals . mPeakMemoryUsed = globals . mMemoryUsed ;
}
2001-11-16 01:40:53 +03:00
/*
* * This will be a new allocation .
*/
newAllocation = __LINE__ ;
}
break ;
case TM_EVENT_CALLOC :
{
/*
* * Update the global counter .
*/
globals . mCallocCount + + ;
2002-01-09 02:20:29 +03:00
/*
* * Update our peak memory used counter
*/
globals . mMemoryUsed + = actualSize ;
if ( globals . mMemoryUsed > globals . mPeakMemoryUsed )
{
globals . mPeakMemoryUsed = globals . mMemoryUsed ;
}
2001-11-16 01:40:53 +03:00
/*
* * This will be a new allocation .
*/
newAllocation = __LINE__ ;
}
break ;
case TM_EVENT_REALLOC :
{
/*
* * Update the global counter .
*/
globals . mReallocCount + + ;
2002-01-09 02:20:29 +03:00
/*
* * Update our peak memory used counter
*/
globals . mMemoryUsed + = actualSize - actualOldSize ;
if ( globals . mMemoryUsed > globals . mPeakMemoryUsed )
{
globals . mPeakMemoryUsed = globals . mMemoryUsed ;
}
2001-11-16 01:40:53 +03:00
/*
* * This might be a new allocation .
*/
if ( NULL = = aOldCallsite )
{
newAllocation = __LINE__ ;
}
else
{
/*
* * Need to search for the original callsite for the
* * index to the allocation .
*/
searchCallsite = aOldCallsite ;
searchHeapID = aOldHeapID ;
}
}
break ;
default :
{
REPORT_ERROR ( __LINE__ , allocationTracker ) ;
}
break ;
}
/*
* * We are either modifying an existing allocation or we are creating
* * a new one .
*/
if ( 0 ! = newAllocation )
{
allocation = ( STAllocation * ) calloc ( 1 , sizeof ( STAllocation ) ) ;
if ( NULL ! = allocation )
{
/*
2001-11-29 04:29:26 +03:00
* * Fixup the min timeval so if logic later will just work .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
allocation - > mMinTimeval = ST_TIMEVAL_MAX ;
allocation - > mMaxTimeval = ST_TIMEVAL_MAX ;
2001-11-16 01:40:53 +03:00
}
}
else if ( NULL ! = searchCallsite & & NULL ! = CALLSITE_RUN ( searchCallsite ) & & 0 ! = searchHeapID )
{
/*
* * We know what to search for , and we reduce what we search
* * by only looking for those allocations at a known callsite .
*/
allocation = getLiveAllocationByHeapID ( CALLSITE_RUN ( searchCallsite ) , searchHeapID ) ;
}
else
{
REPORT_ERROR ( __LINE__ , allocationTracker ) ;
}
if ( NULL ! = allocation )
{
STAllocEvent * appendResult = NULL ;
2001-12-18 04:00:36 +03:00
/*
* * Record the amount of time this allocation event took .
*/
allocation - > mHeapRuntimeCost + = aHeapRuntimeCost ;
2001-11-16 01:40:53 +03:00
/*
* * Now that we have an allocation , we need to make sure it has
* * the proper event .
*/
2001-11-29 04:29:26 +03:00
appendResult = appendEvent ( allocation , aTimeval , aType , aHeapID , aSize , aCallsite ) ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = appendResult )
{
if ( 0 ! = newAllocation )
{
int runAppendResult = 0 ;
int callsiteAppendResult = 0 ;
/*
* * A new allocation needs to be added to the global run .
* * A new allocation needs to be added to the callsite .
*/
runAppendResult = appendAllocation ( & ( globals . mRun ) , allocation ) ;
callsiteAppendResult = appendAllocation ( CALLSITE_RUN ( aCallsite ) , allocation ) ;
if ( 0 ! = runAppendResult & & 0 ! = callsiteAppendResult )
{
/*
* * Success .
*/
retval = allocation ;
}
else
{
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
}
else
{
/*
* * An existing allocation , if a realloc situation ,
* * may need to be added to the new callsite .
* * This can only occur if the new and old callsites
* * differ .
* * Even then , a brute force check will need to be made
* * to ensure the allocation was not added twice ;
* * consider a realloc scenario where two different
* * call stacks bump the allocation back and forth .
*/
if ( aCallsite ! = searchCallsite )
{
int found = 0 ;
found = hasAllocation ( CALLSITE_RUN ( aCallsite ) , allocation ) ;
if ( 0 = = found )
{
int appendResult = 0 ;
appendResult = appendAllocation ( CALLSITE_RUN ( aCallsite ) , allocation ) ;
if ( 0 ! = appendResult )
{
/*
* * Success .
*/
retval = allocation ;
}
else
{
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
}
else
{
/*
* * Already there .
*/
retval = allocation ;
}
}
else
{
/*
* * Success .
*/
retval = allocation ;
}
}
}
else
{
REPORT_ERROR ( __LINE__ , appendEvent ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , allocationTracker ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , allocationTracker ) ;
}
2001-11-29 04:29:26 +03:00
/*
* * Compact the heap a bit if you can .
*/
compactor + + ;
if ( 0 = = ( compactor % frequency ) )
{
heapCompact ( ) ;
}
2001-11-16 01:40:53 +03:00
return retval ;
}
/*
* * trackEvent
* *
* * An allocation event has dropped in on us .
* * We need to do the right thing and track it .
*/
2001-12-18 04:00:36 +03:00
void trackEvent ( PRUint32 aTimeval , char aType , PRUint32 aHeapRuntimeCost , tmcallsite * aCallsite , PRUint32 aHeapID , PRUint32 aSize , tmcallsite * aOldCallsite , PRUint32 aOldHeapID , PRUint32 aOldSize )
2001-11-16 01:40:53 +03:00
{
if ( NULL ! = aCallsite )
{
/*
* * Verify the old callsite just in case .
*/
if ( NULL ! = CALLSITE_RUN ( aCallsite ) & & ( NULL = = aOldCallsite | | NULL ! = CALLSITE_RUN ( aOldCallsite ) ) )
{
STAllocation * allocation = NULL ;
/*
* * Add to the allocation tracking code .
*/
2001-12-18 04:00:36 +03:00
allocation = allocationTracker ( aTimeval , aType , aHeapRuntimeCost , aCallsite , aHeapID , aSize , aOldCallsite , aOldHeapID , aOldSize ) ;
2001-11-16 01:40:53 +03:00
if ( NULL = = allocation )
{
REPORT_ERROR ( __LINE__ , allocationTracker ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , trackEvent ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , trackEvent ) ;
}
}
/*
* * tmEventHandler
* *
* * Callback from the tmreader_eventloop function .
* * Simply tries to sort out what we desire to know .
*/
void tmEventHandler ( tmreader * aReader , tmevent * aEvent )
{
if ( NULL ! = aReader & & NULL ! = aEvent )
{
switch ( aEvent - > type )
{
/*
* * Events we ignore .
*/
case TM_EVENT_LIBRARY :
case TM_EVENT_METHOD :
case TM_EVENT_STATS :
case TM_EVENT_TIMESTAMP :
2002-01-09 22:03:01 +03:00
case TM_EVENT_FILENAME :
2001-11-16 01:40:53 +03:00
break ;
/*
* * Allocation events need to be tracked .
*/
case TM_EVENT_MALLOC :
case TM_EVENT_CALLOC :
case TM_EVENT_REALLOC :
case TM_EVENT_FREE :
{
PRUint32 oldptr = 0 ;
PRUint32 oldsize = 0 ;
tmcallsite * callsite = NULL ;
tmcallsite * oldcallsite = NULL ;
if ( TM_EVENT_REALLOC = = aEvent - > type )
{
/*
* * Only care about old arguments if there were any .
*/
if ( 0 ! = aEvent - > u . alloc . oldserial )
{
oldptr = aEvent - > u . alloc . oldptr ;
oldsize = aEvent - > u . alloc . oldsize ;
oldcallsite = tmreader_callsite ( aReader , aEvent - > u . alloc . oldserial ) ;
if ( NULL = = oldcallsite )
{
REPORT_ERROR ( __LINE__ , tmreader_callsite ) ;
}
}
}
callsite = tmreader_callsite ( aReader , aEvent - > serial ) ;
if ( NULL ! = callsite )
{
/*
* * Verify a callsite run is there .
* * If not , we are ignoring this callsite .
*/
if ( NULL ! = CALLSITE_RUN ( callsite ) )
{
2001-11-29 21:44:15 +03:00
char eventType = aEvent - > type ;
2001-11-29 04:29:26 +03:00
2001-11-29 21:44:15 +03:00
/*
* * Play a nasty trick on reallocs of size zero .
* * They are to become free events .
* * This allows me to avoid all types of special case code .
*/
if ( 0 = = aEvent - > u . alloc . size & & TM_EVENT_REALLOC = = aEvent - > type )
{
eventType = TM_EVENT_FREE ;
}
2001-12-18 04:00:36 +03:00
trackEvent ( ticks2msec ( aReader , aEvent - > u . alloc . interval ) , eventType , ticks2usec ( aReader , aEvent - > u . alloc . cost ) , callsite , aEvent - > u . alloc . ptr , aEvent - > u . alloc . size , oldcallsite , oldptr , oldsize ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
REPORT_ERROR ( __LINE__ , tmreader_callsite ) ;
}
}
break ;
/*
* * Callsite , set up the callsite run if it does not exist .
*/
case TM_EVENT_CALLSITE :
{
tmcallsite * callsite = tmreader_callsite ( aReader , aEvent - > serial ) ;
if ( NULL ! = callsite )
{
if ( NULL = = CALLSITE_RUN ( callsite ) )
{
int createrun = __LINE__ ;
# if defined(MOZILLA_CLIENT)
/*
* * For a mozilla spacetrace , ignore this particular
* * callsite as it is just noise , and causes us to
* * use a lot of memory .
* *
* * This callsite is present on the linux build ,
* * not sure if the other platforms have it .
*/
if ( 0 ! = hasCallsiteMatch ( callsite , " g_main_is_running " , ST_FOLLOW_PARENTS ) )
{
createrun = 0 ;
}
# endif /* MOZILLA_CLIENT */
if ( 0 ! = createrun )
{
callsite - > data = createRun ( 0 ) ;
}
}
}
else
{
REPORT_ERROR ( __LINE__ , tmreader_callsite ) ;
}
}
break ;
/*
* * Unhandled events should not be allowed .
*/
default :
{
REPORT_ERROR ( __LINE__ , tmEventHandler ) ;
}
break ;
}
}
}
/*
* * htmlAnchor
* *
* * Output an HTML anchor , or just the text depending on the mode .
*/
2002-05-02 22:38:42 +04:00
void htmlAnchor ( STRequest * inRequest , const char * aHref , const char * aText , const char * aTarget )
2001-11-16 01:40:53 +03:00
{
2002-01-09 22:03:01 +03:00
if ( NULL = = aTarget | | ' \0 ' = = aTarget [ 0 ] )
{
aTarget = " _st_content " ;
}
2001-11-16 01:40:53 +03:00
if ( NULL ! = aHref & & ' \0 ' ! = * aHref & & NULL ! = aText & & ' \0 ' ! = * aText )
{
int anchorLive = 1 ;
/*
* * In batch mode , we need to verify the anchor is live .
*/
if ( 0 ! = globals . mOptions . mBatchRequestCount )
{
PRUint32 loop = 0 ;
int comparison = 1 ;
for ( loop = 0 ; loop < globals . mOptions . mBatchRequestCount ; loop + + )
{
2002-05-08 03:39:34 +04:00
comparison = strcmp ( aHref , globals . mOptions . mBatchRequest [ loop ] ) ;
2001-11-16 01:40:53 +03:00
if ( 0 = = comparison )
{
break ;
}
}
/*
* * Did we find it ?
*/
if ( 0 = = comparison )
{
anchorLive = 0 ;
}
}
/*
* * In any mode , don ' t make an href to the current page .
*/
2002-05-03 04:32:23 +04:00
if ( 0 ! = anchorLive & & NULL ! = inRequest - > mGetFileName )
2001-11-16 01:40:53 +03:00
{
2002-05-03 04:32:23 +04:00
if ( 0 = = strcmp ( aHref , inRequest - > mGetFileName ) )
2001-11-16 01:40:53 +03:00
{
anchorLive = 0 ;
}
}
/*
* * Do the right thing .
*/
if ( 0 ! = anchorLive )
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <a target= \" %s \" href= \" ./%s \" >%s</a> \n " , aTarget , aHref , aText ) ;
2001-11-16 01:40:53 +03:00
}
else
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " %s \n " , aText ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
REPORT_ERROR ( __LINE__ , htmlAnchor ) ;
}
}
/*
* * htmlAllocationAnchor
* *
* * Output an html achor that will resolve to the allocation in question .
*/
2002-05-02 22:38:42 +04:00
void htmlAllocationAnchor ( STRequest * inRequest , STAllocation * aAllocation , const char * aText )
2001-11-16 01:40:53 +03:00
{
if ( NULL ! = aAllocation & & NULL ! = aText & & ' \0 ' ! = * aText )
{
char buffer [ 128 ] ;
/*
* * This is a total hack .
* * The filename contains the index of the allocation in globals . mRun .
* * Safer than using the raw pointer value . . . .
*/
PR_snprintf ( buffer , sizeof ( buffer ) , " allocation_%u.html " , aAllocation - > mRunIndex ) ;
2002-05-02 22:38:42 +04:00
htmlAnchor ( inRequest , buffer , aText , NULL ) ;
2001-11-16 01:40:53 +03:00
}
else
{
REPORT_ERROR ( __LINE__ , htmlAllocationAnchor ) ;
}
}
2002-01-09 22:03:01 +03:00
/*
* * resolveSourceFile
* *
* * Easy way to get a readable / short name .
* * NULL if not present , not resolvable .
*/
const char * resolveSourceFile ( tmmethodnode * aMethod )
{
const char * retval = NULL ;
if ( NULL ! = aMethod )
{
const char * methodSays = NULL ;
methodSays = aMethod - > sourcefile ;
if ( NULL ! = methodSays & & ' \0 ' ! = methodSays [ 0 ] & & 0 ! = strcmp ( " noname " , methodSays ) )
{
retval = strrchr ( methodSays , ' / ' ) ;
if ( NULL ! = retval )
{
retval + + ;
}
else
{
retval = methodSays ;
}
}
}
return retval ;
}
2001-11-16 01:40:53 +03:00
/*
* * htmlCallsiteAnchor
* *
* * Output an html anchor that will resolve to the callsite in question .
* * If no text is provided , we provide our own .
* *
* * RealName determines wether or not we crawl our parents until the point
* * we no longer match stats .
*/
2002-05-02 22:38:42 +04:00
void htmlCallsiteAnchor ( STRequest * inRequest , tmcallsite * aCallsite , const char * aText , int aRealName )
2001-11-16 01:40:53 +03:00
{
if ( NULL ! = aCallsite )
{
2002-01-09 22:03:01 +03:00
char textBuf [ 512 ] ;
2001-11-16 01:40:53 +03:00
char hrefBuf [ 128 ] ;
tmcallsite * namesite = aCallsite ;
/*
* * Should we use a different name ?
*/
if ( 0 = = aRealName & & NULL ! = namesite - > parent & & NULL ! = namesite - > parent - > method )
{
STRun * myRun = NULL ;
STRun * upRun = NULL ;
do
{
myRun = CALLSITE_RUN ( namesite ) ;
upRun = CALLSITE_RUN ( namesite - > parent ) ;
if ( 0 ! = memcmp ( & myRun - > mStats , & upRun - > mStats , sizeof ( STCallsiteStats ) ) )
{
/*
* * Doesn ' t match , stop .
*/
break ;
}
else
{
/*
* * Matches , keep going up .
*/
namesite = namesite - > parent ;
}
}
while ( NULL ! = namesite - > parent & & NULL ! = namesite - > parent - > method ) ;
}
/*
* * If no text , provide our own .
*/
if ( NULL = = aText | | ' \0 ' = = * aText )
{
const char * methodName = NULL ;
2002-01-09 22:03:01 +03:00
const char * sourceFile = NULL ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = namesite - > method )
{
2002-01-09 22:03:01 +03:00
methodName = tmmethodnode_name ( namesite - > method ) ;
2001-11-16 01:40:53 +03:00
}
else
{
methodName = " ==NONAME== " ;
}
2002-01-09 22:03:01 +03:00
/*
* * Decide which format to use to identify the callsite .
* * If we can detect availability , hook up the filename with lxr information .
*/
sourceFile = resolveSourceFile ( namesite - > method ) ;
if ( NULL ! = sourceFile & & 0 = = strncmp ( " mozilla/ " , namesite - > method - > sourcefile , 8 ) )
{
char lxrHREFBuf [ 512 ] ;
PR_snprintf ( lxrHREFBuf , sizeof ( lxrHREFBuf ) , " <a href= \" http://lxr.mozilla.org/mozilla/source/%s#%u \" target= \" _st_lxr \" >(%s:%u)</a> " , namesite - > method - > sourcefile + 8 , namesite - > method - > linenumber , sourceFile , namesite - > method - > linenumber ) ;
PR_snprintf ( textBuf , sizeof ( textBuf ) , " <b>%s</b>%s " , methodName , lxrHREFBuf ) ;
}
else if ( NULL ! = sourceFile )
{
PR_snprintf ( textBuf , sizeof ( textBuf ) , " <b>%s</b>(%s:%u) " , methodName , sourceFile , namesite - > method - > linenumber ) ;
}
else
{
PR_snprintf ( textBuf , sizeof ( textBuf ) , " <b>%s</b>+%u(%u) " , methodName , namesite - > offset , ( PRUint32 ) namesite - > entry . key ) ;
}
2001-11-16 01:40:53 +03:00
aText = textBuf ;
}
PR_snprintf ( hrefBuf , sizeof ( hrefBuf ) , " callsite_%u.html " , ( PRUint32 ) aCallsite - > entry . key ) ;
2002-05-02 22:38:42 +04:00
htmlAnchor ( inRequest , hrefBuf , aText , NULL ) ;
2001-11-16 01:40:53 +03:00
}
else
{
REPORT_ERROR ( __LINE__ , htmlCallsiteAnchor ) ;
}
}
/*
* * htmlHeader
* *
* * Output a standard header in the report files .
*/
2002-05-02 22:38:42 +04:00
void htmlHeader ( STRequest * inRequest , const char * aTitle )
2001-11-16 01:40:53 +03:00
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD ,
2001-11-16 01:40:53 +03:00
" <html> \n "
" <head> \n "
" <title>%s</title> \n "
" </head> \n "
" <body> \n "
" <div align=right> \n "
2002-04-17 03:36:47 +04:00
" <table border=0><tr><td bgcolor=lightgrey>Category: <b>%s</b></td> \n "
, aTitle ,
globals . mOptions . mCategoryName ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td> " ) ;
htmlAnchor ( inRequest , " index.html " , " [Index] " , NULL ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2002-04-17 03:36:47 +04:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td> " ) ;
htmlAnchor ( inRequest , " options.html " , " [Options] " , NULL ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr></table> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </div> \n <hr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
/*
* * htmlFooter
* *
* * Output a standard footer in the report file .
*/
2002-05-02 22:38:42 +04:00
void htmlFooter ( STRequest * inRequest )
2001-11-16 01:40:53 +03:00
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD ,
2001-11-16 01:40:53 +03:00
" <hr> \n "
" <div align=right> \n "
" <i>SpaceTrace</i> \n "
" </div> \n "
" </body> \n "
" </html> \n "
) ;
}
/*
* * htmlNotFound
* *
* * Not found message .
*/
2002-05-02 22:38:42 +04:00
void htmlNotFound ( STRequest * inRequest )
2001-11-16 01:40:53 +03:00
{
2002-05-02 22:38:42 +04:00
htmlHeader ( inRequest , " File Not Found " ) ;
PR_fprintf ( inRequest - > mFD , " File Not Found \n " ) ;
htmlFooter ( inRequest ) ;
2001-11-16 01:40:53 +03:00
}
/*
* * callsiteArrayFromCallsite
* *
* * Simply return an array of the callsites divulged from the site passed in ,
* * including the site passed in .
* * Do not worry about dups , or the order of the items .
* *
* * Returns the number of items in the array .
* * If the same as aExistingCount , then nothing happened .
*/
PRUint32 callsiteArrayFromCallsite ( tmcallsite * * * aArray , PRUint32 aExistingCount , tmcallsite * aSite , int aFollow )
{
PRUint32 retval = 0 ;
if ( NULL ! = aArray & & NULL ! = aSite )
{
tmcallsite * * expand = NULL ;
/*
* * If we have an existing count , we just keep expanding this .
*/
retval = aExistingCount ;
/*
* * Go through every allocation .
*/
do
{
/*
* * expand the array .
*/
expand = ( tmcallsite * * ) realloc ( * aArray , sizeof ( tmcallsite * ) * ( retval + 1 ) ) ;
if ( NULL ! = expand )
{
/*
* * Set the callsite in case of pointer move .
*/
* aArray = expand ;
/*
* * Assign the value .
*/
( * aArray ) [ retval ] = aSite ;
retval + + ;
}
else
{
REPORT_ERROR ( __LINE__ , realloc ) ;
break ;
}
/*
* * What do we follow ?
*/
switch ( aFollow )
{
case ST_FOLLOW_SIBLINGS :
aSite = aSite - > siblings ;
break ;
case ST_FOLLOW_PARENTS :
aSite = aSite - > parent ;
break ;
default :
aSite = NULL ;
REPORT_ERROR ( __LINE__ , callsiteArrayFromCallsite ) ;
break ;
}
}
while ( NULL ! = aSite & & NULL ! = aSite - > method ) ;
}
return retval ;
}
/*
* * callsiteArrayFromRun
* *
* * Simply return an array of the callsites from the run allocations .
* * We only pay attention to callsites that were not free callsites .
* * Do not worry about dups , or the order of the items .
* *
* * Returns the number of items in the array .
* * If the same as aExistingCount , then nothing happened .
*/
PRUint32 callsiteArrayFromRun ( tmcallsite * * * aArray , PRUint32 aExistingCount , STRun * aRun )
{
PRUint32 retval = 0 ;
if ( NULL ! = aArray & & NULL ! = aRun & & 0 < aRun - > mAllocationCount )
{
PRUint32 allocLoop = 0 ;
PRUint32 eventLoop = 0 ;
int stopLoops = 0 ;
/*
* * If we have an existing count , we just keep expanding this .
*/
retval = aExistingCount ;
/*
* * Go through every allocation .
*/
for ( allocLoop = 0 ; 0 = = stopLoops & & allocLoop < aRun - > mAllocationCount ; allocLoop + + )
{
/*
* * Go through every event .
*/
for ( eventLoop = 0 ; 0 = = stopLoops & & eventLoop < aRun - > mAllocations [ allocLoop ] - > mEventCount ; eventLoop + + )
{
/*
2001-11-29 21:44:15 +03:00
* * Skip the free events .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 21:44:15 +03:00
if ( TM_EVENT_FREE ! = aRun - > mAllocations [ allocLoop ] - > mEvents [ eventLoop ] . mEventType )
2001-11-16 01:40:53 +03:00
{
tmcallsite * * expand = NULL ;
/*
* * expand the array .
*/
expand = ( tmcallsite * * ) realloc ( * aArray , sizeof ( tmcallsite * ) * ( retval + 1 ) ) ;
if ( NULL ! = expand )
{
/*
* * Set the callsite in case of pointer move .
*/
* aArray = expand ;
/*
* * Assign the value .
*/
( * aArray ) [ retval ] = aRun - > mAllocations [ allocLoop ] - > mEvents [ eventLoop ] . mCallsite ;
retval + + ;
}
else
{
REPORT_ERROR ( __LINE__ , realloc ) ;
stopLoops = __LINE__ ;
}
}
}
}
}
return retval ;
}
/*
* * getDataPRUint *
* *
* * Helper to avoid cut and paste code .
* * Failure to find aCheckFor does not mean failure .
* * Returns ! 0 on failure .
*/
int getDataPRUint32Base ( const char * aGetData , const char * aCheckFor , void * aStoreResult , PRUint32 aBits )
{
int retval = 0 ;
if ( NULL ! = aGetData & & NULL ! = aCheckFor & & NULL ! = aStoreResult )
{
const char * found = NULL ;
PRInt32 scanRes = 0 ;
/*
* * Looking for the presence .
*/
found = strstr ( aGetData , aCheckFor ) ;
if ( NULL ! = found )
{
int length = 0 ;
const char * dataPoint = NULL ;
/*
* * Skip the varname .
* * Skip the ' = ' sign .
*/
length = strlen ( aCheckFor ) ;
dataPoint = found + length + 1 ;
/*
* * Just attempt to scan from here .
*/
if ( 64 = = aBits )
{
scanRes = PR_sscanf ( dataPoint , " %llu " , aStoreResult ) ;
}
else
{
scanRes = PR_sscanf ( dataPoint , " %u " , aStoreResult ) ;
}
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_sscanf ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , getDataPRUint32 ) ;
}
return retval ;
}
2001-11-29 04:29:26 +03:00
int getDataPRUint32 ( const char * aGetData , const char * aCheckFor , PRUint32 * aStoreResult , int * aChanged , PRUint32 aConversion )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
PRUint32 value = * aStoreResult ;
retval = getDataPRUint32Base ( aGetData , aCheckFor , aStoreResult , 32 ) ;
2001-11-29 04:29:26 +03:00
* aStoreResult * = aConversion ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = aChanged & & value ! = * aStoreResult )
{
( * aChanged ) = ( * aChanged ) + 1 ;
}
return retval ;
}
int getDataPRUint64 ( const char * aGetData , const char * aCheckFor , PRUint64 * aStoreResult64 , int * aChanged )
{
int retval = 0 ;
PRUint64 value64 = * aStoreResult64 ;
retval = getDataPRUint32Base ( aGetData , aCheckFor , aStoreResult64 , 64 ) ;
if ( NULL ! = aChanged & & LL_NE ( value64 , * aStoreResult64 ) )
{
( * aChanged ) = ( * aChanged ) + 1 ;
}
return retval ;
}
/*
* * getDataString
* *
* * Pull out the string data , if specified .
* * Return ! 0 on failure .
*/
2002-05-03 04:32:23 +04:00
int getDataString ( const char * aGetData , const char * aCheckFor , char * aStoreResult , int inStoreResultLength , int * aChanged )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
2002-05-03 04:32:23 +04:00
if ( NULL ! = aGetData & & NULL ! = aCheckFor & & NULL ! = aStoreResult & & 0 ! = inStoreResultLength )
2001-11-16 01:40:53 +03:00
{
const char * found = NULL ;
PRInt32 scanRes = 0 ;
/*
* * Check for presence .
*/
found = strstr ( aGetData , aCheckFor ) ;
if ( NULL ! = found )
{
int length = 0 ;
const char * dataPoint = NULL ;
const char * endPoint = NULL ;
2002-05-03 04:32:23 +04:00
char oldResult [ ST_OPTION_STRING_MAX ] ;
2001-11-29 04:29:26 +03:00
int theLen = 0 ;
2001-11-16 01:40:53 +03:00
/*
* * Skip the varname .
* * Skip the ' = ' sign .
*/
length = strlen ( aCheckFor ) ;
dataPoint = found + length + 1 ;
/*
* * The length is up to a ' & ' or until end of string .
*/
endPoint = strchr ( dataPoint , ' & ' ) ;
if ( NULL = = endPoint )
{
endPoint = dataPoint + strlen ( dataPoint ) ;
}
/*
* * Store original value if present .
*/
2002-05-03 04:32:23 +04:00
PR_snprintf ( oldResult , sizeof ( oldResult ) , " %s " , aStoreResult ) ;
2001-11-16 01:40:53 +03:00
/*
2002-05-03 04:32:23 +04:00
* * We have enough space for this ?
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
theLen = ( int ) ( endPoint - dataPoint ) ;
2002-05-03 04:32:23 +04:00
if ( inStoreResultLength > theLen )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
int index1 = 0 ;
int index2 = 0 ;
2002-05-03 04:32:23 +04:00
strncpy ( aStoreResult , dataPoint , theLen ) ;
aStoreResult [ theLen ] = ' \0 ' ;
2001-11-29 04:29:26 +03:00
/*
* * Here ' s a totally suboptimal and bug prone unhexcaper .
*/
for ( ; index1 < = theLen ; index1 + + )
{
2002-05-03 04:32:23 +04:00
if ( ' % ' = = aStoreResult [ index1 ] & & ' \0 ' ! = aStoreResult [ index1 + 1 ] & & ' \0 ' ! = aStoreResult [ index1 + 2 ] )
2001-11-29 04:29:26 +03:00
{
int unhex = 0 ;
2002-05-03 04:32:23 +04:00
if ( ' 9 ' > = aStoreResult [ index1 + 1 ] )
2001-11-29 04:29:26 +03:00
{
2002-05-03 04:32:23 +04:00
unhex | = ( ( aStoreResult [ index1 + 1 ] - ' 0 ' ) < < 4 ) ;
2001-11-29 04:29:26 +03:00
}
else
{
2002-05-03 04:32:23 +04:00
unhex | = ( ( toupper ( aStoreResult [ index1 + 1 ] ) - ' A ' + 10 ) < < 4 ) ;
2001-11-29 04:29:26 +03:00
}
2002-05-03 04:32:23 +04:00
if ( ' 9 ' > = aStoreResult [ index1 + 2 ] )
2001-11-29 04:29:26 +03:00
{
2002-05-03 04:32:23 +04:00
unhex | = ( aStoreResult [ index1 + 2 ] - ' 0 ' ) ;
2001-11-29 04:29:26 +03:00
}
else
{
2002-05-03 04:32:23 +04:00
unhex | = ( toupper ( aStoreResult [ index1 + 2 ] ) - ' A ' + 10 ) ;
2001-11-29 04:29:26 +03:00
}
index1 + = 2 ;
2002-05-03 04:32:23 +04:00
aStoreResult [ index1 ] = unhex ;
2001-11-29 04:29:26 +03:00
}
2002-05-03 04:32:23 +04:00
aStoreResult [ index2 + + ] = aStoreResult [ index1 ] ;
2001-11-29 04:29:26 +03:00
}
2001-11-16 01:40:53 +03:00
/*
* * See if the value actually changed .
*/
if ( NULL ! = aChanged )
{
if ( NULL ! = oldResult )
{
2002-05-03 04:32:23 +04:00
if ( 0 ! = strcmp ( oldResult , aStoreResult ) )
2001-11-16 01:40:53 +03:00
{
( * aChanged ) = ( * aChanged ) + 1 ;
}
}
2002-05-03 04:32:23 +04:00
else if ( ' \0 ' ! = aStoreResult [ 0 ] )
2001-11-16 01:40:53 +03:00
{
( * aChanged ) = ( * aChanged ) + 1 ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , malloc ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , getDataPRUint32 ) ;
}
return retval ;
}
/*
* * displayTopAllocations
* *
* * Present the top allocations .
* * The run must be passed in , and it must be pre - sorted .
* *
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int displayTopAllocations ( STRequest * inRequest , STRun * aRun , int aWantCallsite )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aRun )
{
if ( 0 < aRun - > mAllocationCount )
{
PRUint32 loop = 0 ;
STAllocation * current = NULL ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <table border=1> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Rank</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Index</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Byte Size</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Lifespan Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Weight</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Heap Operation Seconds</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = aWantCallsite )
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td><b>Origin Callsite</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Loop over the items , up to some limit or until the end .
*/
for ( loop = 0 ; loop < globals . mOptions . mListItemMax & & loop < aRun - > mAllocationCount ; loop + + )
{
current = aRun - > mAllocations [ loop ] ;
if ( NULL ! = current )
{
2001-11-29 04:29:26 +03:00
PRUint32 lifespan = current - > mMaxTimeval - current - > mMinTimeval ;
2001-11-16 01:40:53 +03:00
PRUint32 size = byteSize ( current ) ;
2001-12-18 04:00:36 +03:00
PRUint32 heapCost = current - > mHeapRuntimeCost ;
2001-11-16 01:40:53 +03:00
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
2001-12-29 01:27:20 +03:00
PRUint64 size64 = LL_INIT ( 0 , 0 ) ;
PRUint64 lifespan64 = LL_INIT ( 0 , 0 ) ;
2001-11-16 01:40:53 +03:00
char buffer [ 32 ] ;
2001-12-29 01:27:20 +03:00
LL_UI2L ( size64 , size ) ;
LL_UI2L ( lifespan64 , lifespan ) ;
LL_MUL ( weight64 , size64 , lifespan64 ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Rank .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right>%u</td> \n " , loop + 1 ) ;
2001-11-16 01:40:53 +03:00
/*
* * Index .
*/
PR_snprintf ( buffer , sizeof ( buffer ) , " %u " , current - > mRunIndex ) ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right> \n " ) ;
htmlAllocationAnchor ( inRequest , current , buffer ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Byte Size .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right>%u</td> \n " , size ) ;
2001-11-16 01:40:53 +03:00
/*
* * Lifespan .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE ( lifespan ) ) ;
2001-11-29 04:29:26 +03:00
/*
* * Weight .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right>%llu</td> \n " , weight64 ) ;
2001-11-16 01:40:53 +03:00
2001-12-18 04:00:36 +03:00
/*
* * Heap operation cost .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right> " ST_MICROVAL_FORMAT " </td> \n " , ST_MICROVAL_PRINTABLE ( heapCost ) ) ;
2001-12-18 04:00:36 +03:00
2001-11-16 01:40:53 +03:00
if ( 0 ! = aWantCallsite )
{
/*
* * Callsite .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td> " ) ;
htmlCallsiteAnchor ( inRequest , current - > mEvents [ 0 ] . mCallsite , NULL , 0 ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </table> \n " ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopAllocations ) ;
}
return retval ;
}
/*
* * displayMemoryLeaks
* *
* * Present the top memory leaks .
* * The run must be passed in , and it must be pre - sorted .
* *
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int displayMemoryLeaks ( STRequest * inRequest , STRun * aRun )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aRun )
{
PRUint32 loop = 0 ;
PRUint32 displayed = 0 ;
STAllocation * current = NULL ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <table border=1> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Rank</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Index</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Byte Size</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Lifespan Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Weight</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Heap Operation Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Origin Callsite</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Loop over all of the items , or until we ' ve displayed enough .
*/
for ( loop = 0 ; displayed < globals . mOptions . mListItemMax & & loop < aRun - > mAllocationCount ; loop + + )
{
current = aRun - > mAllocations [ loop ] ;
if ( NULL ! = current & & 0 ! = current - > mEventCount )
{
/*
* * In order to be a leak , the last event of it ' s life must
* * NOT be a free operation .
* *
2001-11-29 21:44:15 +03:00
* * A free operation is just that , a free .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 21:44:15 +03:00
if ( TM_EVENT_FREE ! = current - > mEvents [ current - > mEventCount - 1 ] . mEventType )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
PRUint32 lifespan = current - > mMaxTimeval - current - > mMinTimeval ;
2001-11-16 01:40:53 +03:00
PRUint32 size = byteSize ( current ) ;
2001-12-18 04:00:36 +03:00
PRUint32 heapCost = current - > mHeapRuntimeCost ;
2001-11-16 01:40:53 +03:00
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
2001-12-29 01:27:20 +03:00
PRUint64 size64 = LL_INIT ( 0 , 0 ) ;
PRUint64 lifespan64 = LL_INIT ( 0 , 0 ) ;
2001-11-16 01:40:53 +03:00
char buffer [ 32 ] ;
2001-12-29 01:27:20 +03:00
LL_UI2L ( size64 , size ) ;
LL_UI2L ( lifespan64 , lifespan ) ;
LL_MUL ( weight64 , size64 , lifespan64 ) ;
2001-11-16 01:40:53 +03:00
/*
* * One more shown .
*/
displayed + + ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Rank .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right>%u</td> \n " , displayed ) ;
2001-11-16 01:40:53 +03:00
/*
* * Index .
*/
PR_snprintf ( buffer , sizeof ( buffer ) , " %u " , current - > mRunIndex ) ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right> \n " ) ;
htmlAllocationAnchor ( inRequest , current , buffer ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Byte Size .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right>%u</td> \n " , size ) ;
2001-11-16 01:40:53 +03:00
/*
* * Lifespan .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE ( lifespan ) ) ;
2001-11-29 04:29:26 +03:00
/*
* * Weight .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right>%llu</td> \n " , weight64 ) ;
2001-11-16 01:40:53 +03:00
2001-12-18 04:00:36 +03:00
/*
* * Heap Operation Seconds .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right> " ST_MICROVAL_FORMAT " </td> \n " , ST_MICROVAL_PRINTABLE ( heapCost ) ) ;
2001-12-18 04:00:36 +03:00
2001-11-16 01:40:53 +03:00
/*
* * Callsite .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td> " ) ;
htmlCallsiteAnchor ( inRequest , current - > mEvents [ 0 ] . mCallsite , NULL , 0 ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
}
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </table> \n " ) ;
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayMemoryLeaks ) ;
}
return retval ;
}
/*
* * displayCallsites
* *
* * Display a table of callsites .
* * If the stamp is non zero , then must match that stamp .
* * If the stamp is zero , then must match the global sorted run stamp .
* * Return ! 0 on error .
*/
2002-05-02 22:38:42 +04:00
int displayCallsites ( STRequest * inRequest , tmcallsite * aCallsite , int aFollow , PRUint32 aStamp , int aRealNames )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aCallsite & & NULL ! = aCallsite - > method )
{
int headerDisplayed = 0 ;
STRun * run = NULL ;
/*
* * Corrent the stamp if need be .
*/
if ( 0 = = aStamp & & NULL ! = globals . mCache . mSortedRun )
{
aStamp = globals . mCache . mSortedRun - > mStats . mStamp ;
}
/*
* * Loop over the callsites looking for a stamp match .
* * A stamp guarantees there is something interesting to look at too .
* * If found , output it .
*/
while ( NULL ! = aCallsite & & NULL ! = aCallsite - > method )
{
run = CALLSITE_RUN ( aCallsite ) ;
if ( NULL ! = run )
{
if ( aStamp = = run - > mStats . mStamp )
{
/*
* * We got a header ?
*/
if ( 0 = = headerDisplayed )
{
headerDisplayed = __LINE__ ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <table border=1> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Callsite</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Byte Size</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Weight</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Heap Object Count</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Heap Operation Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
/*
* * Output the information .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Method name .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td> " ) ;
htmlCallsiteAnchor ( inRequest , aCallsite , NULL , aRealNames ) ;
PR_fprintf ( inRequest - > mFD , " </td> " ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Byte Size .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right>%u</td> \n " , run - > mStats . mSize ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Seconds .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE64 ( run - > mStats . mTimeval64 ) ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Weight .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right>%llu</td> \n " , run - > mStats . mWeight64 ) ;
2001-11-16 01:40:53 +03:00
2001-12-18 04:00:36 +03:00
/*
* * Allocation object count .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right>%u</td> \n " , run - > mStats . mCompositeCount ) ;
2001-12-18 04:00:36 +03:00
/*
* * Heap Operation Seconds .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right> " ST_MICROVAL_FORMAT " </td> \n " , ST_MICROVAL_PRINTABLE ( run - > mStats . mHeapRuntimeCost ) ) ;
2001-12-18 04:00:36 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
break ;
}
/*
* * What do we follow ?
*/
switch ( aFollow )
{
case ST_FOLLOW_SIBLINGS :
aCallsite = aCallsite - > siblings ;
break ;
case ST_FOLLOW_PARENTS :
aCallsite = aCallsite - > parent ;
break ;
default :
aCallsite = NULL ;
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
break ;
}
}
/*
* * Terminate the table if we should .
*/
if ( 0 ! = headerDisplayed )
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </table> \n " ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
}
return retval ;
}
/*
* * displayAllocationDetails
* *
* * Report what we know about the allocation .
* *
* * Returns ! 0 on error .
*/
2002-05-02 22:38:42 +04:00
int displayAllocationDetails ( STRequest * inRequest , STAllocation * aAllocation )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aAllocation )
{
PRUint32 traverse = 0 ;
PRUint32 bytesize = byteSize ( aAllocation ) ;
2001-11-29 04:29:26 +03:00
PRUint32 timeval = aAllocation - > mMaxTimeval - aAllocation - > mMinTimeval ;
2001-12-18 04:00:36 +03:00
PRUint32 heapCost = aAllocation - > mHeapRuntimeCost ;
2001-11-16 01:40:53 +03:00
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
2001-12-29 01:27:20 +03:00
PRUint64 bytesize64 = LL_INIT ( 0 , 0 ) ;
PRUint64 timeval64 = LL_INIT ( 0 , 0 ) ;
2001-11-29 04:29:26 +03:00
PRUint32 cacheval = 0 ;
2001-11-16 01:40:53 +03:00
int displayRes = 0 ;
2001-12-29 01:27:20 +03:00
LL_UI2L ( bytesize64 , bytesize ) ;
LL_UI2L ( timeval64 , timeval ) ;
LL_MUL ( weight64 , bytesize64 , timeval64 ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Allocation %u Details:<p> \n " , aAllocation - > mRunIndex ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <table> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td align=left>Final Size:</td><td align=right>%u</td></tr> \n " , bytesize ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td align=left>Lifespan Seconds:</td><td align=right> " ST_TIMEVAL_FORMAT " </td></tr> \n " , ST_TIMEVAL_PRINTABLE ( timeval ) ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td align=left>Weight:</td><td align=right>%llu</td></tr> \n " , weight64 ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td align=left>Heap Operation Seconds:</td><td align=right> " ST_MICROVAL_FORMAT " </td></tr> \n " , ST_MICROVAL_PRINTABLE ( heapCost ) ) ;
PR_fprintf ( inRequest - > mFD , " </table><p> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * The events .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " %u Life Event(s):<br> \n " , aAllocation - > mEventCount ) ;
PR_fprintf ( inRequest - > mFD , " <table border=1> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Operation</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Size</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
for ( traverse = 0 ; traverse < aAllocation - > mEventCount & & traverse < globals . mOptions . mListItemMax ; traverse + + )
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * count .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right>%u.</td> \n " , traverse + 1 ) ;
2001-11-16 01:40:53 +03:00
/*
* * Operation .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top> " ) ;
2001-11-16 01:40:53 +03:00
switch ( aAllocation - > mEvents [ traverse ] . mEventType )
{
case TM_EVENT_CALLOC :
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " calloc " ) ;
2001-11-16 01:40:53 +03:00
break ;
case TM_EVENT_FREE :
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " free " ) ;
2001-11-16 01:40:53 +03:00
break ;
case TM_EVENT_MALLOC :
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " malloc " ) ;
2001-11-16 01:40:53 +03:00
break ;
case TM_EVENT_REALLOC :
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " realloc " ) ;
2001-11-16 01:40:53 +03:00
break ;
default :
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
break ;
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </td> " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Size .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right>%u</td> \n " , aAllocation - > mEvents [ traverse ] . mHeapSize ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Timeval .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
cacheval = aAllocation - > mEvents [ traverse ] . mTimeval - globals . mMinTimeval ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top align=right> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE ( cacheval ) ) ;
2001-11-16 01:40:53 +03:00
/*
* * Callsite backtrace .
* * Only relevant backtrace is for event 0 for now until
* * trace - malloc outputs proper callsites for all others .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td valign=top> \n " ) ;
2001-11-16 01:40:53 +03:00
if ( 0 = = traverse )
{
2002-05-02 22:38:42 +04:00
displayRes = displayCallsites ( inRequest , aAllocation - > mEvents [ traverse ] . mCallsite , ST_FOLLOW_PARENTS , 0 , __LINE__ ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsite ) ;
}
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </table><p> \n " ) ;
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
}
return retval ;
}
/*
* * compareCallsites
* *
* * qsort callback .
* * Compare the callsites as specified by the options .
2001-12-18 04:00:36 +03:00
* * There must be NO equal callsites , unless they really are duplicates ,
2001-11-16 01:40:53 +03:00
* * this is so that a duplicate detector loop can
* * simply skip sorted items until the callsite is different .
*/
2001-12-18 04:00:36 +03:00
int compareCallsites ( const void * aSite1 , const void * aSite2 , void * aContext )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aSite1 & & NULL ! = aSite2 )
{
tmcallsite * site1 = * ( ( tmcallsite * * ) aSite1 ) ;
tmcallsite * site2 = * ( ( tmcallsite * * ) aSite2 ) ;
if ( NULL ! = site1 & & NULL ! = site2 )
{
STRun * run1 = CALLSITE_RUN ( site1 ) ;
STRun * run2 = CALLSITE_RUN ( site2 ) ;
if ( NULL ! = run1 & & NULL ! = run2 )
{
STCallsiteStats * stats1 = & ( run1 - > mStats ) ;
STCallsiteStats * stats2 = & ( run2 - > mStats ) ;
/*
* * Logic determined by pref / option .
*/
switch ( globals . mOptions . mOrderBy )
{
case ST_WEIGHT :
{
PRUint64 weight164 = stats1 - > mWeight64 ;
PRUint64 weight264 = stats2 - > mWeight64 ;
if ( LL_UCMP ( weight164 , < , weight264 ) )
{
retval = __LINE__ ;
}
else if ( LL_UCMP ( weight164 , > , weight264 ) )
{
retval = - __LINE__ ;
}
}
break ;
case ST_SIZE :
{
PRUint32 size1 = stats1 - > mSize ;
PRUint32 size2 = stats2 - > mSize ;
if ( size1 < size2 )
{
retval = __LINE__ ;
}
else if ( size1 > size2 )
{
retval = - __LINE__ ;
}
}
break ;
2001-11-29 04:29:26 +03:00
case ST_TIMEVAL :
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
PRUint64 timeval164 = stats1 - > mTimeval64 ;
PRUint64 timeval264 = stats2 - > mTimeval64 ;
2001-11-16 01:40:53 +03:00
2001-11-29 04:29:26 +03:00
if ( LL_UCMP ( timeval164 , < , timeval264 ) )
2001-11-16 01:40:53 +03:00
{
retval = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else if ( LL_UCMP ( timeval164 , > , timeval264 ) )
2001-11-16 01:40:53 +03:00
{
retval = - __LINE__ ;
}
}
break ;
2001-12-18 04:00:36 +03:00
case ST_COUNT :
{
PRUint32 count1 = stats1 - > mCompositeCount ;
PRUint32 count2 = stats2 - > mCompositeCount ;
if ( count1 < count2 )
{
retval = __LINE__ ;
}
else if ( count1 > count2 )
{
retval = - __LINE__ ;
}
}
break ;
case ST_HEAPCOST :
{
PRUint32 cost1 = stats1 - > mHeapRuntimeCost ;
PRUint32 cost2 = stats2 - > mHeapRuntimeCost ;
if ( cost1 < cost2 )
{
retval = __LINE__ ;
}
else if ( cost1 > cost2 )
{
retval = - __LINE__ ;
}
}
break ;
2001-11-16 01:40:53 +03:00
default :
{
REPORT_ERROR ( __LINE__ , compareAllocations ) ;
}
break ;
}
/*
* * If the return value is still zero , do a pointer compare .
* * This makes sure we return zero , only iff the same object .
*/
if ( 0 = = retval )
{
if ( stats1 < stats2 )
{
retval = __LINE__ ;
}
else if ( stats1 > stats2 )
{
retval = - __LINE__ ;
}
}
}
}
}
return retval ;
}
/*
* * displayTopCallsites
* *
* * Given a list of callsites , sort it , and output skipping dups .
* * The passed in callsite array is side effected , as in that it will come
* * back sorted . This function will not release the array .
* *
* * Note : If the stamp passed in is non zero , then all callsites must match .
* * If the stamp is zero , all callsites must match global sorted run stamp .
* *
* * Returns ! 0 on error .
*/
2002-05-02 22:38:42 +04:00
int displayTopCallsites ( STRequest * inRequest , tmcallsite * * aCallsites , PRUint32 aCallsiteCount , PRUint32 aStamp , int aRealName )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aCallsites & & 0 < aCallsiteCount )
{
PRUint32 traverse = 0 ;
STRun * run = NULL ;
tmcallsite * site = NULL ;
int headerDisplayed = 0 ;
PRUint32 displayed = 0 ;
/*
* * Fixup the stamp .
*/
if ( 0 = = aStamp & & NULL ! = globals . mCache . mSortedRun )
{
aStamp = globals . mCache . mSortedRun - > mStats . mStamp ;
}
/*
* * Sort the things .
*/
2001-12-18 04:00:36 +03:00
NS_QuickSort ( aCallsites , aCallsiteCount , sizeof ( tmcallsite * ) , compareCallsites , NULL ) ;
2001-11-16 01:40:53 +03:00
/*
* * Time for output .
*/
for ( traverse = 0 ; traverse < aCallsiteCount & & globals . mOptions . mListItemMax > displayed ; traverse + + )
{
site = aCallsites [ traverse ] ;
run = CALLSITE_RUN ( site ) ;
/*
* * Only if the same stamp . . . .
*/
if ( aStamp = = run - > mStats . mStamp )
{
/*
* * We got a header yet ?
*/
if ( 0 = = headerDisplayed )
{
headerDisplayed = __LINE__ ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <table border=1> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Rank</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Callsite</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Size</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Composite Weight</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Heap Object Count</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <td><b>Heap Operation Seconds</b></td> \n " ) ;
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
}
displayed + + ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <tr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Rank .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right valign=top>%u</td> \n " , displayed ) ;
2001-11-16 01:40:53 +03:00
/*
* * Method .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td> " ) ;
htmlCallsiteAnchor ( inRequest , site , NULL , aRealName ) ;
PR_fprintf ( inRequest - > mFD , " </td> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Size .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right valign=top>%u</td> \n " , run - > mStats . mSize ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Timeval .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right valign=top> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE64 ( run - > mStats . mTimeval64 ) ) ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * Weight .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right valign=top>%llu</td> \n " , run - > mStats . mWeight64 ) ;
2001-11-16 01:40:53 +03:00
2001-12-18 04:00:36 +03:00
/*
* * Allocation object count .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right valign=top>%u</td> \n " , run - > mStats . mCompositeCount ) ;
2001-12-18 04:00:36 +03:00
/*
* * Heap operation seconds .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <td align=right valign=top> " ST_MICROVAL_FORMAT " </td> \n " , ST_MICROVAL_PRINTABLE ( run - > mStats . mHeapRuntimeCost ) ) ;
2001-12-18 04:00:36 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </tr> \n " ) ;
2001-11-16 01:40:53 +03:00
if ( globals . mOptions . mListItemMax > displayed )
{
/*
* * Skip any dups .
*/
while ( ( ( traverse + 1 ) < aCallsiteCount ) & & ( site = = aCallsites [ traverse + 1 ] ) )
{
traverse + + ;
}
}
}
}
/*
* * We need to terminate anything ?
*/
if ( 0 ! = headerDisplayed )
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </table> \n " ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopCallsites ) ;
}
return retval ;
}
/*
* * displayCallsiteDetails
* *
* * The callsite specific report .
* * Try to report what we know .
* * This one hits a little harder than the rest .
* *
* * Returns ! 0 on error .
*/
2002-05-02 22:38:42 +04:00
int displayCallsiteDetails ( STRequest * inRequest , tmcallsite * aCallsite )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aCallsite & & NULL ! = aCallsite - > method )
{
STRun * sortedRun = NULL ;
STRun * thisRun = CALLSITE_RUN ( aCallsite ) ;
2002-01-09 22:03:01 +03:00
const char * sourceFile = NULL ;
2001-11-16 01:40:53 +03:00
2002-01-09 22:03:01 +03:00
sourceFile = resolveSourceFile ( aCallsite - > method ) ;
if ( NULL ! = sourceFile )
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <b>%s</b> " , tmmethodnode_name ( aCallsite - > method ) ) ;
PR_fprintf ( inRequest - > mFD , " <a href= \" http://lxr.mozilla.org/mozilla/source/%s#%u \" target= \" _st_lxr \" >(%s:%u)</a> " , aCallsite - > method - > sourcefile , aCallsite - > method - > linenumber , sourceFile , aCallsite - > method - > linenumber ) ;
PR_fprintf ( inRequest - > mFD , " Callsite Details:<p> \n " ) ;
2002-01-09 22:03:01 +03:00
}
else
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <b>%s</b>+%u(%u) Callsite Details:<p> \n " , tmmethodnode_name ( aCallsite - > method ) , aCallsite - > offset , ( PRUint32 ) aCallsite - > entry . key ) ;
2002-01-09 22:03:01 +03:00
}
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <table border=0> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td>Composite Byte Size:</td><td align=right>%u</td></tr> \n " , thisRun - > mStats . mSize ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td>Composite Seconds:</td><td align=right> " ST_TIMEVAL_FORMAT " </td></tr> \n " , ST_TIMEVAL_PRINTABLE64 ( thisRun - > mStats . mTimeval64 ) ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td>Composite Weight:</td><td align=right>%llu</td></tr> \n " , thisRun - > mStats . mWeight64 ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td>Heap Object Count:</td><td align=right>%u</td></tr> \n " , thisRun - > mStats . mCompositeCount ) ;
PR_fprintf ( inRequest - > mFD , " <tr><td>Heap Operation Seconds:</td><td align=right> " ST_MICROVAL_FORMAT " </td></tr> \n " , ST_MICROVAL_PRINTABLE ( thisRun - > mStats . mHeapRuntimeCost ) ) ;
PR_fprintf ( inRequest - > mFD , " </table> \n <p> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Kids ( callsites we call ) :
*/
if ( NULL ! = aCallsite - > kids & & NULL ! = aCallsite - > kids - > method )
{
int displayRes = 0 ;
PRUint32 siteCount = 0 ;
tmcallsite * * sites = NULL ;
/*
* * Collect the kid sibling callsites .
* * Doing it this way sorts them for relevance .
*/
siteCount = callsiteArrayFromCallsite ( & sites , 0 , aCallsite - > kids , ST_FOLLOW_SIBLINGS ) ;
if ( 0 ! = siteCount & & NULL ! = sites )
{
/*
* * Got something to show .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Children Callsites:<br> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayTopCallsites ( inRequest , sites , siteCount , 0 , __LINE__ ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopCallsites ) ;
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <p> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * Done with array .
*/
free ( sites ) ;
sites = NULL ;
}
}
/*
* * Parents ( those who call us ) :
*/
if ( NULL ! = aCallsite - > parent & & NULL ! = aCallsite - > parent - > method )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Parent Callsites:<br> \n " ) ;
displayRes = displayCallsites ( inRequest , aCallsite - > parent , ST_FOLLOW_PARENTS , 0 , __LINE__ ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <p> \n " ) ;
2001-11-16 01:40:53 +03:00
}
/*
* * Allocations we did .
* * Simply harvest our own run .
*/
sortedRun = createRun ( 0 ) ;
if ( NULL ! = sortedRun )
{
int harvestRes = 0 ;
harvestRes = harvestRun ( CALLSITE_RUN ( aCallsite ) , sortedRun , NULL ) ;
if ( 0 = = harvestRes )
{
if ( 0 ! = sortedRun - > mAllocationCount )
{
int sortRes = 0 ;
sortRes = sortRun ( sortedRun ) ;
if ( 0 = = sortRes )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Allocations:<br> \n " ) ;
displayRes = displayTopAllocations ( inRequest , sortedRun , 0 ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopAllocations ) ;
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <p> \n " ) ;
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , sortRun ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , harvestRun ) ;
}
/*
* * Done with the run .
*/
freeRun ( sortedRun ) ;
sortedRun = NULL ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createRun ) ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsiteDetails ) ;
}
return retval ;
}
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
* * graphFootprint
* *
* * Output a PNG graph of the memory usage of the run .
* *
* * Draw the graph within these boundaries .
* * STGD_MARGIN , STGD_MARGIN , STGD_WIDTH - STGD_MARGIN , STGD_HEIGHT - STGD_MARGIN
* *
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int graphFootprint ( STRequest * inRequest , STRun * aRun )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aRun )
{
PRUint32 * YData = NULL ;
PRUint32 YDataArray [ STGD_SPACE_X ] ;
PRUint32 traverse = 0 ;
2001-11-29 04:29:26 +03:00
PRUint32 timeval = globals . mOptions . mGraphTimevalMin ;
2001-11-16 01:40:53 +03:00
PRUint32 loop = 0 ;
/*
* * Decide if this is custom or we should use the global cache .
*/
if ( aRun = = globals . mCache . mSortedRun )
{
YData = globals . mCache . mFootprintYData ;
}
else
{
YData = YDataArray ;
}
/*
* * Only do the computations if we aren ' t cached already .
*/
if ( YData ! = globals . mCache . mFootprintYData | | 0 = = globals . mCache . mFootprintCached )
{
memset ( YData , 0 , sizeof ( PRUint32 ) * STGD_SPACE_X ) ;
/*
* * Initialize our Y data .
* * Pretty brutal loop here . . . .
*/
for ( traverse = 0 ; 0 = = retval & & traverse < STGD_SPACE_X ; traverse + + )
{
/*
2001-11-29 04:29:26 +03:00
* * Compute what timeval this Y data lands in .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
timeval = ( ( traverse * ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) ) / STGD_SPACE_X ) + globals . mMinTimeval + globals . mOptions . mGraphTimevalMin ;
2001-11-16 01:40:53 +03:00
/*
* * Loop over the run .
2001-11-29 04:29:26 +03:00
* * Should an allocation contain said Timeval , we ' re good .
2001-11-16 01:40:53 +03:00
*/
for ( loop = 0 ; loop < aRun - > mAllocationCount ; loop + + )
{
2001-11-29 04:29:26 +03:00
if ( timeval > = aRun - > mAllocations [ loop ] - > mMinTimeval & & timeval < = aRun - > mAllocations [ loop ] - > mMaxTimeval )
2001-11-16 01:40:53 +03:00
{
YData [ traverse ] + = byteSize ( aRun - > mAllocations [ loop ] ) ;
}
}
}
/*
* * Did we cache this ?
*/
if ( YData = = globals . mCache . mFootprintYData )
{
globals . mCache . mFootprintCached = __LINE__ ;
}
}
if ( 0 = = retval )
{
PRUint32 minMemory = ( PRUint32 ) - 1 ;
PRUint32 maxMemory = 0 ;
int transparent = 0 ;
gdImagePtr graph = NULL ;
/*
* * Go through and find the minimum and maximum sizes .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
if ( YData [ traverse ] < minMemory )
{
minMemory = YData [ traverse ] ;
}
if ( YData [ traverse ] > maxMemory )
{
maxMemory = YData [ traverse ] ;
}
}
/*
* * We can now draw the graph .
*/
graph = createGraph ( & transparent ) ;
if ( NULL ! = graph )
{
gdSink theSink ;
int red = 0 ;
int x1 = 0 ;
int y1 = 0 ;
int x2 = 0 ;
int y2 = 0 ;
PRUint32 percents [ 11 ] = { 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 } ;
2001-11-29 04:29:26 +03:00
char * timevals [ 11 ] ;
2001-11-16 01:40:53 +03:00
char * bytes [ 11 ] ;
2001-11-29 04:29:26 +03:00
char timevalSpace [ 11 ] [ 32 ] ;
2001-11-16 01:40:53 +03:00
char byteSpace [ 11 ] [ 32 ] ;
int legendColors [ 1 ] ;
const char * legends [ 1 ] = { " Memory in Use " } ;
2001-11-29 04:29:26 +03:00
PRUint32 cached = 0 ;
2001-11-16 01:40:53 +03:00
/*
* * Figure out what the labels will say .
*/
for ( traverse = 0 ; traverse < 11 ; traverse + + )
{
2001-11-29 04:29:26 +03:00
timevals [ traverse ] = timevalSpace [ traverse ] ;
2001-11-16 01:40:53 +03:00
bytes [ traverse ] = byteSpace [ traverse ] ;
2001-11-29 04:29:26 +03:00
cached = ( ( ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) * percents [ traverse ] ) / 100 ) + globals . mOptions . mGraphTimevalMin ;
PR_snprintf ( timevals [ traverse ] , 32 , ST_TIMEVAL_FORMAT , ST_TIMEVAL_PRINTABLE ( cached ) ) ;
2001-11-16 01:40:53 +03:00
PR_snprintf ( bytes [ traverse ] , 32 , " %u " , ( ( maxMemory - minMemory ) * percents [ traverse ] ) / 100 ) ;
}
red = gdImageColorAllocate ( graph , 255 , 0 , 0 ) ;
legendColors [ 0 ] = red ;
2001-11-29 04:29:26 +03:00
drawGraph ( graph , - 1 , " Memory Footprint Over Time " , " Seconds " , " Bytes " , 11 , percents , ( const char * * ) timevals , 11 , percents , ( const char * * ) bytes , 1 , legendColors , legends ) ;
2001-11-16 01:40:53 +03:00
if ( maxMemory ! = minMemory )
{
PRInt64 in64 = LL_INIT ( 0 , 0 ) ;
PRInt64 ydata64 = LL_INIT ( 0 , 0 ) ;
PRInt64 spacey64 = LL_INIT ( 0 , 0 ) ;
PRInt64 mem64 = LL_INIT ( 0 , 0 ) ;
PRInt32 in32 = 0 ;
/*
* * Go through our Y data and mark it up .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
x1 = traverse + STGD_MARGIN ;
y1 = STGD_HEIGHT - STGD_MARGIN ;
/*
* * Need to do this math in 64 bits .
*/
LL_I2L ( ydata64 , YData [ traverse ] ) ;
LL_I2L ( spacey64 , STGD_SPACE_Y ) ;
LL_I2L ( mem64 , ( maxMemory - minMemory ) ) ;
LL_MUL ( in64 , ydata64 , spacey64 ) ;
LL_DIV ( in64 , in64 , mem64 ) ;
LL_L2I ( in32 , in64 ) ;
x2 = x1 ;
y2 = y1 - in32 ;
gdImageLine ( graph , x1 , y1 , x2 , y2 , red ) ;
}
}
2002-05-02 22:38:42 +04:00
theSink . context = inRequest - > mFD ;
2001-11-16 01:40:53 +03:00
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphFootprint ) ;
}
return retval ;
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * graphTimeval
2001-11-16 01:40:53 +03:00
* *
* * Output a PNG graph of when the memory is allocated .
* *
* * Draw the graph within these boundaries .
* * STGD_MARGIN , STGD_MARGIN , STGD_WIDTH - STGD_MARGIN , STGD_HEIGHT - STGD_MARGIN
* *
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int graphTimeval ( STRequest * inRequest , STRun * aRun )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aRun )
{
PRUint32 * YData = NULL ;
PRUint32 YDataArray [ STGD_SPACE_X ] ;
PRUint32 traverse = 0 ;
2001-11-29 04:29:26 +03:00
PRUint32 timeval = globals . mOptions . mGraphTimevalMin + globals . mMinTimeval ;
2001-11-16 01:40:53 +03:00
PRUint32 loop = 0 ;
/*
* * Decide if this is custom or we should use the global cache .
*/
if ( aRun = = globals . mCache . mSortedRun )
{
2001-11-29 04:29:26 +03:00
YData = globals . mCache . mTimevalYData ;
2001-11-16 01:40:53 +03:00
}
else
{
YData = YDataArray ;
}
/*
* * Only do the computations if we aren ' t cached already .
*/
2001-11-29 04:29:26 +03:00
if ( YData ! = globals . mCache . mTimevalYData | | 0 = = globals . mCache . mTimevalCached )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
PRUint32 prevTimeval = 0 ;
2001-11-16 01:40:53 +03:00
memset ( YData , 0 , sizeof ( PRUint32 ) * STGD_SPACE_X ) ;
/*
* * Initialize our Y data .
* * Pretty brutal loop here . . . .
*/
for ( traverse = 0 ; 0 = = retval & & traverse < STGD_SPACE_X ; traverse + + )
{
/*
2001-11-29 04:29:26 +03:00
* * Compute what timeval this Y data lands in .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
prevTimeval = timeval ;
timeval = ( ( traverse * ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) ) / STGD_SPACE_X ) + globals . mMinTimeval + globals . mOptions . mGraphTimevalMin ;
2001-11-16 01:40:53 +03:00
/*
* * Loop over the run .
* * Should an allocation have been allocated between
2001-11-29 04:29:26 +03:00
* * prevTimeval and timeval . . . .
2001-11-16 01:40:53 +03:00
*/
for ( loop = 0 ; loop < aRun - > mAllocationCount ; loop + + )
{
2001-11-29 04:29:26 +03:00
if ( prevTimeval < aRun - > mAllocations [ loop ] - > mMinTimeval & & timeval > = aRun - > mAllocations [ loop ] - > mMinTimeval )
2001-11-16 01:40:53 +03:00
{
YData [ traverse ] + = byteSize ( aRun - > mAllocations [ loop ] ) ;
}
}
}
/*
* * Did we cache this ?
*/
2001-11-29 04:29:26 +03:00
if ( YData = = globals . mCache . mTimevalYData )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
globals . mCache . mTimevalCached = __LINE__ ;
2001-11-16 01:40:53 +03:00
}
}
if ( 0 = = retval )
{
PRUint32 minMemory = ( PRUint32 ) - 1 ;
PRUint32 maxMemory = 0 ;
int transparent = 0 ;
gdImagePtr graph = NULL ;
/*
* * Go through and find the minimum and maximum sizes .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
if ( YData [ traverse ] < minMemory )
{
minMemory = YData [ traverse ] ;
}
if ( YData [ traverse ] > maxMemory )
{
maxMemory = YData [ traverse ] ;
}
}
/*
* * We can now draw the graph .
*/
graph = createGraph ( & transparent ) ;
if ( NULL ! = graph )
{
gdSink theSink ;
int red = 0 ;
int x1 = 0 ;
int y1 = 0 ;
int x2 = 0 ;
int y2 = 0 ;
PRUint32 percents [ 11 ] = { 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 } ;
2001-11-29 04:29:26 +03:00
char * timevals [ 11 ] ;
2001-11-16 01:40:53 +03:00
char * bytes [ 11 ] ;
2001-11-29 04:29:26 +03:00
char timevalSpace [ 11 ] [ 32 ] ;
2001-11-16 01:40:53 +03:00
char byteSpace [ 11 ] [ 32 ] ;
int legendColors [ 1 ] ;
const char * legends [ 1 ] = { " Memory Allocated " } ;
2001-11-29 04:29:26 +03:00
PRUint32 cached = 0 ;
2001-11-16 01:40:53 +03:00
/*
* * Figure out what the labels will say .
*/
for ( traverse = 0 ; traverse < 11 ; traverse + + )
{
2001-11-29 04:29:26 +03:00
timevals [ traverse ] = timevalSpace [ traverse ] ;
2001-11-16 01:40:53 +03:00
bytes [ traverse ] = byteSpace [ traverse ] ;
2001-11-29 04:29:26 +03:00
cached = ( ( ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) * percents [ traverse ] ) / 100 ) + globals . mOptions . mGraphTimevalMin ;
PR_snprintf ( timevals [ traverse ] , 32 , ST_TIMEVAL_FORMAT , ST_TIMEVAL_PRINTABLE ( cached ) ) ;
2001-11-16 01:40:53 +03:00
PR_snprintf ( bytes [ traverse ] , 32 , " %u " , ( ( maxMemory - minMemory ) * percents [ traverse ] ) / 100 ) ;
}
red = gdImageColorAllocate ( graph , 255 , 0 , 0 ) ;
legendColors [ 0 ] = red ;
2001-11-29 04:29:26 +03:00
drawGraph ( graph , - 1 , " Allocation Times " , " Seconds " , " Bytes " , 11 , percents , ( const char * * ) timevals , 11 , percents , ( const char * * ) bytes , 1 , legendColors , legends ) ;
2001-11-16 01:40:53 +03:00
if ( maxMemory ! = minMemory )
{
PRInt64 in64 = LL_INIT ( 0 , 0 ) ;
PRInt64 ydata64 = LL_INIT ( 0 , 0 ) ;
PRInt64 spacey64 = LL_INIT ( 0 , 0 ) ;
PRInt64 mem64 = LL_INIT ( 0 , 0 ) ;
PRInt32 in32 = 0 ;
/*
* * Go through our Y data and mark it up .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
x1 = traverse + STGD_MARGIN ;
y1 = STGD_HEIGHT - STGD_MARGIN ;
/*
* * Need to do this math in 64 bits .
*/
LL_I2L ( ydata64 , YData [ traverse ] ) ;
LL_I2L ( spacey64 , STGD_SPACE_Y ) ;
LL_I2L ( mem64 , ( maxMemory - minMemory ) ) ;
LL_MUL ( in64 , ydata64 , spacey64 ) ;
LL_DIV ( in64 , in64 , mem64 ) ;
LL_L2I ( in32 , in64 ) ;
x2 = x1 ;
y2 = y1 - in32 ;
gdImageLine ( graph , x1 , y1 , x2 , y2 , red ) ;
}
}
2002-05-02 22:38:42 +04:00
theSink . context = inRequest - > mFD ;
2001-11-16 01:40:53 +03:00
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
2001-11-29 04:29:26 +03:00
REPORT_ERROR ( __LINE__ , graphTimeval ) ;
2001-11-16 01:40:53 +03:00
}
return retval ;
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
* * graphLifespan
* *
* * Output a PNG graph of how long memory lived .
* *
* * Draw the graph within these boundaries .
* * STGD_MARGIN , STGD_MARGIN , STGD_WIDTH - STGD_MARGIN , STGD_HEIGHT - STGD_MARGIN
* *
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int graphLifespan ( STRequest * inRequest , STRun * aRun )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aRun )
{
PRUint32 * YData = NULL ;
PRUint32 YDataArray [ STGD_SPACE_X ] ;
PRUint32 traverse = 0 ;
2001-11-29 04:29:26 +03:00
PRUint32 timeval = globals . mOptions . mGraphTimevalMin ;
2001-11-16 01:40:53 +03:00
PRUint32 loop = 0 ;
/*
* * Decide if this is custom or we should use the global cache .
*/
if ( aRun = = globals . mCache . mSortedRun )
{
YData = globals . mCache . mLifespanYData ;
}
else
{
YData = YDataArray ;
}
/*
* * Only do the computations if we aren ' t cached already .
*/
if ( YData ! = globals . mCache . mLifespanYData | | 0 = = globals . mCache . mLifespanCached )
{
2001-11-29 04:29:26 +03:00
PRUint32 prevTimeval = 0 ;
2001-11-16 01:40:53 +03:00
PRUint32 lifespan = 0 ;
memset ( YData , 0 , sizeof ( PRUint32 ) * STGD_SPACE_X ) ;
/*
* * Initialize our Y data .
* * Pretty brutal loop here . . . .
*/
for ( traverse = 0 ; 0 = = retval & & traverse < STGD_SPACE_X ; traverse + + )
{
/*
2001-11-29 04:29:26 +03:00
* * Compute what timeval this Y data lands in .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
prevTimeval = timeval ;
timeval = ( ( traverse * ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) ) / STGD_SPACE_X ) + globals . mOptions . mGraphTimevalMin ;
2001-11-16 01:40:53 +03:00
/*
* * Loop over the run .
* * Should an allocation have lived between
2001-11-29 04:29:26 +03:00
* * prevTimeval and timeval . . . .
2001-11-16 01:40:53 +03:00
*/
for ( loop = 0 ; loop < aRun - > mAllocationCount ; loop + + )
{
2001-11-29 04:29:26 +03:00
lifespan = aRun - > mAllocations [ loop ] - > mMaxTimeval - aRun - > mAllocations [ loop ] - > mMinTimeval ;
2001-11-16 01:40:53 +03:00
2001-11-29 04:29:26 +03:00
if ( prevTimeval < lifespan & & timeval > = lifespan )
2001-11-16 01:40:53 +03:00
{
YData [ traverse ] + = byteSize ( aRun - > mAllocations [ loop ] ) ;
}
}
}
/*
* * Did we cache this ?
*/
if ( YData = = globals . mCache . mLifespanYData )
{
globals . mCache . mLifespanCached = __LINE__ ;
}
}
if ( 0 = = retval )
{
PRUint32 minMemory = ( PRUint32 ) - 1 ;
PRUint32 maxMemory = 0 ;
int transparent = 0 ;
gdImagePtr graph = NULL ;
/*
* * Go through and find the minimum and maximum sizes .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
if ( YData [ traverse ] < minMemory )
{
minMemory = YData [ traverse ] ;
}
if ( YData [ traverse ] > maxMemory )
{
maxMemory = YData [ traverse ] ;
}
}
/*
* * We can now draw the graph .
*/
graph = createGraph ( & transparent ) ;
if ( NULL ! = graph )
{
gdSink theSink ;
int red = 0 ;
int x1 = 0 ;
int y1 = 0 ;
int x2 = 0 ;
int y2 = 0 ;
PRUint32 percents [ 11 ] = { 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 } ;
2001-11-29 04:29:26 +03:00
char * timevals [ 11 ] ;
2001-11-16 01:40:53 +03:00
char * bytes [ 11 ] ;
2001-11-29 04:29:26 +03:00
char timevalSpace [ 11 ] [ 32 ] ;
2001-11-16 01:40:53 +03:00
char byteSpace [ 11 ] [ 32 ] ;
int legendColors [ 1 ] ;
const char * legends [ 1 ] = { " Live Memory " } ;
2001-11-29 04:29:26 +03:00
PRUint32 cached = 0 ;
2001-11-16 01:40:53 +03:00
/*
* * Figure out what the labels will say .
*/
for ( traverse = 0 ; traverse < 11 ; traverse + + )
{
2001-11-29 04:29:26 +03:00
timevals [ traverse ] = timevalSpace [ traverse ] ;
2001-11-16 01:40:53 +03:00
bytes [ traverse ] = byteSpace [ traverse ] ;
2001-11-29 04:29:26 +03:00
cached = ( ( ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) * percents [ traverse ] ) / 100 ) + globals . mOptions . mGraphTimevalMin ;
PR_snprintf ( timevals [ traverse ] , 32 , ST_TIMEVAL_FORMAT , ST_TIMEVAL_PRINTABLE ( cached ) ) ;
2001-11-16 01:40:53 +03:00
PR_snprintf ( bytes [ traverse ] , 32 , " %u " , ( ( maxMemory - minMemory ) * percents [ traverse ] ) / 100 ) ;
}
red = gdImageColorAllocate ( graph , 255 , 0 , 0 ) ;
legendColors [ 0 ] = red ;
2001-11-29 04:29:26 +03:00
drawGraph ( graph , - 1 , " Allocation Lifespans " , " Lifespan " , " Bytes " , 11 , percents , ( const char * * ) timevals , 11 , percents , ( const char * * ) bytes , 1 , legendColors , legends ) ;
2001-11-16 01:40:53 +03:00
if ( maxMemory ! = minMemory )
{
PRInt64 in64 = LL_INIT ( 0 , 0 ) ;
PRInt64 ydata64 = LL_INIT ( 0 , 0 ) ;
PRInt64 spacey64 = LL_INIT ( 0 , 0 ) ;
PRInt64 mem64 = LL_INIT ( 0 , 0 ) ;
PRInt32 in32 = 0 ;
/*
* * Go through our Y data and mark it up .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
x1 = traverse + STGD_MARGIN ;
y1 = STGD_HEIGHT - STGD_MARGIN ;
/*
* * Need to do this math in 64 bits .
*/
LL_I2L ( ydata64 , YData [ traverse ] ) ;
LL_I2L ( spacey64 , STGD_SPACE_Y ) ;
LL_I2L ( mem64 , ( maxMemory - minMemory ) ) ;
LL_MUL ( in64 , ydata64 , spacey64 ) ;
LL_DIV ( in64 , in64 , mem64 ) ;
LL_L2I ( in32 , in64 ) ;
x2 = x1 ;
y2 = y1 - in32 ;
gdImageLine ( graph , x1 , y1 , x2 , y2 , red ) ;
}
}
2002-05-02 22:38:42 +04:00
theSink . context = inRequest - > mFD ;
2001-11-16 01:40:53 +03:00
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphLifespan ) ;
}
return retval ;
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
* * graphWeight
* *
* * Output a PNG graph of Allocations by Weight
* *
* * Draw the graph within these boundaries .
* * STGD_MARGIN , STGD_MARGIN , STGD_WIDTH - STGD_MARGIN , STGD_HEIGHT - STGD_MARGIN
* *
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int graphWeight ( STRequest * inRequest , STRun * aRun )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aRun )
{
PRUint64 * YData64 = NULL ;
PRUint64 YDataArray64 [ STGD_SPACE_X ] ;
PRUint32 traverse = 0 ;
2001-11-29 04:29:26 +03:00
PRUint32 timeval = globals . mOptions . mGraphTimevalMin + globals . mMinTimeval ;
2001-11-16 01:40:53 +03:00
PRUint32 loop = 0 ;
/*
* * Decide if this is custom or we should use the global cache .
*/
if ( aRun = = globals . mCache . mSortedRun )
{
YData64 = globals . mCache . mWeightYData64 ;
}
else
{
YData64 = YDataArray64 ;
}
/*
* * Only do the computations if we aren ' t cached already .
*/
if ( YData64 ! = globals . mCache . mWeightYData64 | | 0 = = globals . mCache . mWeightCached )
{
2001-11-29 04:29:26 +03:00
PRUint32 prevTimeval = 0 ;
2001-11-16 01:40:53 +03:00
memset ( YData64 , 0 , sizeof ( PRUint64 ) * STGD_SPACE_X ) ;
/*
* * Initialize our Y data .
* * Pretty brutal loop here . . . .
*/
for ( traverse = 0 ; 0 = = retval & & traverse < STGD_SPACE_X ; traverse + + )
{
/*
2001-11-29 04:29:26 +03:00
* * Compute what timeval this Y data lands in .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
prevTimeval = timeval ;
timeval = ( ( traverse * ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) ) / STGD_SPACE_X ) + globals . mMinTimeval + globals . mOptions . mGraphTimevalMin ;
2001-11-16 01:40:53 +03:00
/*
* * Loop over the run .
* * Should an allocation have been allocated between
2001-11-29 04:29:26 +03:00
* * prevTimeval and timeval . . . .
2001-11-16 01:40:53 +03:00
*/
for ( loop = 0 ; loop < aRun - > mAllocationCount ; loop + + )
{
2001-11-29 04:29:26 +03:00
if ( prevTimeval < aRun - > mAllocations [ loop ] - > mMinTimeval & & timeval > = aRun - > mAllocations [ loop ] - > mMinTimeval )
2001-11-16 01:40:53 +03:00
{
PRUint64 size64 = LL_INIT ( 0 , 0 ) ;
PRUint64 lifespan64 = LL_INIT ( 0 , 0 ) ;
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
LL_UI2L ( size64 , byteSize ( aRun - > mAllocations [ loop ] ) ) ;
2001-11-29 04:29:26 +03:00
LL_UI2L ( lifespan64 , ( aRun - > mAllocations [ loop ] - > mMaxTimeval - aRun - > mAllocations [ loop ] - > mMinTimeval ) ) ;
2001-11-16 01:40:53 +03:00
LL_MUL ( weight64 , size64 , lifespan64 ) ;
LL_ADD ( YData64 [ traverse ] , YData64 [ traverse ] , weight64 ) ;
}
}
}
/*
* * Did we cache this ?
*/
if ( YData64 = = globals . mCache . mWeightYData64 )
{
globals . mCache . mWeightCached = __LINE__ ;
}
}
if ( 0 = = retval )
{
PRUint64 minWeight64 = LL_INIT ( 0xFFFFFFFF , 0xFFFFFFFF ) ;
PRUint64 maxWeight64 = LL_INIT ( 0 , 0 ) ;
int transparent = 0 ;
gdImagePtr graph = NULL ;
/*
* * Go through and find the minimum and maximum weights .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
if ( LL_UCMP ( YData64 [ traverse ] , < , minWeight64 ) )
{
minWeight64 = YData64 [ traverse ] ;
}
if ( LL_UCMP ( YData64 [ traverse ] , > , maxWeight64 ) )
{
maxWeight64 = YData64 [ traverse ] ;
}
}
/*
* * We can now draw the graph .
*/
graph = createGraph ( & transparent ) ;
if ( NULL ! = graph )
{
gdSink theSink ;
int red = 0 ;
int x1 = 0 ;
int y1 = 0 ;
int x2 = 0 ;
int y2 = 0 ;
PRUint32 percents [ 11 ] = { 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 } ;
2001-11-29 04:29:26 +03:00
char * timevals [ 11 ] ;
2001-11-16 01:40:53 +03:00
char * bytes [ 11 ] ;
2001-11-29 04:29:26 +03:00
char timevalSpace [ 11 ] [ 32 ] ;
2001-11-16 01:40:53 +03:00
char byteSpace [ 11 ] [ 32 ] ;
int legendColors [ 1 ] ;
const char * legends [ 1 ] = { " Memory Weight " } ;
PRUint64 percent64 = LL_INIT ( 0 , 0 ) ;
PRUint64 result64 = LL_INIT ( 0 , 0 ) ;
PRUint64 hundred64 = LL_INIT ( 0 , 0 ) ;
2001-11-29 04:29:26 +03:00
PRUint32 cached = 0 ;
2001-11-16 01:40:53 +03:00
LL_UI2L ( hundred64 , 100 ) ;
/*
* * Figure out what the labels will say .
*/
for ( traverse = 0 ; traverse < 11 ; traverse + + )
{
2001-11-29 04:29:26 +03:00
timevals [ traverse ] = timevalSpace [ traverse ] ;
2001-11-16 01:40:53 +03:00
bytes [ traverse ] = byteSpace [ traverse ] ;
2001-11-29 04:29:26 +03:00
cached = ( ( ( globals . mOptions . mGraphTimevalMax - globals . mOptions . mGraphTimevalMin ) * percents [ traverse ] ) / 100 ) + globals . mOptions . mGraphTimevalMin ;
PR_snprintf ( timevals [ traverse ] , 32 , ST_TIMEVAL_FORMAT , ST_TIMEVAL_PRINTABLE ( cached ) ) ;
2001-11-16 01:40:53 +03:00
LL_UI2L ( percent64 , percents [ traverse ] ) ;
LL_SUB ( result64 , maxWeight64 , minWeight64 ) ;
LL_MUL ( result64 , result64 , percent64 ) ;
LL_DIV ( result64 , result64 , hundred64 ) ;
PR_snprintf ( bytes [ traverse ] , 32 , " %llu " , result64 ) ;
}
red = gdImageColorAllocate ( graph , 255 , 0 , 0 ) ;
legendColors [ 0 ] = red ;
2001-11-29 04:29:26 +03:00
drawGraph ( graph , - 1 , " Allocation Weights " , " Seconds " , " Weight " , 11 , percents , ( const char * * ) timevals , 11 , percents , ( const char * * ) bytes , 1 , legendColors , legends ) ;
2001-11-16 01:40:53 +03:00
if ( LL_NE ( maxWeight64 , minWeight64 ) )
{
PRInt64 in64 = LL_INIT ( 0 , 0 ) ;
PRInt64 spacey64 = LL_INIT ( 0 , 0 ) ;
PRInt64 weight64 = LL_INIT ( 0 , 0 ) ;
PRInt32 in32 = 0 ;
/*
* * Go through our Y data and mark it up .
*/
for ( traverse = 0 ; traverse < STGD_SPACE_X ; traverse + + )
{
x1 = traverse + STGD_MARGIN ;
y1 = STGD_HEIGHT - STGD_MARGIN ;
/*
* * Need to do this math in 64 bits .
*/
LL_I2L ( spacey64 , STGD_SPACE_Y ) ;
LL_SUB ( weight64 , maxWeight64 , minWeight64 ) ;
LL_MUL ( in64 , YData64 [ traverse ] , spacey64 ) ;
LL_DIV ( in64 , in64 , weight64 ) ;
LL_L2I ( in32 , in64 ) ;
x2 = x1 ;
y2 = y1 - in32 ;
gdImageLine ( graph , x1 , y1 , x2 , y2 , red ) ;
}
}
2002-05-02 22:38:42 +04:00
theSink . context = inRequest - > mFD ;
2001-11-16 01:40:53 +03:00
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphWeight ) ;
}
return retval ;
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
/*
2002-05-04 06:06:52 +04:00
* * fillOptions
2001-11-16 01:40:53 +03:00
* *
2002-05-04 06:06:52 +04:00
* * Given an appropriate hexcaped string , distill the option values
* * and fill the given STOption struct .
* *
* * Note that the options passed in are not touched UNLESS there is
* * a replacement found in the form data .
2001-11-16 01:40:53 +03:00
*/
2002-05-04 06:06:52 +04:00
void fillOptions ( STOptions * outOptions , STOptionChange * outWhatChanged , const char * inFormData )
2001-11-16 01:40:53 +03:00
{
/*
2002-05-04 06:06:52 +04:00
* * Init out args if appropriate .
2001-11-16 01:40:53 +03:00
*/
2002-05-04 06:06:52 +04:00
if ( NULL ! = outWhatChanged )
{
memset ( outWhatChanged , 0 , sizeof ( STOptionChange ) ) ;
}
if ( NULL ! = outOptions & & NULL ! = outWhatChanged & & NULL ! = inFormData )
{
int dontCare = 0 ;
char looper_buf [ 32 ] ;
PRIntn looper = 0 ;
2001-11-16 01:40:53 +03:00
2002-05-04 06:06:52 +04:00
getDataPRUint32 ( inFormData , " mListItemMax " , & outOptions - > mListItemMax , & dontCare , 1 ) ;
2001-11-29 04:29:26 +03:00
2002-05-04 06:06:52 +04:00
getDataPRUint32 ( inFormData , " mTimevalMin " , & outOptions - > mTimevalMin , & outWhatChanged - > mSet , ST_TIMEVAL_RESOLUTION ) ;
getDataPRUint32 ( inFormData , " mTimevalMax " , & outOptions - > mTimevalMax , & outWhatChanged - > mSet , ST_TIMEVAL_RESOLUTION ) ;
2001-11-29 04:29:26 +03:00
2002-05-04 06:06:52 +04:00
getDataPRUint32 ( inFormData , " mAllocationTimevalMin " , & outOptions - > mAllocationTimevalMin , & outWhatChanged - > mSet , ST_TIMEVAL_RESOLUTION ) ;
getDataPRUint32 ( inFormData , " mAllocationTimevalMax " , & outOptions - > mAllocationTimevalMax , & outWhatChanged - > mSet , ST_TIMEVAL_RESOLUTION ) ;
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2002-05-04 06:06:52 +04:00
getDataPRUint32 ( inFormData , " mGraphTimevalMin " , & outOptions - > mGraphTimevalMin , & outWhatChanged - > mGraph , ST_TIMEVAL_RESOLUTION ) ;
getDataPRUint32 ( inFormData , " mGraphTimevalMax " , & outOptions - > mGraphTimevalMax , & outWhatChanged - > mGraph , ST_TIMEVAL_RESOLUTION ) ;
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2002-05-04 06:06:52 +04:00
getDataPRUint32 ( inFormData , " mSizeMin " , & outOptions - > mSizeMin , & outWhatChanged - > mSet , 1 ) ;
getDataPRUint32 ( inFormData , " mSizeMax " , & outOptions - > mSizeMax , & outWhatChanged - > mSet , 1 ) ;
getDataPRUint32 ( inFormData , " mAlignBy " , & outOptions - > mAlignBy , & outWhatChanged - > mSet , 1 ) ;
getDataPRUint32 ( inFormData , " mOverhead " , & outOptions - > mOverhead , & outWhatChanged - > mSet , 1 ) ;
getDataPRUint32 ( inFormData , " mOrderBy " , & outOptions - > mOrderBy , & outWhatChanged - > mOrder , 1 ) ;
getDataPRUint32 ( inFormData , " mLifetimeMin " , & outOptions - > mLifetimeMin , & outWhatChanged - > mSet , ST_TIMEVAL_RESOLUTION ) ;
getDataPRUint32 ( inFormData , " mLifetimeMax " , & outOptions - > mLifetimeMax , & outWhatChanged - > mSet , ST_TIMEVAL_RESOLUTION ) ;
getDataPRUint64 ( inFormData , " mWeightMin " , & outOptions - > mWeightMin64 , & outWhatChanged - > mSet ) ;
getDataPRUint64 ( inFormData , " mWeightMax " , & outOptions - > mWeightMax64 , & outWhatChanged - > mSet ) ;
for ( looper = 0 ; ST_SUBSTRING_MATCH_MAX > looper ; looper + + )
{
PR_snprintf ( looper_buf , sizeof ( looper_buf ) , " mRestrictText%d " , looper ) ;
getDataString ( inFormData , looper_buf , outOptions - > mRestrictText [ looper ] , sizeof ( outOptions - > mRestrictText [ looper ] ) , & outWhatChanged - > mSet ) ;
}
getDataString ( inFormData , " mCategoryName " , outOptions - > mCategoryName , sizeof ( outOptions - > mCategoryName ) , & outWhatChanged - > mCategory ) ;
2002-04-17 03:36:47 +04:00
}
2002-05-04 06:06:52 +04:00
}
/*
* * applySettings
* *
* * Apply settings and update global options .
* * Returns 0 on success . Nonzero on failure .
*/
int applySettings ( STRequest * inRequest )
{
int retval = 0 ;
STOptionChange changed ;
fillOptions ( & globals . mOptions , & changed , inRequest - > mGetData ) ;
2002-04-17 03:36:47 +04:00
/*
* * Sanity check options
*/
2002-05-04 06:06:52 +04:00
if ( changed . mCategory & & ( ! globals . mOptions . mCategoryName [ 0 ]
2002-04-17 05:37:14 +04:00
| | ! findCategoryNode ( globals . mOptions . mCategoryName , & globals ) ) )
2002-04-17 03:36:47 +04:00
{
2002-05-03 04:32:23 +04:00
PR_snprintf ( globals . mOptions . mCategoryName , sizeof ( globals . mOptions . mCategoryName ) , " %s " , ST_ROOT_CATEGORY_NAME ) ;
2002-04-17 03:36:47 +04:00
}
2001-11-16 01:40:53 +03:00
2002-04-17 03:36:47 +04:00
/*
* * Resort the global based on new prefs if needed .
*/
2002-05-04 06:06:52 +04:00
if ( 0 ! = changed . mSet | | 0 ! = changed . mOrder )
2002-04-17 03:36:47 +04:00
{
2001-11-16 01:40:53 +03:00
/*
2002-04-17 03:36:47 +04:00
* * Dont free globals . mCache . mSortedRun anymore . It is held in the root category node .
* * It will get freed automatically when categorization happens .
2001-11-16 01:40:53 +03:00
*/
2002-04-17 03:36:47 +04:00
globals . mCache . mSortedRun = createRunFromGlobal ( ) ;
if ( NULL = = globals . mCache . mSortedRun )
2001-11-16 01:40:53 +03:00
{
2002-05-04 06:06:52 +04:00
retval = __LINE__ ;
2002-04-17 03:36:47 +04:00
REPORT_ERROR ( __LINE__ , createRunFromGlobal ) ;
2001-11-16 01:40:53 +03:00
}
2002-04-17 03:36:47 +04:00
}
2002-05-04 06:06:52 +04:00
else if ( 0 ! = changed . mCategory )
2002-04-17 05:37:14 +04:00
{
/*
* * Just a category change . We dont need to harvest . Just find the
* * right node and set the cache . mSortedRun . We need to recompute
* * cost though . But that is cheap .
*/
2002-05-03 04:32:23 +04:00
STCategoryNode * node = findCategoryNode ( globals . mOptions . mCategoryName , & globals ) ;
if ( node )
2002-04-17 05:37:14 +04:00
{
2002-05-03 04:32:23 +04:00
/* Recalculate cost of run */
recalculateRunCost ( node - > run ) ;
globals . mCache . mSortedRun = node - > run ;
2002-04-17 05:37:14 +04:00
}
}
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2002-04-17 03:36:47 +04:00
/*
* * If any of the set was changed , we need to throw away all our
* * cached graphs .
*/
2002-05-04 06:06:52 +04:00
if ( 0 ! = changed . mSet | | 0 ! = changed . mGraph )
2002-04-17 03:36:47 +04:00
{
2001-11-16 01:40:53 +03:00
/*
2002-04-17 03:36:47 +04:00
* * Automove the graph timeval if required .
2001-11-16 01:40:53 +03:00
*/
2002-04-17 03:36:47 +04:00
if ( ( globals . mMaxTimeval - globals . mMinTimeval ) < globals . mOptions . mGraphTimevalMax )
2001-11-16 01:40:53 +03:00
{
2002-04-17 03:36:47 +04:00
globals . mOptions . mGraphTimevalMax = ( globals . mMaxTimeval - globals . mMinTimeval ) ;
2001-11-16 01:40:53 +03:00
}
2002-04-17 03:36:47 +04:00
globals . mCache . mFootprintCached = 0 ;
globals . mCache . mTimevalCached = 0 ;
globals . mCache . mLifespanCached = 0 ;
globals . mCache . mWeightCached = 0 ;
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-04 06:06:52 +04:00
return retval ;
2002-04-17 03:36:47 +04:00
}
2001-11-16 01:40:53 +03:00
2002-04-17 03:36:47 +04:00
/*
* * displaySettings
* *
* * Present the settings for change during execution .
* * Returns ! 0 on error .
* *
* * Changes are effected via the get data .
*/
2002-05-02 22:38:42 +04:00
int displaySettings ( STRequest * inRequest )
2002-04-17 03:36:47 +04:00
{
int retval = 0 ;
PRUint32 cached = 0 ;
PRIntn looper = 0 ;
int getRes ;
2002-05-02 22:38:42 +04:00
getRes = applySettings ( inRequest ) ;
2002-04-17 03:36:47 +04:00
/*
* * Display header for settings page . Do this after applying settings .
* *
*/
2002-05-02 22:38:42 +04:00
htmlHeader ( inRequest , " SpaceTrace Settings " ) ;
2002-04-17 03:36:47 +04:00
/*
* * Report on the operation .
*/
if ( 0 ! = getRes )
{
REPORT_ERROR ( getRes , applySettings ) ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <blink><b>%u: There was a problem. Some changes may have been applied.</b></blink><br><hr> \n " , PR_IntervalNow ( ) ) ;
2002-04-17 03:36:47 +04:00
}
else
{
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <b>%u: Your changes have been applied.</b><br><hr> \n " , PR_IntervalNow ( ) ) ;
2001-11-16 01:40:53 +03:00
}
2002-04-17 03:36:47 +04:00
2001-11-16 01:40:53 +03:00
/*
* * A small blurb regarding the options .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " NOTES:<p> \n " ) ;
2001-11-29 04:29:26 +03:00
cached = globals . mMaxTimeval - globals . mMinTimeval ;
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " The total seconds in this run is: 0 to " ST_TIMEVAL_FORMAT " <br> \n " , ST_TIMEVAL_PRINTABLE ( cached ) ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " All options should have command line equivalents to support batch mode.<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Changes to the options take effect immediately.<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
2001-11-16 01:40:53 +03:00
/*
* * We ' ve got a form to create .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <form method=get action= \" ./options.html \" > \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Maximum number of items to display in a list?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " This option exists to control how much information you are willing to accept.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mListItemMax \" value= \" %u \" ><br> \n " , globals . mOptions . mListItemMax ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " This option controls the sort order of the lists presented.<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " There are several choices:<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <ul><li>0 is by weight (byte size * seconds).<li>1 is by byte size.<li>2 is by seconds (lifetime).<li>3 is by allocation object count.<li>4 is by heap operation runtime cost.</ul><p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Desired sort order?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mOrderBy \" value= \" %u \" ><br> \n " , globals . mOptions . mOrderBy ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Modify the seconds for which the graphs cover;<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " meaning that a narrower range will produce a more detailed graph for that timespan.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Minimum graph second?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mGraphTimevalMin \" value= \" %u \" ><br> \n " , globals . mOptions . mGraphTimevalMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " Maximum graph second?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mGraphTimevalMax \" value= \" %u \" ><br> \n " , globals . mOptions . mGraphTimevalMax / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " Modify the secondss to target allocations created during a particular timespan;<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " meaning that the allocations created only within the timespan are of interest.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Minimum allocation second?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mAllocationTimevalMin \" value= \" %u \" ><br> \n " , globals . mOptions . mAllocationTimevalMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " Maximum allocation second?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mAllocationTimevalMax \" value= \" %u \" ><br> \n " , globals . mOptions . mAllocationTimevalMax / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Modify the byte sizes to target allocations of a particular byte size.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Minimum byte size?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mSizeMin \" value= \" %u \" ><br> \n " , globals . mOptions . mSizeMin ) ;
PR_fprintf ( inRequest - > mFD , " Maximum byte size?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mSizeMax \" value= \" %u \" ><br> \n " , globals . mOptions . mSizeMax ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Modify the alignment boundry and heap overhead of allocations to see the actual impact an allocation has on a heap;<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " meaning that normally an allocation of 1 bytes actually costs more bytes depending on your heap implementation.<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Overhead is taken into account after allocation alignment.<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " i.e. the msvcrt malloc has an alignment of 16 with an overhead of 8 (1 byte allocation costs 24 heap bytes), the win32 HeapAlloc has an alignment of 8 with an overhead of 8 (1 byte allocation costs 16 heap bytes).<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Align by?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mAlignBy \" value= \" %u \" ><br> \n " , globals . mOptions . mAlignBy ) ;
PR_fprintf ( inRequest - > mFD , " Overhead?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mOverhead \" value= \" %u \" ><br> \n " , globals . mOptions . mOverhead ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Modify the seconds to target allocations of a particular lifespan/duration;<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " meaning that the allocations existed at least or at most the specified span of time.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Minimum lifetime?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mLifetimeMin \" value= \" %u \" ><br> \n " , globals . mOptions . mLifetimeMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " Maximum lifetime?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mLifetimeMax \" value= \" %u \" ><br> \n " , globals . mOptions . mLifetimeMax / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Modify the numbers to target allocations of particular weights;<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " the weight of an allocation is the byte size multiplied by the lifespan.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Minimum weight?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mWeightMin \" value= \" %llu \" ><br> \n " , globals . mOptions . mWeightMin64 ) ;
PR_fprintf ( inRequest - > mFD , " Maximum weight?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mWeightMax \" value= \" %llu \" ><br> \n " , globals . mOptions . mWeightMax64 ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " By manipulating the time range, you narrow or widen the set of live allocations evaluated. Allocations existing solely before the minimum or solely after the maximum will not be considered.<p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Minimum second?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mTimevalMin \" value= \" %u \" ><br> \n " , globals . mOptions . mTimevalMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " Maximum timeval?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mTimevalMax \" value= \" %u \" ><br> \n " , globals . mOptions . mTimevalMax / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Restrict callsite backtraces to thost only containing the specified text. \n " ) ;
PR_fprintf ( inRequest - > mFD , " This allows targeting of specific creation functions. \n " ) ;
PR_fprintf ( inRequest - > mFD , " Keep all the strings together near the top of this list, as the code will stop trying to match on the first empty string.<br> \n " ) ;
2002-01-24 03:58:33 +03:00
for ( looper = 0 ; ST_SUBSTRING_MATCH_MAX > looper ; looper + + )
{
2002-05-03 04:32:23 +04:00
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mRestrictText%d \" value= \" %s \" ><br> \n " , looper , globals . mOptions . mRestrictText [ looper ] ) ;
2002-01-24 03:58:33 +03:00
}
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-04-17 03:36:47 +04:00
/*
* * category selection
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " By giving a category name, you can focus on allocations belonging to that particular category only like css, js, xpcom. Until we have a flashy new ui to browse categories, use a category name in <a href=http://lxr.mozilla.org/mozilla/source/tools/trace-malloc/rules.txt>rules.txt</a><p> \n " ) ;
PR_fprintf ( inRequest - > mFD , " Category to Focus on?<br> \n " ) ;
PR_fprintf ( inRequest - > mFD , " <input type=text name= \" mCategoryName \" value= \" %s \" ><br> \n " , globals . mOptions . mCategoryName ) ;
PR_fprintf ( inRequest - > mFD , " <hr> \n " ) ;
2002-04-17 03:36:47 +04:00
2001-11-16 01:40:53 +03:00
/*
* * And last but not least , the submission button .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <input type=submit value= \" Submit Changes \" ><input type=reset value= \" Obligatory Reset Button \" ><br> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " </form> \n " ) ;
2001-11-16 01:40:53 +03:00
return retval ;
}
/*
* * displayIndex
* *
* * Present a list of the reports you can drill down into .
* * Returns ! 0 on failure .
*/
2002-05-02 22:38:42 +04:00
int displayIndex ( STRequest * inRequest )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
/*
* * Present reports in a list format .
*/
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <ul> " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " root_callsites.html " , " Root Callsites " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " categories_summary.html " , " Categories Report " , NULL ) ;
2002-04-19 00:11:45 +04:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " top_callsites.html " , " Top Callsites Report " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " top_allocations.html " , " Top Allocations Report " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " memory_leaks.html " , " Memory Leak Report " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li>Graphs " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " <ul> " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " footprint_graph.html " , " Footprint " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " lifespan_graph.html " , " Allocation Lifespans " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " times_graph.html " , " Allocation Times " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n <li> " ) ;
htmlAnchor ( inRequest , " weight_graph.html " , " Allocation Weights " , NULL ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n </ul> \n " ) ;
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( inRequest - > mFD , " \n </ul> \n " ) ;
2001-11-16 01:40:53 +03:00
return retval ;
}
2002-05-04 05:07:16 +04:00
/*
* * initRequestOptions
* *
* * Given the request , set the options that are specific to the request .
* * These can generally be determined in the following manner :
* * Copy over global options .
* * If getData present , attempt to use options therein .
* * Iff no getData , then use cookieData .
* *
* * Other code attempt to constantly set the cookie in the client to their
* * current option set .
*/
void initRequestOptions ( STRequest * inRequest , const char * inGetData , const char * inCookieData )
{
2002-05-04 06:06:52 +04:00
if ( NULL ! = inRequest )
2002-05-04 05:07:16 +04:00
{
/*
2002-05-04 06:06:52 +04:00
* * Copy of global options .
2002-05-04 05:07:16 +04:00
*/
2002-05-04 06:06:52 +04:00
memcpy ( & inRequest - > mOptions , & globals . mOptions , sizeof ( globals . mOptions ) ) ;
2002-05-04 05:07:16 +04:00
/*
2002-05-04 06:06:52 +04:00
* * Decide what will override global options if anything .
2002-05-04 05:07:16 +04:00
*/
2002-05-04 06:06:52 +04:00
if ( NULL ! = inGetData )
{
STOptionChange changed ;
fillOptions ( & inRequest - > mOptions , & changed , inGetData ) ;
}
else if ( NULL ! = inCookieData )
{
STOptionChange changed ;
fillOptions ( & inRequest - > mOptions , & changed , inCookieData ) ;
}
2002-05-04 05:07:16 +04:00
}
}
2001-11-16 01:40:53 +03:00
/*
* * handleRequest
* *
* * Based on what file they are asking for , perform some processing .
* * Output the results to aFD .
* *
* * Returns ! 0 on error .
*/
2002-05-04 05:07:16 +04:00
int handleRequest ( tmreader * aTMR , PRFileDesc * aFD , const char * aFileName , const char * aGetData , const char * inCookieData )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
if ( NULL ! = aTMR & & NULL ! = aFD & & NULL ! = aFileName & & ' \0 ' ! = * aFileName )
{
2002-05-02 22:38:42 +04:00
STRequest request ;
2001-11-16 01:40:53 +03:00
/*
2002-05-02 22:38:42 +04:00
* * Init the request .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
memset ( & request , 0 , sizeof ( request ) ) ;
request . mFD = aFD ;
2002-05-03 04:32:23 +04:00
request . mGetFileName = aFileName ;
2002-05-02 22:38:42 +04:00
request . mGetData = aGetData ;
2001-11-16 01:40:53 +03:00
2002-05-04 05:07:16 +04:00
/*
* * Set local options for this request .
*/
initRequestOptions ( & request , aGetData , inCookieData ) ;
2001-11-16 01:40:53 +03:00
/*
* * Attempt to find the file of interest .
*/
if ( 0 = = strcmp ( " index.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Index " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayIndex ( & request ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayIndex ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strcmp ( " settings.html " , aFileName ) | | 0 = = strcmp ( " options.html " , aFileName ) )
{
int settingsRes = 0 ;
2002-05-02 22:38:42 +04:00
settingsRes = displaySettings ( & request ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = settingsRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displaySettings ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strcmp ( " top_allocations.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Top Allocations Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayTopAllocations ( & request , globals . mCache . mSortedRun , 1 ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopAllocations ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strcmp ( " top_callsites.html " , aFileName ) )
{
int displayRes = 0 ;
tmcallsite * * array = NULL ;
PRUint32 arrayCount = 0 ;
2002-04-19 00:11:45 +04:00
/*
* * We can get an argument to focus on a category . Take care of it .
*/
2002-05-02 22:38:42 +04:00
if ( request . mGetData & & * request . mGetData )
2002-04-19 00:11:45 +04:00
{
2002-05-03 04:32:23 +04:00
char categoryName [ ST_OPTION_STRING_MAX ] ;
int getRes = getDataString ( request . mGetData , " mCategory " , categoryName , sizeof ( categoryName ) , NULL ) ;
2002-04-19 00:11:45 +04:00
STCategoryNode * node ;
2002-05-03 04:32:23 +04:00
if ( * categoryName & &
2002-04-19 00:11:45 +04:00
strcmp ( categoryName , globals . mOptions . mCategoryName ) & &
( node = findCategoryNode ( categoryName , & globals ) ) )
{
/*
* * category has changed . Refocus .
*/
recalculateRunCost ( node - > run ) ;
globals . mCache . mSortedRun = node - > run ;
2002-05-03 04:32:23 +04:00
PR_snprintf ( globals . mOptions . mCategoryName , sizeof ( globals . mOptions . mCategoryName ) , " %s " , categoryName ) ;
2002-04-19 00:11:45 +04:00
}
}
/*
* * Display header after we figure out if we are going to focus
* * on a category .
*/
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Top Callsites Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-03 01:52:17 +04:00
if ( NULL ! = globals . mCache . mSortedRun & & 0 < globals . mCache . mSortedRun - > mAllocationCount )
2001-11-16 01:40:53 +03:00
{
arrayCount = callsiteArrayFromRun ( & array , 0 , globals . mCache . mSortedRun ) ;
if ( 0 ! = arrayCount & & NULL ! = array )
{
2002-05-02 22:38:42 +04:00
displayRes = displayTopCallsites ( & request , array , arrayCount , 0 , 0 ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopCallsites ) ;
}
/*
* * Done with the array .
*/
free ( array ) ;
array = NULL ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleRequest ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strcmp ( " memory_leaks.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Memory Leaks Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayMemoryLeaks ( & request , globals . mCache . mSortedRun ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayMemoryLeaks ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strncmp ( " allocation_ " , aFileName , 11 ) )
{
int scanRes = 0 ;
PRUint32 allocationIndex = 0 ;
/*
* * Oh , what a hack . . . .
* * The index to the allocation structure in the global run
* * is in the filename . Better than the pointer value . . . .
*/
scanRes = PR_sscanf ( aFileName + 11 , " %u " , & allocationIndex ) ;
if ( 1 = = scanRes & & globals . mRun . mAllocationCount > allocationIndex & & NULL ! = globals . mRun . mAllocations [ allocationIndex ] )
{
STAllocation * allocation = globals . mRun . mAllocations [ allocationIndex ] ;
char buffer [ 128 ] ;
int displayRes = 0 ;
PR_snprintf ( buffer , sizeof ( buffer ) , " SpaceTrace Allocation %u Details Report " , allocationIndex ) ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , buffer ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayAllocationDetails ( & request , allocation ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else
{
2002-05-02 22:38:42 +04:00
htmlNotFound ( & request ) ;
2001-11-16 01:40:53 +03:00
}
}
else if ( 0 = = strncmp ( " callsite_ " , aFileName , 9 ) )
{
int scanRes = 0 ;
PRUint32 callsiteSerial = 0 ;
tmcallsite * resolved = NULL ;
/*
* * Oh , what a hack . . . .
* * The serial ( key ) to the callsite structure in the hash table
* * is in the filename . Better than the pointer value . . . .
*/
scanRes = PR_sscanf ( aFileName + 9 , " %u " , & callsiteSerial ) ;
if ( 1 = = scanRes & & 0 ! = callsiteSerial & & NULL ! = ( resolved = tmreader_callsite ( aTMR , callsiteSerial ) ) )
{
char buffer [ 128 ] ;
int displayRes = 0 ;
PR_snprintf ( buffer , sizeof ( buffer ) , " SpaceTrace Callsite %u Details Report " , callsiteSerial ) ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , buffer ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayCallsiteDetails ( & request , resolved ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
else
{
2002-05-02 22:38:42 +04:00
htmlNotFound ( & request ) ;
2001-11-16 01:40:53 +03:00
}
}
else if ( 0 = = strcmp ( " root_callsites.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Root Callsites " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
displayRes = displayCallsites ( & request , aTMR - > calltree_root . kids , ST_FOLLOW_SIBLINGS , 0 , __LINE__ ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
else if ( 0 = = strcmp ( " footprint_graph.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Memory Footprint Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( request . mFD , " <div align=center> \n " ) ;
PR_fprintf ( request . mFD , " <img src= \" ./footprint.png \" > \n " ) ;
PR_fprintf ( request . mFD , " </div> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-29 04:29:26 +03:00
else if ( 0 = = strcmp ( " times_graph.html " , aFileName ) )
2001-11-16 01:40:53 +03:00
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Allocation Times Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( request . mFD , " <div align=center> \n " ) ;
PR_fprintf ( request . mFD , " <img src= \" ./times.png \" > \n " ) ;
PR_fprintf ( request . mFD , " </div> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
else if ( 0 = = strcmp ( " lifespan_graph.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Allocation Lifespans Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( request . mFD , " <div align=center> \n " ) ;
PR_fprintf ( request . mFD , " <img src= \" ./lifespan.png \" > \n " ) ;
PR_fprintf ( request . mFD , " </div> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
else if ( 0 = = strcmp ( " weight_graph.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " SpaceTrace Allocation Weights Report " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
PR_fprintf ( request . mFD , " <div align=center> \n " ) ;
PR_fprintf ( request . mFD , " <img src= \" ./weight.png \" > \n " ) ;
PR_fprintf ( request . mFD , " </div> \n " ) ;
2001-11-16 01:40:53 +03:00
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
else if ( 0 = = strcmp ( " footprint.png " , aFileName ) )
{
int graphRes = 0 ;
2002-05-02 22:38:42 +04:00
graphRes = graphFootprint ( & request , globals . mCache . mSortedRun ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphFootprint ) ;
}
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-29 04:29:26 +03:00
else if ( 0 = = strcmp ( " times.png " , aFileName ) )
2001-11-16 01:40:53 +03:00
{
int graphRes = 0 ;
2002-05-02 22:38:42 +04:00
graphRes = graphTimeval ( & request , globals . mCache . mSortedRun ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
2001-11-29 04:29:26 +03:00
REPORT_ERROR ( __LINE__ , graphTimeval ) ;
2001-11-16 01:40:53 +03:00
}
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
else if ( 0 = = strcmp ( " lifespan.png " , aFileName ) )
{
int graphRes = 0 ;
2002-05-02 22:38:42 +04:00
graphRes = graphLifespan ( & request , globals . mCache . mSortedRun ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphLifespan ) ;
}
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
else if ( 0 = = strcmp ( " weight.png " , aFileName ) )
{
int graphRes = 0 ;
2002-05-02 22:38:42 +04:00
graphRes = graphWeight ( & request , globals . mCache . mSortedRun ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphWeight ) ;
}
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2002-04-19 00:11:45 +04:00
else if ( 0 = = strcmp ( " categories_summary.html " , aFileName ) )
{
int displayRes = 0 ;
2002-05-02 22:38:42 +04:00
htmlHeader ( & request , " Category Report " ) ;
2002-04-19 00:11:45 +04:00
2002-05-02 22:38:42 +04:00
displayRes = displayCategoryReport ( & request , & globals . mCategoryRoot , 1 ) ;
2002-04-19 00:11:45 +04:00
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayMemoryLeaks ) ;
}
2002-05-02 22:38:42 +04:00
htmlFooter ( & request ) ;
2002-04-19 00:11:45 +04:00
}
2001-11-16 01:40:53 +03:00
else
{
2002-05-02 22:38:42 +04:00
htmlNotFound ( & request ) ;
2001-11-16 01:40:53 +03:00
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleRequest ) ;
}
2001-11-29 04:29:26 +03:00
/*
* * Compact a little if you can after each request .
*/
heapCompact ( ) ;
2001-11-16 01:40:53 +03:00
return retval ;
}
/*
* * handleClient
* *
2002-05-02 04:28:42 +04:00
* * main ( ) of the new client thread .
2001-11-16 01:40:53 +03:00
* * Read the fd for the request .
* * Output the results .
*/
2002-05-02 04:28:42 +04:00
void handleClient ( void * inArg )
2001-11-16 01:40:53 +03:00
{
2002-05-02 04:28:42 +04:00
PRFileDesc * aFD = NULL ;
2001-11-16 01:40:53 +03:00
2002-05-02 04:28:42 +04:00
aFD = ( PRFileDesc * ) inArg ;
if ( NULL ! = aFD )
2001-11-16 01:40:53 +03:00
{
2002-05-02 04:28:42 +04:00
PRStatus closeRes = PR_SUCCESS ;
2001-11-16 01:40:53 +03:00
char aBuffer [ 2048 ] ;
PRInt32 readRes = 0 ;
readRes = PR_Read ( aFD , aBuffer , sizeof ( aBuffer ) ) ;
if ( 0 < = readRes )
{
const char * sanityCheck = " GET / " ;
if ( 0 = = strncmp ( sanityCheck , aBuffer , 5 ) )
{
char * eourl = NULL ;
char * start = & aBuffer [ 5 ] ;
char * getData = NULL ;
2002-05-04 05:07:16 +04:00
char * cookieData = NULL ;
2001-11-16 01:40:53 +03:00
int realFun = 0 ;
const char * crlf = " \015 \012 " ;
char * eoline = NULL ;
2002-05-04 05:07:16 +04:00
/*
* * Find our option cookie .
*/
cookieData = strstr ( aBuffer , " Cookie: Options= " ) ;
if ( NULL ! = cookieData )
{
cookieData + = 16 ;
/*
* * Truncate for cookie data .
*/
eoline = strstr ( cookieData , crlf ) ;
if ( NULL ! = eoline )
{
* eoline = ' \0 ' ;
}
}
2001-11-16 01:40:53 +03:00
/*
* * Truncate the line if possible .
* * Only want first one .
*/
eoline = strstr ( aBuffer , crlf ) ;
if ( NULL ! = eoline )
{
* eoline = ' \0 ' ;
}
/*
* * Find the whitespace .
* * That is either end of line or the " HTTP/1.x " suffix .
* * We do not care .
*/
for ( eourl = start ; 0 = = isspace ( * eourl ) & & ' \0 ' ! = * eourl ; eourl + + )
{
/*
* * No body .
*/
}
/*
* * Cap it off .
* * Convert empty ' / ' to index . html .
*/
* eourl = ' \0 ' ;
if ( ' \0 ' = = * start )
{
strcpy ( start , " index.html " ) ;
}
/*
* * Have we got any GET form data ?
*/
getData = strchr ( start , ' ? ' ) ;
if ( NULL ! = getData )
{
/*
* * Whack it off .
*/
* getData = ' \0 ' ;
getData + + ;
}
/*
* * This is totally a hack , but oh well . . . .
* *
* * Send that the request was OK , regardless .
* *
2002-05-04 05:07:16 +04:00
* * If we have any get data , then it is a set of options .
* * Set a cookie to remember the options .
* *
2001-11-16 01:40:53 +03:00
* * Other code will tell the user they were wrong .
* * If the filename contains a " .png " , then send the image
* * mime type , otherwise , say it is text / html .
*/
2002-05-04 05:07:16 +04:00
PR_fprintf ( aFD , " HTTP/1.1 200 OK%s " , crlf ) ;
2002-05-09 01:32:24 +04:00
PR_fprintf ( aFD , " Server: %s%s " , " $Id: spacetrace.c,v 1.30 2002-05-08 21:32:23 blythe%netscape.com Exp $ " , crlf ) ;
2002-05-04 05:07:16 +04:00
if ( NULL ! = getData )
{
if ( NULL = = cookieData | | ( NULL ! = cookieData & & 0 ! = strcmp ( getData , cookieData ) ) )
{
PR_fprintf ( aFD , " Set-Cookie: Options=%s; Path=/; Expires=Fri, 20-Dec-2069 00:00:01 GMT%s " , getData , crlf ) ;
}
}
2001-11-16 01:40:53 +03:00
PR_fprintf ( aFD , " Content-type: " ) ;
if ( NULL ! = strstr ( start , " .png " ) )
{
PR_fprintf ( aFD , " image/png " ) ;
}
else if ( NULL ! = strstr ( start , " .jpg " ) )
{
PR_fprintf ( aFD , " image/jpeg " ) ;
}
else if ( NULL ! = strstr ( start , " .txt " ) )
{
PR_fprintf ( aFD , " text/plain " ) ;
}
else
{
PR_fprintf ( aFD , " text/html " ) ;
}
PR_fprintf ( aFD , crlf ) ;
/*
* * One more to seperate headers from content .
*/
PR_fprintf ( aFD , crlf ) ;
/*
* * Ready for the real fun .
*/
2002-05-04 05:07:16 +04:00
realFun = handleRequest ( globals . mTMR , aFD , start , getData , cookieData ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = realFun )
{
REPORT_ERROR ( __LINE__ , handleRequest ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , handleClient ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , lineReader ) ;
}
2002-05-02 04:28:42 +04:00
/*
* * Done with the connection .
*/
closeRes = PR_Close ( aFD ) ;
if ( PR_SUCCESS ! = closeRes )
{
REPORT_ERROR ( __LINE__ , PR_Close ) ;
}
2001-11-16 01:40:53 +03:00
}
else
{
REPORT_ERROR ( __LINE__ , handleClient ) ;
}
}
/*
* * serverMode
* *
* * List on a port as a httpd .
* * Output results interactively on demand .
* *
* * Returns ! 0 on error .
*/
2002-05-02 04:28:42 +04:00
int serverMode ( void )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
PRFileDesc * socket = NULL ;
/*
* * Create a socket .
*/
socket = PR_NewTCPSocket ( ) ;
if ( NULL ! = socket )
{
PRStatus closeRes = PR_SUCCESS ;
PRNetAddr bindAddr ;
PRStatus bindRes = PR_SUCCESS ;
/*
* * Bind it to an interface / port .
* * Any interface .
*/
bindAddr . inet . family = PR_AF_INET ;
bindAddr . inet . port = PR_htons ( ( PRUint16 ) globals . mOptions . mHttpdPort ) ;
bindAddr . inet . ip = PR_htonl ( PR_INADDR_ANY ) ;
bindRes = PR_Bind ( socket , & bindAddr ) ;
if ( PR_SUCCESS = = bindRes )
{
PRStatus listenRes = PR_SUCCESS ;
2002-05-02 04:28:42 +04:00
const int backlog = 0x20 ;
2001-11-16 01:40:53 +03:00
/*
* * Start listening for clients .
* * Give a decent backlog , some of our processing will take
* * a bit .
*/
listenRes = PR_Listen ( socket , backlog ) ;
if ( PR_SUCCESS = = listenRes )
{
PRFileDesc * connection = NULL ;
int failureSum = 0 ;
char message [ 80 ] ;
/*
* * Output a little message saying we are receiving .
*/
PR_snprintf ( message , sizeof ( message ) , " server accepting connections on port %u.... " , globals . mOptions . mHttpdPort ) ;
REPORT_INFO ( message ) ;
2002-01-09 02:20:29 +03:00
PR_fprintf ( PR_STDOUT , " Peak memory used: %s bytes \n " , FormatNumber ( globals . mPeakMemoryUsed ) ) ;
PR_fprintf ( PR_STDOUT , " Total calls : %s " ,
2002-04-20 01:49:55 +04:00
FormatNumber ( globals . mMallocCount + globals . mCallocCount + globals . mReallocCount ) ) ;
2002-01-09 02:20:29 +03:00
PR_fprintf ( PR_STDOUT , " [%s " , FormatNumber ( globals . mMallocCount ) ) ;
PR_fprintf ( PR_STDOUT , " + %s " , FormatNumber ( globals . mCallocCount ) ) ;
2002-04-20 01:49:55 +04:00
PR_fprintf ( PR_STDOUT , " + %s] \n " , FormatNumber ( globals . mReallocCount ) ) ;
2002-01-09 02:20:29 +03:00
2001-11-16 01:40:53 +03:00
/*
2002-05-02 04:28:42 +04:00
* * Keep accepting until we know otherwise .
* *
* * We do a thread per connection .
* * Up to the thread to close the connection when done .
* *
* * This is known by me to be suboptimal , and I would rather
* * do a thread pool if it ever becomes a resource issue .
* * Any issues would simply point to a need to get
* * more machines or a beefier machine to handle the
* * requests , as well as a need to do thread pooling and
* * avoid thread creation overhead .
* * The threads are not tracked , except possibly by NSPR
* * itself and PR_Cleanup will wait on them all to exit as
* * user threads so our shared data is valid .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
while ( 0 = = retval )
2001-11-16 01:40:53 +03:00
{
connection = PR_Accept ( socket , NULL , PR_INTERVAL_NO_TIMEOUT ) ;
if ( NULL ! = connection )
{
2002-05-02 04:28:42 +04:00
PRThread * clientThread = NULL ;
2001-11-16 01:40:53 +03:00
/*
2002-05-02 04:28:42 +04:00
* * Thread per connection .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 04:28:42 +04:00
clientThread = PR_CreateThread (
PR_USER_THREAD , /* PR_Cleanup sync */
handleClient ,
( void * ) connection ,
PR_PRIORITY_NORMAL ,
PR_GLOBAL_THREAD , /* IO enabled */
PR_UNJOINABLE_THREAD ,
0
) ;
if ( NULL = = clientThread )
2001-11-16 01:40:53 +03:00
{
2002-05-02 04:28:42 +04:00
PRStatus closeRes = PR_SUCCESS ;
2001-11-16 01:40:53 +03:00
failureSum + = __LINE__ ;
2002-05-02 04:28:42 +04:00
REPORT_ERROR ( __LINE__ , PR_Accept ) ;
/*
* * Close the connection as well , no service
*/
closeRes = PR_Close ( connection ) ;
if ( PR_FAILURE = = closeRes )
{
REPORT_ERROR ( __LINE__ , PR_Close ) ;
}
2001-11-16 01:40:53 +03:00
}
}
else
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Accept ) ;
}
}
if ( 0 ! = failureSum )
{
retval = __LINE__ ;
}
/*
* * Output a little message saying it is all over .
*/
REPORT_INFO ( " server no longer accepting connections.... " ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Listen ) ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Bind ) ;
}
/*
* * Done with socket .
*/
closeRes = PR_Close ( socket ) ;
if ( PR_SUCCESS ! = closeRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Close ) ;
}
socket = NULL ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_NewTCPSocket ) ;
}
return retval ;
}
/*
* * batchMode
* *
* * Perform whatever batch requests we were asked to do .
*/
2002-05-02 04:28:42 +04:00
int batchMode ( void )
2001-11-16 01:40:53 +03:00
{
int retval = 0 ;
2002-05-02 04:28:42 +04:00
if ( 0 ! = globals . mOptions . mBatchRequestCount )
2001-11-16 01:40:53 +03:00
{
PRUint32 loop = 0 ;
int failureSum = 0 ;
int handleRes = 0 ;
char aFileName [ 1024 ] ;
PRUint32 sprintfRes = 0 ;
/*
* * Go through and process the various files requested .
* * We do not stop on failure , as it is too costly to rerun the
* * batch job .
*/
for ( loop = 0 ; loop < globals . mOptions . mBatchRequestCount ; loop + + )
{
2002-05-08 03:39:34 +04:00
sprintfRes = PR_snprintf ( aFileName , sizeof ( aFileName ) , " %s%c%s " , globals . mOptions . mOutputDir , PR_GetDirectorySeparator ( ) , globals . mOptions . mBatchRequest [ loop ] ) ;
2001-11-16 01:40:53 +03:00
if ( ( PRUint32 ) - 1 ! = sprintfRes )
{
PRFileDesc * outFile = NULL ;
outFile = PR_Open ( aFileName , ST_FLAGS , ST_PERMS ) ;
if ( NULL ! = outFile )
{
PRStatus closeRes = PR_SUCCESS ;
2002-05-08 03:39:34 +04:00
handleRes = handleRequest ( globals . mTMR , outFile , globals . mOptions . mBatchRequest [ loop ] , NULL , NULL ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = handleRes )
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleRequest ) ;
}
closeRes = PR_Close ( outFile ) ;
if ( PR_SUCCESS ! = closeRes )
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Close ) ;
}
}
else
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Open ) ;
}
}
else
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_snprintf ) ;
}
}
if ( 0 ! = failureSum )
{
retval = __LINE__ ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , outputReports ) ;
}
return retval ;
}
/*
* * doRun
* *
* * Perform the actual processing this program requires .
* * Returns ! 0 on failure .
*/
int doRun ( void )
{
int retval = 0 ;
/*
* * Create the new trace - malloc reader .
*/
2002-05-08 03:39:34 +04:00
globals . mTMR = tmreader_new ( globals . mProgramName , NULL ) ;
2002-05-01 03:43:56 +04:00
if ( NULL ! = globals . mTMR )
2001-11-16 01:40:53 +03:00
{
int tmResult = 0 ;
int outputResult = 0 ;
2002-04-17 03:36:47 +04:00
# if defined(DEBUG_dp)
PRIntervalTime start = PR_IntervalNow ( ) ;
fprintf ( stderr , " DEBUG: reading tracemalloc data... \n " ) ;
# endif
2002-05-01 03:43:56 +04:00
tmResult = tmreader_eventloop ( globals . mTMR , globals . mOptions . mFileName , tmEventHandler ) ;
2002-04-17 03:36:47 +04:00
# if defined(DEBUG_dp)
fprintf ( stderr , " DEBUG: reading tracemalloc data ends: %dms [%d allocations] \n " ,
PR_IntervalToMilliseconds ( PR_IntervalNow ( ) - start ) , globals . mRun . mAllocationCount ) ;
# endif
2001-11-16 01:40:53 +03:00
if ( 0 = = tmResult )
{
REPORT_ERROR ( __LINE__ , tmreader_eventloop ) ;
retval = __LINE__ ;
}
if ( 0 = = retval )
{
2002-05-08 03:39:34 +04:00
# if ST_WANT_GRAPHS
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * May want to set the max graph timeval , now that we have it .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
if ( ST_TIMEVAL_MAX = = globals . mOptions . mGraphTimevalMax )
2001-11-16 01:40:53 +03:00
{
2001-11-29 04:29:26 +03:00
globals . mOptions . mGraphTimevalMax = ( globals . mMaxTimeval - globals . mMinTimeval ) ;
2001-11-16 01:40:53 +03:00
}
2002-05-08 03:39:34 +04:00
# endif /* ST_WANT_GRAPHS */
2001-11-16 01:40:53 +03:00
/*
* * Create the default sorted run .
*/
2002-04-17 03:36:47 +04:00
/* NS_ASSERTION(globals.mCache.mSortedRun == NULL, "mSortedRun not null"); */
2001-11-16 01:40:53 +03:00
globals . mCache . mSortedRun = createRunFromGlobal ( ) ;
if ( NULL ! = globals . mCache . mSortedRun )
{
/*
* * Decide if we ' re going into batch mode or server mode .
*/
if ( 0 ! = globals . mOptions . mBatchRequestCount )
{
/*
* * Output in one big step while everything still exists .
*/
2002-05-02 04:28:42 +04:00
outputResult = batchMode ( ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = outputResult )
{
REPORT_ERROR ( __LINE__ , batchMode ) ;
retval = __LINE__ ;
}
}
else
{
int serverRes = 0 ;
/*
* * httpd time .
*/
2002-05-02 04:28:42 +04:00
serverRes = serverMode ( ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = serverRes )
{
REPORT_ERROR ( __LINE__ , serverMode ) ;
retval = __LINE__ ;
}
}
/*
* * Done with global sorted run .
* * Check for NULL again , may have been realloced at some
* * point with failure .
*/
2002-04-17 03:36:47 +04:00
globals . mCache . mSortedRun = NULL ;
/*
* * Clear our categorization tree
*/
freeCategories ( & globals ) ;
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createRunFromGlobal ) ;
}
}
}
else
{
REPORT_ERROR ( __LINE__ , tmreader_new ) ;
retval = __LINE__ ;
}
return retval ;
}
/*
* * main
* *
* * Process entry and exit .
*/
int main ( int aArgCount , char * * aArgArray )
{
int retval = 0 ;
int optionsResult = 0 ;
PRStatus prResult = PR_SUCCESS ;
int showedHelp = 0 ;
2002-01-24 03:58:33 +03:00
int looper = 0 ;
2001-11-16 01:40:53 +03:00
/*
2002-05-02 22:38:42 +04:00
* * NSPR init .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
PR_Init ( PR_USER_THREAD , PR_PRIORITY_NORMAL , 0 ) ;
2001-11-16 01:40:53 +03:00
2002-01-09 02:20:29 +03:00
/*
* * Initialize globals
*/
2002-05-01 03:43:56 +04:00
memset ( & globals , 0 , sizeof ( globals ) ) ;
2002-01-09 02:20:29 +03:00
2002-05-08 03:39:34 +04:00
/*
* * Set the program name .
*/
globals . mProgramName = aArgArray [ 0 ] ;
2001-11-16 01:40:53 +03:00
/*
2002-05-02 22:38:42 +04:00
* * Set the minimum timeval really high so other code
* * that checks the timeval will get it right .
2001-11-16 01:40:53 +03:00
*/
2002-05-02 22:38:42 +04:00
globals . mMinTimeval = ST_TIMEVAL_MAX ;
2001-11-16 01:40:53 +03:00
/*
* * Handle initializing options .
*/
optionsResult = initOptions ( aArgCount , aArgArray ) ;
if ( 0 ! = optionsResult )
{
REPORT_ERROR ( optionsResult , initOptions ) ;
retval = __LINE__ ;
}
/*
* * Show help on usage if need be .
*/
showedHelp = showHelp ( ) ;
/*
* * Only perform the run if everything is checking out .
*/
if ( 0 = = showedHelp & & 0 = = retval )
{
int runResult = 0 ;
runResult = doRun ( ) ;
if ( 0 ! = runResult )
{
REPORT_ERROR ( runResult , doRun ) ;
retval = __LINE__ ;
}
}
if ( 0 ! = retval )
{
REPORT_ERROR ( retval , main ) ;
}
2002-05-01 03:43:56 +04:00
/*
* * Have NSPR join all client threads we started .
*/
2001-11-16 01:40:53 +03:00
prResult = PR_Cleanup ( ) ;
if ( PR_SUCCESS ! = prResult )
{
REPORT_ERROR ( retval , PR_Cleanup ) ;
retval = __LINE__ ;
}
2002-05-01 03:43:56 +04:00
/*
* * Once threads are dead , we are safe to kill our tmreader data .
*/
if ( NULL ! = globals . mTMR )
{
tmreader_destroy ( globals . mTMR ) ;
globals . mTMR = NULL ;
}
2001-11-16 01:40:53 +03:00
return retval ;
}