Refactor checks to use a copy of the new logging backend.

As a bonus, this shrinks the android release version of libjingle_peerconnection_so.so by ~25k in local tests.

We could try to unify the backend with the logging one, but that turns out to be surprisingly tricky due to dependency loops and chromium overrides.

Bug: webrtc:8982
Change-Id: I66854dd333f568d9b2a5f46bbead14b2e31179be
Reviewed-on: https://webrtc-review.googlesource.com/79623
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23634}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 22182b5..79db4f4 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -254,6 +254,7 @@
   deps = [
     ":safe_compare",
     "..:typedefs",
+    "system:inline",
   ]
 }
 
diff --git a/rtc_base/checks.cc b/rtc_base/checks.cc
index ebe53b8..462bef8 100644
--- a/rtc_base/checks.cc
+++ b/rtc_base/checks.cc
@@ -35,64 +35,7 @@
 
 #include "rtc_base/checks.h"
 
-#if defined(_MSC_VER)
-// Warning C4722: destructor never returns, potential memory leak.
-// FatalMessage's dtor very intentionally aborts.
-#pragma warning(disable:4722)
-#endif
-
 namespace rtc {
-namespace {
-
-void VPrintError(const char* format, va_list args) {
-#if defined(WEBRTC_ANDROID)
-  __android_log_vprint(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, format, args);
-#else
-  vfprintf(stderr, format, args);
-#endif
-}
-
-#if defined(__GNUC__)
-void PrintError(const char* format, ...)
-    __attribute__((__format__(__printf__, 1, 2)));
-#endif
-
-void PrintError(const char* format, ...) {
-  va_list args;
-  va_start(args, format);
-  VPrintError(format, args);
-  va_end(args);
-}
-
-}  // namespace
-
-FatalMessage::FatalMessage(const char* file, int line) {
-  Init(file, line);
-}
-
-FatalMessage::FatalMessage(const char* file, int line, std::string* result) {
-  Init(file, line);
-  stream_ << "Check failed: " << *result << std::endl << "# ";
-  delete result;
-}
-
-RTC_NORETURN FatalMessage::~FatalMessage() {
-  fflush(stdout);
-  fflush(stderr);
-  stream_ << std::endl << "#" << std::endl;
-  PrintError("%s", stream_.str().c_str());
-  fflush(stderr);
-  abort();
-}
-
-void FatalMessage::Init(const char* file, int line) {
-  stream_ << std::endl
-          << std::endl
-          << "#" << std::endl
-          << "# Fatal error in " << file << ", line " << line << std::endl
-          << "# last system error: " << LAST_SYSTEM_ERROR << std::endl
-          << "# ";
-}
 
 // MSVC doesn't like complex extern templates and DLLs.
 #if !defined(COMPILER_MSVC)
@@ -108,11 +51,83 @@
 template std::string* MakeCheckOpString<std::string, std::string>(
     const std::string&, const std::string&, const char* name);
 #endif
+namespace webrtc_checks_impl {
+RTC_NORETURN void FatalLog(const char* file,
+                           int line,
+                           const char* message,
+                           const CheckArgType* fmt,
+                           ...) {
+  va_list args;
+  va_start(args, fmt);
 
+  std::ostringstream ss;  // no-presubmit-check TODO(webrtc:8982)
+  ss << "\n\n#\n# Fatal error in: " << file << ", line " << line
+     << "\n# last system error: " << LAST_SYSTEM_ERROR
+     << "\n# Check failed: " << message << "\n# ";
+
+  for (; *fmt != CheckArgType::kEnd; ++fmt) {
+    switch (*fmt) {
+      case CheckArgType::kInt:
+        ss << va_arg(args, int);
+        break;
+      case CheckArgType::kLong:
+        ss << va_arg(args, long);
+        break;
+      case CheckArgType::kLongLong:
+        ss << va_arg(args, long long);
+        break;
+      case CheckArgType::kUInt:
+        ss << va_arg(args, unsigned);
+        break;
+      case CheckArgType::kULong:
+        ss << va_arg(args, unsigned long);
+        break;
+      case CheckArgType::kULongLong:
+        ss << va_arg(args, unsigned long long);
+        break;
+      case CheckArgType::kDouble:
+        ss << va_arg(args, double);
+        break;
+      case CheckArgType::kLongDouble:
+        ss << va_arg(args, long double);
+        break;
+      case CheckArgType::kCharP:
+        ss << va_arg(args, const char*);
+        break;
+      case CheckArgType::kStdString:
+        ss << *va_arg(args, const std::string*);
+        break;
+      case CheckArgType::kVoidP:
+        ss << va_arg(args, const void*);
+        break;
+      default:
+        ss << "[Invalid CheckArgType:" << static_cast<int8_t>(*fmt) << "]";
+        goto processing_loop_end;
+    }
+  }
+processing_loop_end:
+  va_end(args);
+
+  std::string s = ss.str();
+  const char* output = s.c_str();
+
+#if defined(WEBRTC_ANDROID)
+  __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", output);
+#endif
+
+  fflush(stdout);
+  fprintf(stderr, "%s", output);
+  fflush(stderr);
+  abort();
+}
+
+}  // namespace webrtc_checks_impl
 }  // namespace rtc
 
 // Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros.
 RTC_NORETURN void rtc_FatalMessage(const char* file, int line,
                                    const char* msg) {
-  rtc::FatalMessage(file, line).stream() << msg;
+  static constexpr rtc::webrtc_checks_impl::CheckArgType t[] = {
+      rtc::webrtc_checks_impl::CheckArgType::kEnd};
+  FatalLog(file, line, msg, t);
 }
