mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
55b741f5f7
stderr does not go anywhere by default on Android. Debugging output is conventionally sent to the Android log via special functions like __android_log_print. We already have logic for handling this in nsCRTGlue's printf_stderr, but I don't think we can use that in MFBT. Differential Revision: https://phabricator.services.mozilla.com/D26985 --HG-- extra : moz-landing-system : lando
207 lines
6.6 KiB
C++
207 lines
6.6 KiB
C++
/* -*- 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/. */
|
|
|
|
#ifndef mozilla_DbgMacro_h
|
|
#define mozilla_DbgMacro_h
|
|
|
|
/* a MOZ_DBG macro that outputs a wrapped value to stderr then returns it */
|
|
|
|
#include "mozilla/MacroForEach.h"
|
|
#include "mozilla/Span.h"
|
|
|
|
#include <stdio.h>
|
|
#include <sstream>
|
|
|
|
template <typename T>
|
|
class nsTSubstring;
|
|
|
|
#ifdef ANDROID
|
|
# include <android/log.h>
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
|
|
namespace detail {
|
|
|
|
// Predicate to check whether T can be inserted into an ostream.
|
|
template <typename T, typename = decltype(std::declval<std::ostream&>()
|
|
<< std::declval<T>())>
|
|
std::true_type supports_os_test(const T&);
|
|
std::false_type supports_os_test(...);
|
|
|
|
template <typename T>
|
|
using supports_os = decltype(supports_os_test(std::declval<T>()));
|
|
|
|
} // namespace detail
|
|
|
|
// Helper function to write a value to an ostream.
|
|
//
|
|
// This handles pointer values where the type being pointed to supports being
|
|
// inserted into an ostream, and we write out the value being pointed to in
|
|
// addition to the pointer value.
|
|
template <typename T>
|
|
auto DebugValue(std::ostream& aOut, T* aValue)
|
|
-> std::enable_if_t<mozilla::detail::supports_os<T>::value, std::ostream&> {
|
|
if (aValue) {
|
|
aOut << *aValue << " @ " << aValue;
|
|
} else {
|
|
aOut << "null";
|
|
}
|
|
return aOut;
|
|
}
|
|
|
|
// Helper function to write a value to an ostream.
|
|
//
|
|
// This handles all pointer types that cannot be dereferenced and inserted into
|
|
// an ostream.
|
|
template <typename T>
|
|
auto DebugValue(std::ostream& aOut, T* aValue)
|
|
-> std::enable_if_t<!mozilla::detail::supports_os<T>::value,
|
|
std::ostream&> {
|
|
return aOut << aValue;
|
|
}
|
|
|
|
// Helper function to write a value to an ostream.
|
|
//
|
|
// This handles XPCOM string types.
|
|
template <typename T>
|
|
auto DebugValue(std::ostream& aOut, const T& aValue)
|
|
-> std::enable_if_t<std::is_base_of<nsTSubstring<char>, T>::value ||
|
|
std::is_base_of<nsTSubstring<char16_t>, T>::value,
|
|
std::ostream&> {
|
|
return aOut << '"' << aValue << '"';
|
|
}
|
|
|
|
// Helper function to write a value to an ostream.
|
|
//
|
|
// This handles all other types.
|
|
template <typename T>
|
|
auto DebugValue(std::ostream& aOut, const T& aValue)
|
|
-> std::enable_if_t<!std::is_base_of<nsTSubstring<char>, T>::value &&
|
|
!std::is_base_of<nsTSubstring<char16_t>, T>::value,
|
|
std::ostream&> {
|
|
return aOut << aValue;
|
|
}
|
|
|
|
namespace detail {
|
|
|
|
// Helper function template for MOZ_DBG.
|
|
template <typename T>
|
|
auto&& MozDbg(const char* aFile, int aLine, const char* aExpression,
|
|
T&& aValue) {
|
|
std::ostringstream s;
|
|
s << "[MozDbg] [" << aFile << ':' << aLine << "] " << aExpression << " = ";
|
|
mozilla::DebugValue(s, std::forward<T>(aValue));
|
|
s << '\n';
|
|
#ifdef ANDROID
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", s.str().c_str());
|
|
#else
|
|
fputs(s.str().c_str(), stderr);
|
|
#endif
|
|
return std::forward<T>(aValue);
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
} // namespace mozilla
|
|
|
|
template <class ElementType, size_t Extent>
|
|
std::ostream& operator<<(std::ostream& aOut,
|
|
const mozilla::Span<ElementType, Extent>& aSpan) {
|
|
aOut << '[';
|
|
if (!aSpan.IsEmpty()) {
|
|
aOut << aSpan[0];
|
|
for (size_t i = 1; i < aSpan.Length(); ++i) {
|
|
aOut << ", " << aSpan[i];
|
|
}
|
|
}
|
|
return aOut << ']';
|
|
}
|
|
|
|
// Don't define this for char[], since operator<<(ostream&, char*) is already
|
|
// defined.
|
|
template <typename T, size_t N,
|
|
typename = std::enable_if_t<!std::is_same<T, char>::value>>
|
|
std::ostream& operator<<(std::ostream& aOut, const T (&aArray)[N]) {
|
|
return aOut << mozilla::MakeSpan(aArray);
|
|
}
|
|
|
|
// MOZ_DBG is a macro like the Rust dbg!() macro -- it will print out the
|
|
// expression passed to it to stderr and then return the value. It is not
|
|
// available in MOZILLA_OFFICIAL builds, so you shouldn't land any uses of it in
|
|
// the tree.
|
|
//
|
|
// It should work for any type T that has an operator<<(std::ostream&, const T&)
|
|
// defined for it.
|
|
//
|
|
// Note 1: Using MOZ_DBG may cause copies to be made of temporary values:
|
|
//
|
|
// struct A {
|
|
// A(int);
|
|
// A(const A&);
|
|
//
|
|
// int x;
|
|
// };
|
|
//
|
|
// void f(A);
|
|
//
|
|
// f(A{1}); // may (and, in C++17, will) elide the creation of a temporary
|
|
// // for A{1} and instead initialize the function argument
|
|
// // directly using the A(int) constructor
|
|
//
|
|
// f(MOZ_DBG(A{1})); // will create and return a temporary for A{1}, which
|
|
// // then will be passed to the A(const A&) copy
|
|
// // constructor to initialize f's argument
|
|
//
|
|
// Note 2: MOZ_DBG cannot be used to wrap a prvalue that is being used to
|
|
// initialize an object if its type has no move constructor:
|
|
//
|
|
// struct B {
|
|
// B() = default;
|
|
// B(B&&) = delete;
|
|
// };
|
|
//
|
|
// B b1 = B(); // fine, initializes b1 directly
|
|
//
|
|
// B b2 = MOZ_DBG(B()); // compile error: MOZ_DBG needs to materialize a
|
|
// // temporary for B() so it can be passed to
|
|
// // operator<<, but that temporary is returned from
|
|
// // MOZ_DBG as an rvalue reference and so wants to
|
|
// // invoke B's move constructor to initialize b2
|
|
#ifndef MOZILLA_OFFICIAL
|
|
# define MOZ_DBG(expression_...) \
|
|
mozilla::detail::MozDbg(__FILE__, __LINE__, #expression_, expression_)
|
|
#endif
|
|
|
|
// Helper macro for MOZ_DEFINE_DBG.
|
|
#define MOZ_DBG_FIELD(name_) << #name_ << " = " << aValue.name_
|
|
|
|
// Macro to define an operator<<(ostream&) for a struct or class that displays
|
|
// the type name and the values of the specified member variables. Must be
|
|
// called inside the struct or class.
|
|
//
|
|
// For example:
|
|
//
|
|
// struct Point {
|
|
// float x;
|
|
// float y;
|
|
//
|
|
// MOZ_DEFINE_DBG(Point, x, y)
|
|
// };
|
|
//
|
|
// generates an operator<< that outputs strings like
|
|
// "Point { x = 1.0, y = 2.0 }".
|
|
#define MOZ_DEFINE_DBG(type_, members_...) \
|
|
friend std::ostream& operator<<(std::ostream& aOut, const type_& aValue) { \
|
|
return aOut << #type_ \
|
|
<< (MOZ_ARG_COUNT(members_) == 0 ? "" : " { ") \
|
|
MOZ_FOR_EACH_SEPARATED(MOZ_DBG_FIELD, (<< ", "), (), \
|
|
(members_)) \
|
|
<< (MOZ_ARG_COUNT(members_) == 0 ? "" : " }"); \
|
|
}
|
|
|
|
#endif // mozilla_DbgMacro_h
|