Fixed register allocation bug in left-shift operations (bug 606063, r=dmandelin).
[mozilla-central.git] / xpcom / base / nsDebugImpl.cpp
blob97c3cd262e5a6b6a39e637e56342ae873edf6262
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"
51 #include "pratom.h"
53 #ifdef ANDROID
54 #include <android/log.h>
55 #endif
57 #if defined(XP_BEOS)
58 /* For DEBUGGER macros */
59 #include <Debug.h>
60 #endif
62 #if defined(XP_UNIX) || defined(_WIN32) || defined(XP_OS2) || defined(XP_BEOS)
63 /* for abort() and getenv() */
64 #include <stdlib.h>
65 #endif
67 #include "nsTraceRefcntImpl.h"
68 #include "nsISupportsUtils.h"
70 #if defined(XP_UNIX)
71 #include <signal.h>
72 #endif
74 #if defined(XP_WIN)
75 #include <tchar.h>
76 #include "nsString.h"
77 #endif
79 #include "mozilla/mozalloc_abort.h"
81 static void
82 Abort(const char *aMsg);
84 static void
85 RealBreak();
87 static void
88 Break(const char *aMsg);
90 #if defined(XP_OS2)
91 # define INCL_WINDIALOGS // need for WinMessageBox
92 # include <os2.h>
93 # include <string.h>
94 #endif /* XP_OS2 */
96 #if defined(_WIN32)
97 #include <windows.h>
98 #include <signal.h>
99 #include <malloc.h> // for _alloca
100 #elif defined(XP_UNIX)
101 #include <stdlib.h>
102 #endif
104 static PRInt32 gAssertionCount = 0;
106 NS_IMPL_QUERY_INTERFACE2(nsDebugImpl, nsIDebug, nsIDebug2)
108 NS_IMETHODIMP_(nsrefcnt)
109 nsDebugImpl::AddRef()
111 return 2;
114 NS_IMETHODIMP_(nsrefcnt)
115 nsDebugImpl::Release()
117 return 1;
120 NS_IMETHODIMP
121 nsDebugImpl::Assertion(const char *aStr, const char *aExpr,
122 const char *aFile, PRInt32 aLine)
124 NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
125 return NS_OK;
128 NS_IMETHODIMP
129 nsDebugImpl::Warning(const char *aStr, const char *aFile, PRInt32 aLine)
131 NS_DebugBreak(NS_DEBUG_WARNING, aStr, nsnull, aFile, aLine);
132 return NS_OK;
135 NS_IMETHODIMP
136 nsDebugImpl::Break(const char *aFile, PRInt32 aLine)
138 NS_DebugBreak(NS_DEBUG_BREAK, nsnull, nsnull, aFile, aLine);
139 return NS_OK;
142 NS_IMETHODIMP
143 nsDebugImpl::Abort(const char *aFile, PRInt32 aLine)
145 NS_DebugBreak(NS_DEBUG_ABORT, nsnull, nsnull, aFile, aLine);
146 return NS_OK;
149 NS_IMETHODIMP
150 nsDebugImpl::GetIsDebugBuild(PRBool* aResult)
152 #ifdef DEBUG
153 *aResult = PR_TRUE;
154 #else
155 *aResult = PR_FALSE;
156 #endif
157 return NS_OK;
160 NS_IMETHODIMP
161 nsDebugImpl::GetAssertionCount(PRInt32* aResult)
163 *aResult = gAssertionCount;
164 return NS_OK;
168 * Implementation of the nsDebug methods. Note that this code is
169 * always compiled in, in case some other module that uses it is
170 * compiled with debugging even if this library is not.
172 static PRLogModuleInfo* gDebugLog;
174 static void InitLog(void)
176 if (0 == gDebugLog) {
177 gDebugLog = PR_NewLogModule("nsDebug");
178 gDebugLog->level = PR_LOG_DEBUG;
182 enum nsAssertBehavior {
183 NS_ASSERT_UNINITIALIZED,
184 NS_ASSERT_WARN,
185 NS_ASSERT_SUSPEND,
186 NS_ASSERT_STACK,
187 NS_ASSERT_TRAP,
188 NS_ASSERT_ABORT,
189 NS_ASSERT_STACK_AND_ABORT
192 static nsAssertBehavior GetAssertBehavior()
194 static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
195 if (gAssertBehavior != NS_ASSERT_UNINITIALIZED)
196 return gAssertBehavior;
198 #if defined(XP_WIN) || defined(XP_OS2)
199 gAssertBehavior = NS_ASSERT_TRAP;
200 #else
201 gAssertBehavior = NS_ASSERT_WARN;
202 #endif
204 const char *assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
205 if (!assertString || !*assertString)
206 return gAssertBehavior;
208 if (!strcmp(assertString, "warn"))
209 return gAssertBehavior = NS_ASSERT_WARN;
211 if (!strcmp(assertString, "suspend"))
212 return gAssertBehavior = NS_ASSERT_SUSPEND;
214 if (!strcmp(assertString, "stack"))
215 return gAssertBehavior = NS_ASSERT_STACK;
217 if (!strcmp(assertString, "abort"))
218 return gAssertBehavior = NS_ASSERT_ABORT;
220 if (!strcmp(assertString, "trap") || !strcmp(assertString, "break"))
221 return gAssertBehavior = NS_ASSERT_TRAP;
223 if (!strcmp(assertString, "stack-and-abort"))
224 return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
226 fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
227 return gAssertBehavior;
230 struct FixedBuffer
232 FixedBuffer() : curlen(0) { buffer[0] = '\0'; }
234 char buffer[1000];
235 PRUint32 curlen;
238 static PRIntn
239 StuffFixedBuffer(void *closure, const char *buf, PRUint32 len)
241 if (!len)
242 return 0;
244 FixedBuffer *fb = (FixedBuffer*) closure;
246 // strip the trailing null, we add it again later
247 if (buf[len - 1] == '\0')
248 --len;
250 if (fb->curlen + len >= sizeof(fb->buffer))
251 len = sizeof(fb->buffer) - fb->curlen - 1;
253 if (len) {
254 memcpy(fb->buffer + fb->curlen, buf, len);
255 fb->curlen += len;
256 fb->buffer[fb->curlen] = '\0';
259 return len;
262 EXPORT_XPCOM_API(void)
263 NS_DebugBreak(PRUint32 aSeverity, const char *aStr, const char *aExpr,
264 const char *aFile, PRInt32 aLine)
266 InitLog();
268 FixedBuffer buf;
269 PRLogModuleLevel ll = PR_LOG_WARNING;
270 const char *sevString = "WARNING";
272 switch (aSeverity) {
273 case NS_DEBUG_ASSERTION:
274 sevString = "###!!! ASSERTION";
275 ll = PR_LOG_ERROR;
276 break;
278 case NS_DEBUG_BREAK:
279 sevString = "###!!! BREAK";
280 ll = PR_LOG_ALWAYS;
281 break;
283 case NS_DEBUG_ABORT:
284 sevString = "###!!! ABORT";
285 ll = PR_LOG_ALWAYS;
286 break;
288 default:
289 aSeverity = NS_DEBUG_WARNING;
292 PR_sxprintf(StuffFixedBuffer, &buf, "%s: ", sevString);
294 if (aStr)
295 PR_sxprintf(StuffFixedBuffer, &buf, "%s: ", aStr);
297 if (aExpr)
298 PR_sxprintf(StuffFixedBuffer, &buf, "'%s', ", aExpr);
300 if (aFile)
301 PR_sxprintf(StuffFixedBuffer, &buf, "file %s, ", aFile);
303 if (aLine != -1)
304 PR_sxprintf(StuffFixedBuffer, &buf, "line %d", aLine);
306 // Write out the message to the debug log
307 PR_LOG(gDebugLog, ll, ("%s", buf.buffer));
308 PR_LogFlush();
310 // errors on platforms without a debugdlg ring a bell on stderr
311 #if !defined(XP_WIN) && !defined(XP_OS2)
312 if (ll != PR_LOG_WARNING)
313 fprintf(stderr, "\07");
314 #endif
316 #ifdef ANDROID
317 __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
318 #endif
320 // Write the message to stderr
321 fprintf(stderr, "%s\n", buf.buffer);
322 fflush(stderr);
324 switch (aSeverity) {
325 case NS_DEBUG_WARNING:
326 return;
328 case NS_DEBUG_BREAK:
329 Break(buf.buffer);
330 return;
332 case NS_DEBUG_ABORT:
333 #if defined(DEBUG) && defined(_WIN32)
334 RealBreak();
335 #endif
336 nsTraceRefcntImpl::WalkTheStack(stderr);
337 Abort(buf.buffer);
338 return;
341 // Now we deal with assertions
342 PR_AtomicIncrement(&gAssertionCount);
344 switch (GetAssertBehavior()) {
345 case NS_ASSERT_WARN:
346 return;
348 case NS_ASSERT_SUSPEND:
349 #ifdef XP_UNIX
350 fprintf(stderr, "Suspending process; attach with the debugger.\n");
351 kill(0, SIGSTOP);
352 #else
353 Break(buf.buffer);
354 #endif
355 return;
357 case NS_ASSERT_STACK:
358 nsTraceRefcntImpl::WalkTheStack(stderr);
359 return;
361 case NS_ASSERT_STACK_AND_ABORT:
362 nsTraceRefcntImpl::WalkTheStack(stderr);
363 // Fall through to abort
365 case NS_ASSERT_ABORT:
366 Abort(buf.buffer);
367 return;
369 case NS_ASSERT_TRAP:
370 case NS_ASSERT_UNINITIALIZED: // Default to "trap" behavior
371 Break(buf.buffer);
372 return;
376 static void
377 Abort(const char *aMsg)
379 mozalloc_abort(aMsg);
382 static void
383 RealBreak()
385 #if defined(_WIN32)
386 #ifndef WINCE
387 ::DebugBreak();
388 #endif
389 #elif defined(XP_OS2)
390 asm("int $3");
391 #elif defined(XP_BEOS)
392 #elif defined(XP_MACOSX)
393 raise(SIGTRAP);
394 #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
395 asm("int $3");
396 #elif defined(__arm__)
397 asm("BKPT #0");
398 #elif defined(SOLARIS)
399 #if defined(__i386__) || defined(__i386) || defined(__x86_64__)
400 asm("int $3");
401 #else
402 raise(SIGTRAP);
403 #endif
404 #else
405 #warning don't know how to break on this platform
406 #endif
409 // Abort() calls this function, don't call it!
410 static void
411 Break(const char *aMsg)
413 #if defined(_WIN32)
414 #ifndef WINCE // we really just want to crash for now
415 static int ignoreDebugger;
416 if (!ignoreDebugger) {
417 const char *shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
418 ignoreDebugger = 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
420 if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) {
421 DWORD code = IDRETRY;
423 /* Create the debug dialog out of process to avoid the crashes caused by
424 * Windows events leaking into our event loop from an in process dialog.
425 * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
426 * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
428 PROCESS_INFORMATION pi;
429 STARTUPINFOW si;
430 PRUnichar executable[MAX_PATH];
431 PRUnichar* pName;
433 memset(&pi, 0, sizeof(pi));
435 memset(&si, 0, sizeof(si));
436 si.cb = sizeof(si);
437 si.wShowWindow = SW_SHOW;
439 // 2nd arg of CreateProcess is in/out
440 PRUnichar *msgCopy = (PRUnichar*) _alloca((strlen(aMsg) + 1)*sizeof(PRUnichar));
441 wcscpy(msgCopy , (PRUnichar*)NS_ConvertUTF8toUTF16(aMsg).get());
443 if(GetModuleFileNameW(GetModuleHandleW(L"xpcom.dll"), (LPWCH)executable, MAX_PATH) &&
444 NULL != (pName = wcsrchr(executable, '\\')) &&
445 NULL !=
446 wcscpy((WCHAR*)
447 pName+1, L"windbgdlg.exe") &&
448 CreateProcessW((LPCWSTR)executable, (LPWSTR)msgCopy, NULL, NULL, PR_FALSE,
449 DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
450 NULL, NULL, &si, &pi)) {
451 WaitForSingleObject(pi.hProcess, INFINITE);
452 GetExitCodeProcess(pi.hProcess, &code);
453 CloseHandle(pi.hProcess);
454 CloseHandle(pi.hThread);
457 switch(code) {
458 case IDABORT:
459 //This should exit us
460 raise(SIGABRT);
461 //If we are ignored exit this way..
462 _exit(3);
464 case IDIGNORE:
465 return;
469 RealBreak();
470 #endif // WINCE
471 #elif defined(XP_OS2)
472 char msg[1200];
473 PR_snprintf(msg, sizeof(msg),
474 "%s\n\nClick Cancel to Debug Application.\n"
475 "Click Enter to continue running the Application.", aMsg);
476 ULONG code = MBID_ERROR;
477 code = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, msg,
478 "NSGlue_Assertion", 0,
479 MB_ERROR | MB_ENTERCANCEL);
481 /* It is possible that we are executing on a thread that doesn't have a
482 * message queue. In that case, the message won't appear, and code will
483 * be 0xFFFF. We'll give the user a chance to debug it by calling
484 * Break()
485 * Actually, that's a really bad idea since this happens a lot with threadsafe
486 * assertions and since it means that you can't actually run the debug build
487 * outside a debugger without it crashing constantly.
489 if (( code == MBID_ENTER ) || (code == MBID_ERROR))
490 return;
492 RealBreak();
493 #elif defined(XP_BEOS)
494 DEBUGGER(aMsg);
495 RealBreak();
496 #elif defined(XP_MACOSX)
497 /* Note that we put this Mac OS X test above the GNUC/x86 test because the
498 * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
499 * impls to be the same.
501 RealBreak();
502 #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
503 RealBreak();
504 #elif defined(__arm__)
505 RealBreak();
506 #elif defined(SOLARIS)
507 RealBreak();
508 #else
509 #warning don't know how to break on this platform
510 #endif
513 static const nsDebugImpl kImpl;
515 nsresult
516 nsDebugImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
518 NS_ENSURE_NO_AGGREGATION(outer);
520 return const_cast<nsDebugImpl*>(&kImpl)->
521 QueryInterface(aIID, aInstancePtr);
524 ////////////////////////////////////////////////////////////////////////////////
526 NS_COM nsresult
527 NS_ErrorAccordingToNSPR()
529 PRErrorCode err = PR_GetError();
530 switch (err) {
531 case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
532 case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
533 case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
534 case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
535 case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
536 case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
537 case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
538 case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
539 case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
540 case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
541 case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
542 case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
543 case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
544 case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
545 default: return NS_ERROR_FAILURE;
549 ////////////////////////////////////////////////////////////////////////////////
551 #ifdef XP_WIN
552 NS_COM PRBool sXPCOMHasLoadedNewDLLs = PR_FALSE;
554 NS_EXPORT void
555 NS_SetHasLoadedNewDLLs()
557 sXPCOMHasLoadedNewDLLs = PR_TRUE;
559 #endif