Proxy: solve event tracing with compile time strings.

This change creates trace events with a single parameter
composed of ClassName::Method.

The change additionally causes the duration of the proxy call to be
traced, not only the occurrence.

Fixed: webrtc:12787
Change-Id: I1689862318d4c6fc1dcef343c3ccf3ae9f7e17df
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219788
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34149}
diff --git a/pc/dtmf_sender.h b/pc/dtmf_sender.h
index cb515e8..b64b50e 100644
--- a/pc/dtmf_sender.h
+++ b/pc/dtmf_sender.h
@@ -102,7 +102,6 @@
 
 // Define proxy for DtmfSenderInterface.
 BEGIN_PRIMARY_PROXY_MAP(DtmfSender)
-
 PROXY_PRIMARY_THREAD_DESTRUCTOR()
 PROXY_METHOD1(void, RegisterObserver, DtmfSenderObserverInterface*)
 PROXY_METHOD0(void, UnregisterObserver)
@@ -112,7 +111,7 @@
 PROXY_CONSTMETHOD0(int, duration)
 PROXY_CONSTMETHOD0(int, inter_tone_gap)
 PROXY_CONSTMETHOD0(int, comma_delay)
-END_PROXY_MAP()
+END_PROXY_MAP(DtmfSender)
 
 // Get DTMF code from the DTMF event character.
 bool GetDtmfCode(char tone, int* code);
diff --git a/pc/media_stream_proxy.h b/pc/media_stream_proxy.h
index b2d4443..36069a4 100644
--- a/pc/media_stream_proxy.h
+++ b/pc/media_stream_proxy.h
@@ -37,7 +37,7 @@
 PROXY_METHOD1(bool, RemoveTrack, VideoTrackInterface*)
 PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
 PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
-END_PROXY_MAP()
+END_PROXY_MAP(MediaStream)
 
 }  // namespace webrtc
 
diff --git a/pc/media_stream_track_proxy.h b/pc/media_stream_track_proxy.h
index 1727799..f563137 100644
--- a/pc/media_stream_track_proxy.h
+++ b/pc/media_stream_track_proxy.h
@@ -37,7 +37,7 @@
 PROXY_METHOD1(bool, set_enabled, bool)
 PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
 PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
-END_PROXY_MAP()
+END_PROXY_MAP(AudioTrack)
 
 BEGIN_PROXY_MAP(VideoTrack)
 PROXY_PRIMARY_THREAD_DESTRUCTOR()
@@ -57,7 +57,7 @@
 
 PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
 PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
-END_PROXY_MAP()
+END_PROXY_MAP(VideoTrack)
 
 }  // namespace webrtc
 
diff --git a/pc/peer_connection_factory_proxy.h b/pc/peer_connection_factory_proxy.h
index b257be7..59e373d 100644
--- a/pc/peer_connection_factory_proxy.h
+++ b/pc/peer_connection_factory_proxy.h
@@ -51,7 +51,7 @@
               AudioSourceInterface*)
 PROXY_SECONDARY_METHOD2(bool, StartAecDump, FILE*, int64_t)
 PROXY_SECONDARY_METHOD0(void, StopAecDump)
-END_PROXY_MAP()
+END_PROXY_MAP(PeerConnectionFactory)
 
 }  // namespace webrtc
 
diff --git a/pc/peer_connection_proxy.h b/pc/peer_connection_proxy.h
index 212f619..7601c9d 100644
--- a/pc/peer_connection_proxy.h
+++ b/pc/peer_connection_proxy.h
@@ -157,7 +157,7 @@
 PROXY_METHOD0(void, StopRtcEventLog)
 PROXY_METHOD0(void, Close)
 BYPASS_PROXY_CONSTMETHOD0(rtc::Thread*, signaling_thread)
-END_PROXY_MAP()
+END_PROXY_MAP(PeerConnection)
 
 }  // namespace webrtc
 
