Skip to content

Commit

Permalink
Internal change that helps identify the origin of reflection calls.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 510255963
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Feb 16, 2023
1 parent 649cc30 commit d41deb9
Show file tree
Hide file tree
Showing 5 changed files with 363 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ cc_dist_library(
"//src/google/protobuf:arena_align",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf:wkt_cc_proto",
"//src/google/protobuf:reflection_mode",
"//src/google/protobuf/compiler:importer",
"//src/google/protobuf/json",
"//src/google/protobuf/util:delimited_message_util",
Expand Down
25 changes: 25 additions & 0 deletions src/google/protobuf/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,31 @@ cc_test(
],
)

cc_library(
name = "reflection_mode",
srcs = ["reflection_mode.cc"],
hdrs = ["reflection_mode.h"],
include_prefix = "google/protobuf",
visibility = [
"//:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
":port_def",
":protobuf",
],
)

cc_test(
name = "reflection_mode_test",
srcs = ["reflection_mode_test.cc"],
deps = [
":reflection_mode",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)

################################################################################
# Helper targets for Kotlin tests
################################################################################
Expand Down
57 changes: 57 additions & 0 deletions src/google/protobuf/reflection_mode.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "google/protobuf/reflection_mode.h"

// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace internal {

#if !defined(PROTOBUF_NO_THREADLOCAL)

#if defined(PROTOBUF_USE_DLLS)
ReflectionMode& ScopedReflectionMode::reflection_mode() {
static PROTOBUF_THREAD_LOCAL ReflectionMode reflection_mode =
ReflectionMode::kDefault;
return reflection_mode;
}
#else
PROTOBUF_CONSTINIT PROTOBUF_THREAD_LOCAL ReflectionMode
ScopedReflectionMode::reflection_mode_ = ReflectionMode::kDefault;
#endif

#endif

} // namespace internal
} // namespace protobuf
} // namespace google
165 changes: 165 additions & 0 deletions src/google/protobuf/reflection_mode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This header provides support for a per thread 'reflection mode'.
//
// Some protocol buffer optimizations use interceptors to determine which
// fields are effectively used in the application. These optimizations are
// disabled if certain reflection calls are intercepted as the assumption is
// then that any field data can be accessed.
//
// The 'reflection mode' defined in this header is intended to be used by
// logic such as ad-hoc profilers to indicate that any scoped reflection usage
// is not originating from, or affecting application code. This reflection mode
// can then be used by such interceptors to ignore any reflection calls not
// affecting the application behavior.

#ifndef GOOGLE_PROTOBUF_REFLECTION_MODE_H__
#define GOOGLE_PROTOBUF_REFLECTION_MODE_H__

#include <cstddef>

// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace internal {

// The ReflectionModes are ordered in observability levels:
// kDefault: Lowest level. All reflection calls are observable.
// kDebugString: Middle level. Only reflection calls in Message::DebugString are
// observable.
// kDiagnostics: Highest level. No reflection calls are observable.
enum class ReflectionMode {
kDefault,
kDebugString,
kDiagnostics,
};

// Returns the current ReflectionMode of protobuf for the current thread. This
// reflection mode can be used by interceptors to ignore any reflection calls
// not affecting the application behavior.
// Always returns `kDefault' if the current platform does not support thread
// local data.
ReflectionMode GetReflectionMode();

