зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1790873 - Move printf_stderr() to mozglue so it can be used within js/src r=glandium,nika
Differential Revision: https://phabricator.services.mozilla.com/D157354
This commit is contained in:
Родитель
938a635e60
Коммит
ba7d3327fc
|
@ -69,6 +69,7 @@ included_inclnames_to_ignore = set(
|
||||||
"jit/LIROpsGenerated.h", # generated in $OBJDIR
|
"jit/LIROpsGenerated.h", # generated in $OBJDIR
|
||||||
"jit/MIROpsGenerated.h", # generated in $OBJDIR
|
"jit/MIROpsGenerated.h", # generated in $OBJDIR
|
||||||
"js/ProfilingCategoryList.h", # comes from mozglue/baseprofiler
|
"js/ProfilingCategoryList.h", # comes from mozglue/baseprofiler
|
||||||
|
"mozilla/glue/Debug.h", # comes from mozglue/misc, shadowed by <mozilla/Debug.h>
|
||||||
"jscustomallocator.h", # provided by embedders; allowed to be missing
|
"jscustomallocator.h", # provided by embedders; allowed to be missing
|
||||||
"js-config.h", # generated in $OBJDIR
|
"js-config.h", # generated in $OBJDIR
|
||||||
"fdlibm.h", # fdlibm
|
"fdlibm.h", # fdlibm
|
||||||
|
|
|
@ -147,6 +147,10 @@ def main():
|
||||||
"Decimal.o",
|
"Decimal.o",
|
||||||
# Ignore use of std::string in regexp AST debug output.
|
# Ignore use of std::string in regexp AST debug output.
|
||||||
"regexp-ast.o",
|
"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)
|
all_ignored_files = set((f, 1) for f in ignored_files)
|
||||||
|
|
||||||
|
|
|
@ -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 <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef XP_WIN
|
||||||
|
# include <io.h>
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
# include <android/log.h>
|
||||||
|
#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<char[]>(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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,12 @@
|
||||||
#ifndef mozilla_glue_Debug_h
|
#ifndef mozilla_glue_Debug_h
|
||||||
#define 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 <cstdarg>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
/* This header file intends to supply debugging utilities for use in code
|
/* This header file intends to supply debugging utilities for use in code
|
||||||
* that cannot use XPCOM debugging facilities like nsDebug.h.
|
* that cannot use XPCOM debugging facilities like nsDebug.h.
|
||||||
* e.g. mozglue, browser/app
|
* e.g. mozglue, browser/app
|
||||||
|
@ -15,52 +21,53 @@
|
||||||
* care; avoid including from header files.
|
* care; avoid including from header files.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <io.h>
|
|
||||||
#if defined(XP_WIN)
|
|
||||||
# include <windows.h>
|
|
||||||
#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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
void printf_stderr(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2);
|
/**
|
||||||
inline void printf_stderr(const char* fmt, ...) {
|
* printf_stderr(...) is much like fprintf(stderr, ...), except that:
|
||||||
#if defined(XP_WIN)
|
* - on Android and Firefox OS, *instead* of printing to stderr, it
|
||||||
if (IsDebuggerPresent()) {
|
* prints to logcat. (Newlines in the string lead to multiple lines
|
||||||
char buf[2048];
|
* of logcat, but each function call implicitly completes a line even
|
||||||
va_list args;
|
* if the string does not end with a newline.)
|
||||||
va_start(args, fmt);
|
* - on Windows, if a debugger is present, it calls OutputDebugString
|
||||||
VsprintfLiteral(buf, fmt, args);
|
* in *addition* to writing to stderr
|
||||||
va_end(args);
|
*/
|
||||||
OutputDebugStringA(buf);
|
MFBT_API void printf_stderr(const char* aFmt, ...) MOZ_FORMAT_PRINTF(1, 2);
|
||||||
}
|
|
||||||
#endif // defined(XP_WIN)
|
|
||||||
|
|
||||||
// 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.
|
* Same as printf_stderr, but taking va_list instead of varargs
|
||||||
int fd = _fileno(stderr);
|
*/
|
||||||
if (fd == -2) return;
|
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);
|
* print_stderr and fprint_stderr are like printf_stderr and fprintf_stderr,
|
||||||
vfprintf(fp, fmt, args);
|
* except they deal with Android logcat line length limitations. They do this
|
||||||
va_end(args);
|
* by printing individual lines out of the provided stringstream using separate
|
||||||
|
* calls to logcat.
|
||||||
fclose(fp);
|
*/
|
||||||
}
|
MFBT_API void print_stderr(std::stringstream& aStr);
|
||||||
|
MFBT_API void fprint_stderr(FILE* aFile, std::stringstream& aStr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ if CONFIG["OS_ARCH"] == "WINNT":
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
"AutoProfilerLabel.cpp",
|
"AutoProfilerLabel.cpp",
|
||||||
"AwakeTimeStamp.cpp",
|
"AwakeTimeStamp.cpp",
|
||||||
|
"Debug.cpp",
|
||||||
"MmapFaultHandler.cpp",
|
"MmapFaultHandler.cpp",
|
||||||
"Printf.cpp",
|
"Printf.cpp",
|
||||||
"SIMD.cpp",
|
"SIMD.cpp",
|
||||||
|
|
|
@ -19,11 +19,6 @@
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
# include "mozilla/UniquePtr.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
# include <android/log.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FUZZING_SNAPSHOT
|
#ifdef FUZZING_SNAPSHOT
|
||||||
|
@ -229,105 +224,3 @@ void NS_MakeRandomString(char* aBuf, int32_t aBufLen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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<char[]>(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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "nsXPCOM.h"
|
#include "nsXPCOM.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/glue/Debug.h"
|
||||||
#include "mozilla/DbgMacro.h"
|
#include "mozilla/DbgMacro.h"
|
||||||
#include "mozilla/Likely.h"
|
#include "mozilla/Likely.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
@ -326,59 +327,4 @@ void NS_ABORT_OOM(size_t aSize);
|
||||||
inline void NS_ABORT_OOM(size_t) { MOZ_CRASH(); }
|
inline void NS_ABORT_OOM(size_t) { MOZ_CRASH(); }
|
||||||
#endif
|
#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___ */
|
#endif /* nsDebug_h___ */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче