RTC_LOG_* macros: Implement argument passing with a single variadic call
Instead of making multiple calls to the std::stringstream << operator,
collect all the arguments and make a single printf-like variadic call
under the hood.
Besides reducing our reliance on iostreams, this makes each RTC_LOG_*
call site smaller; in aggregate, this reduces the size of
libjingle_peerconnection_so.so by 28-32 kB.
A quick benchmark indicates that this change makes log statements
a few percent slower.
Bug: webrtc:8982, webrtc:9185
Change-Id: I3137a4dd8ac510e8d910acccb0c97ce4fffb61c9
Reviewed-on: https://webrtc-review.googlesource.com/75440
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Jonas Olsson <jonasolsson@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23375}
diff --git a/rtc_base/logging.h b/rtc_base/logging.h
index ff90a81..f16aca6 100644
--- a/rtc_base/logging.h
+++ b/rtc_base/logging.h
@@ -57,7 +57,7 @@
#include "rtc_base/constructormagic.h"
#include "rtc_base/deprecation.h"
-#include "rtc_base/system/no_inline.h"
+#include "rtc_base/system/inline.h"
#include "rtc_base/thread_annotations.h"
#if !defined(NDEBUG) || defined(DLOG_ALWAYS_ON)
@@ -122,6 +122,255 @@
virtual void OnLogMessage(const std::string& message) = 0;
};
+namespace webrtc_logging_impl {
+
+class LogMetadata {
+ public:
+ LogMetadata(const char* file, int line, LoggingSeverity severity)
+ : file_(file),
+ line_and_sev_(static_cast<uint32_t>(line) << 3 | severity) {}
+ LogMetadata() = default;
+
+ const char* File() const { return file_; }
+ int Line() const { return line_and_sev_ >> 3; }
+ LoggingSeverity Severity() const {
+ return static_cast<LoggingSeverity>(line_and_sev_ & 0x7);
+ }
+
+ private:
+ const char* file_;
+
+ // Line number and severity, the former in the most significant 29 bits, the
+ // latter in the least significant 3 bits. (This is an optimization; since
+ // both numbers are usually compile-time constants, this way we can load them
+ // both with a single instruction.)
+ uint32_t line_and_sev_;
+};
+static_assert(std::is_trivial<LogMetadata>::value, "");
+
+struct LogMetadataErr {
+ LogMetadata meta;
+ LogErrorContext err_ctx;
+ int err;
+};
+
+#ifdef WEBRTC_ANDROID
+struct LogMetadataTag {
+ LoggingSeverity severity;
+ const char* tag;
+};
+#endif
+
+enum class LogArgType : int8_t {
+ kEnd = 0,
+ kInt,
+ kLong,
+ kLongLong,
+ kUInt,
+ kULong,
+ kULongLong,
+ kDouble,
+ kLongDouble,
+ kCharP,
+ kStdString,
+ // TODO(kwiberg): Add absl::StringView.
+ kVoidP,
+ kLogMetadata,
+ kLogMetadataErr,
+#ifdef WEBRTC_ANDROID
+ kLogMetadataTag,
+#endif
+};
+
+// Wrapper for log arguments. Only ever make values of this type with the
+// MakeVal() functions.
+template <LogArgType N, typename T>
+struct Val {
+ static constexpr LogArgType Type() { return N; }
+ T GetVal() const { return val; }
+ T val;
+};
+
+// TODO(bugs.webrtc.org/9278): Get rid of this specialization when callers
+// don't need it anymore. No in-tree caller does, but some external callers
+// still do.
+template <>
+struct Val<LogArgType::kStdString, std::string> {
+ static constexpr LogArgType Type() { return LogArgType::kStdString; }
+ const std::string* GetVal() const { return &val; }
+ std::string val;
+};
+
+inline Val<LogArgType::kInt, int> MakeVal(int x) {
+ return {x};
+}
+inline Val<LogArgType::kLong, long> MakeVal(long x) {
+ return {x};
+}
+inline Val<LogArgType::kLongLong, long long> MakeVal(long long x) {
+ return {x};
+}
+inline Val<LogArgType::kUInt, unsigned int> MakeVal(unsigned int x) {
+ return {x};
+}
+inline Val<LogArgType::kULong, unsigned long> MakeVal(unsigned long x) {
+ return {x};
+}
+inline Val<LogArgType::kULongLong, unsigned long long> MakeVal(
+ unsigned long long x) {
+ return {x};
+}
+
+inline Val<LogArgType::kDouble, double> MakeVal(double x) {
+ return {x};
+}
+inline Val<LogArgType::kLongDouble, long double> MakeVal(long double x) {
+ return {x};
+}
+
+inline Val<LogArgType::kCharP, const char*> MakeVal(const char* x) {
+ return {x};
+}
+inline Val<LogArgType::kStdString, const std::string*> MakeVal(
+ const std::string& x) {
+ return {&x};
+}
+// TODO(kwiberg): Add absl::string_view
+
+inline Val<LogArgType::kVoidP, const void*> MakeVal(const void* x) {
+ return {x};
+}
+
+inline Val<LogArgType::kLogMetadata, LogMetadata> MakeVal(
+ const LogMetadata& x) {
+ return {x};
+}
+inline Val<LogArgType::kLogMetadataErr, LogMetadataErr> MakeVal(
+ const LogMetadataErr& x) {
+ return {x};
+}
+
+#ifdef WEBRTC_ANDROID
+inline Val<LogArgType::kLogMetadataTag, LogMetadataTag> MakeVal(
+ const LogMetadataTag& x) {
+ return {x};
+}
+#endif
+
+// Handle arbitrary types other than the above by falling back to stringstream.
+// TODO(bugs.webrtc.org/9278): Get rid of this overload when callers don't need
+// it anymore. No in-tree caller does, but some external callers still do.
+template <
+ typename T,
+ typename T1 =
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type,
+ typename std::enable_if<
+ std::is_class<T1>::value && !std::is_same<T1, std::string>::value &&
+ !std::is_same<T1, LogMetadata>::value &&
+#ifdef WEBRTC_ANDROID
+ !std::is_same<T1, LogMetadataTag>::value &&
+#endif
+ !std::is_same<T1, LogMetadataErr>::value>::type* = nullptr>
+Val<LogArgType::kStdString, std::string> MakeVal(const T& x) {
+ std::ostringstream os; // no-presubmit-check TODO(webrtc:8982)
+ os << x;
+ return {os.str()};
+}
+
+void Log(const LogArgType* fmt, ...);
+
+// 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_FORCE_INLINE static void Call(const Us&... args) {
+ static constexpr LogArgType t[] = {Us::Type()..., LogArgType::kEnd};
+ Log(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_FORCE_INLINE void Call(const Us&... args) const {
+ prior_->Call(arg_, args...);
+ }
+
+ private:
+ // The most recent argument.
+ T arg_;
+
+ // Earlier arguments.
+ const LogStreamer<Ts...>* prior_;
+};
+
+class LogCall final {
+ public:
+ // This can be any binary operator with precedence lower than <<.
+ template <typename... Ts>
+ RTC_FORCE_INLINE void operator&(const LogStreamer<Ts...>& streamer) {
+ streamer.Call();
+ }
+};
+
+// TODO(bugs.webrtc.org/9278): Remove this once it's no longer used.
+struct LogMessageVoidify {
+ void operator&(std::ostream&) {} // no-presubmit-check TODO(webrtc:8982)
+};
+
+} // namespace webrtc_logging_impl
+
+// Direct use of this class is deprecated; please use the logging macros
+// instead.
+// TODO(bugs.webrtc.org/9278): Move this class to an unnamed namespace in the
+// .cc file.
class LogMessage {
public:
LogMessage(const char* file, int line, LoggingSeverity sev);
@@ -154,6 +403,8 @@
~LogMessage();
+ void AddTag(const char* tag);
+
static bool Loggable(LoggingSeverity sev);
// Same as the above, but using a template argument instead of a function
@@ -271,39 +522,27 @@
// Logging Helpers
//////////////////////////////////////////////////////////////////////
-// The following non-obvious technique for implementation of a
-// conditional log stream was stolen from google3/base/logging.h.
-
-// This class is used to explicitly ignore values in the conditional
-// logging macros. This avoids compiler warnings like "value computed
-// is not used" and "statement has no effect".
-
-class LogMessageVoidify {
- public:
- LogMessageVoidify() { }
- // This has to be an operator with a precedence lower than << but
- // higher than ?:
- void operator&(std::ostream&) { }
-};
-
+// DEPRECATED.
+// TODO(bugs.webrtc.org/9278): Remove once there are no more users.
#define RTC_LOG_SEVERITY_PRECONDITION(sev) \
- !(rtc::LogMessage::Loggable(sev)) \
- ? (void) 0 \
- : rtc::LogMessageVoidify() &
+ !(rtc::LogMessage::Loggable(sev)) \
+ ? static_cast<void>(0) \
+ : rtc::webrtc_logging_impl::LogMessageVoidify()&
-#define RTC_LOG_SEVERITY_PRECONDITION_C(sev) \
- !(rtc::LogMessage::Loggable<rtc::sev>()) ? (void)0 : rtc::LogMessageVoidify()&
-#define RTC_LOG(sev) \
- RTC_LOG_SEVERITY_PRECONDITION_C(sev) \
- rtc::LogMessage(__FILE__, __LINE__, \
- std::integral_constant<rtc::LoggingSeverity, rtc::sev>()) \
- .stream()
+#define RTC_LOG(sev) \
+ for (bool do_log = rtc::LogMessage::Loggable<rtc::sev>(); do_log; \
+ do_log = false) \
+ rtc::webrtc_logging_impl::LogCall() & \
+ rtc::webrtc_logging_impl::LogStreamer<>() \
+ << rtc::webrtc_logging_impl::LogMetadata(__FILE__, __LINE__, \
+ rtc::sev)
-// The _V version is for when a variable is passed in. It doesn't do the
-// namespace concatenation.
-#define RTC_LOG_V(sev) \
- RTC_LOG_SEVERITY_PRECONDITION(sev) \
- rtc::LogMessage(__FILE__, __LINE__, sev).stream()
+// The _V version is for when a variable is passed in.
+#define RTC_LOG_V(sev) \
+ for (bool do_log = rtc::LogMessage::Loggable(sev); do_log; do_log = false) \
+ rtc::webrtc_logging_impl::LogCall() & \
+ rtc::webrtc_logging_impl::LogStreamer<>() \
+ << rtc::webrtc_logging_impl::LogMetadata(__FILE__, __LINE__, sev)
// The _F version prefixes the message with the current function name.
#if (defined(__GNUC__) && !defined(NDEBUG)) || defined(WANT_PRETTY_LOG_F)
@@ -324,11 +563,14 @@
return (LogMessage::GetMinLogSeverity() <= sev);
}
-#define RTC_LOG_E(sev, ctx, err, ...) \
- RTC_LOG_SEVERITY_PRECONDITION_C(sev) \
- rtc::LogMessage(__FILE__, __LINE__, rtc::sev, rtc::ERRCTX_##ctx, err, \
- ##__VA_ARGS__) \
- .stream()
+#define RTC_LOG_E(sev, ctx, err) \
+ for (bool do_log = rtc::LogMessage::Loggable<rtc::sev>(); do_log; \
+ do_log = false) \
+ rtc::webrtc_logging_impl::LogCall() & \
+ rtc::webrtc_logging_impl::LogStreamer<>() \
+ << rtc::webrtc_logging_impl::LogMetadataErr { \
+ {__FILE__, __LINE__, rtc::sev}, rtc::ERRCTX_##ctx, (err) \
+ }
#define RTC_LOG_T(sev) RTC_LOG(sev) << this << ": "
@@ -340,8 +582,7 @@
#if defined(WEBRTC_WIN)
#define RTC_LOG_GLE_EX(sev, err) \
RTC_LOG_E(sev, HRESULT, err)
-#define RTC_LOG_GLE(sev) \
- RTC_LOG_GLE_EX(sev, GetLastError())
+#define RTC_LOG_GLE(sev) RTC_LOG_GLE_EX(sev, static_cast<int>(GetLastError()))
#define RTC_LOG_ERR_EX(sev, err) \
RTC_LOG_GLE_EX(sev, err)
#define RTC_LOG_ERR(sev) \
@@ -358,20 +599,27 @@
RTC_LOG_ERRNO(sev)
#endif // WEBRTC_WIN
-#if defined(WEBRTC_ANDROID)
-namespace internal {
-// Inline adapters provided for backwards compatibility for downstream projects.
+#ifdef WEBRTC_ANDROID
+
+namespace webrtc_logging_impl {
+// TODO(kwiberg): Replace these with absl::string_view.
inline const char* AdaptString(const char* str) { return str; }
inline const char* AdaptString(const std::string& str) { return str.c_str(); }
-} // namespace internal
-#define RTC_LOG_TAG(sev, tag) \
- RTC_LOG_SEVERITY_PRECONDITION(sev) \
- rtc::LogMessage(nullptr, 0, sev, rtc::internal::AdaptString(tag)).stream()
+} // namespace webrtc_logging_impl
+
+#define RTC_LOG_TAG(sev, tag) \
+ for (bool do_log = rtc::LogMessage::Loggable(sev); do_log; do_log = false) \
+ rtc::webrtc_logging_impl::LogCall() & \
+ rtc::webrtc_logging_impl::LogStreamer<>() \
+ << rtc::webrtc_logging_impl::LogMetadataTag { \
+ sev, rtc::webrtc_logging_impl::AdaptString(tag) \
+ }
+
#else
+
// DEPRECATED. This macro is only intended for Android.
-#define RTC_LOG_TAG(sev, tag) \
- RTC_LOG_SEVERITY_PRECONDITION(sev) \
- rtc::LogMessage(nullptr, 0, sev).stream()
+#define RTC_LOG_TAG(sev, tag) RTC_LOG_V(sev)
+
#endif
// The RTC_DLOG macros are equivalent to their RTC_LOG counterparts except that
@@ -381,14 +629,12 @@
#define RTC_DLOG_V(sev) RTC_LOG_V(sev)
#define RTC_DLOG_F(sev) RTC_LOG_F(sev)
#else
-#define RTC_DLOG_EAT_STREAM_PARAMS(sev) \
- (true ? true : ((void)(sev), true)) \
- ? static_cast<void>(0) \
- : rtc::LogMessageVoidify() & \
- rtc::LogMessage(__FILE__, __LINE__, sev).stream()
-#define RTC_DLOG(sev) RTC_DLOG_EAT_STREAM_PARAMS(rtc::sev)
-#define RTC_DLOG_V(sev) RTC_DLOG_EAT_STREAM_PARAMS(sev)
-#define RTC_DLOG_F(sev) RTC_DLOG_EAT_STREAM_PARAMS(rtc::sev)
+#define RTC_DLOG_EAT_STREAM_PARAMS() \
+ while (false) \
+ rtc::webrtc_logging_impl::LogStreamer<>()
+#define RTC_DLOG(sev) RTC_DLOG_EAT_STREAM_PARAMS()
+#define RTC_DLOG_V(sev) RTC_DLOG_EAT_STREAM_PARAMS()
+#define RTC_DLOG_F(sev) RTC_DLOG_EAT_STREAM_PARAMS()
#endif
} // namespace rtc