// Scoping class to set the specific ReflectionMode for a given scope.
class PROTOBUF_EXPORT ScopedReflectionMode final {
public:
// Sets the current reflection mode, which will be restored at destruction.
// The reflection mode can only be 'elevated' in observability levels.
// For instance, if the current mode is `kDiagnostics` then scope will remain
// unchanged regardless of `mode`.
explicit ScopedReflectionMode(ReflectionMode mode);

// Restores the previous reflection mode.
~ScopedReflectionMode();

// Returns the scoped ReflectionMode for the current thread.
// See `GetReflectionMode()` for more information on purpose and usage.
static ReflectionMode current_reflection_mode();

// ScopedReflectionMode is only intended to be used as a locally scoped
// instance to set a reflection mode for the code scoped by this instance.
ScopedReflectionMode(const ScopedReflectionMode&) = delete;
ScopedReflectionMode& operator=(const ScopedReflectionMode&) = delete;

private:
#if !defined(PROTOBUF_NO_THREADLOCAL)
const ReflectionMode previous_mode_;
#if defined(PROTOBUF_USE_DLLS)
static ReflectionMode& reflection_mode();
#else
PROTOBUF_CONSTINIT static PROTOBUF_THREAD_LOCAL ReflectionMode
reflection_mode_;
#endif // PROTOBUF_USE_DLLS
#endif // !PROTOBUF_NO_THREADLOCAL
};

#if !defined(PROTOBUF_NO_THREADLOCAL)

#if defined(PROTOBUF_USE_DLLS)

inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode)
: previous_mode_(reflection_mode()) {
if (mode > reflection_mode()) {
reflection_mode() = mode;
}
}

inline ScopedReflectionMode::~ScopedReflectionMode() {
reflection_mode() = previous_mode_;
}

inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
return reflection_mode();
}

#else

inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode)
: previous_mode_(reflection_mode_) {
if (mode > reflection_mode_) {
reflection_mode_ = mode;
}
}

inline ScopedReflectionMode::~ScopedReflectionMode() {
reflection_mode_ = previous_mode_;
}

inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
return reflection_mode_;
}

#endif // PROTOBUF_USE_DLLS

#else

inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode) {}
inline ScopedReflectionMode::~ScopedReflectionMode() {}
inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
return ReflectionMode::kDefault;
}

#endif // !PROTOBUF_NO_THREADLOCAL

inline ReflectionMode GetReflectionMode() {
return ScopedReflectionMode::current_reflection_mode();
}

} // namespace internal
} // namespace protobuf
} // namespace google

#include "google/protobuf/port_undef.inc"

#endif // GOOGLE_PROTOBUF_REFLECTION_MODE_H__
115 changes: 115 additions & 0 deletions src/google/protobuf/reflection_mode_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/reflection_mode.h"

#include <gmock/gmock.h>
#include <gtest/gtest.h>

namespace google {
namespace protobuf {
namespace internal {

#ifndef PROTOBUF_NO_THREADLOCAL

TEST(ReflectionModeTest, SimpleScopedReflection) {
ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
ScopedReflectionMode scope(ReflectionMode::kDiagnostics);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDiagnostics);
}

TEST(ReflectionModeTest, CleanNestedScopedReflection) {
ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
{
ScopedReflectionMode scope1(ReflectionMode::kDebugString);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDebugString);
{
ScopedReflectionMode scope2(ReflectionMode::kDiagnostics);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDiagnostics);
}
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDebugString);
}
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
}

TEST(ReflectionModeTest, UglyNestedScopedReflection) {
ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
ScopedReflectionMode scope1(ReflectionMode::kDebugString);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDebugString);
ScopedReflectionMode scope2(ReflectionMode::kDiagnostics);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDiagnostics);
}

TEST(ReflectionModeTest, DebugStringModeDoesNotReplaceDiagnosticsMode) {
ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
ScopedReflectionMode scope1(ReflectionMode::kDiagnostics);
{
ScopedReflectionMode scope2(ReflectionMode::kDebugString);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDiagnostics);
}
}

#else

TEST(ReflectionModeTest, AlwaysReturnDefaultWhenNoThreadLocal) {
ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
{
ScopedReflectionMode scope1(ReflectionMode::kDebugString);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
{
ScopedReflectionMode scope2(ReflectionMode::kDiagnostics);
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
}
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
}
EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
ReflectionMode::kDefault);
}

#endif

} // namespace internal
} // namespace protobuf
} // namespace google

0 comments on commit d41deb9

Please sign in to comment.