Back to index

lightning-sunbird  0.9+nobinonly
nsDebugImpl.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   IBM Corp.
00024  *   Henry Sobotka
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsDebugImpl.h"
00041 #include "nsDebug.h"
00042 #include "prprf.h"
00043 #include "prlog.h"
00044 #include "prinit.h"
00045 #include "plstr.h"
00046 #include "nsError.h"
00047 #include "prerror.h"
00048 #include "prerr.h"
00049 
00050 #if defined(XP_BEOS)
00051 /* For DEBUGGER macros */
00052 #include <Debug.h>
00053 #endif
00054 
00055 #if defined(XP_UNIX) || defined(_WIN32) || defined(XP_OS2) || defined(XP_BEOS)
00056 /* for abort() and getenv() */
00057 #include <stdlib.h>
00058 #endif
00059 
00060 #if defined(XP_UNIX) && !defined(UNIX_CRASH_ON_ASSERT)
00061 #include <signal.h>
00062 /* for nsTraceRefcnt::WalkTheStack() */
00063 #include "nsISupportsUtils.h"
00064 #include "nsTraceRefcntImpl.h"
00065 
00066 // we put Mac OS X first in this test because the GNUC/x86 test is also true
00067 // on Intel Mac OS X and we want the same impl for PPC/x86 Mac OS X
00068 #if defined(XP_MACOSX)
00069 #define DebugBreak() { raise(SIGTRAP); }
00070 #elif defined(__GNUC__) && (defined(__i386) || defined(__i386__) || defined(__x86_64__))
00071 #define DebugBreak() { asm("int $3"); }
00072 #else
00073 #define DebugBreak()
00074 #endif
00075 #endif
00076 
00077 #if defined(XP_OS2)
00078   /* Added definitions for DebugBreak() for 2 different OS/2 compilers.  Doing
00079    * the int3 on purpose so that a developer can step over the
00080    * instruction if so desired.  Not always possible if trapping due to exception
00081    * handling IBM-AKR
00082    */
00083   #define INCL_WINDIALOGS  // need for WinMessageBox
00084   #include <os2.h>
00085   #include <string.h>
00086   
00087   #if defined(DEBUG)
00088     #define DebugBreak() { asm("int $3"); }
00089   #else
00090     #define DebugBreak()
00091   #endif /* DEBUG */
00092 #endif /* XP_OS2 */
00093 
00094 #if defined(_WIN32)
00095 #include <windows.h>
00096 #include <signal.h>
00097 #elif defined(XP_UNIX)
00098 #include <stdlib.h>
00099 #endif
00100 
00101 /*
00102  * Determine if debugger is present in windows.
00103  */
00104 #if defined (_WIN32)
00105 
00106 typedef WINBASEAPI BOOL (WINAPI* LPFNISDEBUGGERPRESENT)();
00107 PRBool InDebugger()
00108 {
00109 #ifndef WINCE
00110    PRBool fReturn = PR_FALSE;
00111    LPFNISDEBUGGERPRESENT lpfnIsDebuggerPresent = NULL;
00112    HINSTANCE hKernel = LoadLibrary("Kernel32.dll");
00113 
00114    if(hKernel)
00115       {
00116       lpfnIsDebuggerPresent = 
00117          (LPFNISDEBUGGERPRESENT)GetProcAddress(hKernel, "IsDebuggerPresent");
00118       if(lpfnIsDebuggerPresent)
00119          {
00120          fReturn = (*lpfnIsDebuggerPresent)();
00121          }
00122       FreeLibrary(hKernel);
00123       }
00124 
00125    return fReturn;
00126 #else
00127    return PR_FALSE;
00128 #endif
00129 }
00130 
00131 #endif /* WIN32*/
00132 
00133 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDebugImpl, nsIDebug)
00134 
00135 nsDebugImpl::nsDebugImpl()
00136 {
00137 }
00138 
00144 static PRLogModuleInfo* gDebugLog;
00145 
00146 static void InitLog(void)
00147 {
00148   if (0 == gDebugLog) {
00149     gDebugLog = PR_NewLogModule("nsDebug");
00150     gDebugLog->level = PR_LOG_DEBUG;
00151   }
00152 }
00153 
00154 
00155 
00156 NS_IMETHODIMP
00157 nsDebugImpl::Assertion(const char *aStr, const char *aExpr, const char *aFile, PRInt32 aLine)
00158 {
00159    InitLog();
00160 
00161    char buf[1000];
00162    PR_snprintf(buf, sizeof(buf),
00163               "###!!! ASSERTION: %s: '%s', file %s, line %d",
00164               aStr, aExpr, aFile, aLine);
00165 
00166    // Write out the assertion message to the debug log
00167    PR_LOG(gDebugLog, PR_LOG_ERROR, ("%s", buf));
00168    PR_LogFlush();
00169 
00170    // And write it out to the stderr
00171    fprintf(stderr, "%s\n", buf);
00172    fflush(stderr);
00173 
00174 #if defined(_WIN32)
00175    char* assertBehavior = getenv("XPCOM_DEBUG_BREAK");
00176    if (assertBehavior && strcmp(assertBehavior, "warn") == 0)
00177      return NS_OK;
00178 
00179 #ifndef WINCE // we really just want to crash for now
00180   static int ignoreDebugger;
00181   if (!ignoreDebugger) {
00182     const char *shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
00183     ignoreDebugger = 1 + !(shouldIgnoreDebugger && strcmp(shouldIgnoreDebugger, "1"));
00184   }
00185    if((ignoreDebugger == 2) || !InDebugger())
00186       {
00187       DWORD code = IDRETRY;
00188 
00189       /* Create the debug dialog out of process to avoid the crashes caused by 
00190        * Windows events leaking into our event loop from an in process dialog.
00191        * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
00192        * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
00193        */
00194       PROCESS_INFORMATION pi;
00195       STARTUPINFO si;
00196       char executable[MAX_PATH];
00197       char* pName;
00198 
00199       memset(&pi, 0, sizeof(pi));
00200 
00201       memset(&si, 0, sizeof(si));
00202       si.cb          = sizeof(si);
00203       si.wShowWindow = SW_SHOW;
00204 
00205       if(GetModuleFileName(GetModuleHandle("xpcom.dll"), executable, MAX_PATH) &&
00206          NULL != (pName = strrchr(executable, '\\')) &&
00207          NULL != strcpy(pName+1, "windbgdlg.exe") &&
00208 #ifdef DEBUG_jband
00209          (printf("Launching %s\n", executable), PR_TRUE) &&
00210 #endif         
00211          CreateProcess(executable, buf, NULL, NULL, PR_FALSE,
00212                        DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
00213                        NULL, NULL, &si, &pi))
00214       {
00215         WaitForSingleObject(pi.hProcess, INFINITE);
00216         GetExitCodeProcess(pi.hProcess, &code);
00217         CloseHandle(pi.hProcess);
00218       }                         
00219 
00220       switch(code)
00221          {
00222          case IDABORT:
00223             //This should exit us
00224             raise(SIGABRT);
00225             //If we are ignored exit this way..
00226             _exit(3);
00227             break;
00228          
00229          case IDIGNORE:
00230             return NS_OK;
00231             // Fall Through
00232          }
00233       }
00234 #endif // WINCE
00235 #endif
00236 
00237 #if defined(XP_OS2)
00238    char* assertBehavior = getenv("XPCOM_DEBUG_BREAK");
00239    if (assertBehavior && strcmp(assertBehavior, "warn") == 0)
00240      return NS_OK;
00241 
00242       char msg[1200];
00243       PR_snprintf(msg, sizeof(msg),
00244                 "%s\n\nClick Cancel to Debug Application.\n"
00245                 "Click Enter to continue running the Application.", buf);
00246       ULONG code = MBID_ERROR;
00247       code = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, msg, 
00248                            "NSGlue_Assertion", 0,
00249                            MB_ERROR | MB_ENTERCANCEL);
00250 
00251       /* It is possible that we are executing on a thread that doesn't have a
00252        * message queue.  In that case, the message won't appear, and code will
00253        * be 0xFFFF.  We'll give the user a chance to debug it by calling
00254        * Break()
00255        * Actually, that's a really bad idea since this happens a lot with threadsafe
00256        * assertions and since it means that you can't actually run the debug build
00257        * outside a debugger without it crashing constantly.
00258        */
00259       if(( code == MBID_ENTER ) || (code == MBID_ERROR))
00260       {
00261          return NS_OK;
00262          // If Retry, Fall Through
00263       }
00264 #endif
00265 
00266       Break(aFile, aLine);
00267       return NS_OK;
00268 }
00269 
00270 NS_IMETHODIMP 
00271 nsDebugImpl::Break(const char *aFile, PRInt32 aLine)
00272 {
00273 #ifndef TEMP_MAC_HACK
00274   // Write out the assertion message to the debug log
00275    InitLog();
00276 
00277    PR_LOG(gDebugLog, PR_LOG_ERROR, 
00278          ("###!!! Break: at file %s, line %d", aFile, aLine));
00279    PR_LogFlush();
00280 
00281   fprintf(stderr, "Break: at file %s, line %d\n",aFile, aLine);  fflush(stderr);
00282   fflush(stderr);
00283 
00284 #if defined(_WIN32)
00285 #ifdef _M_IX86
00286    ::DebugBreak();
00287 #endif
00288 #elif defined(XP_UNIX) && !defined(UNIX_CRASH_ON_ASSERT)
00289     fprintf(stderr, "\07");
00290 
00291     char *assertBehavior = getenv("XPCOM_DEBUG_BREAK");
00292 
00293     if (!assertBehavior) {
00294 
00295       // the default; nothing else to do
00296       ;
00297 
00298     } else if ( strcmp(assertBehavior, "suspend")== 0 ) {
00299 
00300       // the suspend case is first because we wanna send the signal before 
00301       // other threads have had a chance to get too far from the state that
00302       // caused this assertion (in case they happen to have been involved).
00303       //
00304       fprintf(stderr, "Suspending process; attach with the debugger.\n");
00305       kill(0, SIGSTOP);
00306 
00307     } else if ( strcmp(assertBehavior, "warn")==0 ) {
00308       
00309       // same as default; nothing else to do (see "suspend" case comment for
00310       // why this compare isn't done as part of the default case)
00311       //
00312       ;
00313 
00314     } 
00315     else if ( strcmp(assertBehavior,"stack")==0 ) {
00316 
00317       // walk the stack
00318       //
00319       nsTraceRefcntImpl::WalkTheStack(stderr);
00320     } 
00321     else if ( strcmp(assertBehavior,"abort")==0 ) {
00322 
00323       // same as UNIX_CRASH_ON_ASSERT
00324       //
00325       Abort(aFile, aLine);
00326 
00327     } else if ( strcmp(assertBehavior,"trap")==0 ) {
00328 
00329       DebugBreak();
00330 
00331     } else {
00332 
00333       fprintf(stderr, "unrecognized value of XPCOM_DEBUG_BREAK env var!\n");
00334 
00335     }    
00336     fflush(stderr); // this shouldn't really be necessary, i don't think,
00337                     // but maybe there's some lame stdio that buffers stderr
00338 
00339 #elif defined(XP_BEOS)
00340   {
00341 #ifdef UNIX_CRASH_ON_ASSERT
00342        char buf[2000];
00343        sprintf(buf, "Break: at file %s, line %d", aFile, aLine);
00344        DEBUGGER(buf);
00345 #endif
00346   }
00347 #else
00348   Abort(aFile, aLine);
00349 #endif
00350 #endif // TEMP_MAC_HACK
00351   return NS_OK;
00352 }
00353 
00354 NS_IMETHODIMP 
00355 nsDebugImpl::Abort(const char *aFile, PRInt32 aLine)
00356 {
00357   InitLog();
00358 
00359    PR_LOG(gDebugLog, PR_LOG_ERROR, 
00360          ("###!!! Abort: at file %s, line %d", aFile, aLine));
00361    PR_LogFlush();
00362    fprintf(stderr, "\07 Abort\n");  fflush(stderr);
00363    fflush(stderr);
00364 
00365 #if defined(_WIN32)
00366 #ifdef _M_IX86
00367   long* __p = (long*) 0x7;
00368   *__p = 0x7;
00369 #else /* _M_ALPHA */
00370   PR_Abort();
00371 #endif
00372 #elif defined(XP_UNIX)
00373   PR_Abort();
00374 #elif defined(XP_OS2)
00375   DebugBreak();
00376   return NS_OK;
00377 #elif defined(XP_BEOS)
00378   {
00379 #ifndef DEBUG_cls
00380        char buf[2000];
00381        sprintf(buf, "Abort: at file %s, line %d", aFile, aLine);
00382        DEBUGGER(buf);
00383 #endif
00384   } 
00385 #endif
00386   return NS_OK;
00387 }
00388 
00389 NS_IMETHODIMP 
00390 nsDebugImpl::Warning(const char* aMessage,
00391                      const char* aFile, PRIntn aLine)
00392 {
00393   InitLog();
00394 
00395   char buf[1000];
00396   PR_snprintf(buf, sizeof(buf),
00397               "WARNING: %s, file %s, line %d",
00398               aMessage, aFile, aLine);
00399 
00400   // Write out the warning message to the debug log
00401   PR_LOG(gDebugLog, PR_LOG_ERROR, ("%s", buf));
00402 
00403   // And write it out to the stdout
00404   fprintf(stderr, "%s\n", buf);
00405   fflush(stderr);
00406   return NS_OK;
00407 }
00408 
00409 NS_METHOD
00410 nsDebugImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
00411 {
00412   *aInstancePtr = nsnull;
00413   nsIDebug* debug = new nsDebugImpl();
00414   if (!debug)
00415     return NS_ERROR_OUT_OF_MEMORY;
00416   
00417   nsresult rv = debug->QueryInterface(aIID, aInstancePtr);
00418   if (NS_FAILED(rv)) {
00419     delete debug;
00420   }
00421   
00422   return rv;
00423 }
00424 
00426 
00427 NS_COM nsresult
00428 NS_ErrorAccordingToNSPR()
00429 {
00430     PRErrorCode err = PR_GetError();
00431     switch (err) {
00432       case PR_OUT_OF_MEMORY_ERROR:              return NS_ERROR_OUT_OF_MEMORY;
00433       case PR_WOULD_BLOCK_ERROR:                return NS_BASE_STREAM_WOULD_BLOCK;
00434       case PR_FILE_NOT_FOUND_ERROR:             return NS_ERROR_FILE_NOT_FOUND;
00435       case PR_READ_ONLY_FILESYSTEM_ERROR:       return NS_ERROR_FILE_READ_ONLY;
00436       case PR_NOT_DIRECTORY_ERROR:              return NS_ERROR_FILE_NOT_DIRECTORY;
00437       case PR_IS_DIRECTORY_ERROR:               return NS_ERROR_FILE_IS_DIRECTORY;
00438       case PR_LOOP_ERROR:                       return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
00439       case PR_FILE_EXISTS_ERROR:                return NS_ERROR_FILE_ALREADY_EXISTS;
00440       case PR_FILE_IS_LOCKED_ERROR:             return NS_ERROR_FILE_IS_LOCKED;
00441       case PR_FILE_TOO_BIG_ERROR:               return NS_ERROR_FILE_TOO_BIG;
00442       case PR_NO_DEVICE_SPACE_ERROR:            return NS_ERROR_FILE_NO_DEVICE_SPACE;
00443       case PR_NAME_TOO_LONG_ERROR:              return NS_ERROR_FILE_NAME_TOO_LONG;
00444       case PR_DIRECTORY_NOT_EMPTY_ERROR:        return NS_ERROR_FILE_DIR_NOT_EMPTY;
00445       case PR_NO_ACCESS_RIGHTS_ERROR:           return NS_ERROR_FILE_ACCESS_DENIED;
00446       default:                                  return NS_ERROR_FAILURE;
00447     }
00448 }
00449 
00451