diff --git a/pc/proxy.cc b/pc/proxy.cc
index 7dc4786..5f4e0b8 100644
--- a/pc/proxy.cc
+++ b/pc/proxy.cc
@@ -14,8 +14,12 @@
 
 namespace webrtc {
 namespace proxy_internal {
-void TraceApiCall(const char* class_name, const char* method_name) {
-  TRACE_EVENT1("webrtc", class_name, "method", method_name);
+ScopedTrace::ScopedTrace(const char* class_and_method_name)
+    : class_and_method_name_(class_and_method_name) {
+  TRACE_EVENT_BEGIN0("webrtc", class_and_method_name_);
+}
+ScopedTrace::~ScopedTrace() {
+  TRACE_EVENT_END0("webrtc", class_and_method_name_);
 }
 }  // namespace proxy_internal
 }  // namespace webrtc
diff --git a/pc/proxy.h b/pc/proxy.h
index c4e4966..565ae80 100644
--- a/pc/proxy.h
+++ b/pc/proxy.h
@@ -71,16 +71,30 @@
 #include "rtc_base/event.h"
 #include "rtc_base/message_handler.h"
 #include "rtc_base/ref_counted_object.h"
+#include "rtc_base/string_utils.h"
 #include "rtc_base/system/rtc_export.h"
 #include "rtc_base/thread.h"
 
+#if !defined(RTC_DISABLE_PROXY_TRACE_EVENTS) && !defined(WEBRTC_CHROMIUM_BUILD)
+#define RTC_DISABLE_PROXY_TRACE_EVENTS
+#endif
+
 namespace rtc {
 class Location;
 }
 
 namespace webrtc {
 namespace proxy_internal {
-RTC_EXPORT void TraceApiCall(const char* class_name, const char* method_name);
+
+// Class for tracing the lifetime of MethodCall::Marshal.
+class ScopedTrace {
+ public:
+  explicit ScopedTrace(const char* class_and_method_name);
+  ~ScopedTrace();
+
+ private:
+  const char* const class_and_method_name_;
+};
 }  // namespace proxy_internal
 
 template <typename R>
@@ -195,8 +209,8 @@
   template <class INTERNAL_CLASS>                         \
   class c##ProxyWithInternal : public c##Interface {      \
    protected:                                             \
+    static constexpr char proxy_name_[] = #c "Proxy";     \
     typedef c##Interface C;                               \
-    const char* class_name_ = PROXY_STRINGIZE(c);         \
                                                           \
    public:                                                \
     const INTERNAL_CLASS* internal() const { return c_; } \
@@ -205,8 +219,10 @@
 // clang-format off
 // clang-format would put the semicolon alone,
 // leading to a presubmit error (cpplint.py)
-#define END_PROXY_MAP() \
-  };
+#define END_PROXY_MAP(c)          \
+  };                              \
+  template <class INTERNAL_CLASS> \
+  constexpr char c##ProxyWithInternal<INTERNAL_CLASS>::proxy_name_[];
 // clang-format on
 
 #define PRIMARY_PROXY_MAP_BOILERPLATE(c)                               \
@@ -306,53 +322,67 @@
                                                                        \
  public:  // NOLINTNEXTLINE
 
