• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 //
8 // This header provides support for a per thread 'reflection mode'.
9 //
10 // Some protocol buffer optimizations use interceptors to determine which
11 // fields are effectively used in the application. These optimizations are
12 // disabled if certain reflection calls are intercepted as the assumption is
13 // then that any field data can be accessed.
14 //
15 // The 'reflection mode' defined in this header is intended to be used by
16 // logic such as ad-hoc profilers to indicate that any scoped reflection usage
17 // is not originating from, or affecting application code. This reflection mode
18 // can then be used by such interceptors to ignore any reflection calls not
19 // affecting the application behavior.
20 
21 #ifndef GOOGLE_PROTOBUF_REFLECTION_MODE_H__
22 #define GOOGLE_PROTOBUF_REFLECTION_MODE_H__
23 
24 #include <cstddef>
25 
26 // Must be included last.
27 #include "google/protobuf/port_def.inc"
28 
29 namespace google {
30 namespace protobuf {
31 namespace internal {
32 
33 // The ReflectionModes are ordered in observability levels:
34 // kDefault: Lowest level. All reflection calls are observable.
35 // kDebugString: Middle level. Only reflection calls in Message::DebugString are
36 //               observable.
37 // kDiagnostics: Highest level. No reflection calls are observable.
38 enum class ReflectionMode {
39   kDefault,
40   kDebugString,
41   kDiagnostics,
42 };
43 
44 // Returns the current ReflectionMode of protobuf for the current thread. This
45 // reflection mode can be used by interceptors to ignore any reflection calls
46 // not affecting the application behavior.
47 // Always returns `kDefault' if the current platform does not support thread
48 // local data.
49 ReflectionMode GetReflectionMode();
50 
51 // Scoping class to set the specific ReflectionMode for a given scope.
52 class PROTOBUF_EXPORT ScopedReflectionMode final {
53  public:
54   // Sets the current reflection mode, which will be restored at destruction.
55   // The reflection mode can only be 'elevated' in observability levels.
56   // For instance, if the current mode is `kDiagnostics` then scope will remain
57   // unchanged regardless of `mode`.
58   explicit ScopedReflectionMode(ReflectionMode mode);
59 
60   // Restores the previous reflection mode.
61   ~ScopedReflectionMode();
62 
63   // Returns the scoped ReflectionMode for the current thread.
64   // See `GetReflectionMode()` for more information on purpose and usage.
65   static ReflectionMode current_reflection_mode();
66 
67   // ScopedReflectionMode is only intended to be used as a locally scoped
68   // instance to set a reflection mode for the code scoped by this instance.
69   ScopedReflectionMode(const ScopedReflectionMode&) = delete;
70   ScopedReflectionMode& operator=(const ScopedReflectionMode&) = delete;
71 
72  private:
73 #if !defined(PROTOBUF_NO_THREADLOCAL)
74   const ReflectionMode previous_mode_;
75 #if defined(PROTOBUF_USE_DLLS) && defined(_WIN32)
76   // Thread local variables cannot be exposed through MSVC DLL interface but we
77   // can wrap them in static functions.
78   static ReflectionMode& reflection_mode();
79 #else
80   PROTOBUF_CONSTINIT static PROTOBUF_THREAD_LOCAL ReflectionMode
81       reflection_mode_;
82 #endif  // PROTOBUF_USE_DLLS && _MSC_VER
83 #endif  // !PROTOBUF_NO_THREADLOCAL
84 };
85 
86 #if !defined(PROTOBUF_NO_THREADLOCAL)
87 
88 #if defined(PROTOBUF_USE_DLLS) && defined(_WIN32)
89 
ScopedReflectionMode(ReflectionMode mode)90 inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode)
91     : previous_mode_(reflection_mode()) {
92   if (mode > reflection_mode()) {
93     reflection_mode() = mode;
94   }
95 }
96 
~ScopedReflectionMode()97 inline ScopedReflectionMode::~ScopedReflectionMode() {
98   reflection_mode() = previous_mode_;
99 }
100 
current_reflection_mode()101 inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
102   return reflection_mode();
103 }
104 
105 #else
106 
ScopedReflectionMode(ReflectionMode mode)107 inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode)
108     : previous_mode_(reflection_mode_) {
109   if (mode > reflection_mode_) {
110     reflection_mode_ = mode;
111   }
112 }
113 
~ScopedReflectionMode()114 inline ScopedReflectionMode::~ScopedReflectionMode() {
115   reflection_mode_ = previous_mode_;
116 }
117 
current_reflection_mode()118 inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
119   return reflection_mode_;
120 }
121 
122 #endif  // PROTOBUF_USE_DLLS && _MSC_VER
123 
124 #else
125 
ScopedReflectionMode(ReflectionMode mode)126 inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode) {}
~ScopedReflectionMode()127 inline ScopedReflectionMode::~ScopedReflectionMode() {}
current_reflection_mode()128 inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
129   return ReflectionMode::kDefault;
130 }
131 
132 #endif  // !PROTOBUF_NO_THREADLOCAL
133 
GetReflectionMode()134 inline ReflectionMode GetReflectionMode() {
135   return ScopedReflectionMode::current_reflection_mode();
136 }
137 
138 }  // namespace internal
139 }  // namespace protobuf
140 }  // namespace google
141 
142 #include "google/protobuf/port_undef.inc"
143 
144 #endif  // GOOGLE_PROTOBUF_REFLECTION_MODE_H__
145