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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * IBM Corp.
24 : * Henry Sobotka
25 : * Benjamin Smedberg <benjamin@smedbergs.us>
26 : *
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.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsDebugImpl.h"
42 : #include "nsDebug.h"
43 : #ifdef MOZ_CRASHREPORTER
44 : # include "nsExceptionHandler.h"
45 : #endif
46 : #include "nsStringGlue.h"
47 : #include "prprf.h"
48 : #include "prlog.h"
49 : #include "prinit.h"
50 : #include "plstr.h"
51 : #include "nsError.h"
52 : #include "prerror.h"
53 : #include "prerr.h"
54 : #include "prenv.h"
55 : #include "pratom.h"
56 :
57 : #ifdef ANDROID
58 : #include <android/log.h>
59 : #endif
60 :
61 : #if defined(XP_UNIX) || defined(_WIN32) || defined(XP_OS2)
62 : /* for abort() and getenv() */
63 : #include <stdlib.h>
64 : #endif
65 :
66 : #include "nsTraceRefcntImpl.h"
67 : #include "nsISupportsUtils.h"
68 :
69 : #if defined(XP_UNIX)
70 : #include <signal.h>
71 : #endif
72 :
73 : #if defined(XP_WIN)
74 : #include <tchar.h>
75 : #include "nsString.h"
76 : #endif
77 :
78 : #include "mozilla/mozalloc_abort.h"
79 :
80 : static void
81 : Abort(const char *aMsg);
82 :
83 : static void
84 : RealBreak();
85 :
86 : static void
87 : Break(const char *aMsg);
88 :
89 : #if defined(XP_OS2)
90 : # define INCL_WINDIALOGS // need for WinMessageBox
91 : # include <os2.h>
92 : # include <string.h>
93 : #endif /* XP_OS2 */
94 :
95 : #if defined(_WIN32)
96 : #include <windows.h>
97 : #include <signal.h>
98 : #include <malloc.h> // for _alloca
99 : #elif defined(XP_UNIX)
100 : #include <stdlib.h>
101 : #endif
102 :
103 : static PRInt32 gAssertionCount = 0;
104 :
105 0 : NS_IMPL_QUERY_INTERFACE2(nsDebugImpl, nsIDebug, nsIDebug2)
106 :
107 : NS_IMETHODIMP_(nsrefcnt)
108 0 : nsDebugImpl::AddRef()
109 : {
110 0 : return 2;
111 : }
112 :
113 : NS_IMETHODIMP_(nsrefcnt)
114 0 : nsDebugImpl::Release()
115 : {
116 0 : return 1;
117 : }
118 :
119 : NS_IMETHODIMP
120 0 : nsDebugImpl::Assertion(const char *aStr, const char *aExpr,
121 : const char *aFile, PRInt32 aLine)
122 : {
123 0 : NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
124 0 : return NS_OK;
125 : }
126 :
127 : NS_IMETHODIMP
128 0 : nsDebugImpl::Warning(const char *aStr, const char *aFile, PRInt32 aLine)
129 : {
130 0 : NS_DebugBreak(NS_DEBUG_WARNING, aStr, nsnull, aFile, aLine);
131 0 : return NS_OK;
132 : }
133 :
134 : NS_IMETHODIMP
135 0 : nsDebugImpl::Break(const char *aFile, PRInt32 aLine)
136 : {
137 0 : NS_DebugBreak(NS_DEBUG_BREAK, nsnull, nsnull, aFile, aLine);
138 0 : return NS_OK;
139 : }
140 :
141 : NS_IMETHODIMP
142 0 : nsDebugImpl::Abort(const char *aFile, PRInt32 aLine)
143 : {
144 0 : NS_DebugBreak(NS_DEBUG_ABORT, nsnull, nsnull, aFile, aLine);
145 0 : return NS_OK;
146 : }
147 :
148 : NS_IMETHODIMP
149 0 : nsDebugImpl::GetIsDebugBuild(bool* aResult)
150 : {
151 : #ifdef DEBUG
152 0 : *aResult = true;
153 : #else
154 : *aResult = false;
155 : #endif
156 0 : return NS_OK;
157 : }
158 :
159 : NS_IMETHODIMP
160 0 : nsDebugImpl::GetAssertionCount(PRInt32* aResult)
161 : {
162 0 : *aResult = gAssertionCount;
163 0 : return NS_OK;
164 : }
165 :
166 : /**
167 : * Implementation of the nsDebug methods. Note that this code is
168 : * always compiled in, in case some other module that uses it is
169 : * compiled with debugging even if this library is not.
170 : */
171 : static PRLogModuleInfo* gDebugLog;
172 :
173 6012 : static void InitLog(void)
174 : {
175 6012 : if (0 == gDebugLog) {
176 1398 : gDebugLog = PR_NewLogModule("nsDebug");
177 1398 : gDebugLog->level = PR_LOG_DEBUG;
178 : }
179 6012 : }
180 :
181 : enum nsAssertBehavior {
182 : NS_ASSERT_UNINITIALIZED,
183 : NS_ASSERT_WARN,
184 : NS_ASSERT_SUSPEND,
185 : NS_ASSERT_STACK,
186 : NS_ASSERT_TRAP,
187 : NS_ASSERT_ABORT,
188 : NS_ASSERT_STACK_AND_ABORT
189 : };
190 :
191 0 : static nsAssertBehavior GetAssertBehavior()
192 : {
193 : static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
194 0 : if (gAssertBehavior != NS_ASSERT_UNINITIALIZED)
195 0 : return gAssertBehavior;
196 :
197 : #if defined(XP_WIN) || defined(XP_OS2)
198 : gAssertBehavior = NS_ASSERT_TRAP;
199 : #else
200 0 : gAssertBehavior = NS_ASSERT_WARN;
201 : #endif
202 :
203 0 : const char *assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
204 0 : if (!assertString || !*assertString)
205 0 : return gAssertBehavior;
206 :
207 0 : if (!strcmp(assertString, "warn"))
208 0 : return gAssertBehavior = NS_ASSERT_WARN;
209 :
210 0 : if (!strcmp(assertString, "suspend"))
211 0 : return gAssertBehavior = NS_ASSERT_SUSPEND;
212 :
213 0 : if (!strcmp(assertString, "stack"))
214 0 : return gAssertBehavior = NS_ASSERT_STACK;
215 :
216 0 : if (!strcmp(assertString, "abort"))
217 0 : return gAssertBehavior = NS_ASSERT_ABORT;
218 :
219 0 : if (!strcmp(assertString, "trap") || !strcmp(assertString, "break"))
220 0 : return gAssertBehavior = NS_ASSERT_TRAP;
221 :
222 0 : if (!strcmp(assertString, "stack-and-abort"))
223 0 : return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
224 :
225 0 : fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
226 0 : return gAssertBehavior;
227 : }
228 :
229 : struct FixedBuffer
230 : {
231 6012 : FixedBuffer() : curlen(0) { buffer[0] = '\0'; }
232 :
233 : char buffer[1000];
234 : PRUint32 curlen;
235 : };
236 :
237 : static PRIntn
238 144288 : StuffFixedBuffer(void *closure, const char *buf, PRUint32 len)
239 : {
240 144288 : if (!len)
241 0 : return 0;
242 :
243 144288 : FixedBuffer *fb = (FixedBuffer*) closure;
244 :
245 : // strip the trailing null, we add it again later
246 144288 : if (buf[len - 1] == '\0')
247 24048 : --len;
248 :
249 144288 : if (fb->curlen + len >= sizeof(fb->buffer))
250 0 : len = sizeof(fb->buffer) - fb->curlen - 1;
251 :
252 144288 : if (len) {
253 120240 : memcpy(fb->buffer + fb->curlen, buf, len);
254 120240 : fb->curlen += len;
255 120240 : fb->buffer[fb->curlen] = '\0';
256 : }
257 :
258 144288 : return len;
259 : }
260 :
261 : EXPORT_XPCOM_API(void)
262 6012 : NS_DebugBreak(PRUint32 aSeverity, const char *aStr, const char *aExpr,
263 : const char *aFile, PRInt32 aLine)
264 : {
265 6012 : InitLog();
266 :
267 6012 : FixedBuffer buf;
268 6012 : PRLogModuleLevel ll = PR_LOG_WARNING;
269 6012 : const char *sevString = "WARNING";
270 :
271 6012 : switch (aSeverity) {
272 : case NS_DEBUG_ASSERTION:
273 0 : sevString = "###!!! ASSERTION";
274 0 : ll = PR_LOG_ERROR;
275 0 : break;
276 :
277 : case NS_DEBUG_BREAK:
278 0 : sevString = "###!!! BREAK";
279 0 : ll = PR_LOG_ALWAYS;
280 0 : break;
281 :
282 : case NS_DEBUG_ABORT:
283 0 : sevString = "###!!! ABORT";
284 0 : ll = PR_LOG_ALWAYS;
285 0 : break;
286 :
287 : default:
288 6012 : aSeverity = NS_DEBUG_WARNING;
289 : };
290 :
291 6012 : PR_sxprintf(StuffFixedBuffer, &buf, "%s: ", sevString);
292 :
293 6012 : if (aStr)
294 6012 : PR_sxprintf(StuffFixedBuffer, &buf, "%s: ", aStr);
295 :
296 6012 : if (aExpr)
297 0 : PR_sxprintf(StuffFixedBuffer, &buf, "'%s', ", aExpr);
298 :
299 6012 : if (aFile)
300 6012 : PR_sxprintf(StuffFixedBuffer, &buf, "file %s, ", aFile);
301 :
302 6012 : if (aLine != -1)
303 6012 : PR_sxprintf(StuffFixedBuffer, &buf, "line %d", aLine);
304 :
305 : // Write out the message to the debug log
306 6012 : PR_LOG(gDebugLog, ll, ("%s", buf.buffer));
307 6012 : PR_LogFlush();
308 :
309 : // errors on platforms without a debugdlg ring a bell on stderr
310 : #if !defined(XP_WIN) && !defined(XP_OS2)
311 6012 : if (ll != PR_LOG_WARNING)
312 0 : fprintf(stderr, "\07");
313 : #endif
314 :
315 : #ifdef ANDROID
316 : __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
317 : #endif
318 :
319 : // Write the message to stderr
320 6012 : fprintf(stderr, "%s\n", buf.buffer);
321 6012 : fflush(stderr);
322 :
323 6012 : switch (aSeverity) {
324 : case NS_DEBUG_WARNING:
325 6012 : return;
326 :
327 : case NS_DEBUG_BREAK:
328 0 : Break(buf.buffer);
329 0 : return;
330 :
331 : case NS_DEBUG_ABORT: {
332 : #if defined(MOZ_CRASHREPORTER)
333 0 : nsCString note("xpcom_runtime_abort(");
334 0 : note += buf.buffer;
335 0 : note += ")";
336 0 : CrashReporter::AppendAppNotesToCrashReport(note);
337 : #endif // MOZ_CRASHREPORTER
338 :
339 : #if defined(DEBUG) && defined(_WIN32)
340 : RealBreak();
341 : #endif
342 : #ifdef DEBUG
343 0 : nsTraceRefcntImpl::WalkTheStack(stderr);
344 : #endif
345 0 : Abort(buf.buffer);
346 : return;
347 : }
348 : }
349 :
350 : // Now we deal with assertions
351 0 : PR_ATOMIC_INCREMENT(&gAssertionCount);
352 :
353 0 : switch (GetAssertBehavior()) {
354 : case NS_ASSERT_WARN:
355 0 : return;
356 :
357 : case NS_ASSERT_SUSPEND:
358 : #ifdef XP_UNIX
359 0 : fprintf(stderr, "Suspending process; attach with the debugger.\n");
360 0 : kill(0, SIGSTOP);
361 : #else
362 : Break(buf.buffer);
363 : #endif
364 0 : return;
365 :
366 : case NS_ASSERT_STACK:
367 0 : nsTraceRefcntImpl::WalkTheStack(stderr);
368 0 : return;
369 :
370 : case NS_ASSERT_STACK_AND_ABORT:
371 0 : nsTraceRefcntImpl::WalkTheStack(stderr);
372 : // Fall through to abort
373 :
374 : case NS_ASSERT_ABORT:
375 0 : Abort(buf.buffer);
376 0 : return;
377 :
378 : case NS_ASSERT_TRAP:
379 : case NS_ASSERT_UNINITIALIZED: // Default to "trap" behavior
380 0 : Break(buf.buffer);
381 0 : return;
382 : }
383 : }
384 :
385 : static void
386 0 : Abort(const char *aMsg)
387 : {
388 0 : mozalloc_abort(aMsg);
389 : }
390 :
391 : static void
392 0 : RealBreak()
393 : {
394 : #if defined(_WIN32)
395 : ::DebugBreak();
396 : #elif defined(XP_OS2)
397 : asm("int $3");
398 : #elif defined(XP_MACOSX)
399 : raise(SIGTRAP);
400 : #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
401 0 : asm("int $3");
402 : #elif defined(__arm__)
403 : asm(
404 : #ifdef __ARM_ARCH_4T__
405 : /* ARMv4T doesn't support the BKPT instruction, so if the compiler target
406 : * is ARMv4T, we want to ensure the assembler will understand that ARMv5T
407 : * instruction, while keeping the resulting object tagged as ARMv4T.
408 : */
409 : ".arch armv5t\n"
410 : ".object_arch armv4t\n"
411 : #endif
412 : "BKPT #0");
413 : #elif defined(SOLARIS)
414 : #if defined(__i386__) || defined(__i386) || defined(__x86_64__)
415 : asm("int $3");
416 : #else
417 : raise(SIGTRAP);
418 : #endif
419 : #else
420 : #warning do not know how to break on this platform
421 : #endif
422 0 : }
423 :
424 : // Abort() calls this function, don't call it!
425 : static void
426 0 : Break(const char *aMsg)
427 : {
428 : #if defined(_WIN32)
429 : static int ignoreDebugger;
430 : if (!ignoreDebugger) {
431 : const char *shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
432 : ignoreDebugger = 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
433 : }
434 : if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) {
435 : DWORD code = IDRETRY;
436 :
437 : /* Create the debug dialog out of process to avoid the crashes caused by
438 : * Windows events leaking into our event loop from an in process dialog.
439 : * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
440 : * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
441 : */
442 : PROCESS_INFORMATION pi;
443 : STARTUPINFOW si;
444 : PRUnichar executable[MAX_PATH];
445 : PRUnichar* pName;
446 :
447 : memset(&pi, 0, sizeof(pi));
448 :
449 : memset(&si, 0, sizeof(si));
450 : si.cb = sizeof(si);
451 : si.wShowWindow = SW_SHOW;
452 :
453 : // 2nd arg of CreateProcess is in/out
454 : PRUnichar *msgCopy = (PRUnichar*) _alloca((strlen(aMsg) + 1)*sizeof(PRUnichar));
455 : wcscpy(msgCopy , (PRUnichar*)NS_ConvertUTF8toUTF16(aMsg).get());
456 :
457 : if(GetModuleFileNameW(GetModuleHandleW(L"xpcom.dll"), (LPWCH)executable, MAX_PATH) &&
458 : NULL != (pName = wcsrchr(executable, '\\')) &&
459 : NULL !=
460 : wcscpy((WCHAR*)
461 : pName+1, L"windbgdlg.exe") &&
462 : CreateProcessW((LPCWSTR)executable, (LPWSTR)msgCopy, NULL, NULL, false,
463 : DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
464 : NULL, NULL, &si, &pi)) {
465 : WaitForSingleObject(pi.hProcess, INFINITE);
466 : GetExitCodeProcess(pi.hProcess, &code);
467 : CloseHandle(pi.hProcess);
468 : CloseHandle(pi.hThread);
469 : }
470 :
471 : switch(code) {
472 : case IDABORT:
473 : //This should exit us
474 : raise(SIGABRT);
475 : //If we are ignored exit this way..
476 : _exit(3);
477 :
478 : case IDIGNORE:
479 : return;
480 : }
481 : }
482 :
483 : RealBreak();
484 : #elif defined(XP_OS2)
485 : char msg[1200];
486 : PR_snprintf(msg, sizeof(msg),
487 : "%s\n\nClick Cancel to Debug Application.\n"
488 : "Click Enter to continue running the Application.", aMsg);
489 : ULONG code = MBID_ERROR;
490 : code = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, msg,
491 : "NSGlue_Assertion", 0,
492 : MB_ERROR | MB_ENTERCANCEL);
493 :
494 : /* It is possible that we are executing on a thread that doesn't have a
495 : * message queue. In that case, the message won't appear, and code will
496 : * be 0xFFFF. We'll give the user a chance to debug it by calling
497 : * Break()
498 : * Actually, that's a really bad idea since this happens a lot with threadsafe
499 : * assertions and since it means that you can't actually run the debug build
500 : * outside a debugger without it crashing constantly.
501 : */
502 : if (( code == MBID_ENTER ) || (code == MBID_ERROR))
503 : return;
504 :
505 : RealBreak();
506 : #elif defined(XP_MACOSX)
507 : /* Note that we put this Mac OS X test above the GNUC/x86 test because the
508 : * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
509 : * impls to be the same.
510 : */
511 : RealBreak();
512 : #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
513 0 : RealBreak();
514 : #elif defined(__arm__)
515 : RealBreak();
516 : #elif defined(SOLARIS)
517 : RealBreak();
518 : #else
519 : #warning do not know how to break on this platform
520 : #endif
521 0 : }
522 :
523 1464 : static const nsDebugImpl kImpl;
524 :
525 : nsresult
526 0 : nsDebugImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
527 : {
528 0 : NS_ENSURE_NO_AGGREGATION(outer);
529 :
530 : return const_cast<nsDebugImpl*>(&kImpl)->
531 0 : QueryInterface(aIID, aInstancePtr);
532 : }
533 :
534 : ////////////////////////////////////////////////////////////////////////////////
535 :
536 : nsresult
537 767 : NS_ErrorAccordingToNSPR()
538 : {
539 767 : PRErrorCode err = PR_GetError();
540 767 : switch (err) {
541 0 : case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
542 0 : case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
543 767 : case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
544 0 : case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
545 0 : case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
546 0 : case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
547 0 : case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
548 0 : case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
549 0 : case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
550 0 : case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
551 0 : case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
552 0 : case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
553 0 : case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
554 0 : case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
555 0 : default: return NS_ERROR_FAILURE;
556 : }
557 4392 : }
558 :
559 : ////////////////////////////////////////////////////////////////////////////////
560 :
561 : #ifdef XP_WIN
562 : bool sXPCOMHasLoadedNewDLLs = false;
563 :
564 : NS_EXPORT void
565 : NS_SetHasLoadedNewDLLs()
566 : {
567 : sXPCOMHasLoadedNewDLLs = true;
568 : }
569 : #endif
|