diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 22182b5473..79db4f43ea 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -254,6 +254,7 @@ rtc_source_set("checks") { deps = [ ":safe_compare", "..:typedefs", + "system:inline", ] } diff --git a/rtc_base/checks.cc b/rtc_base/checks.cc index ebe53b876e..462bef8275 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( template std::string* MakeCheckOpString( 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(*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 76b849f619..b37df0daba 100644 --- a/rtc_base/checks.h +++ b/rtc_base/checks.h @@ -44,6 +44,7 @@ RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg); #include #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 @@ RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg); // 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(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 +struct Val { + static constexpr CheckArgType Type() { return N; } + T GetVal() const { return val; } + T val; +}; + +inline Val MakeVal(int x) { + return {x}; +} +inline Val MakeVal(long x) { + return {x}; +} +inline Val MakeVal(long long x) { + return {x}; +} +inline Val MakeVal(unsigned int x) { + return {x}; +} +inline Val MakeVal(unsigned long x) { + return {x}; +} +inline Val MakeVal( + unsigned long long x) { + return {x}; +} + +inline Val MakeVal(double x) { + return {x}; +} +inline Val MakeVal(long double x) { + return {x}; +} + +inline Val MakeVal(const char* x) { + return {x}; +} +inline Val MakeVal( + const std::string& x) { + return {&x}; +} + +inline Val MakeVal(const void* x) { + return {x}; +} + +// Ephemeral type that represents the result of the logging << operator. +template +class LogStreamer; + +// Base case: Before the first << argument. +template <> +class LogStreamer<> final { + public: + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + RTC_FORCE_INLINE LogStreamer()))> operator<<( + U arg) const { + return LogStreamer()))>(MakeVal(arg), + this); + } + + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + RTC_FORCE_INLINE LogStreamer()))> operator<<( + const U& arg) const { + return LogStreamer()))>(MakeVal(arg), + this); + } + + template + 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 +class LogStreamer final { + public: + RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer* prior) + : arg_(arg), prior_(prior) {} + + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + RTC_FORCE_INLINE LogStreamer())), T, Ts...> + operator<<(U arg) const { + return LogStreamer())), T, Ts...>( + MakeVal(arg), this); + } + + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + RTC_FORCE_INLINE LogStreamer())), T, Ts...> + operator<<(const U& arg) const { + return LogStreamer())), T, Ts...>( + MakeVal(arg), this); + } + + template + 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* 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 + RTC_NORETURN RTC_FORCE_INLINE void operator&( + const LogStreamer& 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(0) \ - : rtc::FatalMessageVoidify() & rtc::FatalMessage("", 0).stream() +#define RTC_EAT_STREAM_PARAMETERS(ignored) \ + (true ? true : ((void)(ignored), true)) \ + ? static_cast(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 @@ namespace rtc { // 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 @@ namespace rtc { // takes ownership of the returned string. template 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_CHECK_OP_IMPL(Gt) #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 1be0b24b87..f3f0ea56e5 100644 --- a/rtc_base/logging_unittest.cc +++ b/rtc_base/logging_unittest.cc @@ -87,6 +87,37 @@ TEST(LogTest, SingleStream) { 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 722a785ea1..c7ef09bfdd 100644 --- a/sdk/android/src/jni/pc/peerconnection.cc +++ b/sdk/android/src/jni/pc/peerconnection.cc @@ -305,8 +305,7 @@ void PeerConnectionObserverJni::OnRemoveStream( rtc::scoped_refptr 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);