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
* IS " basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* 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 */
/*
* * Turn on to attempt adding support for graphs on your platform .
*/
# if defined(HAVE_BOUTELL_GD)
# define WANT_GRAPHS 1
# endif /* HAVE_BOUTELL_GD */
# if !defined(WANT_GRAPHS)
# define WANT_GRAPHS 0
# endif
/*
* * Turn on to add the ability to quit the server from the client .
* * A dubious feature at best .
*/
# define WANT_QUIT 0
/*
* * the globals variables . happy joy .
*/
static STGlobals globals ;
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 ;
if ( 0 ! = globals . mOptions . mShowHelp )
{
PR_fprintf ( PR_STDOUT ,
" Usage: \t %s [OPTION]... [-|filename] \n \n " ,
globals . mOptions . mProgramName ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" OPTIONS: \n "
" -h Show this help. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -p<port> Listen for http requests on the specified <port>. \n "
" Default port is '1969'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -d<dir> Place -b output in <dir>. \n "
" The directory must exist. \n "
" The default directory is '.'. \n "
" Very important to not have a trailing slash! \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -b<filepath> Execute in batch mode, multiple -b's allowed. \n "
" Save <filepath> into -d<dir>, then exit. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -l<max> Set the maximum number of items to display in a list. \n "
" The default <max> is '500'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -o<num> Sets the order in which lists are sorted when displayed. \n "
2001-11-29 04:29:26 +03:00
" '0' is by weight (lifespan * byte size). \n "
2001-11-16 01:40:53 +03:00
" '1' is by byte size. \n "
2001-11-29 04:29:26 +03:00
" '2' is by time (lifetime). \n "
2001-11-16 01:40:53 +03:00
" By default, <num> is '0'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -smin<num> Set the minimum byte size to exclude smaller allocations. \n "
" The default <num> is '0'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -smax<num> Set the maximum byte size to exclude larger allocations. \n "
" By default, there is no maximum. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT ,
2001-11-29 04:29:26 +03:00
" -tmin<num> Set the minimum allocation lifetime in seconds. \n "
" Excludes allocations which do not live at least said seconds. \n "
" The default <num> is '%u' seconds. \n "
" \n " , ST_DEFAULT_LIFETIME_MIN ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -tmax<num> Set the maximum allocation lifetime in seconds. \n "
" Excludes allocations which live longer than the said seconds. \n "
2001-11-16 01:40:53 +03:00
" By default, there is no maximum. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -wmin<num> Set the minimum allocation weight. \n "
2001-11-29 04:29:26 +03:00
" Weight is lifespan * byte size. \n "
2001-11-16 01:40:53 +03:00
" Excludes allocations which do not have the weight. \n "
" The default <num> is '0'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
" -wmax<num> Set the maximum allocation weight. \n "
2001-11-29 04:29:26 +03:00
" Weight is lifespan * byte size. \n "
2001-11-16 01:40:53 +03:00
" Excludes allocations which are over weight. \n "
" By default, there is no maximum. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -imin<num> Set the minimum in seconds. \n "
" Excludes allocations existing solely before said second. \n "
2001-11-16 01:40:53 +03:00
" The default <num> is '0'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -imax<num> Set the maximum in seconds. \n "
" Excludes allocations existing solely after said second. \n "
2001-11-16 01:40:53 +03:00
" By default, there is no maximum. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -amin<num> Set the allocation minimum in seconds. \n "
" Excludes allocations created before said second. \n "
2001-11-16 01:40:53 +03:00
" The default <num> is '0'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -amax<num> Set the allocation maximum in seconds. \n "
" Excludes allocations created after said second. \n "
2001-11-16 01:40:53 +03:00
" By default, there is no maximum. \n "
" \n " ) ;
# if WANT_GRAPHS
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -gmin<num> Set the graph minimum in seconds. \n "
" Excludes representing graph intervals before said second. \n "
2001-11-16 01:40:53 +03:00
" The default <num> is '0'. \n "
" \n " ) ;
PR_fprintf ( PR_STDOUT , " %s " ,
2001-11-29 04:29:26 +03:00
" -gmax<num> Set the graph maximum in seconds. \n "
" Excludes representing graph intervals after said second. \n "
2001-11-16 01:40:53 +03:00
" By default, there is no maximum. \n "
" \n " ) ;
# endif /* WANT_GRAPHS */
2001-11-29 04:29:26 +03:00
PR_fprintf ( PR_STDOUT ,
" -a<num> Set an allocation alignment boundry. \n "
" All allocations are a factor of <num>. \n "
" Meaning, an allocation of 1 byte would actually count as <num> bytes. \n "
" Set <num> to '1' in order to see the actual allocation sizes. \n "
" By default, <num> is %u. \n "
" \n " , ST_DEFAULT_ALIGNMENT_SIZE ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( PR_STDOUT , " %s " ,
" -c<text> Restrict callsite backtraces to only those containing <text>. \n "
" Allows targeting of specific object creation methods. \n "
" By default, there is no <text> restriction. \n "
" \n " ) ;
/*
* * Showed something .
*/
retval = __LINE__ ;
}
return retval ;
}
/*
* * initOptions
* *
* * Determine global settings for the application .
* * Returns 0 on success .
*/
int initOptions ( int aArgCount , char * * aArgArray )
{
int retval = 0 ;
int traverse = 0 ;
const char * stdinDash = " - " ;
const char * outputDir = " . " ;
const PRUint32 httpdPort = 1969 ;
const PRUint32 listItemMax = 500 ;
PRStatus prStatus = PR_SUCCESS ;
/*
* * Set the program name .
*/
globals . mOptions . mProgramName = aArgArray [ 0 ] ;
/*
* * As a default , stdin is the input .
*/
globals . mOptions . mFileName = stdinDash ;
/*
* * As a default , this directory is the output .
*/
globals . mOptions . mOutputDir = outputDir ;
/*
* * As a default , this is the port to listen for http requests .
*/
globals . mOptions . mHttpdPort = httpdPort ;
/*
* * As a default , the number of list items to limit display to .
*/
globals . mOptions . mListItemMax = listItemMax ;
/*
2001-11-29 04:29:26 +03:00
* * As a default , there is no maximum timeval on the dataset .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
globals . mOptions . mTimevalMax = ST_TIMEVAL_MAX ;
2001-11-16 01:40:53 +03:00
/*
* * As a default , there is no maximum allocation size on the dataset .
*/
globals . mOptions . mSizeMax = ( PRUint32 ) - 1 ;
/*
* * As a default , want to look at allocations which live at least . . .
*/
2001-11-29 04:29:26 +03:00
globals . mOptions . mLifetimeMin = ST_DEFAULT_LIFETIME_MIN * ST_TIMEVAL_RESOLUTION ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * As a default , there is no maximum allocation lifetime timeval .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
globals . mOptions . mLifetimeMax = ST_TIMEVAL_MAX ;
2001-11-16 01:40:53 +03:00
/*
* * As a default , there is no maximum weight allowed .
*/
globals . mOptions . mWeightMax64 = LL_INIT ( 0xFFFFFFFF , 0xFFFFFFFF ) ;
/*
2001-11-29 04:29:26 +03:00
* * As a default , there is no maximum allocation timeval .
*/
globals . mOptions . mAllocationTimevalMax = ST_TIMEVAL_MAX ;
/*
* * As a default , there is no maximum graph timeval .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
globals . mOptions . mGraphTimevalMax = ST_TIMEVAL_MAX ;
2001-11-16 01:40:53 +03:00
/*
2001-11-29 04:29:26 +03:00
* * As a default , we align byte sizes to a particular size .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
globals . mOptions . mAlignBy = ST_DEFAULT_ALIGNMENT_SIZE ;
2001-11-16 01:40:53 +03:00
/*
* * Go through all arguments .
* * If argument does not being with a dash it is a file name .
* * If argument does begin with a dash but is only a dash
* * it means input comes from stdin .
* * If argument begins with a dash and does not end , then it
* * maybe an option .
*/
for ( traverse = 1 ; traverse < aArgCount ; traverse + + )
{
if ( ' - ' = = aArgArray [ traverse ] [ 0 ] )
{
/*
* * Regular dash options .
* * Detect what to do .
*/
switch ( tolower ( aArgArray [ traverse ] [ 1 ] ) )
{
case ' \0 ' :
{
/*
* * If the entire option is a dash ,
* * then input is stdin .
*/
globals . mOptions . mFileName = stdinDash ;
}
break ;
case ' h ' :
{
/*
* * Help .
*/
globals . mOptions . mShowHelp = __LINE__ ;
}
break ;
case ' p ' :
{
PRInt32 scanRes = 0 ;
/*
* * Port .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 2 ] , " %u " , & globals . mOptions . mHttpdPort ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' d ' :
{
/*
* * Where to stick the ' - b ' output .
*/
if ( ' \0 ' ! = aArgArray [ traverse ] [ 2 ] )
{
globals . mOptions . mOutputDir = & aArgArray [ traverse ] [ 2 ] ;
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' b ' :
{
/*
* * Batch mode request .
*/
if ( ' \0 ' ! = aArgArray [ traverse ] [ 2 ] )
{
const char * * expand = NULL ;
/*
* * Increase size of batch buffer .
*/
2001-11-29 04:29:26 +03:00
expand = ( const char * * ) realloc ( ( void * ) globals . mOptions . mBatchRequests , sizeof ( const char * ) * ( globals . mOptions . mBatchRequestCount + 1 ) ) ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = expand )
{
/*
* * Reassign in case of pointer move .
*/
globals . mOptions . mBatchRequests = expand ;
/*
* * Add new entry , increase the count .
*/
globals . mOptions . mBatchRequests [ globals . mOptions . mBatchRequestCount ] = & aArgArray [ traverse ] [ 2 ] ;
globals . mOptions . mBatchRequestCount + + ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , realloc ) ;
}
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' l ' :
{
PRInt32 scanRes = 0 ;
/*
* * List item max .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 2 ] , " %u " , & globals . mOptions . mListItemMax ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' i ' :
{
if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " min " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
2001-11-29 04:29:26 +03:00
* * Timeval min .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mTimevalMin ) ;
2001-11-16 01:40:53 +03:00
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mTimevalMin * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " max " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
2001-11-29 04:29:26 +03:00
* * Timeval max
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mTimevalMax ) ;
2001-11-16 01:40:53 +03:00
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mTimevalMax * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' a ' :
{
if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " min " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
2001-11-29 04:29:26 +03:00
* * Allocation Timeval min .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mAllocationTimevalMin ) ;
2001-11-16 01:40:53 +03:00
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mAllocationTimevalMin * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " max " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
2001-11-29 04:29:26 +03:00
* * Allocation timeval max
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mAllocationTimevalMax ) ;
2001-11-16 01:40:53 +03:00
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mAllocationTimevalMax * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else
{
2001-11-29 04:29:26 +03:00
PRInt32 scanRes = 0 ;
/*
* * Align by .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 2 ] , " %u " , & globals . mOptions . mAlignBy ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-16 01:40:53 +03:00
}
}
break ;
# if WANT_GRAPHS
case ' g ' :
{
if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " min " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
2001-11-29 04:29:26 +03:00
* * Graph Timeval min .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mGraphTimevalMin ) ;
2001-11-16 01:40:53 +03:00
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mGraphTimevalMin * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " max " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
2001-11-29 04:29:26 +03:00
* * Graph Timeval max
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mGraphTimevalMax ) ;
2001-11-16 01:40:53 +03:00
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mGraphTimevalMax * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
# endif /* WANT_GRAPHS */
case ' s ' :
{
if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " min " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
* * Size min .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mSizeMin ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
else if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " max " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
* * Size max .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mSizeMax ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' t ' :
{
if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " min " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
* * Lifetime min .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mLifetimeMin ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mLifetimeMin * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " max " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
* * Lifetime max .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %u " , & globals . mOptions . mLifetimeMax ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
2001-11-29 04:29:26 +03:00
else
{
globals . mOptions . mLifetimeMax * = ST_TIMEVAL_RESOLUTION ;
}
2001-11-16 01:40:53 +03:00
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' w ' :
{
if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " min " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
* * Weight min .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %llu " , & globals . mOptions . mWeightMin64 ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
else if ( 0 = = strncmp ( & aArgArray [ traverse ] [ 2 ] , " max " , 3 ) )
{
PRInt32 scanRes = 0 ;
/*
* * Weight max .
*/
scanRes = PR_sscanf ( & aArgArray [ traverse ] [ 5 ] , " %llu " , & globals . mOptions . mWeightMax64 ) ;
if ( 1 ! = scanRes )
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
case ' c ' :
{
/*
* * Restrict callsite text .
*/
if ( ' \0 ' ! = aArgArray [ traverse ] [ 2 ] )
{
if ( NULL ! = globals . mOptions . mRestrictText )
{
free ( globals . mOptions . mRestrictText ) ;
}
globals . mOptions . mRestrictText = strdup ( & aArgArray [ traverse ] [ 2 ] ) ;
if ( NULL = = globals . mOptions . mRestrictText )
{
retval = __LINE__ ;
}
}
else
{
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
}
break ;
default :
{
/*
* * Unknown option .
* * Error and show help .
*/
retval = __LINE__ ;
globals . mOptions . mShowHelp = __LINE__ ;
}
break ;
}
/*
* * Check for some type of error , so we know to break the
* * loop if need be .
*/
if ( 0 ! = retval )
{
break ;
}
}
else
{
/*
* * File to process .
*/
globals . mOptions . mFileName = aArgArray [ traverse ] ;
}
}
return retval ;
}
# if WANT_GRAPHS
/*
* * 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 ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
/*
* * 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 ;
}
}
}
# endif /* WANT_GRAPHS */
# 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 */
/*
* * 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 .
* * Unless that event is a realloc of size 0 ( odd free ) .
*/
do
{
index - - ;
retval = aAlloc - > mEvents [ index ] . mHeapSize ;
}
while ( 0 = = retval & & 0 ! = index & & TM_EVENT_REALLOC = = aAlloc - > mEvents [ index ] . mEventType ) ;
}
2001-11-29 04:29:26 +03:00
/*
* * Need to bump the result by our alignment .
* * The idea here is that an allocation actually costs you more than you
* * thought ( 1 bytes = 16 bytes ) .
*/
if ( 0 ! = retval & & 1 < globals . mOptions . mAlignBy )
{
PRUint32 mod = 0 ;
mod = retval % globals . mOptions . mAlignBy ;
if ( 0 ! = mod )
{
retval + = globals . mOptions . mAlignBy - mod ;
}
}
2001-11-16 01:40:53 +03:00
return retval ;
}
/*
* * 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__ ;
/*
* * 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 )
{
2001-11-29 04:29:26 +03:00
PRUint32 timeval = aAllocation - > mMaxTimeval - aAllocation - > mMinTimeval ;
2001-11-16 01:40:53 +03:00
PRUint32 size = byteSize ( aAllocation ) ;
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
PRUint32 eventLoop = 0 ;
2001-11-29 04:29:26 +03:00
LL_MUL ( weight64 , ( PRUint64 ) timeval , ( PRUint64 ) size ) ;
2001-11-16 01:40:53 +03:00
/*
* * First , update this run .
*/
aRun - > mStats . mSize + = size ;
2001-11-29 04:29:26 +03:00
LL_ADD ( aRun - > mStats . mTimeval64 , aRun - > mStats . mTimeval64 , ( PRUint64 ) timeval ) ;
2001-11-16 01:40:53 +03:00
LL_ADD ( aRun - > mStats . mWeight64 , aRun - > mStats . mWeight64 , weight64 ) ;
/*
* * Next , go through the events of the allocation .
*/
for ( eventLoop = 0 ; eventLoop < aAllocation - > mEventCount ; eventLoop + + )
{
/*
* * If not a free , and not a realloc of size 0. . .
*/
if ( TM_EVENT_FREE ! = aAllocation - > mEvents [ eventLoop ] . mEventType & & ( TM_EVENT_REALLOC ! = aAllocation - > mEvents [ eventLoop ] . mEventType | | 0 ! = aAllocation - > mEvents [ eventLoop ] . mHeapSize ) )
{
tmcallsite * callsite = aAllocation - > mEvents [ eventLoop ] . 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 .
* * The realloc size and weights are a bit off ,
* * as they represent the final size . . . .
* * Hmm . . . This means that if the same site
* * reallocs N times , we are N - 1 times over .
*/
callsiteRun - > mStats . mSize + = size ;
2001-11-29 04:29:26 +03:00
LL_ADD ( callsiteRun - > mStats . mTimeval64 , callsiteRun - > mStats . mTimeval64 , ( PRUint64 ) timeval ) ;
2001-11-16 01:40:53 +03:00
LL_ADD ( callsiteRun - > mStats . mWeight64 , callsiteRun - > mStats . mWeight64 , weight64 ) ;
}
callsite = callsite - > parent ;
}
}
}
}
}
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
{
methodName = tmgraphnode_name ( aCallsite - > method ) ;
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 ;
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 ) ;
int appendRes = 0 ;
/*
* * 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 .
*/
LL_MUL ( weight64 , ( PRUint64 ) bytesize , ( PRUint64 ) lifetime ) ;
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 .
*/
if ( NULL ! = globals . mOptions . mRestrictText & & ' \0 ' ! = globals . mOptions . mRestrictText [ 0 ] )
{
if ( 0 = = hasCallsiteMatch ( current - > mEvents [ 0 ] . mCallsite , globals . mOptions . mRestrictText , ST_FOLLOW_PARENTS ) )
{
continue ;
}
}
/*
* * You get here , we add to the run .
*/
appendRes = appendAllocation ( aOutRun , current ) ;
if ( 0 = = appendRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , appendAllocation ) ;
}
}
}
}
return retval ;
}
/*
* * compareAllocations
* *
* * qsort callback .
* * Compare the allocations as specified by the options .
*/
int compareAllocations ( const void * aAlloc1 , const void * aAlloc2 )
{
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 )
{
case ST_WEIGHT :
{
PRUint64 weight164 = LL_INIT ( 0 , 0 ) ;
PRUint64 weight264 = LL_INIT ( 0 , 0 ) ;
2001-11-29 04:29:26 +03:00
LL_MUL ( weight164 , ( PRUint64 ) byteSize ( alloc1 ) , ( PRUint64 ) ( alloc1 - > mMaxTimeval - alloc1 - > mMinTimeval ) ) ;
LL_MUL ( weight264 , ( PRUint64 ) byteSize ( alloc2 ) , ( PRUint64 ) ( alloc2 - > mMaxTimeval - alloc2 - > mMinTimeval ) ) ;
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 :
{
PRUint32 size1 = byteSize ( alloc1 ) ;
PRUint32 size2 = byteSize ( alloc2 ) ;
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 ;
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 )
{
qsort ( aRun - > mAllocations , aRun - > mAllocationCount , sizeof ( STAllocation * ) , compareAllocations ) ;
}
}
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 )
{
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__ ;
}
if ( 0 ! = failure )
{
freeRun ( retval ) ;
retval = NULL ;
REPORT_ERROR ( failure , createRunFromGlobal ) ;
}
}
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 ;
/*
* * Can not be a realloc of size zero ( free ) .
* * Fall through if OK .
*/
case TM_EVENT_REALLOC :
{
if ( 0 = = event - > mHeapSize )
{
break ;
}
}
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 .
*/
if ( TM_EVENT_FREE = = aEventType | | ( TM_EVENT_REALLOC = = aEventType & & 0 = = aHeapSize ) )
{
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-11-29 04:29:26 +03:00
STAllocation * allocationTracker ( PRUint32 aTimeval , char aType , 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 ;
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 + + ;
/*
* * 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 + + ;
/*
* * This will be a new allocation .
*/
newAllocation = __LINE__ ;
}
break ;
case TM_EVENT_CALLOC :
{
/*
* * Update the global counter .
*/
globals . mCallocCount + + ;
/*
* * This will be a new allocation .
*/
newAllocation = __LINE__ ;
}
break ;
case TM_EVENT_REALLOC :
{
/*
* * Update the global counter .
*/
globals . mReallocCount + + ;
/*
* * 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 ;
/*
* * 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-11-29 04:29:26 +03:00
void trackEvent ( PRUint32 aTimeval , char aType , 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-11-29 04:29:26 +03:00
allocation = allocationTracker ( aTimeval , aType , 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 :
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 04:29:26 +03:00
static PRIntervalTime sec2int = 0 ;
PRUint32 timeval = 0 ;
PRUint64 timeval64 = LL_INIT ( 0 , 0 ) ;
if ( 0 = = sec2int )
{
sec2int = PR_SecondsToInterval ( 1 ) ;
}
/*
* * Convert the interval to a timeval .
*/
LL_MUL ( timeval64 , ( PRUint64 ) aEvent - > u . alloc . interval , ( PRUint64 ) ST_TIMEVAL_RESOLUTION ) ;
LL_DIV ( timeval64 , timeval64 , ( PRUint64 ) sec2int ) ;
LL_L2UI ( timeval , timeval64 ) ;
trackEvent ( timeval , aEvent - > type , 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 .
*/
void htmlAnchor ( const char * aHref , const char * aText )
{
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 + + )
{
comparison = strcmp ( aHref , globals . mOptions . mBatchRequests [ loop ] ) ;
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 .
*/
if ( 0 ! = anchorLive & & NULL ! = globals . mRequest . mFileName )
{
if ( 0 = = strcmp ( aHref , globals . mRequest . mFileName ) )
{
anchorLive = 0 ;
}
}
/*
* * Do the right thing .
*/
if ( 0 ! = anchorLive )
{
PR_fprintf ( globals . mRequest . mFD , " <a href= \" ./%s \" >%s</a> \n " , aHref , aText ) ;
}
else
{
PR_fprintf ( globals . mRequest . mFD , " %s \n " , aText ) ;
}
}
else
{
REPORT_ERROR ( __LINE__ , htmlAnchor ) ;
}
}
/*
* * htmlAllocationAnchor
* *
* * Output an html achor that will resolve to the allocation in question .
*/
void htmlAllocationAnchor ( STAllocation * aAllocation , const char * aText )
{
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 ) ;
htmlAnchor ( buffer , aText ) ;
}
else
{
REPORT_ERROR ( __LINE__ , htmlAllocationAnchor ) ;
}
}
/*
* * 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 .
*/
void htmlCallsiteAnchor ( tmcallsite * aCallsite , const char * aText , int aRealName )
{
if ( NULL ! = aCallsite )
{
char textBuf [ 256 ] ;
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 ;
if ( NULL ! = namesite - > method )
{
methodName = tmgraphnode_name ( namesite - > method ) ;
}
else
{
methodName = " ==NONAME== " ;
}
PR_snprintf ( textBuf , sizeof ( textBuf ) , " %s+%u(%u) " , methodName , namesite - > offset , ( PRUint32 ) namesite - > entry . key ) ;
aText = textBuf ;
}
PR_snprintf ( hrefBuf , sizeof ( hrefBuf ) , " callsite_%u.html " , ( PRUint32 ) aCallsite - > entry . key ) ;
htmlAnchor ( hrefBuf , aText ) ;
}
else
{
REPORT_ERROR ( __LINE__ , htmlCallsiteAnchor ) ;
}
}
/*
* * htmlHeader
* *
* * Output a standard header in the report files .
*/
void htmlHeader ( const char * aTitle )
{
PR_fprintf ( globals . mRequest . mFD ,
" <html> \n "
" <head> \n "
" <title>%s</title> \n "
" </head> \n "
" <body> \n "
" <div align=right> \n "
, aTitle ) ;
htmlAnchor ( " index.html " , " [Index] " ) ;
htmlAnchor ( " options.html " , " [Options] " ) ;
/*
* * This is a dubious feature at best .
*/
# if WANT_QUIT
htmlAnchor ( " quit.html " , " [Quit] " ) ;
# endif
PR_fprintf ( globals . mRequest . mFD , " </div> \n <hr> \n " ) ;
}
/*
* * htmlFooter
* *
* * Output a standard footer in the report file .
*/
void htmlFooter ( void )
{
PR_fprintf ( globals . mRequest . mFD ,
" <hr> \n "
" <div align=right> \n "
" <i>SpaceTrace</i> \n "
" </div> \n "
" </body> \n "
" </html> \n "
) ;
}
/*
* * htmlNotFound
* *
* * Not found message .
*/
void htmlNotFound ( void )
{
htmlHeader ( " File Not Found " ) ;
PR_fprintf ( globals . mRequest . mFD , " File Not Found \n " ) ;
htmlFooter ( ) ;
}
/*
* * 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 + + )
{
/*
* * Skip the free events ( or realloc by zero ) .
*/
if ( TM_EVENT_FREE ! = aRun - > mAllocations [ allocLoop ] - > mEvents [ eventLoop ] . mEventType & & ( TM_EVENT_REALLOC ! = aRun - > mAllocations [ allocLoop ] - > mEvents [ eventLoop ] . mEventType | | 0 ! = aRun - > mAllocations [ allocLoop ] - > mEvents [ eventLoop ] . mHeapSize ) )
{
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 .
*/
int getDataString ( const char * aGetData , const char * aCheckFor , char * * aStoreResult , int * aChanged )
{
int retval = 0 ;
if ( NULL ! = aGetData & & NULL ! = aCheckFor & & NULL ! = aStoreResult )
{
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 ;
char * oldResult = NULL ;
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 .
*/
if ( NULL ! = * aStoreResult )
{
oldResult = * aStoreResult ;
}
/*
* * Allocate space for new string .
*/
2001-11-29 04:29:26 +03:00
theLen = ( int ) ( endPoint - dataPoint ) ;
* aStoreResult = ( char * ) malloc ( ( size_t ) ( theLen + 1 ) ) ;
2001-11-16 01:40:53 +03:00
if ( NULL ! = * aStoreResult )
{
2001-11-29 04:29:26 +03:00
int index1 = 0 ;
int index2 = 0 ;
strncpy ( * aStoreResult , dataPoint , theLen ) ;
( * aStoreResult ) [ theLen ] = ' \0 ' ;
/*
* * Here ' s a totally suboptimal and bug prone unhexcaper .
*/
for ( ; index1 < = theLen ; index1 + + )
{
if ( ' % ' = = ( * aStoreResult ) [ index1 ] & & ' \0 ' ! = ( * aStoreResult ) [ index1 + 1 ] & & ' \0 ' ! = ( * aStoreResult ) [ index1 + 2 ] )
{
int unhex = 0 ;
if ( ' 9 ' > = ( * aStoreResult ) [ index1 + 1 ] )
{
unhex | = ( ( ( * aStoreResult ) [ index1 + 1 ] - ' 0 ' ) < < 4 ) ;
}
else
{
unhex | = ( ( toupper ( ( * aStoreResult ) [ index1 + 1 ] ) - ' A ' + 10 ) < < 4 ) ;
}
if ( ' 9 ' > = ( * aStoreResult ) [ index1 + 2 ] )
{
unhex | = ( ( * aStoreResult ) [ index1 + 2 ] - ' 0 ' ) ;
}
else
{
unhex | = ( toupper ( ( * aStoreResult ) [ index1 + 2 ] ) - ' A ' + 10 ) ;
}
index1 + = 2 ;
( * aStoreResult ) [ index1 ] = unhex ;
}
( * aStoreResult ) [ index2 + + ] = ( * aStoreResult ) [ index1 ] ;
}
2001-11-16 01:40:53 +03:00
/*
* * See if the value actually changed .
*/
if ( NULL ! = aChanged )
{
if ( NULL ! = oldResult )
{
if ( 0 ! = strcmp ( oldResult , * aStoreResult ) )
{
( * aChanged ) = ( * aChanged ) + 1 ;
}
}
else if ( ' \0 ' ! = ( * aStoreResult ) [ 0 ] )
{
( * aChanged ) = ( * aChanged ) + 1 ;
}
}
/*
* * Free off the prior value if relevant .
*/
if ( NULL ! = oldResult )
{
free ( oldResult ) ;
oldResult = NULL ;
}
}
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 .
*/
int displayTopAllocations ( STRun * aRun , int aWantCallsite )
{
int retval = 0 ;
if ( NULL ! = aRun )
{
if ( 0 < aRun - > mAllocationCount )
{
PRUint32 loop = 0 ;
STAllocation * current = NULL ;
PR_fprintf ( globals . mRequest . mFD , " <table border=1> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Rank</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Index</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Byte Size</b></td> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td><b>Lifespan Seconds</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Weight</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = aWantCallsite )
{
PR_fprintf ( globals . mRequest . mFD , " <td><b>Origin Callsite</b></td> \n " ) ;
}
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
/*
* * 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 ) ;
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
char buffer [ 32 ] ;
LL_MUL ( weight64 , ( PRUint64 ) size , ( PRUint64 ) lifespan ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
/*
* * Rank .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right>%u</td> \n " , loop + 1 ) ;
/*
* * Index .
*/
PR_snprintf ( buffer , sizeof ( buffer ) , " %u " , current - > mRunIndex ) ;
PR_fprintf ( globals . mRequest . mFD , " <td align=right> \n " ) ;
htmlAllocationAnchor ( current , buffer ) ;
PR_fprintf ( globals . mRequest . mFD , " </td> \n " ) ;
/*
* * Byte Size .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right>%u</td> \n " , size ) ;
/*
* * Lifespan .
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td align=right> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE ( lifespan ) ) ;
/*
* * Weight .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right>%llu</td> \n " , weight64 ) ;
2001-11-16 01:40:53 +03:00
if ( 0 ! = aWantCallsite )
{
/*
* * Callsite .
*/
PR_fprintf ( globals . mRequest . mFD , " <td> " ) ;
htmlCallsiteAnchor ( current - > mEvents [ 0 ] . mCallsite , NULL , 0 ) ;
PR_fprintf ( globals . mRequest . mFD , " </td> \n " ) ;
}
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
}
}
PR_fprintf ( globals . mRequest . mFD , " </table> \n " ) ;
}
}
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 .
*/
int displayMemoryLeaks ( STRun * aRun )
{
int retval = 0 ;
if ( NULL ! = aRun )
{
PRUint32 loop = 0 ;
PRUint32 displayed = 0 ;
STAllocation * current = NULL ;
PR_fprintf ( globals . mRequest . mFD , " <table border=1> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Rank</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Index</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Byte Size</b></td> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td><b>Lifespan Seconds</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Weight</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td><b>Origin Callsite</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
/*
* * 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 .
* *
* * A free operation is just that , a free , or it can also
* * be a realloc of size 0.
*/
if ( TM_EVENT_FREE ! = current - > mEvents [ current - > mEventCount - 1 ] . mEventType & & ( TM_EVENT_REALLOC ! = current - > mEvents [ current - > mEventCount - 1 ] . mEventType | | 0 ! = current - > mEvents [ current - > mEventCount - 1 ] . mHeapSize ) )
{
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 ) ;
PRUint64 weight64 = LL_INIT ( 0 , 0 ) ;
char buffer [ 32 ] ;
LL_MUL ( weight64 , ( PRUint64 ) size , ( PRUint64 ) lifespan ) ;
/*
* * One more shown .
*/
displayed + + ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
/*
* * Rank .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right>%u</td> \n " , displayed ) ;
/*
* * Index .
*/
PR_snprintf ( buffer , sizeof ( buffer ) , " %u " , current - > mRunIndex ) ;
PR_fprintf ( globals . mRequest . mFD , " <td align=right> \n " ) ;
htmlAllocationAnchor ( current , buffer ) ;
PR_fprintf ( globals . mRequest . mFD , " </td> \n " ) ;
/*
* * Byte Size .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right>%u</td> \n " , size ) ;
/*
* * Lifespan .
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td align=right> " ST_TIMEVAL_FORMAT " </td> \n " , ST_TIMEVAL_PRINTABLE ( lifespan ) ) ;
/*
* * Weight .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right>%llu</td> \n " , weight64 ) ;
2001-11-16 01:40:53 +03:00
/*
* * Callsite .
*/
PR_fprintf ( globals . mRequest . mFD , " <td> " ) ;
htmlCallsiteAnchor ( current - > mEvents [ 0 ] . mCallsite , NULL , 0 ) ;
PR_fprintf ( globals . mRequest . mFD , " </td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
}
}
}
PR_fprintf ( globals . mRequest . mFD , " </table> \n " ) ;
}
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 .
*/
int displayCallsites ( tmcallsite * aCallsite , int aFollow , PRUint32 aStamp , int aRealNames )
{
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__ ;
PR_fprintf ( globals . mRequest . mFD , " <table border=1> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Callsite</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Composite Byte Size</b></td> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td><b>Composite Seconds</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Composite Weight</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
}
/*
* * Output the information .
*/
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
/*
* * Method name .
*/
PR_fprintf ( globals . mRequest . mFD , " <td> " ) ;
htmlCallsiteAnchor ( aCallsite , NULL , aRealNames ) ;
PR_fprintf ( globals . mRequest . mFD , " </td> " ) ;
/*
2001-11-29 04:29:26 +03:00
* * Byte Size .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . 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
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . 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
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td valign=top align=right>%llu</td> \n " , run - > mStats . mWeight64 ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
}
}
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 )
{
PR_fprintf ( globals . mRequest . mFD , " </table> \n " ) ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
}
return retval ;
}
/*
* * displayAllocationDetails
* *
* * Report what we know about the allocation .
* *
* * Returns ! 0 on error .
*/
int displayAllocationDetails ( STAllocation * aAllocation )
{
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-11-16 01:40:53 +03:00
PRUint64 weight64 = 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-11-29 04:29:26 +03:00
LL_MUL ( weight64 , ( PRUint64 ) bytesize , ( PRUint64 ) timeval ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " Allocation %u Details:<p> \n " , aAllocation - > mRunIndex ) ;
PR_fprintf ( globals . mRequest . mFD , " <table> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr><td align=left>Final Size:</td><td align=right>%u</td></tr> \n " , bytesize ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <tr><td align=left>Lifespan Seconds:</td><td align=right> " ST_TIMEVAL_FORMAT " </td></tr> \n " , ST_TIMEVAL_PRINTABLE ( timeval ) ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr><td align=left>Weight:</td><td align=right>%llu</td></tr> \n " , weight64 ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </table><p> \n " ) ;
/*
* * The events .
*/
PR_fprintf ( globals . mRequest . mFD , " %u Life Event(s):<br> \n " , aAllocation - > mEventCount ) ;
PR_fprintf ( globals . mRequest . mFD , " <table border=1> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Operation</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Size</b></td> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td><b>Seconds</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
for ( traverse = 0 ; traverse < aAllocation - > mEventCount & & traverse < globals . mOptions . mListItemMax ; traverse + + )
{
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
/*
* * count .
*/
PR_fprintf ( globals . mRequest . mFD , " <td valign=top align=right>%u.</td> \n " , traverse + 1 ) ;
/*
* * Operation .
*/
PR_fprintf ( globals . mRequest . mFD , " <td valign=top> " ) ;
switch ( aAllocation - > mEvents [ traverse ] . mEventType )
{
case TM_EVENT_CALLOC :
PR_fprintf ( globals . mRequest . mFD , " calloc " ) ;
break ;
case TM_EVENT_FREE :
PR_fprintf ( globals . mRequest . mFD , " free " ) ;
break ;
case TM_EVENT_MALLOC :
PR_fprintf ( globals . mRequest . mFD , " malloc " ) ;
break ;
case TM_EVENT_REALLOC :
PR_fprintf ( globals . mRequest . mFD , " realloc " ) ;
break ;
default :
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
break ;
}
PR_fprintf ( globals . mRequest . mFD , " </td> " ) ;
/*
* * Size .
*/
PR_fprintf ( globals . mRequest . mFD , " <td valign=top align=right>%u</td> \n " , aAllocation - > mEvents [ traverse ] . mHeapSize ) ;
/*
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 ;
PR_fprintf ( globals . mRequest . 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 .
*/
PR_fprintf ( globals . mRequest . mFD , " <td valign=top> \n " ) ;
if ( 0 = = traverse )
{
displayRes = displayCallsites ( aAllocation - > mEvents [ traverse ] . mCallsite , ST_FOLLOW_PARENTS , 0 , __LINE__ ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsite ) ;
}
}
PR_fprintf ( globals . mRequest . mFD , " </td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
}
PR_fprintf ( globals . mRequest . mFD , " </table><p> \n " ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
}
return retval ;
}
/*
* * compareCallsites
* *
* * qsort callback .
* * Compare the callsites as specified by the options .
* * There must be NO equal callsites , unless they erally are duplicates ,
* * this is so that a duplicate detector loop can
* * simply skip sorted items until the callsite is different .
*/
int compareCallsites ( const void * aSite1 , const void * aSite2 )
{
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 ;
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 .
*/
int displayTopCallsites ( tmcallsite * * aCallsites , PRUint32 aCallsiteCount , PRUint32 aStamp , int aRealName )
{
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 .
*/
qsort ( aCallsites , aCallsiteCount , sizeof ( tmcallsite * ) , compareCallsites ) ;
/*
* * 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__ ;
PR_fprintf ( globals . mRequest . mFD , " <table border=1> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Rank</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Callsite</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Composite Size</b></td> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td><b>Composite Seconds</b></td> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <td><b>Composite Weight</b></td> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
}
displayed + + ;
PR_fprintf ( globals . mRequest . mFD , " <tr> \n " ) ;
/*
* * Rank .
*/
PR_fprintf ( globals . mRequest . mFD , " <td align=right valign=top>%u</td> \n " , displayed ) ;
/*
* * Method .
*/
PR_fprintf ( globals . mRequest . mFD , " <td> " ) ;
htmlCallsiteAnchor ( site , NULL , aRealName ) ;
PR_fprintf ( globals . mRequest . mFD , " </td> \n " ) ;
/*
2001-11-29 04:29:26 +03:00
* * Size .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . 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
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . 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
*/
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <td align=right valign=top>%llu</td> \n " , run - > mStats . mWeight64 ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </tr> \n " ) ;
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 )
{
PR_fprintf ( globals . mRequest . mFD , " </table> \n " ) ;
}
}
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 .
*/
int displayCallsiteDetails ( tmcallsite * aCallsite )
{
int retval = 0 ;
if ( NULL ! = aCallsite & & NULL ! = aCallsite - > method )
{
STRun * sortedRun = NULL ;
STRun * thisRun = CALLSITE_RUN ( aCallsite ) ;
PR_fprintf ( globals . mRequest . mFD , " %s+%u(%u) Callsite Details:<p> \n " , tmgraphnode_name ( aCallsite - > method ) , aCallsite - > offset , ( PRUint32 ) aCallsite - > entry . key ) ;
PR_fprintf ( globals . mRequest . mFD , " <table border=0> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr><td>Composite Byte Size:</td><td align=right>%u</td></tr> \n " , thisRun - > mStats . mSize ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <tr><td>Composite Seconds:</td><td align=right> " ST_TIMEVAL_FORMAT " </td></tr> \n " , ST_TIMEVAL_PRINTABLE64 ( thisRun - > mStats . mTimeval64 ) ) ;
PR_fprintf ( globals . mRequest . mFD , " <tr><td>Composite Weight:</td><td align=right>%llu</td></tr> \n " , thisRun - > mStats . mWeight64 ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </table> \n <p> \n " ) ;
/*
* * 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 .
*/
PR_fprintf ( globals . mRequest . mFD , " Children Callsites:<br> \n " ) ;
displayRes = displayTopCallsites ( sites , siteCount , 0 , __LINE__ ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopCallsites ) ;
}
PR_fprintf ( globals . mRequest . mFD , " <p> \n " ) ;
/*
* * Done with array .
*/
free ( sites ) ;
sites = NULL ;
}
}
/*
* * Parents ( those who call us ) :
*/
if ( NULL ! = aCallsite - > parent & & NULL ! = aCallsite - > parent - > method )
{
int displayRes = 0 ;
PR_fprintf ( globals . mRequest . mFD , " Parent Callsites:<br> \n " ) ;
displayRes = displayCallsites ( aCallsite - > parent , ST_FOLLOW_PARENTS , 0 , __LINE__ ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
}
PR_fprintf ( globals . mRequest . mFD , " <p> \n " ) ;
}
/*
* * 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 ;
PR_fprintf ( globals . mRequest . mFD , " Allocations:<br> \n " ) ;
displayRes = displayTopAllocations ( sortedRun , 0 ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopAllocations ) ;
}
PR_fprintf ( globals . mRequest . mFD , " <p> \n " ) ;
}
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 ;
}
# if WANT_GRAPHS
/*
* * 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 .
*/
int graphFootprint ( STRun * aRun )
{
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 ) ;
}
}
theSink . context = globals . mRequest . mFD ;
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphFootprint ) ;
}
return retval ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
/*
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 .
*/
2001-11-29 04:29:26 +03:00
int graphTimeval ( 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 ) ;
}
}
theSink . context = globals . mRequest . mFD ;
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 ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
/*
* * 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 .
*/
int graphLifespan ( STRun * aRun )
{
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 ) ;
}
}
theSink . context = globals . mRequest . mFD ;
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphLifespan ) ;
}
return retval ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
/*
* * 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 .
*/
int graphWeight ( STRun * aRun )
{
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 ) ;
}
}
theSink . context = globals . mRequest . mFD ;
theSink . sink = pngSink ;
gdImagePngToSink ( graph , & theSink ) ;
gdImageDestroy ( graph ) ;
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createGraph ) ;
}
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphWeight ) ;
}
return retval ;
}
# endif /* WANT_GRAPHS */
/*
* * displaySettings
* *
* * Present the settings for change during execution .
* * Returns ! 0 on error .
* *
* * Changes are effected via the get data .
*/
int displaySettings ( void )
{
int retval = 0 ;
2001-11-29 04:29:26 +03:00
PRUint32 cached = 0 ;
2001-11-16 01:40:53 +03:00
/*
* * If we ' ve got get data , we need to attempt to enact the changes .
* * That way , when we show the page , it will have the new changes .
*/
if ( NULL ! = globals . mRequest . mGetData & & ' \0 ' ! = * globals . mRequest . mGetData )
{
int getRes = 0 ;
int changedSet = 0 ;
int changedOrder = 0 ;
int changedGraph = 0 ;
int changedDontCare = 0 ;
2001-11-29 04:29:26 +03:00
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mListItemMax " , & globals . mOptions . mListItemMax , & changedDontCare , 1 ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mTimevalMin " , & globals . mOptions . mTimevalMin , & changedSet , ST_TIMEVAL_RESOLUTION ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mTimevalMax " , & globals . mOptions . mTimevalMax , & changedSet , ST_TIMEVAL_RESOLUTION ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mAllocationTimevalMin " , & globals . mOptions . mAllocationTimevalMin , & changedSet , ST_TIMEVAL_RESOLUTION ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mAllocationTimevalMax " , & globals . mOptions . mAllocationTimevalMax , & changedSet , ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
# if WANT_GRAPHS
2001-11-29 04:29:26 +03:00
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mGraphTimevalMin " , & globals . mOptions . mGraphTimevalMin , & changedGraph , ST_TIMEVAL_RESOLUTION ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mGraphTimevalMax " , & globals . mOptions . mGraphTimevalMax , & changedGraph , ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
# endif /* WANT_GRAPHS */
2001-11-29 04:29:26 +03:00
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mSizeMin " , & globals . mOptions . mSizeMin , & changedSet , 1 ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mSizeMax " , & globals . mOptions . mSizeMax , & changedSet , 1 ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mAlignBy " , & globals . mOptions . mAlignBy , & changedSet , 1 ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mOrderBy " , & globals . mOptions . mOrderBy , & changedOrder , 1 ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mLifetimeMin " , & globals . mOptions . mLifetimeMin , & changedSet , ST_TIMEVAL_RESOLUTION ) ;
getRes + = getDataPRUint32 ( globals . mRequest . mGetData , " mLifetimeMax " , & globals . mOptions . mLifetimeMax , & changedSet , ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
getRes + = getDataPRUint64 ( globals . mRequest . mGetData , " mWeightMin " , & globals . mOptions . mWeightMin64 , & changedSet ) ;
getRes + = getDataPRUint64 ( globals . mRequest . mGetData , " mWeightMax " , & globals . mOptions . mWeightMax64 , & changedSet ) ;
getRes + = getDataString ( globals . mRequest . mGetData , " mRestrictText " , & globals . mOptions . mRestrictText , & changedSet ) ;
/*
* * Resort the global based on new prefs if needed .
*/
if ( 0 ! = changedSet | | 0 ! = changedOrder )
{
if ( NULL ! = globals . mCache . mSortedRun )
{
freeRun ( globals . mCache . mSortedRun ) ;
}
globals . mCache . mSortedRun = createRunFromGlobal ( ) ;
if ( NULL = = globals . mCache . mSortedRun )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createRunFromGlobal ) ;
}
}
# if WANT_GRAPHS
/*
* * If any of the set was changed , we need to throw away all our
* * cached graphs .
*/
if ( 0 ! = changedSet | | 0 ! = changedGraph )
{
/*
2001-11-29 04:29:26 +03:00
* * Automove the graph timeval if required .
2001-11-16 01:40:53 +03:00
*/
2001-11-29 04:29:26 +03:00
if ( ( globals . mMaxTimeval - globals . mMinTimeval ) < 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
}
globals . mCache . mFootprintCached = 0 ;
2001-11-29 04:29:26 +03:00
globals . mCache . mTimevalCached = 0 ;
2001-11-16 01:40:53 +03:00
globals . mCache . mLifespanCached = 0 ;
globals . mCache . mWeightCached = 0 ;
}
# endif /* WANT_GRAPHS */
/*
* * Report on the operation .
*/
if ( 0 ! = getRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , getDataPRUint32 ) ;
PR_fprintf ( globals . mRequest . mFD , " <blink><b>%u: There was a problem. Some changes may have been applied.</b></blink><br><hr> \n " , PR_IntervalNow ( ) ) ;
}
else
{
PR_fprintf ( globals . mRequest . mFD , " <b>%u: Your changes have been applied.</b><br><hr> \n " , PR_IntervalNow ( ) ) ;
}
}
/*
* * A small blurb regarding the options .
*/
PR_fprintf ( globals . mRequest . mFD , " NOTES:<p> \n " ) ;
2001-11-29 04:29:26 +03:00
cached = globals . mMaxTimeval - globals . mMinTimeval ;
PR_fprintf ( globals . mRequest . 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
PR_fprintf ( globals . mRequest . mFD , " All options should have command line equivalents to support batch mode.<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Changes to the options take effect immediately.<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
/*
* * We ' ve got a form to create .
*/
PR_fprintf ( globals . mRequest . mFD , " <form method=get action= \" ./options.html \" > \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Maximum number of items to display in a list?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " This option exists to control how much information you are willing to accept.<p> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mListItemMax \" value= \" %u \" ><br> \n " , globals . mOptions . mListItemMax ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " This option controls the sort order of the lists presented.<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " There are 3 choices:<br> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <ul><li>0 is by weight (byte size * seconds).<li>1 is by byte size.<li>2 is by seconds (lifetime).</ul><p> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " Desired sort order?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mOrderBy \" value= \" %u \" ><br> \n " , globals . mOptions . mOrderBy ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
# if WANT_GRAPHS
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " Modify the seconds for which the graphs cover;<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " meaning that a narrower range will produce a more detailed graph for that timespan.<p> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Minimum graph second?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mGraphTimevalMin \" value= \" %u \" ><br> \n " , globals . mOptions . mGraphTimevalMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( globals . mRequest . mFD , " Maximum graph second?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mGraphTimevalMax \" value= \" %u \" ><br> \n " , globals . mOptions . mGraphTimevalMax / ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
# endif /* WANT_GRAPHS */
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " Modify the secondss to target allocations created during a particular timespan;<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " meaning that the allocations created only within the timespan are of interest.<p> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Minimum allocation second?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mAllocationTimevalMin \" value= \" %u \" ><br> \n " , globals . mOptions . mAllocationTimevalMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( globals . mRequest . mFD , " Maximum allocation second?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mAllocationTimevalMax \" value= \" %u \" ><br> \n " , globals . mOptions . mAllocationTimevalMax / ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Modify the byte sizes to target allocations of a particular byte size.<p> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Minimum byte size?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mSizeMin \" value= \" %u \" ><br> \n " , globals . mOptions . mSizeMin ) ;
PR_fprintf ( globals . mRequest . mFD , " Maximum byte size?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mSizeMax \" value= \" %u \" ><br> \n " , globals . mOptions . mSizeMax ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " Modify the alignment boundry of allocations to see the actual impact an allocation has on a heap;<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " meaning that normally an allocation of 1 bytes actually costs more bytes depending on your heap implementation.<p> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Align by?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mAlignBy \" value= \" %u \" ><br> \n " , globals . mOptions . mAlignBy ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Modify the seconds to target allocations of a particular lifespan/duration;<br> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " meaning that the allocations existed at least or at most the specified span of time.<p> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Minimum lifetime?<br> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mLifetimeMin \" value= \" %u \" ><br> \n " , globals . mOptions . mLifetimeMin / ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " Maximum lifetime?<br> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mLifetimeMax \" value= \" %u \" ><br> \n " , globals . mOptions . mLifetimeMax / ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Modify the numbers to target allocations of particular weights;<br> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " the weight of an allocation is the byte size multiplied by the lifespan.<p> \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " Minimum weight?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mWeightMin \" value= \" %llu \" ><br> \n " , globals . mOptions . mWeightMin64 ) ;
PR_fprintf ( globals . mRequest . mFD , " Maximum weight?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mWeightMax \" value= \" %llu \" ><br> \n " , globals . mOptions . mWeightMax64 ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . 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 ( globals . mRequest . mFD , " Minimum second?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mTimevalMin \" value= \" %u \" ><br> \n " , globals . mOptions . mTimevalMin / ST_TIMEVAL_RESOLUTION ) ;
PR_fprintf ( globals . mRequest . mFD , " Maximum timeval?<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mTimevalMax \" value= \" %u \" ><br> \n " , globals . mOptions . mTimevalMax / ST_TIMEVAL_RESOLUTION ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " Restrict callsite backtraces to thost only containing the specified text. \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " This allows targeting of specific creation functions.<br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <input type=text name= \" mRestrictText \" value= \" %s \" ><br> \n " , NULL = = globals . mOptions . mRestrictText ? " " : globals . mOptions . mRestrictText ) ;
PR_fprintf ( globals . mRequest . mFD , " <hr> \n " ) ;
/*
* * And last but not least , the submission button .
*/
PR_fprintf ( globals . mRequest . mFD , " <input type=submit value= \" Submit Changes \" ><input type=reset value= \" Obligatory Reset Button \" ><br> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </form> \n " ) ;
return retval ;
}
/*
* * displayIndex
* *
* * Present a list of the reports you can drill down into .
* * Returns ! 0 on failure .
*/
int displayIndex ( void )
{
int retval = 0 ;
/*
* * Present reports in a list format .
*/
PR_fprintf ( globals . mRequest . mFD , " <ul> " ) ;
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
htmlAnchor ( " root_callsites.html " , " Root Callsites " ) ;
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
htmlAnchor ( " top_callsites.html " , " Top Callsites Report " ) ;
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
htmlAnchor ( " top_allocations.html " , " Top Allocations Report " ) ;
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
htmlAnchor ( " memory_leaks.html " , " Memory Leak Report " ) ;
# if WANT_GRAPHS
PR_fprintf ( globals . mRequest . mFD , " \n <li>Graphs " ) ;
PR_fprintf ( globals . mRequest . mFD , " <ul> " ) ;
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
htmlAnchor ( " footprint_graph.html " , " Footprint " ) ;
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
2001-11-29 04:29:26 +03:00
htmlAnchor ( " lifespan_graph.html " , " Allocation Lifespans " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
2001-11-29 04:29:26 +03:00
htmlAnchor ( " times_graph.html " , " Allocation Times " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " \n <li> " ) ;
2001-11-29 04:29:26 +03:00
htmlAnchor ( " weight_graph.html " , " Allocation Weights " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " \n </ul> \n " ) ;
# endif /* WANT_GRAPHS */
PR_fprintf ( globals . mRequest . mFD , " \n </ul> \n " ) ;
return retval ;
}
/*
* * handleRequest
* *
* * Based on what file they are asking for , perform some processing .
* * Output the results to aFD .
* *
* * Returns ! 0 on error .
*/
int handleRequest ( tmreader * aTMR , PRFileDesc * aFD , const char * aFileName , const char * aGetData )
{
int retval = 0 ;
if ( NULL ! = aTMR & & NULL ! = aFD & & NULL ! = aFileName & & ' \0 ' ! = * aFileName )
{
/*
* * Init the global request .
*/
globals . mRequest . mFD = aFD ;
globals . mRequest . mTMR = aTMR ;
globals . mRequest . mFileName = aFileName ;
globals . mRequest . mGetData = aGetData ;
/*
* * Attempt to find the file of interest .
*/
if ( 0 = = strcmp ( " index.html " , aFileName ) )
{
int displayRes = 0 ;
htmlHeader ( " SpaceTrace Index " ) ;
displayRes = displayIndex ( ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayIndex ) ;
}
htmlFooter ( ) ;
}
else if ( 0 = = strcmp ( " settings.html " , aFileName ) | | 0 = = strcmp ( " options.html " , aFileName ) )
{
int settingsRes = 0 ;
htmlHeader ( " SpaceTrace Settings " ) ;
settingsRes = displaySettings ( ) ;
if ( 0 ! = settingsRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displaySettings ) ;
}
htmlFooter ( ) ;
}
else if ( 0 = = strcmp ( " top_allocations.html " , aFileName ) )
{
int displayRes = 0 ;
htmlHeader ( " SpaceTrace Top Allocations Report " ) ;
displayRes = displayTopAllocations ( globals . mCache . mSortedRun , 1 ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopAllocations ) ;
}
htmlFooter ( ) ;
}
else if ( 0 = = strcmp ( " top_callsites.html " , aFileName ) )
{
int displayRes = 0 ;
tmcallsite * * array = NULL ;
PRUint32 arrayCount = 0 ;
htmlHeader ( " SpaceTrace Top Callsites Report " ) ;
if ( 0 < globals . mCache . mSortedRun - > mAllocationCount )
{
arrayCount = callsiteArrayFromRun ( & array , 0 , globals . mCache . mSortedRun ) ;
if ( 0 ! = arrayCount & & NULL ! = array )
{
displayRes = displayTopCallsites ( array , arrayCount , 0 , 0 ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayTopCallsites ) ;
}
/*
* * Done with the array .
*/
free ( array ) ;
array = NULL ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleRequest ) ;
}
htmlFooter ( ) ;
}
else if ( 0 = = strcmp ( " memory_leaks.html " , aFileName ) )
{
int displayRes = 0 ;
htmlHeader ( " SpaceTrace Memory Leaks Report " ) ;
displayRes = displayMemoryLeaks ( globals . mCache . mSortedRun ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayMemoryLeaks ) ;
}
htmlFooter ( ) ;
}
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 ) ;
htmlHeader ( buffer ) ;
displayRes = displayAllocationDetails ( allocation ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
}
htmlFooter ( ) ;
}
else
{
htmlNotFound ( ) ;
}
}
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 ) ;
htmlHeader ( buffer ) ;
displayRes = displayCallsiteDetails ( resolved ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayAllocationDetails ) ;
}
htmlFooter ( ) ;
}
else
{
htmlNotFound ( ) ;
}
}
else if ( 0 = = strcmp ( " root_callsites.html " , aFileName ) )
{
int displayRes = 0 ;
htmlHeader ( " SpaceTrace Root Callsites " ) ;
displayRes = displayCallsites ( aTMR - > calltree_root . kids , ST_FOLLOW_SIBLINGS , 0 , __LINE__ ) ;
if ( 0 ! = displayRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , displayCallsites ) ;
}
htmlFooter ( ) ;
}
# if WANT_GRAPHS
else if ( 0 = = strcmp ( " footprint_graph.html " , aFileName ) )
{
int displayRes = 0 ;
htmlHeader ( " SpaceTrace Memory Footprint Report " ) ;
PR_fprintf ( globals . mRequest . mFD , " <div align=center> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <img src= \" ./footprint.png \" > \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </div> \n " ) ;
htmlFooter ( ) ;
}
# endif /* WANT_GRAPHS */
# if 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 ;
2001-11-29 04:29:26 +03:00
htmlHeader ( " SpaceTrace Allocation Times Report " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <div align=center> \n " ) ;
2001-11-29 04:29:26 +03:00
PR_fprintf ( globals . mRequest . mFD , " <img src= \" ./times.png \" > \n " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " </div> \n " ) ;
htmlFooter ( ) ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
else if ( 0 = = strcmp ( " lifespan_graph.html " , aFileName ) )
{
int displayRes = 0 ;
2001-11-29 04:29:26 +03:00
htmlHeader ( " SpaceTrace Allocation Lifespans Report " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <div align=center> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <img src= \" ./lifespan.png \" > \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </div> \n " ) ;
htmlFooter ( ) ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
else if ( 0 = = strcmp ( " weight_graph.html " , aFileName ) )
{
int displayRes = 0 ;
2001-11-29 04:29:26 +03:00
htmlHeader ( " SpaceTrace Allocation Weights Report " ) ;
2001-11-16 01:40:53 +03:00
PR_fprintf ( globals . mRequest . mFD , " <div align=center> \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " <img src= \" ./weight.png \" > \n " ) ;
PR_fprintf ( globals . mRequest . mFD , " </div> \n " ) ;
htmlFooter ( ) ;
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
else if ( 0 = = strcmp ( " footprint.png " , aFileName ) )
{
int graphRes = 0 ;
graphRes = graphFootprint ( globals . mCache . mSortedRun ) ;
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphFootprint ) ;
}
}
# endif /* WANT_GRAPHS */
# if 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 ;
2001-11-29 04:29:26 +03:00
graphRes = graphTimeval ( 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
}
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
else if ( 0 = = strcmp ( " lifespan.png " , aFileName ) )
{
int graphRes = 0 ;
graphRes = graphLifespan ( globals . mCache . mSortedRun ) ;
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphLifespan ) ;
}
}
# endif /* WANT_GRAPHS */
# if WANT_GRAPHS
else if ( 0 = = strcmp ( " weight.png " , aFileName ) )
{
int graphRes = 0 ;
graphRes = graphWeight ( globals . mCache . mSortedRun ) ;
if ( 0 ! = graphRes )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , graphWeight ) ;
}
}
# endif /* WANT_GRAPHS */
# if WANT_QUIT
else if ( 0 = = strcmp ( " quit.html " , aFileName ) | | 0 = = strcmp ( " exit.html " , aFileName ) )
{
/*
* * Request to quit the server .
*/
globals . mStopHttpd = __LINE__ ;
htmlHeader ( " SpaceTrace Goodbye " ) ;
PR_fprintf ( globals . mRequest . mFD , " The server is exiting. \n " ) ;
htmlFooter ( ) ;
}
# endif /* WANT_QUIT */
else
{
htmlNotFound ( ) ;
}
/*
* * Clear out global request .
*/
memset ( & globals . mRequest , 0 , sizeof ( globals . mRequest ) ) ;
}
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
* *
* * Read the fd for the request .
* * Output the results .
* * Returns ! 0 on error .
*/
int handleClient ( tmreader * aTMR , PRFileDesc * aFD )
{
int retval = 0 ;
if ( NULL ! = aTMR & & NULL ! = aFD )
{
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 ;
int realFun = 0 ;
const char * crlf = " \015 \012 " ;
char * eoline = NULL ;
/*
* * 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 .
* *
* * 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 .
*/
PR_fprintf ( aFD , " HTTP/1.0 200%s " , crlf ) ;
PR_fprintf ( aFD , " Server: SpaceTrace/0.1%s " , crlf ) ;
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 .
*/
realFun = handleRequest ( aTMR , aFD , start , getData ) ;
if ( 0 ! = realFun )
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleRequest ) ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleClient ) ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , lineReader ) ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleClient ) ;
}
return retval ;
}
/*
* * serverMode
* *
* * List on a port as a httpd .
* * Output results interactively on demand .
* *
* * Returns ! 0 on error .
*/
int serverMode ( tmreader * aTMR )
{
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 ;
const int backlog = 10 ;
/*
* * 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 handleRes = 0 ;
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 ) ;
/*
* * Keep accepting until we know otherwise .
* * We stay single threaded , as a result page may output
* * a URL to an image which requires the same processing ,
* * and we serialize the caching of that common data by
* * only accepting one connection at a time .
* * Plus , I can defend I ' m still sane by not trying to
* * write a full on HTTPD if I stay single threaded .
*/
while ( 0 = = globals . mStopHttpd & & 0 = = retval )
{
connection = PR_Accept ( socket , NULL , PR_INTERVAL_NO_TIMEOUT ) ;
if ( NULL ! = connection )
{
/*
* * Hand off the connection .
* * However , no error for each connection will cause us
* * to stop .
* * The load time is too painful to bail out here .
*/
handleRes = handleClient ( aTMR , connection ) ;
if ( 0 ! = handleRes )
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , handleClient ) ;
}
/*
* * Done with the connection .
*/
closeRes = PR_Close ( connection ) ;
if ( PR_SUCCESS ! = closeRes )
{
failureSum + = __LINE__ ;
REPORT_ERROR ( __LINE__ , PR_Close ) ;
}
}
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 .
*/
int batchMode ( tmreader * aTMR )
{
int retval = 0 ;
if ( NULL ! = aTMR & & 0 ! = globals . mOptions . mBatchRequestCount )
{
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 + + )
{
sprintfRes = PR_snprintf ( aFileName , sizeof ( aFileName ) , " %s%c%s " , globals . mOptions . mOutputDir , PR_GetDirectorySeparator ( ) , globals . mOptions . mBatchRequests [ loop ] ) ;
if ( ( PRUint32 ) - 1 ! = sprintfRes )
{
PRFileDesc * outFile = NULL ;
outFile = PR_Open ( aFileName , ST_FLAGS , ST_PERMS ) ;
if ( NULL ! = outFile )
{
PRStatus closeRes = PR_SUCCESS ;
handleRes = handleRequest ( aTMR , outFile , globals . mOptions . mBatchRequests [ loop ] , NULL ) ;
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 ;
tmreader * tmr = NULL ;
/*
* * Create the new trace - malloc reader .
*/
tmr = tmreader_new ( globals . mOptions . mProgramName , NULL ) ;
if ( NULL ! = tmr )
{
int tmResult = 0 ;
int outputResult = 0 ;
tmResult = tmreader_eventloop ( tmr , globals . mOptions . mFileName , tmEventHandler ) ;
if ( 0 = = tmResult )
{
REPORT_ERROR ( __LINE__ , tmreader_eventloop ) ;
retval = __LINE__ ;
}
if ( 0 = = retval )
{
# if WANT_GRAPHS
/*
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
}
# endif /* WANT_GRAPHS */
/*
* * Create the default sorted run .
*/
if ( NULL ! = globals . mCache . mSortedRun )
{
freeRun ( globals . mCache . mSortedRun ) ;
}
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 .
*/
outputResult = batchMode ( tmr ) ;
if ( 0 ! = outputResult )
{
REPORT_ERROR ( __LINE__ , batchMode ) ;
retval = __LINE__ ;
}
}
else
{
int serverRes = 0 ;
/*
* * httpd time .
*/
serverRes = serverMode ( tmr ) ;
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 .
*/
if ( NULL ! = globals . mCache . mSortedRun )
{
freeRun ( globals . mCache . mSortedRun ) ;
globals . mCache . mSortedRun = NULL ;
}
}
else
{
retval = __LINE__ ;
REPORT_ERROR ( __LINE__ , createRunFromGlobal ) ;
}
}
/*
* * All done .
*/
tmreader_destroy ( tmr ) ;
tmr = NULL ;
}
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 ;
/*
2001-11-29 04:29:26 +03: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
*/
2001-11-29 04:29:26 +03:00
globals . mMinTimeval = ST_TIMEVAL_MAX ;
2001-11-16 01:40:53 +03:00
/*
* * NSPR init .
*/
PR_Init ( PR_USER_THREAD , PR_PRIORITY_NORMAL , 0 ) ;
/*
* * 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__ ;
}
}
/*
* * Options cleanup .
*/
if ( NULL ! = globals . mOptions . mRestrictText )
{
free ( globals . mOptions . mRestrictText ) ;
globals . mOptions . mRestrictText = NULL ;
}
/*
* * All done .
*/
if ( 0 ! = retval )
{
REPORT_ERROR ( retval , main ) ;
}
prResult = PR_Cleanup ( ) ;
if ( PR_SUCCESS ! = prResult )
{
REPORT_ERROR ( retval , PR_Cleanup ) ;
retval = __LINE__ ;
}
return retval ;
}