diff --git a/api/include/opentelemetry/baggage/baggage_context.h b/api/include/opentelemetry/baggage/baggage_context.h index a0016353ba..e3eedf019f 100644 --- a/api/include/opentelemetry/baggage/baggage_context.h +++ b/api/include/opentelemetry/baggage/baggage_context.h @@ -18,9 +18,11 @@ static const std::string kBaggageHeader = "baggage"; inline nostd::shared_ptr GetBaggage(const context::Context &context) noexcept { context::ContextValue context_value = context.GetValue(kBaggageHeader); - if (nostd::holds_alternative>(context_value)) + + if (const nostd::shared_ptr *value = + nostd::get_if>(&context_value)) { - return nostd::get>(context_value); + return *value; } static nostd::shared_ptr empty_baggage{new Baggage()}; return empty_baggage; diff --git a/api/include/opentelemetry/common/macros.h b/api/include/opentelemetry/common/macros.h index 9d1f322ae5..191f951b25 100644 --- a/api/include/opentelemetry/common/macros.h +++ b/api/include/opentelemetry/common/macros.h @@ -420,6 +420,44 @@ point. # define OPENTELEMETRY_HAVE_EXCEPTIONS 0 #endif +// Exception handling macros +// +// When exceptions are enabled, expands to real try/catch. +// When exceptions are disabled, expands to if(true)/else-if(false) +// +// Usage: +// bool NoexceptMethod() noexcept +// { +// OPENTELEMETRY_TRY +// { +// return ThrowingMethod(); +// } +// OPENTELEMETRY_CATCH_ALL +// { +// return false; +// } +// } +#if OPENTELEMETRY_HAVE_EXCEPTIONS +# define OPENTELEMETRY_TRY try +# define OPENTELEMETRY_CATCH_ALL catch (...) +#else +# define OPENTELEMETRY_TRY if (true) +# define OPENTELEMETRY_CATCH_ALL else +#endif + +// Marks a path as unreachable +#if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) +# include +# define OPENTELEMETRY_UNREACHABLE() std::unreachable() +#elif OPENTELEMETRY_HAVE_BUILTIN(__builtin_unreachable) +# define OPENTELEMETRY_UNREACHABLE() __builtin_unreachable() +#elif defined(_MSC_VER) +# define OPENTELEMETRY_UNREACHABLE() __assume(0) +#else +# include +# define OPENTELEMETRY_UNREACHABLE() abort() +#endif + /* OPENTELEMETRY_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function parameter or implicit object parameter is retained by the return value of the diff --git a/api/include/opentelemetry/common/string_util.h b/api/include/opentelemetry/common/string_util.h index 5108f186e7..638ddd1ea7 100644 --- a/api/include/opentelemetry/common/string_util.h +++ b/api/include/opentelemetry/common/string_util.h @@ -5,6 +5,7 @@ #include +#include "opentelemetry/common/macros.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/version.h" @@ -25,7 +26,15 @@ class StringUtil { right--; } - return str.substr(left, 1 + right - left); + OPENTELEMETRY_TRY + { + return str.substr(left, 1 + right - left); + } + OPENTELEMETRY_CATCH_ALL + { + return nostd::string_view(); + } + OPENTELEMETRY_UNREACHABLE(); } static nostd::string_view Trim(nostd::string_view str) noexcept diff --git a/api/include/opentelemetry/nostd/function_ref.h b/api/include/opentelemetry/nostd/function_ref.h index 6947777966..874d2a2a87 100644 --- a/api/include/opentelemetry/nostd/function_ref.h +++ b/api/include/opentelemetry/nostd/function_ref.h @@ -78,10 +78,10 @@ class function_ref std::is_convertible::type, R>::value, #endif int>::type = 0> + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) function_ref(F &&f) { // Binding by named variable here intentionally keeps function_ref non-owning. - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) BindTo(f); // not forward } diff --git a/api/include/opentelemetry/nostd/string_view.h b/api/include/opentelemetry/nostd/string_view.h index a0c8081473..13c8cfe560 100644 --- a/api/include/opentelemetry/nostd/string_view.h +++ b/api/include/opentelemetry/nostd/string_view.h @@ -71,7 +71,7 @@ class string_view { if (pos > length_) { -# if __EXCEPTIONS +# if OPENTELEMETRY_HAVE_EXCEPTIONS throw std::out_of_range{"opentelemetry::nostd::string_view"}; # else std::terminate(); diff --git a/api/include/opentelemetry/nostd/variant.h b/api/include/opentelemetry/nostd/variant.h index 3294554c58..b55ce35ce1 100644 --- a/api/include/opentelemetry/nostd/variant.h +++ b/api/include/opentelemetry/nostd/variant.h @@ -18,7 +18,7 @@ // Header-only. Without compiling the actual Abseil binary. As Abseil moves on to new // toolchains, it may drop support for Visual Studio 2015 in future versions. -# if defined(__EXCEPTIONS) +# if OPENTELEMETRY_HAVE_EXCEPTIONS # include OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd diff --git a/api/include/opentelemetry/plugin/detail/utility.h b/api/include/opentelemetry/plugin/detail/utility.h index 8e78fb4270..c29cd2c53f 100644 --- a/api/include/opentelemetry/plugin/detail/utility.h +++ b/api/include/opentelemetry/plugin/detail/utility.h @@ -3,36 +3,30 @@ #pragma once -#if __EXCEPTIONS -# include -#endif // __EXCEPTIONS +#include "opentelemetry/version.h" #include -#include "opentelemetry/version.h" - OPENTELEMETRY_BEGIN_NAMESPACE namespace plugin { namespace detail { inline void CopyErrorMessage(const char *source, std::string &destination) noexcept -#if __EXCEPTIONS -try -#endif { - if (source == nullptr) + OPENTELEMETRY_TRY + { + if (source == nullptr) + { + return; + } + destination.assign(source); + } + OPENTELEMETRY_CATCH_ALL { return; } - destination.assign(source); -} -#if __EXCEPTIONS -catch (const std::bad_alloc &) -{ - return; } -#endif } // namespace detail } // namespace plugin OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/variant.h b/api/include/opentelemetry/std/variant.h index 58bc510c5f..e1c660eb6d 100644 --- a/api/include/opentelemetry/std/variant.h +++ b/api/include/opentelemetry/std/variant.h @@ -33,7 +33,7 @@ using monostate = std::monostate; // Apple Platforms provide std::bad_variant_access only in newer versions of OS. // To keep API compatible with any version of OS - we are providing our own // implementation of nostd::bad_variant_access exception. -# if __EXCEPTIONS +# if OPENTELEMETRY_HAVE_EXCEPTIONS // nostd::bad_variant_access class bad_variant_access : public std::exception @@ -48,7 +48,7 @@ class bad_variant_access : public std::exception } # endif -# if __EXCEPTIONS +# if OPENTELEMETRY_HAVE_EXCEPTIONS # define THROW_BAD_VARIANT_ACCESS throw_bad_variant_access() # else # define THROW_BAD_VARIANT_ACCESS std::terminate() diff --git a/api/include/opentelemetry/trace/context.h b/api/include/opentelemetry/trace/context.h index 08b1e327ca..146f0d65f9 100644 --- a/api/include/opentelemetry/trace/context.h +++ b/api/include/opentelemetry/trace/context.h @@ -15,20 +15,21 @@ namespace trace // Get Span from explicit context inline nostd::shared_ptr GetSpan(const context::Context &context) noexcept { - context::ContextValue span = context.GetValue(kSpanKey); - if (nostd::holds_alternative>(span)) + context::ContextValue span_value = context.GetValue(kSpanKey); + if (const nostd::shared_ptr *value = nostd::get_if>(&span_value)) { - return nostd::get>(span); + return *value; } return nostd::shared_ptr(new DefaultSpan(SpanContext::GetInvalid())); } +// Check if the context is from a root span inline bool IsRootSpan(const context::Context &context) noexcept { context::ContextValue is_root_span = context.GetValue(kIsRootSpanKey); - if (nostd::holds_alternative(is_root_span)) + if (const bool *value = nostd::get_if(&is_root_span)) { - return nostd::get(is_root_span); + return *value; } return false; } diff --git a/api/include/opentelemetry/trace/trace_state.h b/api/include/opentelemetry/trace/trace_state.h index 7ed83284cb..3d6133e646 100644 --- a/api/include/opentelemetry/trace/trace_state.h +++ b/api/include/opentelemetry/trace/trace_state.h @@ -54,36 +54,44 @@ class OPENTELEMETRY_EXPORT TraceState */ static nostd::shared_ptr FromHeader(nostd::string_view header) noexcept { - - common::KeyValueStringTokenizer kv_str_tokenizer(header); - size_t cnt = kv_str_tokenizer.NumTokens(); // upper bound on number of kv pairs - if (cnt > kMaxKeyValuePairs) - { - // trace state should be discarded if count exceeds - return GetDefault(); - } - - nostd::shared_ptr ts(new TraceState(cnt)); - bool kv_valid{false}; - nostd::string_view key, value; - while (kv_str_tokenizer.next(kv_valid, key, value) && ts->kv_properties_->Size() < cnt) + OPENTELEMETRY_TRY { - if (kv_valid == false) + common::KeyValueStringTokenizer kv_str_tokenizer(header); + size_t cnt = kv_str_tokenizer.NumTokens(); // upper bound on number of kv pairs + if (cnt > kMaxKeyValuePairs) { + // trace state should be discarded if count exceeds return GetDefault(); } - if (!IsValidKey(key) || !IsValidValue(value)) + nostd::shared_ptr ts(new TraceState(cnt)); + bool kv_valid{false}; + nostd::string_view key; + nostd::string_view value; + while (kv_str_tokenizer.next(kv_valid, key, value) && ts->kv_properties_->Size() < cnt) { - // invalid header. return empty TraceState - ts->kv_properties_.reset(new common::KeyValueProperties()); - break; + if (kv_valid == false) + { + return GetDefault(); + } + + if (!IsValidKey(key) || !IsValidValue(value)) + { + // invalid header. return empty TraceState + ts->kv_properties_.reset(new common::KeyValueProperties()); + break; + } + + ts->kv_properties_->AddEntry(key, value); } - ts->kv_properties_->AddEntry(key, value); + return ts; } - - return ts; + OPENTELEMETRY_CATCH_ALL + { + return GetDefault(); + } + OPENTELEMETRY_UNREACHABLE(); } /** @@ -117,11 +125,17 @@ class OPENTELEMETRY_EXPORT TraceState */ bool Get(nostd::string_view key, std::string &value) const noexcept { - if (!IsValidKey(key)) + OPENTELEMETRY_TRY + { + if (!IsValidKey(key)) + { + return false; + } + } + OPENTELEMETRY_CATCH_ALL { return false; } - return kv_properties_->GetValue(key, value); } @@ -138,29 +152,37 @@ class OPENTELEMETRY_EXPORT TraceState nostd::shared_ptr Set(const nostd::string_view &key, const nostd::string_view &value) noexcept { - auto curr_size = kv_properties_->Size(); - if (!IsValidKey(key) || !IsValidValue(value)) - { - // max size reached or invalid key/value. Returning empty TraceState - return TraceState::GetDefault(); - } - auto allocate_size = curr_size; - if (curr_size < kMaxKeyValuePairs) + OPENTELEMETRY_TRY { - allocate_size += 1; + if (!IsValidKey(key) || !IsValidValue(value)) + { + // max size reached or invalid key/value. Returning empty TraceState + return TraceState::GetDefault(); + } + const auto curr_size = kv_properties_->Size(); + auto allocate_size = curr_size; + if (curr_size < kMaxKeyValuePairs) + { + allocate_size += 1; + } + nostd::shared_ptr ts(new TraceState(allocate_size)); + if (curr_size < kMaxKeyValuePairs) + { + // add new field first + ts->kv_properties_->AddEntry(key, value); + } + // add rest of the fields. + kv_properties_->GetAllEntries([&ts](nostd::string_view key, nostd::string_view value) { + ts->kv_properties_->AddEntry(key, value); + return true; + }); + return ts; } - nostd::shared_ptr ts(new TraceState(allocate_size)); - if (curr_size < kMaxKeyValuePairs) + OPENTELEMETRY_CATCH_ALL { - // add new field first - ts->kv_properties_->AddEntry(key, value); + return TraceState::GetDefault(); } - // add rest of the fields. - kv_properties_->GetAllEntries([&ts](nostd::string_view key, nostd::string_view value) { - ts->kv_properties_->AddEntry(key, value); - return true; - }); - return ts; + OPENTELEMETRY_UNREACHABLE(); } /** @@ -171,25 +193,35 @@ class OPENTELEMETRY_EXPORT TraceState */ nostd::shared_ptr Delete(const nostd::string_view &key) noexcept { - if (!IsValidKey(key)) + OPENTELEMETRY_TRY { - return TraceState::GetDefault(); + if (!IsValidKey(key)) + { + return TraceState::GetDefault(); + } + const auto curr_size = kv_properties_->Size(); + auto allocate_size = curr_size; + std::string unused; + if (kv_properties_->GetValue(key, unused)) + { + allocate_size -= 1; + } + nostd::shared_ptr ts(new TraceState(allocate_size)); + kv_properties_->GetAllEntries( + [&ts, &key](nostd::string_view e_key, nostd::string_view e_value) { + if (key != e_key) + { + ts->kv_properties_->AddEntry(e_key, e_value); + } + return true; + }); + return ts; } - auto curr_size = kv_properties_->Size(); - auto allocate_size = curr_size; - std::string unused; - if (kv_properties_->GetValue(key, unused)) + OPENTELEMETRY_CATCH_ALL { - allocate_size -= 1; + return TraceState::GetDefault(); } - nostd::shared_ptr ts(new TraceState(allocate_size)); - kv_properties_->GetAllEntries( - [&ts, &key](nostd::string_view e_key, nostd::string_view e_value) { - if (key != e_key) - ts->kv_properties_->AddEntry(e_key, e_value); - return true; - }); - return ts; + OPENTELEMETRY_UNREACHABLE(); } // Returns true if there are no keys, false otherwise. diff --git a/api/include/opentelemetry/trace/tracer.h b/api/include/opentelemetry/trace/tracer.h index 8ce6d6ed0e..95aab3b240 100644 --- a/api/include/opentelemetry/trace/tracer.h +++ b/api/include/opentelemetry/trace/tracer.h @@ -159,14 +159,11 @@ class Tracer static nostd::shared_ptr GetCurrentSpan() noexcept { context::ContextValue active_span = context::RuntimeContext::GetValue(kSpanKey); - if (nostd::holds_alternative>(active_span)) + if (const nostd::shared_ptr *value = nostd::get_if>(&active_span)) { - return nostd::get>(active_span); - } - else - { - return nostd::shared_ptr(new DefaultSpan(SpanContext::GetInvalid())); + return *value; } + return nostd::shared_ptr(new DefaultSpan(SpanContext::GetInvalid())); } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 diff --git a/api/test/common/string_util_test.cc b/api/test/common/string_util_test.cc index 4da8563175..03f49d5538 100644 --- a/api/test/common/string_util_test.cc +++ b/api/test/common/string_util_test.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include "opentelemetry/nostd/string_view.h" @@ -50,3 +51,12 @@ TEST(StringUtilTest, TrimString) EXPECT_EQ(StringUtil::Trim(testcase.input), testcase.expected); } } + +TEST(StringUtilTest, TrimStringOutOfRange) +{ +#if OPENTELEMETRY_HAVE_EXCEPTIONS + EXPECT_EQ(StringUtil::Trim("x", 2, 1), ""); +#else + EXPECT_DEATH(StringUtil::Trim("x", 2, 1), ""); +#endif +} diff --git a/api/test/nostd/string_view_test.cc b/api/test/nostd/string_view_test.cc index f32da62b42..14bc69b4af 100644 --- a/api/test/nostd/string_view_test.cc +++ b/api/test/nostd/string_view_test.cc @@ -8,6 +8,7 @@ #include #include +#include "opentelemetry/common/macros.h" #include "opentelemetry/nostd/string_view.h" using opentelemetry::nostd::string_view; @@ -74,7 +75,7 @@ TEST(StringViewTest, SubstrPortion) TEST(StringViewTest, SubstrOutOfRange) { string_view s = "abc123"; -#if __EXCEPTIONS || (defined(OPENTELEMETRY_STL_VERSION) && (OPENTELEMETRY_STL_VERSION >= 2017)) +#if OPENTELEMETRY_HAVE_EXCEPTIONS EXPECT_THROW((void)s.substr(10), std::out_of_range); #else EXPECT_DEATH({ s.substr(10); }, ""); diff --git a/api/test/nostd/variant_test.cc b/api/test/nostd/variant_test.cc index 193bd4874a..5ebadd6eac 100644 --- a/api/test/nostd/variant_test.cc +++ b/api/test/nostd/variant_test.cc @@ -51,7 +51,7 @@ TEST(VariantTest, Get) EXPECT_EQ(nostd::get(w), 12); EXPECT_EQ(*nostd::get_if(&v), 12); EXPECT_EQ(nostd::get_if(&v), nullptr); -#if __EXCEPTIONS || (defined(OPENTELEMETRY_STL_VERSION) && (OPENTELEMETRY_STL_VERSION >= 2017)) +#if OPENTELEMETRY_HAVE_EXCEPTIONS EXPECT_THROW(nostd::get(w), nostd::bad_variant_access); #else EXPECT_DEATH({ nostd::get(w); }, ""); diff --git a/api/test/trace/BUILD b/api/test/trace/BUILD index fd39ec9ea5..72c9ab6104 100644 --- a/api/test/trace/BUILD +++ b/api/test/trace/BUILD @@ -20,6 +20,22 @@ cc_test( ], ) +cc_test( + name = "context_test", + srcs = [ + "context_test.cc", + ], + tags = [ + "api", + "test", + "trace", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "noop_test", srcs = [ diff --git a/api/test/trace/CMakeLists.txt b/api/test/trace/CMakeLists.txt index 34b49db84d..f13e4a342e 100644 --- a/api/test/trace/CMakeLists.txt +++ b/api/test/trace/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(propagation) foreach( testname + context_test key_value_iterable_view_test provider_test span_id_test diff --git a/api/test/trace/context_test.cc b/api/test/trace/context_test.cc new file mode 100644 index 0000000000..cb9b2766c2 --- /dev/null +++ b/api/test/trace/context_test.cc @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include +#include + +#include "opentelemetry/context/context.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/context.h" +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/span_metadata.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" + +namespace +{ + +namespace context_api = opentelemetry::context; +namespace trace_api = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +nostd::shared_ptr MakeValidSpan() +{ + const uint8_t trace_id_bytes[trace_api::TraceId::kSize] = {0x10, 0x32, 0x54, 0x76, 0x98, 0xba, + 0xdc, 0xfe, 0x01, 0x23, 0x45, 0x67, + 0x89, 0xab, 0xcd, 0xef}; + const uint8_t span_id_bytes[trace_api::SpanId::kSize] = {0xde, 0xad, 0xbe, 0xef, + 0xca, 0xfe, 0xba, 0xbe}; + + trace_api::SpanContext valid_span_context( + trace_api::TraceId(trace_id_bytes), trace_api::SpanId(span_id_bytes), + trace_api::TraceFlags(trace_api::TraceFlags::kIsSampled), false); + + return nostd::shared_ptr(new trace_api::DefaultSpan(valid_span_context)); +} + +TEST(TraceContextTest, GetSpan) +{ + { + context_api::Context context; + auto span = trace_api::GetSpan(context); + ASSERT_TRUE(span != nullptr); + EXPECT_FALSE(span->GetContext().IsValid()); + } + + { + context_api::Context context; + auto context_with_wrong_type = context.SetValue(trace_api::kSpanKey, true); + auto span = trace_api::GetSpan(context_with_wrong_type); + ASSERT_TRUE(span != nullptr); + EXPECT_FALSE(span->GetContext().IsValid()); + } + + { + context_api::Context context; + auto input_span = MakeValidSpan(); + ASSERT_TRUE(input_span->GetContext().IsValid()); + auto context_with_span = context.SetValue(trace_api::kSpanKey, input_span); + auto output_span = trace_api::GetSpan(context_with_span); + ASSERT_TRUE(output_span != nullptr); + EXPECT_TRUE(output_span->GetContext().IsValid()); + EXPECT_EQ(output_span.get(), input_span.get()); + } +} + +TEST(TraceContextTest, SetSpan) +{ + context_api::Context context; + auto input_span = MakeValidSpan(); + + auto context_with_span = trace_api::SetSpan(context, input_span); + auto output_span = trace_api::GetSpan(context_with_span); + ASSERT_TRUE(output_span != nullptr); + EXPECT_EQ(output_span.get(), input_span.get()); +} + +TEST(TraceContextTest, IsRootSpan) +{ + { + context_api::Context context; + EXPECT_FALSE(trace_api::IsRootSpan(context)); + auto root_context = context.SetValue(trace_api::kIsRootSpanKey, true); + EXPECT_TRUE(trace_api::IsRootSpan(root_context)); + } + + { + context_api::Context context; + uint64_t value{1}; + auto context_with_wrong_type = context.SetValue(trace_api::kIsRootSpanKey, value); + EXPECT_FALSE(trace_api::IsRootSpan(context_with_wrong_type)); + } +} +} // namespace diff --git a/api/test/trace/trace_state_test.cc b/api/test/trace/trace_state_test.cc index 6613d9270c..d8b5aaa7e3 100644 --- a/api/test/trace/trace_state_test.cc +++ b/api/test/trace/trace_state_test.cc @@ -58,6 +58,7 @@ TEST(TraceStateTest, ValidateHeaderParsing) } testcases[] = {{"k1=v1", "k1=v1"}, {"K1=V1", ""}, {"k1=v1,k2=v2,k3=v3", "k1=v1,k2=v2,k3=v3"}, + {"k1=v1,InvalidKey=v2", ""}, {"k1=v1,k2=v2,,", "k1=v1,k2=v2"}, {"k1=v1,k2=v2,invalidmember", ""}, {"1a-2f@foo=bar1,a*/foo-_/bar=bar4", "1a-2f@foo=bar1,a*/foo-_/bar=bar4"}, @@ -73,6 +74,15 @@ TEST(TraceStateTest, ValidateHeaderParsing) } } +TEST(TraceStateTest, ExceedsMaxKeyValuePairs) +{ + std::string header = header_with_max_members(); + header += ",overflow=value"; + + auto ts = TraceState::FromHeader(header); + EXPECT_EQ(ts->ToHeader(), ""); +} + TEST(TraceStateTest, TraceStateGet) {