-#define PROXY_METHOD0(r, method)                                        \
-  r method() override {                                                 \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r> call(c_, &C::method);                              \
-    return call.Marshal(RTC_FROM_HERE, primary_thread_);                \
+#if defined(RTC_DISABLE_PROXY_TRACE_EVENTS)
+#define TRACE_BOILERPLATE(method) \
+  do {                            \
+  } while (0)
+#else  // if defined(RTC_DISABLE_PROXY_TRACE_EVENTS)
+#define TRACE_BOILERPLATE(method)                       \
+  static constexpr auto class_and_method_name =         \
+      rtc::MakeCompileTimeString(proxy_name_)           \
+          .Concat(rtc::MakeCompileTimeString("::"))     \
+          .Concat(rtc::MakeCompileTimeString(#method)); \
+  proxy_internal::ScopedTrace scoped_trace(class_and_method_name.string)
+
+#endif  // if defined(RTC_DISABLE_PROXY_TRACE_EVENTS)
+
+#define PROXY_METHOD0(r, method)                         \
+  r method() override {                                  \
+    TRACE_BOILERPLATE(method);                           \
+    MethodCall<C, r> call(c_, &C::method);               \
+    return call.Marshal(RTC_FROM_HERE, primary_thread_); \
   }
 
-#define PROXY_CONSTMETHOD0(r, method)                                   \
-  r method() const override {                                           \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    ConstMethodCall<C, r> call(c_, &C::method);                         \
-    return call.Marshal(RTC_FROM_HERE, primary_thread_);                \
+#define PROXY_CONSTMETHOD0(r, method)                    \
+  r method() const override {                            \
+    TRACE_BOILERPLATE(method);                           \
+    ConstMethodCall<C, r> call(c_, &C::method);          \
+    return call.Marshal(RTC_FROM_HERE, primary_thread_); \
   }
 
-#define PROXY_METHOD1(r, method, t1)                                    \
-  r method(t1 a1) override {                                            \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r, t1> call(c_, &C::method, std::move(a1));           \
-    return call.Marshal(RTC_FROM_HERE, primary_thread_);                \
+#define PROXY_METHOD1(r, method, t1)                          \
+  r method(t1 a1) override {                                  \
+    TRACE_BOILERPLATE(method);                                \
+    MethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
+    return call.Marshal(RTC_FROM_HERE, primary_thread_);      \
   }
 
-#define PROXY_CONSTMETHOD1(r, method, t1)                               \
-  r method(t1 a1) const override {                                      \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    ConstMethodCall<C, r, t1> call(c_, &C::method, std::move(a1));      \
-    return call.Marshal(RTC_FROM_HERE, primary_thread_);                \
+#define PROXY_CONSTMETHOD1(r, method, t1)                          \
+  r method(t1 a1) const override {                                 \
+    TRACE_BOILERPLATE(method);                                     \
+    ConstMethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
+    return call.Marshal(RTC_FROM_HERE, primary_thread_);           \
   }
 
-#define PROXY_METHOD2(r, method, t1, t2)                                \
-  r method(t1 a1, t2 a2) override {                                     \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1),        \
-                                  std::move(a2));                       \
-    return call.Marshal(RTC_FROM_HERE, primary_thread_);                \
+#define PROXY_METHOD2(r, method, t1, t2)                         \
+  r method(t1 a1, t2 a2) override {                              \
+    TRACE_BOILERPLATE(method);                                   \
+    MethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
+                                  std::move(a2));                \
+    return call.Marshal(RTC_FROM_HERE, primary_thread_);         \
   }
 
-#define PROXY_METHOD3(r, method, t1, t2, t3)                            \
-  r method(t1 a1, t2 a2, t3 a3) override {                              \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1),    \
-                                      std::move(a2), std::move(a3));    \
-    return call.Marshal(RTC_FROM_HERE, primary_thread_);                \
+#define PROXY_METHOD3(r, method, t1, t2, t3)                         \
+  r method(t1 a1, t2 a2, t3 a3) override {                           \
+    TRACE_BOILERPLATE(method);                                       \
+    MethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
+                                      std::move(a2), std::move(a3)); \
+    return call.Marshal(RTC_FROM_HERE, primary_thread_);             \
   }
 
 #define PROXY_METHOD4(r, method, t1, t2, t3, t4)                         \
   r method(t1 a1, t2 a2, t3 a3, t4 a4) override {                        \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method));  \
+    TRACE_BOILERPLATE(method);                                           \
     MethodCall<C, r, t1, t2, t3, t4> call(c_, &C::method, std::move(a1), \
                                           std::move(a2), std::move(a3),  \
                                           std::move(a4));                \
@@ -361,7 +391,7 @@
 
 #define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5)                         \
   r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override {                     \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method));      \
