Back to index

lightning-sunbird  0.9+nobinonly
nsXPCToolsProfiler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   John Bandhauer <jband@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /* Implements nsXPCToolsProfiler. */
00042 
00043 #include "xpctools_private.h"
00044 
00045 /***************************************************************************/
00046 
00047 class FunctionKey : public nsHashKey {
00048 protected:
00049     uintN         mLineno;
00050     uintN         mExtent;
00051 
00052 public:
00053     FunctionKey(uintN         aLineno,
00054                 uintN         aExtent)
00055         : mLineno(aLineno), mExtent(aExtent) {}
00056     ~FunctionKey(void) {}
00057 
00058     PRUint32 HashCode(void) const 
00059         {return (17*mLineno) + (7*mExtent);}
00060     PRBool Equals(const nsHashKey* aKey) const
00061         {const FunctionKey* o = (const FunctionKey*) aKey; 
00062          return (mLineno == o->mLineno) && (mExtent == o->mExtent);}
00063     nsHashKey* Clone() const
00064         {return new FunctionKey(mLineno, mExtent);}
00065 };
00066 
00067 /***************************************************************************/
00068 
00069 ProfilerFile::ProfilerFile(const char* filename)
00070     :   mName(filename ? nsCRT::strdup(filename) : nsnull),
00071         mFunctionTable(new nsHashtable(16, PR_FALSE))
00072 {
00073     // empty
00074 }
00075 
00076 ProfilerFile::~ProfilerFile()
00077 {
00078     if(mName)
00079         nsCRT::free(mName);
00080     if(mFunctionTable)
00081         delete mFunctionTable;            
00082 }
00083 
00084 ProfilerFunction* 
00085 ProfilerFile::FindOrAddFunction(const char* aName,
00086                                 uintN aBaseLineNumber,
00087                                 uintN aLineExtent,
00088                                 size_t aTotalSize)
00089 {
00090     if(!mFunctionTable)
00091         return nsnull;
00092     FunctionKey key(aBaseLineNumber, aLineExtent);
00093     ProfilerFunction* fun = (ProfilerFunction*) mFunctionTable->Get(&key);
00094     if(!fun)
00095     {
00096         fun = new ProfilerFunction(aName, aBaseLineNumber, aLineExtent,
00097                                    aTotalSize, this);
00098         if(fun)
00099             mFunctionTable->Put(&key, fun);
00100     }
00101     return fun; 
00102 }
00103 
00104 void ProfilerFile::EnumerateFunctions(nsHashtableEnumFunc aEnumFunc, void* closure)
00105 {
00106     if(mFunctionTable)
00107         mFunctionTable->Enumerate(aEnumFunc, closure);
00108             
00109 }
00110 
00111 /***************************************************************************/
00112 
00113 ProfilerFunction::ProfilerFunction(const char* name, 
00114                                    uintN lineno, uintn extent, size_t totalsize,
00115                                    ProfilerFile* file)
00116     :   mName(name ? nsCRT::strdup(name) : nsnull),
00117         mBaseLineNumber(lineno),
00118         mLineExtent(extent),
00119         mTotalSize(totalsize),
00120         mFile(file),
00121         mCallCount(0),
00122         mCompileCount(0),
00123         mQuickTime((PRUint32) -1),
00124         mLongTime(0),
00125         mStartTime(0),
00126         mSum(0)
00127 {
00128     // empty        
00129 }
00130 
00131 ProfilerFunction::~ProfilerFunction()
00132 {
00133     if(mName)
00134         nsCRT::free(mName);
00135 }
00136 
00137 /***************************************************************************/
00138 
00139 
00140 NS_IMPL_ISUPPORTS1(nsXPCToolsProfiler, nsIXPCToolsProfiler)
00141 
00142 nsXPCToolsProfiler::nsXPCToolsProfiler()
00143     :   mLock(PR_NewLock()),
00144         mRuntime(nsnull),
00145         mFileTable(new nsHashtable(128, PR_FALSE)),
00146         mScriptTable(new nsHashtable(256, PR_FALSE))
00147 {
00148     InitializeRuntime();
00149 }
00150 
00151 JS_STATIC_DLL_CALLBACK(PRBool)
00152 xpctools_ProfilerFunctionDeleter(nsHashKey *aKey, void *aData, void* closure)
00153 {
00154     delete (ProfilerFunction*) aData;
00155     return PR_TRUE;        
00156 }        
00157 
00158 JS_STATIC_DLL_CALLBACK(PRBool)
00159 xpctools_ProfilerFileDeleter(nsHashKey *aKey, void *aData, void* closure)
00160 {
00161     ProfilerFile* file = (ProfilerFile*) aData;
00162     file->EnumerateFunctions(xpctools_ProfilerFunctionDeleter, closure);
00163     delete file;
00164     return PR_TRUE;        
00165 }        
00166 
00167 nsXPCToolsProfiler::~nsXPCToolsProfiler()
00168 {
00169     Stop();
00170     if(mLock)
00171         PR_DestroyLock(mLock);
00172     if(mFileTable)
00173     {
00174         mFileTable->Reset(xpctools_ProfilerFileDeleter, this);
00175         delete mFileTable;
00176     }
00177     if(mScriptTable)
00178     {
00179         // elements not owned - don't purge them
00180         delete mScriptTable;
00181     }
00182 }
00183 
00184 /***************************************************************************/
00185 // the hooks...
00186 
00187 /* called just after script creation */
00188 JS_STATIC_DLL_CALLBACK(void)
00189 xpctools_JSNewScriptHook(JSContext  *cx,
00190                          const char *filename,  /* URL of script */
00191                          uintN      lineno,     /* line script starts */
00192                          JSScript   *script,
00193                          JSFunction *fun,
00194                          void       *callerdata)
00195 {
00196     if(!script)
00197         return;
00198     if(!filename)
00199         filename = "<<<!!! has no name so may represent many different pages !!!>>>";
00200     nsXPCToolsProfiler* self = (nsXPCToolsProfiler*) callerdata;    
00201     nsAutoLock lock(self->mLock);
00202 
00203     if(self->mFileTable)
00204     {
00205         nsCStringKey key(filename);
00206         ProfilerFile* file = (ProfilerFile*) self->mFileTable->Get(&key);
00207         if(!file)
00208         {
00209             file = new ProfilerFile(filename);
00210             self->mFileTable->Put(&key, file);
00211         }
00212         if(file)
00213         {
00214             ProfilerFunction* function = 
00215                 file->FindOrAddFunction(fun ? JS_GetFunctionName(fun) : nsnull, 
00216                                         JS_GetScriptBaseLineNumber(cx, script),
00217                                         JS_GetScriptLineExtent(cx, script),
00218                                         fun
00219                                         ? JS_GetFunctionTotalSize(cx, fun)
00220                                         : JS_GetScriptTotalSize(cx, script));
00221             if(function)
00222             {
00223                 function->IncrementCompileCount();
00224                 if(self->mScriptTable)
00225                 {
00226                     nsVoidKey scriptkey(script);
00227                     self->mScriptTable->Put(&scriptkey, function);
00228                 }
00229             }
00230         }
00231     }
00232 }
00233 
00234 /* called just before script destruction */
00235 JS_STATIC_DLL_CALLBACK(void)
00236 xpctools_JSDestroyScriptHook(JSContext  *cx,
00237                              JSScript   *script,
00238                              void       *callerdata)
00239 {
00240     if(!script)
00241         return;
00242     nsXPCToolsProfiler* self = (nsXPCToolsProfiler*) callerdata;    
00243     nsAutoLock lock(self->mLock);
00244     if(self->mScriptTable)
00245     {
00246         nsVoidKey scriptkey(script);
00247         self->mScriptTable->Remove(&scriptkey);
00248     }
00249 }
00250 
00251 
00252 /* called on entry and return of functions and top level scripts */
00253 JS_STATIC_DLL_CALLBACK(void*)
00254 xpctools_InterpreterHook(JSContext *cx, JSStackFrame *fp, JSBool before,
00255                          JSBool *ok, void *closure)
00256 {
00257     // ignore returns
00258     NS_ASSERTION(fp, "bad frame pointer!");
00259 
00260     JSScript* script = fp->script;
00261     if(script)
00262     {
00263         nsXPCToolsProfiler* self = (nsXPCToolsProfiler*) closure;    
00264         nsAutoLock lock(self->mLock);
00265         if(self->mScriptTable)
00266         {
00267             nsVoidKey scriptkey(script);
00268             ProfilerFunction* fun = 
00269                 (ProfilerFunction*) self->mScriptTable->Get(&scriptkey);
00270             if(fun)
00271             {
00272                 if(before == PR_TRUE)
00273                 {
00274                     fun->IncrementCallCount();
00275                     fun->SetStartTime();
00276                 }
00277                 else
00278                 {
00279                     fun->SetEndTime();
00280                 }
00281             }
00282         }
00283     }   
00284     return closure;
00285 }
00286 
00287 /***************************************************************************/
00288 // interface methods
00289 
00290 /* void start (); */
00291 NS_IMETHODIMP nsXPCToolsProfiler::Start()
00292 {
00293     nsAutoLock lock(mLock);
00294     if(!VerifyRuntime())
00295         return NS_ERROR_UNEXPECTED; 
00296     
00297     JS_SetNewScriptHook(mRuntime, xpctools_JSNewScriptHook, this);
00298     JS_SetDestroyScriptHook(mRuntime, xpctools_JSDestroyScriptHook, this);
00299     JS_SetExecuteHook(mRuntime, xpctools_InterpreterHook, this);
00300     JS_SetCallHook(mRuntime, xpctools_InterpreterHook, this);
00301 
00302     return NS_OK;
00303 }
00304 
00305 /* void stop (); */
00306 NS_IMETHODIMP nsXPCToolsProfiler::Stop()
00307 {
00308     nsAutoLock lock(mLock);
00309     if(!VerifyRuntime())
00310         return NS_ERROR_UNEXPECTED; 
00311     
00312     JS_SetNewScriptHook(mRuntime, nsnull, nsnull);
00313     JS_SetDestroyScriptHook(mRuntime, nsnull, nsnull);
00314     JS_SetExecuteHook(mRuntime, nsnull, this);
00315     JS_SetCallHook(mRuntime, nsnull, this);
00316     
00317     return NS_OK;
00318 }
00319 
00320 /* void clear (); */
00321 NS_IMETHODIMP nsXPCToolsProfiler::Clear()
00322 {
00323   // XXX implement me!
00324   return NS_ERROR_NOT_IMPLEMENTED;
00325 }
00326 
00327 
00328 JS_STATIC_DLL_CALLBACK(PRBool)
00329 xpctools_FunctionNamePrinter(nsHashKey *aKey, void *aData, void* closure)
00330 {
00331     ProfilerFunction* fun = (ProfilerFunction*) aData;
00332     FILE* out = (FILE*) closure;
00333     const char* name = fun->GetName();
00334     PRUint32 average;
00335     PRUint32 count;
00336 
00337     count = fun->GetCallCount();
00338     if (count != 0)
00339         average = fun->GetSum() / count;
00340     if(!name)
00341         name = "<top level>"; 
00342     fprintf(out,
00343         "    [%lu,%lu] %s() {%d-%d} %lu ",
00344         (unsigned long) fun->GetCompileCount(),
00345         (unsigned long) fun->GetCallCount(),
00346         name,
00347         (int) fun->GetBaseLineNumber(),
00348         (int)(fun->GetBaseLineNumber()+fun->GetLineExtent()-1),
00349         (unsigned long) fun->GetTotalSize());
00350     if(count != 0)
00351         fprintf(out,
00352             "{min %lu, max %lu avg %lu}\n",
00353             (unsigned long) fun->GetQuickTime(),
00354             (unsigned long) fun->GetLongTime(),
00355             (unsigned long) average);
00356     else
00357         fprintf(out, "\n" );
00358     return PR_TRUE;        
00359 }        
00360 
00361 JS_STATIC_DLL_CALLBACK(PRBool)
00362 xpctools_FilenamePrinter(nsHashKey *aKey, void *aData, void* closure)
00363 {
00364     ProfilerFile* file = (ProfilerFile*) aData;
00365     FILE* out = (FILE*) closure;
00366     fprintf(out, "%s\n", file->GetName());
00367     file->EnumerateFunctions(xpctools_FunctionNamePrinter, closure);
00368     return PR_TRUE;        
00369 }        
00370 
00371 /* void writeResults (in nsILocalFile aFile); */
00372 NS_IMETHODIMP nsXPCToolsProfiler::WriteResults(nsILocalFile *aFile)
00373 {
00374     nsAutoLock lock(mLock);
00375     if(!aFile)
00376         return NS_ERROR_FAILURE;
00377     FILE* out;
00378     if(NS_FAILED(aFile->OpenANSIFileDesc("w", &out)) || ! out)
00379         return NS_ERROR_FAILURE;
00380 
00381     if(mFileTable)
00382         mFileTable->Enumerate(xpctools_FilenamePrinter, out);
00383     return NS_OK;
00384 }
00385 
00386 /***************************************************************************/
00387 // additional utility methods
00388 
00389 JSBool
00390 nsXPCToolsProfiler::VerifyRuntime()
00391 {
00392     JSRuntime* rt;
00393     nsCOMPtr<nsIJSRuntimeService> rts = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
00394     return rts && NS_SUCCEEDED(rts->GetRuntime(&rt)) && rt && rt == mRuntime;
00395 }
00396 
00397 JSBool 
00398 nsXPCToolsProfiler::InitializeRuntime()
00399 {
00400     NS_ASSERTION(!mRuntime, "can't init runtime twice");
00401     JSRuntime* rt;
00402     nsCOMPtr<nsIJSRuntimeService> rts = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
00403     if(rts && NS_SUCCEEDED(rts->GetRuntime(&rt)) && rt)
00404         mRuntime = rt;
00405     return mRuntime != nsnull;
00406 }