diff --git a/rtc_base/checks.h b/rtc_base/checks.h
index 76b849f..b37df0d 100644
--- a/rtc_base/checks.h
+++ b/rtc_base/checks.h
@@ -44,6 +44,7 @@
 #include <string>
 
 #include "rtc_base/numerics/safe_compare.h"
+#include "rtc_base/system/inline.h"
 
 // The macros here print a message to stderr and abort under various
 // conditions. All will accept additional stream messages. For example:
@@ -86,21 +87,183 @@
 // consolidation with system_wrappers/logging.h should happen first.
 
 namespace rtc {
+namespace webrtc_checks_impl {
+enum class CheckArgType : int8_t {
+  kEnd = 0,
+  kInt,
+  kLong,
+  kLongLong,
+  kUInt,
+  kULong,
+  kULongLong,
+  kDouble,
+  kLongDouble,
+  kCharP,
+  kStdString,
+  kVoidP,
+};
 
-// Helper macro which avoids evaluating the arguments to a stream if
-// the condition doesn't hold.
-#define RTC_LAZY_STREAM(stream, condition)                                    \
-  !(condition) ? static_cast<void>(0) : rtc::FatalMessageVoidify() & (stream)
+RTC_NORETURN void FatalLog(const char* file,
+                           int line,
+                           const char* message,
+                           const CheckArgType* fmt,
+                           ...);
+
+// Wrapper for log arguments. Only ever make values of this type with the
+// MakeVal() functions.
+template <CheckArgType N, typename T>
+struct Val {
+  static constexpr CheckArgType Type() { return N; }
+  T GetVal() const { return val; }
+  T val;
+};
+
+inline Val<CheckArgType::kInt, int> MakeVal(int x) {
+  return {x};
+}
+inline Val<CheckArgType::kLong, long> MakeVal(long x) {
+  return {x};
+}
+inline Val<CheckArgType::kLongLong, long long> MakeVal(long long x) {
+  return {x};
+}
+inline Val<CheckArgType::kUInt, unsigned int> MakeVal(unsigned int x) {
+  return {x};
+}
+inline Val<CheckArgType::kULong, unsigned long> MakeVal(unsigned long x) {
+  return {x};
+}
+inline Val<CheckArgType::kULongLong, unsigned long long> MakeVal(
+    unsigned long long x) {
+  return {x};
+}
+
+inline Val<CheckArgType::kDouble, double> MakeVal(double x) {
+  return {x};
+}
+inline Val<CheckArgType::kLongDouble, long double> MakeVal(long double x) {
+  return {x};
+}
+
+inline Val<CheckArgType::kCharP, const char*> MakeVal(const char* x) {
+  return {x};
+}
+inline Val<CheckArgType::kStdString, const std::string*> MakeVal(
+    const std::string& x) {
+  return {&x};
+}
+
+inline Val<CheckArgType::kVoidP, const void*> MakeVal(const void* x) {
+  return {x};
+}
+
+// Ephemeral type that represents the result of the logging << operator.
+template <typename... Ts>
+class LogStreamer;
+
+// Base case: Before the first << argument.
+template <>
+class LogStreamer<> final {
+ public:
+  template <
+      typename U,
+      typename std::enable_if<std::is_arithmetic<U>::value>::type* = nullptr>
+  RTC_FORCE_INLINE LogStreamer<decltype(MakeVal(std::declval<U>()))> operator<<(
+      U arg) const {
+    return LogStreamer<decltype(MakeVal(std::declval<U>()))>(MakeVal(arg),
+                                                             this);
+  }
+
+  template <
+      typename U,
+      typename std::enable_if<!std::is_arithmetic<U>::value>::type* = nullptr>
+  RTC_FORCE_INLINE LogStreamer<decltype(MakeVal(std::declval<U>()))> operator<<(
+      const U& arg) const {
+    return LogStreamer<decltype(MakeVal(std::declval<U>()))>(MakeVal(arg),
+                                                             this);
+  }
+
+  template <typename... Us>
+  RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file,
+                                                 const int line,
+                                                 const char* message,
+                                                 const Us&... args) {
+    static constexpr CheckArgType t[] = {Us::Type()..., CheckArgType::kEnd};
+    FatalLog(file, line, message, t, args.GetVal()...);
+  }
+};
+
+// Inductive case: We've already seen at least one << argument. The most recent
+// one had type `T`, and the earlier ones had types `Ts`.
+template <typename T, typename... Ts>
+class LogStreamer<T, Ts...> final {
+ public:
+  RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer<Ts...>* prior)
+      : arg_(arg), prior_(prior) {}
+
+  template <
+      typename U,
+      typename std::enable_if<std::is_arithmetic<U>::value>::type* = nullptr>
+  RTC_FORCE_INLINE LogStreamer<decltype(MakeVal(std::declval<U>())), T, Ts...>
+  operator<<(U arg) const {
+    return LogStreamer<decltype(MakeVal(std::declval<U>())), T, Ts...>(
+        MakeVal(arg), this);
+  }
+
+  template <
+      typename U,
+      typename std::enable_if<!std::is_arithmetic<U>::value>::type* = nullptr>
+  RTC_FORCE_INLINE LogStreamer<decltype(MakeVal(std::declval<U>())), T, Ts...>
+  operator<<(const U& arg) const {
+    return LogStreamer<decltype(MakeVal(std::declval<U>())), T, Ts...>(
+        MakeVal(arg), this);
+  }
+
+  template <typename... Us>
+  RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file,
+                                          const int line,
+                                          const char* message,
+                                          const Us&... args) const {
+    prior_->Call(file, line, message, arg_, args...);
+  }
+
+ private:
+  // The most recent argument.
+  T arg_;
+
+  // Earlier arguments.
+  const LogStreamer<Ts...>* prior_;
+};
+
+class FatalLogCall final {
+ public:
+  FatalLogCall(const char* file, int line, const char* message)
+      : file_(file), line_(line), message_(message) {}
+
+  // This can be any binary operator with precedence lower than <<.
+  template <typename... Ts>
+  RTC_NORETURN RTC_FORCE_INLINE void operator&(
+      const LogStreamer<Ts...>& streamer) {
+    streamer.Call(file_, line_, message_);
+  }
+
+ private:
+  const char* file_;
+  int line_;
+  const char* message_;
+};
+}  // namespace webrtc_checks_impl
 
 // The actual stream used isn't important. We reference |ignored| in the code
 // but don't evaluate it; this is to avoid "unused variable" warnings (we do so
 // in a particularly convoluted way with an extra ?: because that appears to be
 // the simplest construct that keeps Visual Studio from complaining about
 // condition being unused).