+    TRACE_BOILERPLATE(method);                                               \
     MethodCall<C, r, t1, t2, t3, t4, t5> call(c_, &C::method, std::move(a1), \
                                               std::move(a2), std::move(a3),  \
                                               std::move(a4), std::move(a5)); \
@@ -369,61 +399,61 @@
   }
 
 // Define methods which should be invoked on the secondary thread.
-#define PROXY_SECONDARY_METHOD0(r, method)                              \
-  r method() override {                                                 \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r> call(c_, &C::method);                              \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_METHOD0(r, method)                 \
+  r method() override {                                    \
+    TRACE_BOILERPLATE(method);                             \
+    MethodCall<C, r> call(c_, &C::method);                 \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
   }
 
-#define PROXY_SECONDARY_CONSTMETHOD0(r, method)                         \
-  r method() const override {                                           \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    ConstMethodCall<C, r> call(c_, &C::method);                         \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_CONSTMETHOD0(r, method)            \
+  r method() const override {                              \
+    TRACE_BOILERPLATE(method);                             \
+    ConstMethodCall<C, r> call(c_, &C::method);            \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
   }
 
-#define PROXY_SECONDARY_METHOD1(r, method, t1)                          \
-  r method(t1 a1) override {                                            \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r, t1> call(c_, &C::method, std::move(a1));           \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_METHOD1(r, method, t1)                \
+  r method(t1 a1) override {                                  \
+    TRACE_BOILERPLATE(method);                                \
+    MethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_);    \
   }
 
-#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1)                     \
-  r method(t1 a1) const override {                                      \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    ConstMethodCall<C, r, t1> call(c_, &C::method, std::move(a1));      \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1)                \
+  r method(t1 a1) const override {                                 \
+    TRACE_BOILERPLATE(method);                                     \
+    ConstMethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_);         \
   }
 
-#define PROXY_SECONDARY_METHOD2(r, method, t1, t2)                      \
-  r method(t1 a1, t2 a2) override {                                     \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1),        \
-                                  std::move(a2));                       \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_METHOD2(r, method, t1, t2)               \
+  r method(t1 a1, t2 a2) override {                              \
+    TRACE_BOILERPLATE(method);                                   \
+    MethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
+                                  std::move(a2));                \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_);       \
   }
 
-#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2)                 \
-  r method(t1 a1, t2 a2) const override {                               \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    ConstMethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1),   \
-                                       std::move(a2));                  \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2)               \
+  r method(t1 a1, t2 a2) const override {                             \
+    TRACE_BOILERPLATE(method);                                        \
+    ConstMethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
+                                       std::move(a2));                \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_);            \
   }
 
-#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3)                  \
-  r method(t1 a1, t2 a2, t3 a3) override {                              \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \
-    MethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1),    \
-                                      std::move(a2), std::move(a3));    \
-    return call.Marshal(RTC_FROM_HERE, secondary_thread_);              \
+#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3)               \
+  r method(t1 a1, t2 a2, t3 a3) override {                           \
+    TRACE_BOILERPLATE(method);                                       \
+    MethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
+                                      std::move(a2), std::move(a3)); \
+    return call.Marshal(RTC_FROM_HERE, secondary_thread_);           \
   }
 
 #define PROXY_SECONDARY_CONSTMETHOD3(r, method, t1, t2)                   \
   r method(t1 a1, t2 a2, t3 a3) const override {                          \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method));   \
+    TRACE_BOILERPLATE(method);                                            \
     ConstMethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
                                            std::move(a2), std::move(a3)); \
     return call.Marshal(RTC_FROM_HERE, secondary_thread_);                \
@@ -432,10 +462,10 @@
 // For use when returning purely const state (set during construction).
 // Use with caution. This method should only be used when the return value will
 // always be the same.
