diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index 5e4200001cc5..31e918198525 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -69,6 +69,7 @@ included_inclnames_to_ignore = set( "jit/LIROpsGenerated.h", # generated in $OBJDIR "jit/MIROpsGenerated.h", # generated in $OBJDIR "js/ProfilingCategoryList.h", # comes from mozglue/baseprofiler + "mozilla/glue/Debug.h", # comes from mozglue/misc, shadowed by "jscustomallocator.h", # provided by embedders; allowed to be missing "js-config.h", # generated in $OBJDIR "fdlibm.h", # fdlibm diff --git a/config/check_vanilla_allocations.py b/config/check_vanilla_allocations.py index 32e313d38604..928f7cd6a757 100644 --- a/config/check_vanilla_allocations.py +++ b/config/check_vanilla_allocations.py @@ -147,6 +147,10 @@ def main(): "Decimal.o", # Ignore use of std::string in regexp AST debug output. "regexp-ast.o", + # mozglue/misc/Debug.cpp contains a call to `printf_stderr("%s", aStr.str().c_str())` + # where `aStr` is a `std::stringstream`. In inlined opt builds, this calls + # `operator new()` and `operator delete` for a temporary. + "Debug.o", ] all_ignored_files = set((f, 1) for f in ignored_files) diff --git a/mozglue/misc/Debug.cpp b/mozglue/misc/Debug.cpp new file mode 100644 index 000000000000..c3a2ca89e079 --- /dev/null +++ b/mozglue/misc/Debug.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/glue/Debug.h" +#include "mozilla/Fuzzing.h" +#include "mozilla/Sprintf.h" + +#include +#include + +#ifdef XP_WIN +# include +# include +#endif + +#ifdef ANDROID +# include +#endif + +#ifndef ANDROID +static void vprintf_stderr_buffered(const char* aFmt, va_list aArgs) { + // Avoid interleaving by writing to an on-stack buffer and then writing in one + // go with fputs, as long as the output fits into the buffer. + char buffer[1024]; + va_list argsCpy; + va_copy(argsCpy, aArgs); + int n = VsprintfLiteral(buffer, aFmt, aArgs); + if (n < int(sizeof(buffer))) { + fputs(buffer, stderr); + } else { + // Message too long for buffer. Just print it, not worrying about + // interleaving. (We could malloc, but the underlying write() syscall could + // get interleaved if the output is too big anyway.) + vfprintf(stderr, aFmt, argsCpy); + } + va_end(argsCpy); + fflush(stderr); +} +#endif + +#if defined(XP_WIN) +MFBT_API void vprintf_stderr(const char* aFmt, va_list aArgs) { + if (IsDebuggerPresent()) { + int lengthNeeded = _vscprintf(aFmt, aArgs); + if (lengthNeeded) { + lengthNeeded++; + auto buf = mozilla::MakeUnique(lengthNeeded); + if (buf) { + va_list argsCpy; + va_copy(argsCpy, aArgs); + vsnprintf(buf.get(), lengthNeeded, aFmt, argsCpy); + buf[lengthNeeded - 1] = '\0'; + va_end(argsCpy); + OutputDebugStringA(buf.get()); + } + } + } + + vprintf_stderr_buffered(aFmt, aArgs); +} + +#elif defined(ANDROID) +MFBT_API void vprintf_stderr(const char* aFmt, va_list aArgs) { + __android_log_vprint(ANDROID_LOG_INFO, "Gecko", aFmt, aArgs); +} +#elif defined(FUZZING_SNAPSHOT) +MFBT_API void vprintf_stderr(const char* aFmt, va_list aArgs) { + if (nyx_puts) { + auto msgbuf = mozilla::Vsmprintf(aFmt, aArgs); + nyx_puts(msgbuf.get()); + } else { + vprintf_stderr_buffered(aFmt, aArgs); + } +} +#else +MFBT_API void vprintf_stderr(const char* aFmt, va_list aArgs) { + vprintf_stderr_buffered(aFmt, aArgs); +} +#endif + +MFBT_API void printf_stderr(const char* aFmt, ...) { + va_list args; + va_start(args, aFmt); + vprintf_stderr(aFmt, args); + va_end(args); +} + +MFBT_API void fprintf_stderr(FILE* aFile, const char* aFmt, ...) { + va_list args; + va_start(args, aFmt); + if (aFile == stderr) { + vprintf_stderr(aFmt, args); + } else { + vfprintf(aFile, aFmt, args); + } + va_end(args); +} + +MFBT_API void print_stderr(std::stringstream& aStr) { +#if defined(ANDROID) + // On Android logcat output is truncated to 1024 chars per line, and + // we usually use std::stringstream to build up giant multi-line gobs + // of output. So to avoid the truncation we find the newlines and + // print the lines individually. + std::string line; + while (std::getline(aStr, line)) { + printf_stderr("%s\n", line.c_str()); + } +#else + printf_stderr("%s", aStr.str().c_str()); +#endif +} + +MFBT_API void fprint_stderr(FILE* aFile, std::stringstream& aStr) { + if (aFile == stderr) { + print_stderr(aStr); + } else { + fprintf_stderr(aFile, "%s", aStr.str().c_str()); + } +} diff --git a/mozglue/misc/Debug.h b/mozglue/misc/Debug.h index f8f05c629904..43380878b9f7 100644 --- a/mozglue/misc/Debug.h +++ b/mozglue/misc/Debug.h @@ -7,6 +7,12 @@ #ifndef mozilla_glue_Debug_h #define mozilla_glue_Debug_h +#include "mozilla/Attributes.h" // For MOZ_FORMAT_PRINTF +#include "mozilla/Types.h" // For MFBT_API + +#include +#include + /* This header file intends to supply debugging utilities for use in code * that cannot use XPCOM debugging facilities like nsDebug.h. * e.g. mozglue, browser/app @@ -15,52 +21,53 @@ * care; avoid including from header files. */ -#include -#if defined(XP_WIN) -# include -#endif // defined(XP_WIN) -#include "mozilla/Attributes.h" -#include "mozilla/Sprintf.h" - -#if defined(MOZILLA_INTERNAL_API) -# error Do not include this file from XUL sources. -#endif - -// Though this is a separate implementation than nsDebug's, we want to make the -// declarations compatible to avoid confusing the linker if both headers are -// included. #ifdef __cplusplus extern "C" { #endif // __cplusplus -void printf_stderr(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2); -inline void printf_stderr(const char* fmt, ...) { -#if defined(XP_WIN) - if (IsDebuggerPresent()) { - char buf[2048]; - va_list args; - va_start(args, fmt); - VsprintfLiteral(buf, fmt, args); - va_end(args); - OutputDebugStringA(buf); - } -#endif // defined(XP_WIN) +/** + * printf_stderr(...) is much like fprintf(stderr, ...), except that: + * - on Android and Firefox OS, *instead* of printing to stderr, it + * prints to logcat. (Newlines in the string lead to multiple lines + * of logcat, but each function call implicitly completes a line even + * if the string does not end with a newline.) + * - on Windows, if a debugger is present, it calls OutputDebugString + * in *addition* to writing to stderr + */ +MFBT_API void printf_stderr(const char* aFmt, ...) MOZ_FORMAT_PRINTF(1, 2); - // stderr is unbuffered by default so we open a new FILE (which is buffered) - // so that calls to printf_stderr are not as likely to get mixed together. - int fd = _fileno(stderr); - if (fd == -2) return; +/** + * Same as printf_stderr, but taking va_list instead of varargs + */ +MFBT_API void vprintf_stderr(const char* aFmt, va_list aArgs) + MOZ_FORMAT_PRINTF(1, 0); - FILE* fp = _fdopen(_dup(fd), "a"); - if (!fp) return; +/** + * fprintf_stderr is like fprintf, except that if its file argument + * is stderr, it invokes printf_stderr instead. + * + * This is useful for general debugging code that logs information to a + * file, but that you would like to be useful on Android and Firefox OS. + * If you use fprintf_stderr instead of fprintf in such debugging code, + * then callers can pass stderr to get logging that works on Android and + * Firefox OS (and also the other side-effects of using printf_stderr). + * + * Code that is structured this way needs to be careful not to split a + * line of output across multiple calls to fprintf_stderr, since doing + * so will cause it to appear in multiple lines in logcat output. + * (Producing multiple lines at once is fine.) + */ +MFBT_API void fprintf_stderr(FILE* aFile, const char* aFmt, ...) + MOZ_FORMAT_PRINTF(2, 3); - va_list args; - va_start(args, fmt); - vfprintf(fp, fmt, args); - va_end(args); - - fclose(fp); -} +/* + * print_stderr and fprint_stderr are like printf_stderr and fprintf_stderr, + * except they deal with Android logcat line length limitations. They do this + * by printing individual lines out of the provided stringstream using separate + * calls to logcat. + */ +MFBT_API void print_stderr(std::stringstream& aStr); +MFBT_API void fprint_stderr(FILE* aFile, std::stringstream& aStr); #ifdef __cplusplus } diff --git a/mozglue/misc/moz.build b/mozglue/misc/moz.build index 0976d80a6d36..c0ff86ea0517 100644 --- a/mozglue/misc/moz.build +++ b/mozglue/misc/moz.build @@ -43,6 +43,7 @@ if CONFIG["OS_ARCH"] == "WINNT": SOURCES += [ "AutoProfilerLabel.cpp", "AwakeTimeStamp.cpp", + "Debug.cpp", "MmapFaultHandler.cpp", "Printf.cpp", "SIMD.cpp", diff --git a/xpcom/base/nsCRTGlue.cpp b/xpcom/base/nsCRTGlue.cpp index c5c6f473d70f..99289daa6316 100644 --- a/xpcom/base/nsCRTGlue.cpp +++ b/xpcom/base/nsCRTGlue.cpp @@ -19,11 +19,6 @@ #ifdef XP_WIN # include # include -# include "mozilla/UniquePtr.h" -#endif - -#ifdef ANDROID -# include #endif #ifdef FUZZING_SNAPSHOT @@ -229,105 +224,3 @@ void NS_MakeRandomString(char* aBuf, int32_t aBufLen) { } #endif - -#ifndef ANDROID -void vprintf_stderr_buffered(const char* aFmt, va_list aArgs) { - // Avoid interleaving by writing to an on-stack buffer and then writing in one - // go with fputs, as long as the output fits into the buffer. - char buffer[1024]; - va_list argsCpy; - va_copy(argsCpy, aArgs); - int n = VsprintfLiteral(buffer, aFmt, aArgs); - if (n < sizeof(buffer)) { - fputs(buffer, stderr); - } else { - // Message too long for buffer. Just print it, not worrying about - // interleaving. (We could malloc, but the underlying write() syscall could - // get interleaved if the output is too big anyway.) - vfprintf(stderr, aFmt, argsCpy); - } - va_end(argsCpy); - fflush(stderr); -} -#endif - -#if defined(XP_WIN) -void vprintf_stderr(const char* aFmt, va_list aArgs) { - if (IsDebuggerPresent()) { - int lengthNeeded = _vscprintf(aFmt, aArgs); - if (lengthNeeded) { - lengthNeeded++; - auto buf = MakeUnique(lengthNeeded); - if (buf) { - va_list argsCpy; - va_copy(argsCpy, aArgs); - vsnprintf(buf.get(), lengthNeeded, aFmt, argsCpy); - buf[lengthNeeded - 1] = '\0'; - va_end(argsCpy); - OutputDebugStringA(buf.get()); - } - } - } - - vprintf_stderr_buffered(aFmt, aArgs); -} - -#elif defined(ANDROID) -void vprintf_stderr(const char* aFmt, va_list aArgs) { - __android_log_vprint(ANDROID_LOG_INFO, "Gecko", aFmt, aArgs); -} -#elif defined(FUZZING_SNAPSHOT) -void vprintf_stderr(const char* aFmt, va_list aArgs) { - if (nyx_puts) { - auto msgbuf = mozilla::Vsmprintf(aFmt, aArgs); - nyx_puts(msgbuf.get()); - } else { - vprintf_stderr_buffered(aFmt, aArgs); - } -} -#else -void vprintf_stderr(const char* aFmt, va_list aArgs) { - vprintf_stderr_buffered(aFmt, aArgs); -} -#endif - -void printf_stderr(const char* aFmt, ...) { - va_list args; - va_start(args, aFmt); - vprintf_stderr(aFmt, args); - va_end(args); -} - -void fprintf_stderr(FILE* aFile, const char* aFmt, ...) { - va_list args; - va_start(args, aFmt); - if (aFile == stderr) { - vprintf_stderr(aFmt, args); - } else { - vfprintf(aFile, aFmt, args); - } - va_end(args); -} - -void print_stderr(std::stringstream& aStr) { -#if defined(ANDROID) - // On Android logcat output is truncated to 1024 chars per line, and - // we usually use std::stringstream to build up giant multi-line gobs - // of output. So to avoid the truncation we find the newlines and - // print the lines individually. - std::string line; - while (std::getline(aStr, line)) { - printf_stderr("%s\n", line.c_str()); - } -#else - printf_stderr("%s", aStr.str().c_str()); -#endif -} - -void fprint_stderr(FILE* aFile, std::stringstream& aStr) { - if (aFile == stderr) { - print_stderr(aStr); - } else { - fprintf_stderr(aFile, "%s", aStr.str().c_str()); - } -} diff --git a/xpcom/base/nsDebug.h b/xpcom/base/nsDebug.h index 349c297ea8bf..3e3fc4d89eef 100644 --- a/xpcom/base/nsDebug.h +++ b/xpcom/base/nsDebug.h @@ -12,6 +12,7 @@ #include "nsXPCOM.h" #include "mozilla/Assertions.h" +#include "mozilla/glue/Debug.h" #include "mozilla/DbgMacro.h" #include "mozilla/Likely.h" #include @@ -326,59 +327,4 @@ void NS_ABORT_OOM(size_t aSize); inline void NS_ABORT_OOM(size_t) { MOZ_CRASH(); } #endif -/* When compiling the XPCOM Glue on Windows, we pretend that it's going to - * be linked with a static CRT (-MT) even when it's not. This means that we - * cannot link to data exports from the CRT, only function exports. So, - * instead of referencing "stderr" directly, use fdopen. - */ -#ifdef __cplusplus -extern "C" { -#endif - -/** - * printf_stderr(...) is much like fprintf(stderr, ...), except that: - * - on Android and Firefox OS, *instead* of printing to stderr, it - * prints to logcat. (Newlines in the string lead to multiple lines - * of logcat, but each function call implicitly completes a line even - * if the string does not end with a newline.) - * - on Windows, if a debugger is present, it calls OutputDebugString - * in *addition* to writing to stderr - */ -void printf_stderr(const char* aFmt, ...) MOZ_FORMAT_PRINTF(1, 2); - -/** - * Same as printf_stderr, but taking va_list instead of varargs - */ -void vprintf_stderr(const char* aFmt, va_list aArgs) MOZ_FORMAT_PRINTF(1, 0); - -/** - * fprintf_stderr is like fprintf, except that if its file argument - * is stderr, it invokes printf_stderr instead. - * - * This is useful for general debugging code that logs information to a - * file, but that you would like to be useful on Android and Firefox OS. - * If you use fprintf_stderr instead of fprintf in such debugging code, - * then callers can pass stderr to get logging that works on Android and - * Firefox OS (and also the other side-effects of using printf_stderr). - * - * Code that is structured this way needs to be careful not to split a - * line of output across multiple calls to fprintf_stderr, since doing - * so will cause it to appear in multiple lines in logcat output. - * (Producing multiple lines at once is fine.) - */ -void fprintf_stderr(FILE* aFile, const char* aFmt, ...) MOZ_FORMAT_PRINTF(2, 3); - -/* - * print_stderr and fprint_stderr are like printf_stderr and fprintf_stderr, - * except they deal with Android logcat line length limitations. They do this - * by printing individual lines out of the provided stringstream using separate - * calls to logcat. - */ -void print_stderr(std::stringstream& aStr); -void fprint_stderr(FILE* aFile, std::stringstream& aStr); - -#ifdef __cplusplus -} -#endif - #endif /* nsDebug_h___ */