-#define RTC_EAT_STREAM_PARAMETERS(ignored) \
-  (true ? true : ((void)(ignored), true))  \
-      ? static_cast<void>(0)               \
-      : rtc::FatalMessageVoidify() & rtc::FatalMessage("", 0).stream()
+#define RTC_EAT_STREAM_PARAMETERS(ignored)                 \
+  (true ? true : ((void)(ignored), true))                  \
+      ? static_cast<void>(0)                               \
+      : rtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \
+            rtc::webrtc_checks_impl::LogStreamer<>()
 
 // Call RTC_EAT_STREAM_PARAMETERS with an argument that fails to compile if
 // values of the same types as |a| and |b| can't be compared with the given
@@ -112,22 +275,22 @@
 // controlled by NDEBUG or anything else, so the check will be executed
 // regardless of compilation mode.
 //
-// We make sure RTC_CHECK et al. always evaluates their arguments, as
+// We make sure RTC_CHECK et al. always evaluates |condition|, as
 // doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom.
-#define RTC_CHECK(condition)                                      \
-  RTC_LAZY_STREAM(rtc::FatalMessage(__FILE__, __LINE__).stream(), \
-                  !(condition))                                   \
-      << "Check failed: " #condition << std::endl << "# "
+#define RTC_CHECK(condition)                                              \
+  while (!(condition))                                                    \
+  rtc::webrtc_checks_impl::FatalLogCall(__FILE__, __LINE__, #condition) & \
+      rtc::webrtc_checks_impl::LogStreamer<>()
 
 // Helper macro for binary operators.
 // Don't use this macro directly in your code, use RTC_CHECK_EQ et al below.
-//
-// TODO(akalin): Rewrite this so that constructs like if (...)
 // RTC_CHECK_EQ(...) else { ... } work properly.
-#define RTC_CHECK_OP(name, op, val1, val2)                                 \
-  if (std::string* _result =                                               \
-          rtc::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
-    rtc::FatalMessage(__FILE__, __LINE__, _result).stream()
+#define RTC_CHECK_OP(name, op, val1, val2)                                    \
+  while (std::string* _result =                                               \
+             rtc::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+  rtc::webrtc_checks_impl::FatalLogCall(__FILE__, __LINE__,                   \
+                                        _result->c_str()) &                   \
+      rtc::webrtc_checks_impl::LogStreamer<>()
 
 // Build the error message string.  This is separate from the "Impl"
 // function template because it is not performance critical and so can
@@ -135,6 +298,7 @@
 // takes ownership of the returned string.
 template<class t1, class t2>
 std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+  // TODO(jonasolsson): Use absl::StrCat() instead.
   std::ostringstream ss;
   ss << names << " (" << v1 << " vs. " << v2 << ")";
   std::string* msg = new std::string(ss.str());
@@ -216,39 +380,13 @@
 #define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Gt, v1, v2)
 #endif
 
-// This is identical to LogMessageVoidify but in name.
-class FatalMessageVoidify {
- public:
-  FatalMessageVoidify() { }
-  // This has to be an operator with a precedence lower than << but
-  // higher than ?:
-  void operator&(std::ostream&) { }
-};
-
 #define RTC_UNREACHABLE_CODE_HIT false
 #define RTC_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT)
 
 // TODO(bugs.webrtc.org/8454): Add an RTC_ prefix or rename differently.
-#define FATAL() rtc::FatalMessage(__FILE__, __LINE__).stream()
-// TODO(ajm): Consider adding RTC_NOTIMPLEMENTED macro when
-// base/logging.h and system_wrappers/logging.h are consolidated such that we
-// can match the Chromium behavior.
-
-// Like a stripped-down LogMessage from logging.h, except that it aborts.
-class FatalMessage {
- public:
-  FatalMessage(const char* file, int line);
-  // Used for RTC_CHECK_EQ(), etc. Takes ownership of the given string.
-  FatalMessage(const char* file, int line, std::string* result);
-  RTC_NORETURN ~FatalMessage();
-
-  std::ostream& stream() { return stream_; }
-
- private:
-  void Init(const char* file, int line);
-
-  std::ostringstream stream_;
-};
+#define FATAL()                                                          \
+  rtc::webrtc_checks_impl::FatalLogCall(__FILE__, __LINE__, "FATAL()") & \
+      rtc::webrtc_checks_impl::LogStreamer<>()
 
 // Performs the integer division a/b and returns the result. CHECKs that the
 // remainder is zero.
diff --git a/rtc_base/logging_unittest.cc b/rtc_base/logging_unittest.cc
index 1be0b24..f3f0ea5 100644
--- a/rtc_base/logging_unittest.cc
+++ b/rtc_base/logging_unittest.cc
@@ -87,6 +87,37 @@
   EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
 }
 
+#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST(LogTest, Checks) {
+  EXPECT_DEATH(FATAL() << "message",
+               "\n\n#\n"
+               "# Fatal error in: \\S+, line \\d+\n"
+               "# last system error: \\d+\n"
+               "# Check failed: FATAL\\(\\)\n"
+               "# message"
+               );
+
+  int a = 1, b = 2;
+  EXPECT_DEATH(RTC_CHECK_EQ(a, b) << 1 << 2u,
+               "\n\n#\n"
+               "# Fatal error in: \\S+, line \\d+\n"
+               "# last system error: \\d+\n"
+               "# Check failed: a == b \\(1 vs. 2\\)\n"
+               "# 12"
+               );
+  RTC_CHECK_EQ(5, 5);
+
+  RTC_CHECK(true) << "Shouldn't crash" << 1;
+  EXPECT_DEATH(RTC_CHECK(false) << "Hi there!",
+               "\n\n#\n"
+               "# Fatal error in: \\S+, line \\d+\n"
+               "# last system error: \\d+\n"
+               "# Check failed: false\n"
+               "# Hi there!"
+               );
+}
+#endif
+
 // Test using multiple log streams. The INFO stream should get the INFO message,
 // the VERBOSE stream should get the INFO and the VERBOSE.
 // We should restore the correct global state at the end.
diff --git a/sdk/android/src/jni/pc/peerconnection.cc b/sdk/android/src/jni/pc/peerconnection.cc
index 722a785..c7ef09b 100644
--- a/sdk/android/src/jni/pc/peerconnection.cc
+++ b/sdk/android/src/jni/pc/peerconnection.cc
@@ -305,8 +305,7 @@
     rtc::scoped_refptr<MediaStreamInterface> stream) {
   JNIEnv* env = AttachCurrentThreadIfNeeded();
   NativeToJavaStreamsMap::iterator it = remote_streams_.find(stream);
-  RTC_CHECK(it != remote_streams_.end())
-      << "unexpected stream: " << std::hex << stream;
+  RTC_CHECK(it != remote_streams_.end()) << "unexpected stream: " << stream;
   Java_Observer_onRemoveStream(env, j_observer_global_,
                                it->second.j_media_stream());
   remote_streams_.erase(it);