-#define BYPASS_PROXY_CONSTMETHOD0(r, method)                                \
-  r method() const override {                                               \
-    proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method));     \
-    return c_->method();                                                    \
+#define BYPASS_PROXY_CONSTMETHOD0(r, method) \
+  r method() const override {                \
+    TRACE_BOILERPLATE(method);               \
+    return c_->method();                     \
   }
 
 }  // namespace webrtc
diff --git a/pc/proxy_unittest.cc b/pc/proxy_unittest.cc
index 497c99a..ef3d97e 100644
--- a/pc/proxy_unittest.cc
+++ b/pc/proxy_unittest.cc
@@ -71,7 +71,7 @@
 PROXY_SECONDARY_METHOD1(std::string, Method1, std::string)
 PROXY_CONSTMETHOD1(std::string, ConstMethod1, std::string)
 PROXY_SECONDARY_METHOD2(std::string, Method2, std::string, std::string)
-END_PROXY_MAP()
+END_PROXY_MAP(Fake)
 
 // Preprocessor hack to get a proxy class a name different than FakeProxy.
 #define FakeProxy FakeSignalingProxy
@@ -84,7 +84,7 @@
 PROXY_METHOD1(std::string, Method1, std::string)
 PROXY_CONSTMETHOD1(std::string, ConstMethod1, std::string)
 PROXY_METHOD2(std::string, Method2, std::string, std::string)
-END_PROXY_MAP()
+END_PROXY_MAP(Fake)
 #undef FakeProxy
 
 class SignalingProxyTest : public ::testing::Test {
@@ -272,7 +272,7 @@
 BEGIN_OWNED_PROXY_MAP(Foo)
 PROXY_PRIMARY_THREAD_DESTRUCTOR()
 PROXY_METHOD0(void, Bar)
-END_PROXY_MAP()
+END_PROXY_MAP(Foo)
 
 class OwnedProxyTest : public ::testing::Test {
  public:
diff --git a/pc/rtp_receiver_proxy.h b/pc/rtp_receiver_proxy.h
index 409612e..d4114e0 100644
--- a/pc/rtp_receiver_proxy.h
+++ b/pc/rtp_receiver_proxy.h
@@ -47,7 +47,7 @@
 PROXY_SECONDARY_METHOD1(void,
                         SetDepacketizerToDecoderFrameTransformer,
                         rtc::scoped_refptr<FrameTransformerInterface>)
-END_PROXY_MAP()
+END_PROXY_MAP(RtpReceiver)
 
 }  // namespace webrtc
 
diff --git a/pc/rtp_sender_proxy.h b/pc/rtp_sender_proxy.h
index e23f57b..2f8fe2c 100644
--- a/pc/rtp_sender_proxy.h
+++ b/pc/rtp_sender_proxy.h
@@ -44,7 +44,7 @@
 PROXY_METHOD1(void,
               SetEncoderToPacketizerFrameTransformer,
               rtc::scoped_refptr<FrameTransformerInterface>)
-END_PROXY_MAP()
+END_PROXY_MAP(RtpSender)
 
 }  // namespace webrtc
 
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index 3c7db95..6b1307b 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -310,7 +310,7 @@
 PROXY_METHOD1(webrtc::RTCError,
               SetOfferedRtpHeaderExtensions,
               rtc::ArrayView<const RtpHeaderExtensionCapability>)
-END_PROXY_MAP()
+END_PROXY_MAP(RtpTransceiver)
 
 }  // namespace webrtc
 
diff --git a/pc/sctp_data_channel.cc b/pc/sctp_data_channel.cc
index 76091c2..359cc79 100644
--- a/pc/sctp_data_channel.cc
+++ b/pc/sctp_data_channel.cc
@@ -65,7 +65,7 @@
 PROXY_METHOD0(void, Close)
 // TODO(bugs.webrtc.org/11547): Change to run on the network thread.
 PROXY_METHOD1(bool, Send, const DataBuffer&)
