Bug 449474, Fx3.0.2 updates should force modules/distribution.js, and use the version...
[mozilla-1.9.git] / xpcom / base / nsDebugImpl.cpp
blob6c7a7f3576a247bba2de264f84fdf3a793d61d5d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * IBM Corp.
24 * Henry Sobotka
25 * Benjamin Smedberg <benjamin@smedbergs.us>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsDebugImpl.h"
42 #include "nsDebug.h"
43 #include "prprf.h"
44 #include "prlog.h"
45 #include "prinit.h"
46 #include "plstr.h"
47 #include "nsError.h"
48 #include "prerror.h"
49 #include "prerr.h"
50 #include "prenv.h"
52 #if defined(XP_BEOS)
53 /* For DEBUGGER macros */
54 #include <Debug.h>
55 #endif
57 #if defined(XP_UNIX) || defined(_WIN32) || defined(XP_OS2) || defined(XP_BEOS)
58 /* for abort() and getenv() */
59 #include <stdlib.h>
60 #endif
62 #include "nsTraceRefcntImpl.h"
63 #include "nsISupportsUtils.h"
65 #if defined(XP_UNIX)
66 #include <signal.h>
67 #endif
69 static void
70 Abort(const char *aMsg);
72 static void
73 Break(const char *aMsg);
75 #if defined(XP_OS2)
76 # define INCL_WINDIALOGS // need for WinMessageBox
77 # include <os2.h>
78 # include <string.h>
79 #endif /* XP_OS2 */
81 #if defined(_WIN32)
82 #include <windows.h>
83 #include <signal.h>
84 #include <malloc.h> // for _alloca
85 #elif defined(XP_UNIX)
86 #include <stdlib.h>
87 #endif
90 * Determine if debugger is present in windows.
92 #if defined (_WIN32)
94 typedef WINBASEAPI BOOL (WINAPI* LPFNISDEBUGGERPRESENT)();
95 PRBool InDebugger()
97 #ifndef WINCE
98 PRBool fReturn = PR_FALSE;
99 LPFNISDEBUGGERPRESENT lpfnIsDebuggerPresent = NULL;
100 HINSTANCE hKernel = LoadLibrary("Kernel32.dll");
102 if(hKernel)
104 lpfnIsDebuggerPresent =
105 (LPFNISDEBUGGERPRESENT)GetProcAddress(hKernel, "IsDebuggerPresent");
106 if(lpfnIsDebuggerPresent)
108 fReturn = (*lpfnIsDebuggerPresent)();
110 FreeLibrary(hKernel);
113 return fReturn;
114 #else
115 return PR_FALSE;
116 #endif
119 #endif /* WIN32*/
121 NS_IMPL_QUERY_INTERFACE1(nsDebugImpl, nsIDebug)
123 NS_IMETHODIMP_(nsrefcnt)
124 nsDebugImpl::AddRef()
126 return 2;
129 NS_IMETHODIMP_(nsrefcnt)
130 nsDebugImpl::Release()
132 return 1;
135 NS_IMETHODIMP
136 nsDebugImpl::Assertion(const char *aStr, const char *aExpr,
137 const char *aFile, PRInt32 aLine)
139 NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
140 return NS_OK;
143 NS_IMETHODIMP
144 nsDebugImpl::Warning(const char *aStr, const char *aFile, PRInt32 aLine)
146 NS_DebugBreak(NS_DEBUG_WARNING, aStr, nsnull, aFile, aLine);
147 return NS_OK;
150 NS_IMETHODIMP
151 nsDebugImpl::Break(const char *aFile, PRInt32 aLine)
153 NS_DebugBreak(NS_DEBUG_BREAK, nsnull, nsnull, aFile, aLine);
154 return NS_OK;
157 NS_IMETHODIMP
158 nsDebugImpl::Abort(const char *aFile, PRInt32 aLine)
160 NS_DebugBreak(NS_DEBUG_ABORT, nsnull, nsnull, aFile, aLine);
161 return NS_OK;
165 * Implementation of the nsDebug methods. Note that this code is
166 * always compiled in, in case some other module that uses it is
167 * compiled with debugging even if this library is not.
169 static PRLogModuleInfo* gDebugLog;
171 static void InitLog(void)
173 if (0 == gDebugLog) {
174 gDebugLog = PR_NewLogModule("nsDebug");
175 gDebugLog->level = PR_LOG_DEBUG;
179 enum nsAssertBehavior {
180 NS_ASSERT_UNINITIALIZED,
181 NS_ASSERT_WARN,
182 NS_ASSERT_SUSPEND,
183 NS_ASSERT_STACK,
184 NS_ASSERT_TRAP,
185 NS_ASSERT_ABORT,
186 NS_ASSERT_STACK_AND_ABORT
189 static nsAssertBehavior GetAssertBehavior()
191 static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
192 if (gAssertBehavior != NS_ASSERT_UNINITIALIZED)
193 return gAssertBehavior;
195 #if defined(XP_WIN) || defined(XP_OS2)
196 gAssertBehavior = NS_ASSERT_TRAP;
197 #else
198 gAssertBehavior = NS_ASSERT_WARN;
199 #endif
201 const char *assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
202 if (!assertString || !*assertString)
203 return gAssertBehavior;
205 if (!strcmp(assertString, "warn"))
206 return gAssertBehavior = NS_ASSERT_WARN;
208 if (!strcmp(assertString, "suspend"))
209 return gAssertBehavior = NS_ASSERT_SUSPEND;
211 if (!strcmp(assertString, "stack"))
212 return gAssertBehavior = NS_ASSERT_STACK;
214 if (!strcmp(assertString, "abort"))
215 return gAssertBehavior = NS_ASSERT_ABORT;
217 if (!strcmp(assertString, "trap") || !strcmp(assertString, "break"))
218 return gAssertBehavior = NS_ASSERT_TRAP;
220 if (!strcmp(assertString, "stack-and-abort"))
221 return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
223 fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
224 return gAssertBehavior;
227 struct FixedBuffer
229 FixedBuffer() : curlen(0) { buffer[0] = '\0'; }
231 char buffer[1000];
232 PRUint32 curlen;
235 static PRIntn
236 StuffFixedBuffer(void *closure, const char *buf, PRUint32 len)
238 if (!len)
239 return 0;
241 FixedBuffer *fb = (FixedBuffer*) closure;
243 // strip the trailing null, we add it again later
244 if (buf[len - 1] == '\0')
245 --len;
247 if (fb->curlen + len >= sizeof(fb->buffer))
248 len = sizeof(fb->buffer) - fb->curlen - 1;
250 if (len) {
251 memcpy(fb->buffer + fb->curlen, buf, len);
252 fb->curlen += len;
253 fb->buffer[fb->curlen] = '\0';
256 return len;
259 EXPORT_XPCOM_API(void)
260 NS_DebugBreak(PRUint32 aSeverity, const char *aStr, const char *aExpr,
261 const char *aFile, PRInt32 aLine)
263 InitLog();
265 FixedBuffer buf;
266 PRLogModuleLevel ll = PR_LOG_WARNING;
267 const char *sevString = "WARNING";
269 switch (aSeverity) {
270 case NS_DEBUG_ASSERTION:
271 sevString = "###!!! ASSERTION";
272 ll = PR_LOG_ERROR;
273 break;
275 case NS_DEBUG_BREAK:
276 sevString = "###!!! BREAK";
277 ll = PR_LOG_ALWAYS;
278 break;
280 case NS_DEBUG_ABORT:
281 sevString = "###!!! ABORT";
282 ll = PR_LOG_ALWAYS;
283 break;
285 default:
286 aSeverity = NS_DEBUG_WARNING;
289 PR_sxprintf(StuffFixedBuffer, &buf, "%s: ", sevString);
291 if (aStr)
292 PR_sxprintf(StuffFixedBuffer, &buf, "%s: ", aStr);
294 if (aExpr)
295 PR_sxprintf(StuffFixedBuffer, &buf, "'%s', ", aExpr);
297 if (aFile)
298 PR_sxprintf(StuffFixedBuffer, &buf, "file %s, ", aFile);
300 if (aLine != -1)
301 PR_sxprintf(StuffFixedBuffer, &buf, "line %d", aLine);
303 // Write out the message to the debug log
304 PR_LOG(gDebugLog, ll, ("%s", buf.buffer));
305 PR_LogFlush();
307 // errors on platforms without a debugdlg ring a bell on stderr
308 #if !defined(XP_WIN) && !defined(XP_OS2)
309 if (ll != PR_LOG_WARNING)
310 fprintf(stderr, "\07");
311 #endif
313 // Write the message to stderr
314 fprintf(stderr, "%s\n", buf.buffer);
315 fflush(stderr);
317 switch (aSeverity) {
318 case NS_DEBUG_WARNING:
319 return;
321 case NS_DEBUG_BREAK:
322 Break(buf.buffer);
323 return;
325 case NS_DEBUG_ABORT:
326 Abort(buf.buffer);
327 return;
330 // Now we deal with assertions
332 switch (GetAssertBehavior()) {
333 case NS_ASSERT_WARN:
334 return;
336 case NS_ASSERT_SUSPEND:
337 #ifdef XP_UNIX
338 fprintf(stderr, "Suspending process; attach with the debugger.\n");
339 kill(0, SIGSTOP);
340 #else
341 Break(buf.buffer);
342 #endif
343 return;
345 case NS_ASSERT_STACK:
346 nsTraceRefcntImpl::WalkTheStack(stderr);
347 return;
349 case NS_ASSERT_STACK_AND_ABORT:
350 nsTraceRefcntImpl::WalkTheStack(stderr);
351 // Fall through to abort
353 case NS_ASSERT_ABORT:
354 Abort(buf.buffer);
355 return;
357 case NS_ASSERT_TRAP:
358 Break(buf.buffer);
362 static void
363 Abort(const char *aMsg)
365 #if defined(_WIN32)
366 //This should exit us
367 raise(SIGABRT);
368 //If we are ignored exit this way..
369 _exit(3);
370 #elif defined(XP_UNIX)
371 PR_Abort();
372 #elif defined(XP_BEOS)
374 #ifndef DEBUG_cls
375 DEBUGGER(aMsg);
376 #endif
378 #else
379 // Don't know how to abort on this platform! call Break() instead
380 Break(aMsg);
381 #endif
384 // Abort() calls this function, don't call it!
385 static void
386 Break(const char *aMsg)
388 #if defined(_WIN32)
389 #ifndef WINCE // we really just want to crash for now
390 static int ignoreDebugger;
391 if (!ignoreDebugger) {
392 const char *shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
393 ignoreDebugger = 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
395 if((ignoreDebugger == 2) || !InDebugger()) {
396 DWORD code = IDRETRY;
398 /* Create the debug dialog out of process to avoid the crashes caused by
399 * Windows events leaking into our event loop from an in process dialog.
400 * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
401 * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
403 PROCESS_INFORMATION pi;
404 STARTUPINFO si;
405 char executable[MAX_PATH];
406 char* pName;
408 memset(&pi, 0, sizeof(pi));
410 memset(&si, 0, sizeof(si));
411 si.cb = sizeof(si);
412 si.wShowWindow = SW_SHOW;
414 // 2nd arg of CreateProcess is in/out
415 char *msgCopy = (char*) _alloca(strlen(aMsg) + 1);
416 strcpy(msgCopy, aMsg);
418 if(GetModuleFileName(GetModuleHandle("xpcom.dll"), executable, MAX_PATH) &&
419 NULL != (pName = strrchr(executable, '\\')) &&
420 NULL != strcpy(pName+1, "windbgdlg.exe") &&
421 CreateProcess(executable, msgCopy, NULL, NULL, PR_FALSE,
422 DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
423 NULL, NULL, &si, &pi)) {
424 WaitForSingleObject(pi.hProcess, INFINITE);
425 GetExitCodeProcess(pi.hProcess, &code);
426 CloseHandle(pi.hProcess);
427 CloseHandle(pi.hThread);
430 switch(code) {
431 case IDABORT:
432 //This should exit us
433 raise(SIGABRT);
434 //If we are ignored exit this way..
435 _exit(3);
437 case IDIGNORE:
438 return;
442 ::DebugBreak();
444 #endif // WINCE
445 #elif defined(XP_OS2)
446 char msg[1200];
447 PR_snprintf(msg, sizeof(msg),
448 "%s\n\nClick Cancel to Debug Application.\n"
449 "Click Enter to continue running the Application.", aMsg);
450 ULONG code = MBID_ERROR;
451 code = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, msg,
452 "NSGlue_Assertion", 0,
453 MB_ERROR | MB_ENTERCANCEL);
455 /* It is possible that we are executing on a thread that doesn't have a
456 * message queue. In that case, the message won't appear, and code will
457 * be 0xFFFF. We'll give the user a chance to debug it by calling
458 * Break()
459 * Actually, that's a really bad idea since this happens a lot with threadsafe
460 * assertions and since it means that you can't actually run the debug build
461 * outside a debugger without it crashing constantly.
463 if (( code == MBID_ENTER ) || (code == MBID_ERROR))
464 return;
466 asm("int $3");
467 #elif defined(XP_BEOS)
468 DEBUGGER(aMsg);
469 #elif defined(XP_MACOSX)
470 /* Note that we put this Mac OS X test above the GNUC/x86 test because the
471 * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
472 * impls to be the same.
474 raise(SIGTRAP);
475 #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
476 asm("int $3");
477 #else
478 // don't know how to break on this platform
479 #endif
482 static const nsDebugImpl kImpl;
484 NS_METHOD
485 nsDebugImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
487 NS_ENSURE_NO_AGGREGATION(outer);
489 return const_cast<nsDebugImpl*>(&kImpl)->
490 QueryInterface(aIID, aInstancePtr);
493 ////////////////////////////////////////////////////////////////////////////////
495 NS_COM nsresult
496 NS_ErrorAccordingToNSPR()
498 PRErrorCode err = PR_GetError();
499 switch (err) {
500 case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
501 case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
502 case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
503 case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
504 case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
505 case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
506 case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
507 case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
508 case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
509 case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
510 case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
511 case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
512 case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
513 case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
514 default: return NS_ERROR_FAILURE;
518 ////////////////////////////////////////////////////////////////////////////////