diff --git a/api/include/opentelemetry/common/macros.h b/api/include/opentelemetry/common/macros.h index db1ca54a86..b4a270084d 100644 --- a/api/include/opentelemetry/common/macros.h +++ b/api/include/opentelemetry/common/macros.h @@ -3,6 +3,73 @@ #pragma once +/* + OPENTELEMETRY_HAVE_BUILTIN&OPENTELEMETRY_HAVE_FEATURE + + Checks whether the compiler supports a Clang Feature Checking Macro, and if + so, checks whether it supports the provided builtin function "x" where x + is one of the functions noted in + https://clang.llvm.org/docs/LanguageExtensions.html + + Note: Use this macro to avoid an extra level of #ifdef __has_builtin check. + http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html +*/ +#if !defined(OPENTELEMETRY_HAVE_BUILTIN) +# ifdef __has_builtin +# define OPENTELEMETRY_HAVE_BUILTIN(x) __has_builtin(x) +# else +# define OPENTELEMETRY_HAVE_BUILTIN(x) 0 +# endif +#endif + +#if !defined(OPENTELEMETRY_HAVE_FEATURE) +# ifdef __has_feature +# define OPENTELEMETRY_HAVE_FEATURE(f) __has_feature(f) +# else +# define OPENTELEMETRY_HAVE_FEATURE(f) 0 +# endif +#endif + +/* + has feature + + OPENTELEMETRY_HAVE_ATTRIBUTE + + A function-like feature checking macro that is a wrapper around + `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a + nonzero constant integer if the attribute is supported or 0 if not. + + It evaluates to zero if `__has_attribute` is not defined by the compiler. + + GCC: https://gcc.gnu.org/gcc-5/changes.html + Clang: https://clang.llvm.org/docs/LanguageExtensions.html +*/ +#if !defined(OPENTELEMETRY_HAVE_ATTRIBUTE) +# ifdef __has_attribute +# define OPENTELEMETRY_HAVE_ATTRIBUTE(x) __has_attribute(x) +# else +# define OPENTELEMETRY_HAVE_ATTRIBUTE(x) 0 +# endif +#endif + +/* + OPENTELEMETRY_HAVE_CPP_ATTRIBUTE + + A function-like feature checking macro that accepts C++11 style attributes. + It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 + (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't + find `__has_cpp_attribute`, will evaluate to 0. +*/ +#if !defined(OPENTELEMETRY_HAVE_CPP_ATTRIBUTE) +# if defined(__cplusplus) && defined(__has_cpp_attribute) +// NOTE: requiring __cplusplus above should not be necessary, but +// works around https://bugs.llvm.org/show_bug.cgi?id=23435. +# define OPENTELEMETRY_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +# else +# define OPENTELEMETRY_HAVE_CPP_ATTRIBUTE(x) 0 +# endif +#endif + /* Expected usage pattern: @@ -319,3 +386,107 @@ point. # define OPENTELEMETRY_EXPORT #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 + annotated function (or, for a parameter of a constructor, in the value of the + constructed object). This attribute causes warnings to be produced if a + temporary object does not live long enough. + + When applied to a reference parameter, the referenced object is assumed to be + retained by the return value of the function. When applied to a non-reference + parameter (for example, a pointer or a class type), all temporaries + referenced by the parameter are assumed to be retained by the return value of + the function. + + See also the upstream documentation: + https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +*/ +#ifndef OPENTELEMETRY_ATTRIBUTE_LIFETIME_BOUND +# if OPENTELEMETRY_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +# define OPENTELEMETRY_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +# elif OPENTELEMETRY_HAVE_ATTRIBUTE(lifetimebound) +# define OPENTELEMETRY_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +# else +# define OPENTELEMETRY_ATTRIBUTE_LIFETIME_BOUND +# endif +#endif + +// OPENTELEMETRY_HAVE_MEMORY_SANITIZER +// +// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of +// a compiler instrumentation module and a run-time library. +#ifndef OPENTELEMETRY_HAVE_MEMORY_SANITIZER +# if !defined(__native_client__) && OPENTELEMETRY_HAVE_FEATURE(memory_sanitizer) +# define OPENTELEMETRY_HAVE_MEMORY_SANITIZER 1 +# else +# define OPENTELEMETRY_HAVE_MEMORY_SANITIZER 0 +# endif +#endif + +#if OPENTELEMETRY_HAVE_MEMORY_SANITIZER && OPENTELEMETRY_HAVE_ATTRIBUTE(no_sanitize_memory) +# define OPENTELEMETRY_SANITIZER_NO_MEMORY \ + __attribute__((no_sanitize_memory)) // __attribute__((no_sanitize("memory"))) +#else +# define OPENTELEMETRY_SANITIZER_NO_MEMORY +#endif + +// OPENTELEMETRY_HAVE_THREAD_SANITIZER +// +// ThreadSanitizer (TSan) is a fast data race detector. +#ifndef OPENTELEMETRY_HAVE_THREAD_SANITIZER +# if defined(__SANITIZE_THREAD__) +# define OPENTELEMETRY_HAVE_THREAD_SANITIZER 1 +# elif OPENTELEMETRY_HAVE_FEATURE(thread_sanitizer) +# define OPENTELEMETRY_HAVE_THREAD_SANITIZER 1 +# else +# define OPENTELEMETRY_HAVE_THREAD_SANITIZER 0 +# endif +#endif + +#if OPENTELEMETRY_HAVE_THREAD_SANITIZER && OPENTELEMETRY_HAVE_ATTRIBUTE(no_sanitize_thread) +# define OPENTELEMETRY_SANITIZER_NO_THREAD \ + __attribute__((no_sanitize_thread)) // __attribute__((no_sanitize("thread"))) +#else +# define OPENTELEMETRY_SANITIZER_NO_THREAD +#endif + +// OPENTELEMETRY_HAVE_ADDRESS_SANITIZER +// +// AddressSanitizer (ASan) is a fast memory error detector. +#ifndef OPENTELEMETRY_HAVE_ADDRESS_SANITIZER +# if defined(__SANITIZE_ADDRESS__) +# define OPENTELEMETRY_HAVE_ADDRESS_SANITIZER 1 +# elif OPENTELEMETRY_HAVE_FEATURE(address_sanitizer) +# define OPENTELEMETRY_HAVE_ADDRESS_SANITIZER 1 +# else +# define OPENTELEMETRY_HAVE_ADDRESS_SANITIZER 0 +# endif +#endif + +// OPENTELEMETRY_HAVE_HWADDRESS_SANITIZER +// +// Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan +// memory error detector which can use CPU features like ARM TBI, Intel LAM or +// AMD UAI. +#ifndef OPENTELEMETRY_HAVE_HWADDRESS_SANITIZER +# if defined(__SANITIZE_HWADDRESS__) +# define OPENTELEMETRY_HAVE_HWADDRESS_SANITIZER 1 +# elif OPENTELEMETRY_HAVE_FEATURE(hwaddress_sanitizer) +# define OPENTELEMETRY_HAVE_HWADDRESS_SANITIZER 1 +# else +# define OPENTELEMETRY_HAVE_HWADDRESS_SANITIZER 0 +# endif +#endif + +#if OPENTELEMETRY_HAVE_ADDRESS_SANITIZER && OPENTELEMETRY_HAVE_ATTRIBUTE(no_sanitize_address) +# define OPENTELEMETRY_SANITIZER_NO_ADDRESS \ + __attribute__((no_sanitize_address)) // __attribute__((no_sanitize("address"))) +#elif OPENTELEMETRY_HAVE_ADDRESS_SANITIZER && defined(_MSC_VER) && _MSC_VER >= 1928 +# define OPENTELEMETRY_SANITIZER_NO_ADDRESS __declspec(no_sanitize_address) +#elif OPENTELEMETRY_HAVE_HWADDRESS_SANITIZER && OPENTELEMETRY_HAVE_ATTRIBUTE(no_sanitize) +# define OPENTELEMETRY_SANITIZER_NO_ADDRESS __attribute__((no_sanitize("hwaddress"))) +#else +# define OPENTELEMETRY_SANITIZER_NO_ADDRESS +#endif diff --git a/api/include/opentelemetry/nostd/string_view.h b/api/include/opentelemetry/nostd/string_view.h index 9f9fb42180..5ee86dce23 100644 --- a/api/include/opentelemetry/nostd/string_view.h +++ b/api/include/opentelemetry/nostd/string_view.h @@ -18,6 +18,7 @@ # include # include +# include "opentelemetry/common/macros.h" # include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -43,7 +44,7 @@ class string_view string_view(const char *str) noexcept : length_(std::strlen(str)), data_(str) {} - string_view(const std::basic_string &str) noexcept + string_view(const std::basic_string &str OPENTELEMETRY_ATTRIBUTE_LIFETIME_BOUND) noexcept : length_(str.length()), data_(str.c_str()) {} diff --git a/exporters/otlp/src/otlp_file_client.cc b/exporters/otlp/src/otlp_file_client.cc index 2549c369d1..9678b22476 100644 --- a/exporters/otlp/src/otlp_file_client.cc +++ b/exporters/otlp/src/otlp_file_client.cc @@ -22,6 +22,7 @@ #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" // clang-format on +#include "opentelemetry/common/macros.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/common/base64.h" @@ -30,6 +31,7 @@ #include #include +#include #include #include #include @@ -106,6 +108,14 @@ snprintf(buffer, static_cast(bufsz), fmt, ##args) #endif +#if (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) +# define OTLP_FILE_OPEN(f, path, mode) fopen_s(&f, path, mode) +#else +# include +# define OTLP_FILE_OPEN(f, path, mode) f = fopen(path, mode) +#endif + OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter { @@ -978,6 +988,16 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender } } + // Written size is not required to be precise, we can just ignore tsan report here. + OPENTELEMETRY_SANITIZER_NO_THREAD void MaybeRotateLog(std::size_t data_size) + { + if (file_->written_size > 0 && file_->written_size + data_size > options_.file_size) + { + RotateLog(); + } + CheckUpdate(); + } + void Export(nostd::string_view data, std::size_t record_count) override { if (!is_initialized_.load(std::memory_order_acquire)) @@ -985,20 +1005,15 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender Initialize(); } - if (file_->written_size > 0 && file_->written_size + data.size() > options_.file_size) - { - RotateLog(); - } - CheckUpdate(); + MaybeRotateLog(data.size()); - std::shared_ptr out = OpenLogFile(true); + std::shared_ptr out = OpenLogFile(true); if (!out) { return; } - out->write(data.data(), data.size()); - out->write("\n", 1); + fwrite(data.data(), 1, data.size(), out.get()); { std::lock_guard lock_guard{file_->file_lock}; @@ -1006,7 +1021,7 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender file_->record_count += record_count; // Pipe file size always returns 0, we ignore the size limit of it. - auto written_size = out->tellp(); + auto written_size = ftell(out.get()); if (written_size >= 0) { file_->written_size = static_cast(written_size); @@ -1018,7 +1033,7 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender { file_->left_flush_record_count = options_.flush_count; - out->flush(); + fflush(out.get()); file_->flushed_record_count.store(file_->record_count.load(std::memory_order_acquire), std::memory_order_release); @@ -1030,7 +1045,7 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender } } - // Maybe need spawn a background thread to flush ostream + // Maybe need spawn a background thread to flush FILE SpawnBackgroundWorkThread(); } @@ -1180,11 +1195,11 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender OpenLogFile(false); } - std::shared_ptr OpenLogFile(bool destroy_content) + std::shared_ptr OpenLogFile(bool destroy_content) { std::lock_guard lock_guard{file_->file_lock}; - if (file_->current_file && file_->current_file->good()) + if (file_->current_file) { return file_->current_file; } @@ -1198,11 +1213,11 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender { OTEL_INTERNAL_LOG_ERROR("[OTLP FILE Client] Generate file path from pattern " << options_.file_pattern << " failed"); - return std::shared_ptr(); + return nullptr; } file_path[file_path_size] = 0; - std::shared_ptr of = std::make_shared(); + std::shared_ptr of = std::make_shared(); std::string directory_name = FileSystemUtil::DirName(file_path); if (!directory_name.empty()) @@ -1231,20 +1246,21 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender if (destroy_content && FileSystemUtil::IsExist(file_path)) { - std::fstream trunc_file; - trunc_file.open(file_path, std::ios::binary | std::ios::out | std::ios::trunc); - if (!trunc_file.is_open()) + FILE *trunc_file = nullptr; + OTLP_FILE_OPEN(trunc_file, file_path, "wb"); + if (nullptr == trunc_file) { OTEL_INTERNAL_LOG_ERROR("[OTLP FILE Client] Open " << static_cast(file_path) << " failed with pattern: " << options_.file_pattern); - return std::shared_ptr(); + return nullptr; } - trunc_file.close(); + fclose(trunc_file); } - of->open(file_path, std::ios::binary | std::ios::out | std::ios::app); - if (!of->is_open()) + std::FILE *new_file = nullptr; + OTLP_FILE_OPEN(new_file, file_path, "ab"); + if (nullptr == new_file) { std::string hint; if (!directory_name.empty()) @@ -1255,11 +1271,12 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender OTEL_INTERNAL_LOG_ERROR("[OTLP FILE Client] Open " << static_cast(file_path) << " failed with pattern: " << options_.file_pattern << hint); - return std::shared_ptr(); + return nullptr; } + of = std::shared_ptr(new_file, fclose); - of->seekp(0, std::ios_base::end); - file_->written_size = static_cast(of->tellp()); + fseek(of.get(), 0, SEEK_END); + file_->written_size = static_cast(ftell(of.get())); file_->current_file = of; file_->last_checkpoint = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); @@ -1447,7 +1464,7 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender if (concurrency_file->current_file) { - concurrency_file->current_file->flush(); + fflush(concurrency_file->current_file.get()); } concurrency_file->flushed_record_count.store(current_record_count, @@ -1479,7 +1496,7 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender std::size_t rotate_index; std::size_t written_size; std::size_t left_flush_record_count; - std::shared_ptr current_file; + std::shared_ptr current_file; std::mutex file_lock; std::time_t last_checkpoint; std::string file_path; @@ -1509,7 +1526,6 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileOstreamBackend : public OtlpFileAppende void Export(nostd::string_view data, std::size_t /*record_count*/) override { os_.get().write(data.data(), data.size()); - os_.get().write("\n", 1); } bool ForceFlush(std::chrono::microseconds /*timeout*/) noexcept override @@ -1577,6 +1593,7 @@ opentelemetry::sdk::common::ExportResult OtlpFileClient::Export( if (backend_) { + post_body_json += '\n'; backend_->Export(post_body_json, record_count); return ::opentelemetry::sdk::common::ExportResult::kSuccess; }