-END_PROXY_MAP()
+END_PROXY_MAP(DataChannel)
 
 }  // namespace
 
diff --git a/pc/video_track_source_proxy.h b/pc/video_track_source_proxy.h
index 5a6def6..8914dd0 100644
--- a/pc/video_track_source_proxy.h
+++ b/pc/video_track_source_proxy.h
@@ -42,7 +42,7 @@
 PROXY_SECONDARY_METHOD1(void,
                         RemoveEncodedSink,
                         rtc::VideoSinkInterface<RecordableEncodedFrame>*)
-END_PROXY_MAP()
+END_PROXY_MAP(VideoTrackSource)
 
 }  // namespace webrtc
 
diff --git a/rtc_base/string_utils.h b/rtc_base/string_utils.h
index 23c55cb..d844e5e 100644
--- a/rtc_base/string_utils.h
+++ b/rtc_base/string_utils.h
@@ -88,6 +88,43 @@
 // TODO(jonasolsson): replace with absl::Hex when that becomes available.
 std::string ToHex(const int i);
 
+// CompileTimeString comprises of a string-like object which can be used as a
+// regular const char* in compile time and supports concatenation. Useful for
+// concatenating constexpr strings in for example macro declarations.
+namespace rtc_base_string_utils_internal {
+template <int NPlus1>
+struct CompileTimeString {
+  char string[NPlus1] = {0};
+  constexpr CompileTimeString() = default;
+  template <int MPlus1>
+  explicit constexpr CompileTimeString(const char (&chars)[MPlus1]) {
+    char* chars_pointer = string;
+    for (auto c : chars)
+      *chars_pointer++ = c;
+  }
+  template <int MPlus1>
+  constexpr auto Concat(CompileTimeString<MPlus1> b) {
+    CompileTimeString<NPlus1 + MPlus1 - 1> result;
+    char* chars_pointer = result.string;
+    for (auto c : string)
+      *chars_pointer++ = c;
+    chars_pointer = result.string + NPlus1 - 1;
+    for (auto c : b.string)
+      *chars_pointer++ = c;
+    result.string[NPlus1 + MPlus1 - 2] = 0;
+    return result;
+  }
+  constexpr operator const char*() { return string; }
+};
+}  // namespace rtc_base_string_utils_internal
+
+// Makes a constexpr CompileTimeString<X> without having to specify X
+// explicitly.
+template <int N>
+constexpr auto MakeCompileTimeString(const char (&a)[N]) {
+  return rtc_base_string_utils_internal::CompileTimeString<N>(a);
+}
+
 }  // namespace rtc
 
 #endif  // RTC_BASE_STRING_UTILS_H_
diff --git a/rtc_base/string_utils_unittest.cc b/rtc_base/string_utils_unittest.cc
index 2fa1f22..120f7e6 100644
--- a/rtc_base/string_utils_unittest.cc
+++ b/rtc_base/string_utils_unittest.cc
@@ -39,4 +39,29 @@
 
 #endif  // WEBRTC_WIN
 
+TEST(CompileTimeString, MakeActsLikeAString) {
+  EXPECT_STREQ(MakeCompileTimeString("abc123"), "abc123");
+}
+
+TEST(CompileTimeString, ConvertibleToStdString) {
+  EXPECT_EQ(std::string(MakeCompileTimeString("abab")), "abab");
+}
+
+namespace detail {
+constexpr bool StringEquals(const char* a, const char* b) {
+  while (*a && *a == *b)
+    a++, b++;
+  return *a == *b;
+}
+}  // namespace detail
+
+static_assert(detail::StringEquals(MakeCompileTimeString("handellm"),
+                                   "handellm"),
+              "String should initialize.");
+
+static_assert(detail::StringEquals(MakeCompileTimeString("abc123").Concat(
+                                       MakeCompileTimeString("def456ghi")),
+                                   "abc123def456ghi"),
+              "Strings should concatenate.");
+
 }  // namespace rtc