From e2cda382cc677e4234cac23399e075dbe7bcb4e0 Mon Sep 17 00:00:00 2001 From: mishal23 Date: Mon, 26 Oct 2020 00:32:45 +0530 Subject: [PATCH] initial files added (not compiling) --- api/include/opentelemetry/event/UUID.hpp | 409 +++++++++++ .../opentelemetry/trace/key_value_iterable.h | 51 ++ .../trace/key_value_iterable_view.h | 65 ++ api/include/opentelemetry/trace/span.h | 11 + .../opentelemetry/trace/span_context.h | 4 + api/include/opentelemetry/trace/tracer.h | 2 + exporters/etw/BUILD | 2 + .../exporters/etw/etw_provider_exporter.h | 644 ++++++++++++++++++ .../exporters/etw/etw_tracer_exporter.h | 407 +++++++++++ .../opentelemetry/exporters/etw/utils.h | 227 ++++++ exporters/etw/src/etw_provider_exporter.cc | 3 + exporters/etw/src/etw_tracer_exporter.cc | 3 + 12 files changed, 1828 insertions(+) create mode 100644 api/include/opentelemetry/event/UUID.hpp create mode 100644 api/include/opentelemetry/trace/key_value_iterable.h create mode 100644 api/include/opentelemetry/trace/key_value_iterable_view.h create mode 100644 exporters/etw/include/opentelemetry/exporters/etw/utils.h diff --git a/api/include/opentelemetry/event/UUID.hpp b/api/include/opentelemetry/event/UUID.hpp new file mode 100644 index 0000000000..1ae28a4640 --- /dev/null +++ b/api/include/opentelemetry/event/UUID.hpp @@ -0,0 +1,409 @@ +#pragma once + +#include "opentelemetry/version.h" + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include "Windows.h" +#endif + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace event +{ + +/// +/// The UUID structure represents the portable cross-platform implementation of a GUID (Globally +/// Unique ID). +/// +/// +/// UUIDs identify objects such as interfaces, manager entry-point vectors (EPVs), and class +/// objects. A UUID is a 128-bit value consisting of one group of eight hexadecimal digits, followed +/// by three groups of four hexadecimal digits, each followed by one group of 12 hexadecimal digits. +/// +#pragma pack(push) /* push current alignment to stack */ +#pragma pack(1) /* set alignment to 1 byte boundary */ +struct UUID +{ + /// + /// Specifies the first eight hexadecimal digits of the GUID. + /// + uint32_t Data1; + + /// + /// Specifies the first group of four hexadecimal digits. + /// + uint16_t Data2; + + /// + /// Specifies the second group of four hexadecimal digits. + /// + uint16_t Data3; + + /// + /// An array of eight bytes. + /// The first two bytes contain the third group of four hexadecimal digits. + /// The remaining six bytes contain the final 12 hexadecimal digits. + /// + uint8_t Data4[8]; + + /// + /// The default UUID constructor. + /// Creates a null instance of the UUID object (initialized to all zeros). + /// {00000000-0000-0000-0000-000000000000}. + /// + UUID() : Data1(0), Data2(0), Data3(0) + { + for (size_t i = 0; i < 8; i++) + { + Data4[i] = 0; + } + }; + + /// + /// A constructor that creates a UUID object from a hyphenated string as defined by + /// https://tools.ietf.org/html/rfc4122#page-4 + /// + /// A hyphenated string that contains the UUID (curly braces + /// optional). + UUID(const char *uuid_string) + { + const char *str = uuid_string; + // Skip curly brace + if (str[0] == '{') + { + str++; + } + // Convert to set of integer values + unsigned long p0; + unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; + if ( + // Parse input with dashes + (11 == sscanf(str, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2, &p3, + &p4, &p5, &p6, &p7, &p8, &p9, &p10)) || + // Parse input without dashes + (11 == sscanf(str, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2, &p3, &p4, + &p5, &p6, &p7, &p8, &p9, &p10))) + { + Data1 = static_cast(p0); + Data2 = static_cast(p1); + Data3 = static_cast(p2); + Data4[0] = static_cast(p3); + Data4[1] = static_cast(p4); + Data4[2] = static_cast(p5); + Data4[3] = static_cast(p6); + Data4[4] = static_cast(p7); + Data4[5] = static_cast(p8); + Data4[6] = static_cast(p9); + Data4[7] = static_cast(p10); + } + else // Invalid input--use a safe default value + { + Data1 = 0; + Data2 = 0; + Data3 = 0; + Data4[0] = 0; + Data4[1] = 0; + Data4[2] = 0; + Data4[3] = 0; + Data4[4] = 0; + Data4[5] = 0; + Data4[6] = 0; + Data4[7] = 0; + } + } + + /// + /// A constructor that creates a UUID object from a byte array. + /// + /// A byte array. + /// + /// A boolean value that specifies the byte order.
+ /// A value of true specifies the more natural human-readable order.
+ /// A value of false (the default) specifies the same order as the .NET GUID constructor. + /// + UUID(const uint8_t guid_bytes[16], bool bigEndian = false) + { + if (bigEndian) + { + /* Use big endian - human-readable */ + // Part 1 + Data1 = guid_bytes[3]; + Data1 |= ((uint32_t)(guid_bytes[2])) << 8; + Data1 |= ((uint32_t)(guid_bytes[1])) << 16; + Data1 |= ((uint32_t)(guid_bytes[0])) << 24; + // Part 2 + Data2 = guid_bytes[5]; + Data2 |= ((uint16_t)(guid_bytes[4])) << 8; + // Part 3 + Data3 = guid_bytes[7]; + Data3 |= ((uint16_t)(guid_bytes[6])) << 8; + } + else + { + /* Use little endian - the same order as .NET C# Guid() class uses */ + // Part 1 + Data1 = guid_bytes[0]; + Data1 |= ((uint32_t)(guid_bytes[1])) << 8; + Data1 |= ((uint32_t)(guid_bytes[2])) << 16; + Data1 |= ((uint32_t)(guid_bytes[3])) << 24; + // Part 2 + Data2 = guid_bytes[4]; + Data2 |= ((uint16_t)(guid_bytes[5])) << 8; + // Part 3 + Data3 = guid_bytes[6]; + Data3 |= ((uint16_t)(guid_bytes[7])) << 8; + } + // Part 4 + for (size_t i = 0; i < 8; i++) + { + Data4[i] = guid_bytes[8 + i]; + } + } + + /// + /// A constructor that creates a UUID object from three integers and a byte array. + /// + /// An integer that specifies the first eight hexadecimal digits of the + /// UUID. An integer that specifies the first group of four hexadecimal + /// digits. An integer that specifies the second group of four + /// hexadecimal digits. A reference to an array of eight bytes. The first + /// two bytes contain the third group of four hexadecimal digits. The remaining six bytes contain + /// the final 12 hexadecimal digits. + UUID(int d1, int d2, int d3, const std::initializer_list &v) + : Data1((uint32_t)d1), Data2((uint16_t)d2), Data3((uint16_t)d3) + { + size_t i = 0; + for (auto val : v) + { + Data4[i] = val; + i++; + } + } + + /// + /// The UUID copy constructor. + /// + /// A UUID object. + UUID(const UUID &uuid) + { + this->Data1 = uuid.Data1; + this->Data2 = uuid.Data2; + this->Data3 = uuid.Data3; + memcpy(&(this->Data4[0]), &(uuid.Data4[0]), sizeof(uuid.Data4)); + } + +#ifdef _WIN32 + + /// + /// A constructor that creates a UUID object from a Windows GUID object. + /// + /// A Windows GUID object. + UUID(GUID guid) + { + this->Data1 = guid.Data1; + this->Data2 = guid.Data2; + this->Data3 = guid.Data3; + std::memcpy(&(this->Data4[0]), &(guid.Data4[0]), sizeof(guid.Data4)); + } + + /// + /// Converts a standard vector of bytes into a Windows GUID object. + /// + /// A standard vector of bytes. + /// A GUID. + static GUID to_GUID(std::vector const &bytes) + { + UUID temp_t = UUID(bytes.data()); + GUID temp; + temp.Data1 = temp_t.Data1; + temp.Data2 = temp_t.Data2; + temp.Data3 = temp_t.Data3; + for (size_t i = 0; i < 8; i++) + { + temp.Data4[i] = temp_t.Data4[i]; + } + return temp; + } + + GUID to_GUID() + { + GUID temp; + temp.Data1 = Data1; + temp.Data2 = Data2; + temp.Data3 = Data3; + for (size_t i = 0; i < 8; i++) + { + temp.Data4[i] = Data4[i]; + } + return temp; + } + +#endif + + /// + /// Converts this UUID to an array of bytes. + /// + /// A uint8_t array of 16 bytes. + void to_bytes(uint8_t (&guid_bytes)[16]) const + { + // Part 1 + guid_bytes[0] = (uint8_t)((Data1)&0xFF); + guid_bytes[1] = (uint8_t)((Data1 >> 8) & 0xFF); + guid_bytes[2] = (uint8_t)((Data1 >> 16) & 0xFF); + guid_bytes[3] = (uint8_t)((Data1 >> 24) & 0xFF); + // Part 2 + guid_bytes[4] = (uint8_t)((Data2)&0xFF); + guid_bytes[5] = (uint8_t)((Data2 >> 8) & 0xFF); + // Part 3 + guid_bytes[6] = (uint8_t)((Data3)&0xFF); + guid_bytes[7] = (uint8_t)((Data3 >> 8) & 0xFF); + // Part 4 + for (size_t i = 0; i < 8; i++) + { + guid_bytes[8 + i] = Data4[i]; + } + } + + /// + /// Convert this UUID object to a string. + /// + /// This UUID object in a string. + std::string to_string() const + { + static char inttoHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + const unsigned buffSize = 36 + 1; // 36 + null-terminator + char buf[buffSize] = {0}; + + int test = (Data1 >> 28 & 0x0000000F); + buf[0] = inttoHex[test]; + test = (int)(Data1 >> 24 & 0x0000000F); + buf[1] = inttoHex[test]; + test = (int)(Data1 >> 20 & 0x0000000F); + buf[2] = inttoHex[test]; + test = (int)(Data1 >> 16 & 0x0000000F); + buf[3] = inttoHex[test]; + test = (int)(Data1 >> 12 & 0x0000000F); + buf[4] = inttoHex[test]; + test = (int)(Data1 >> 8 & 0x0000000F); + buf[5] = inttoHex[test]; + test = (int)(Data1 >> 4 & 0x0000000F); + buf[6] = inttoHex[test]; + test = (int)(Data1 & 0x0000000F); + buf[7] = inttoHex[test]; + buf[8] = '-'; + test = (int)(Data2 >> 12 & 0x000F); + buf[9] = inttoHex[test]; + test = (int)(Data2 >> 8 & 0x000F); + buf[10] = inttoHex[test]; + test = (int)(Data2 >> 4 & 0x000F); + buf[11] = inttoHex[test]; + test = (int)(Data2 & 0x000F); + buf[12] = inttoHex[test]; + buf[13] = '-'; + test = (int)(Data3 >> 12 & 0x000F); + buf[14] = inttoHex[test]; + test = (int)(Data3 >> 8 & 0x000F); + buf[15] = inttoHex[test]; + test = (int)(Data3 >> 4 & 0x000F); + buf[16] = inttoHex[test]; + test = (int)(Data3 & 0x000F); + buf[17] = inttoHex[test]; + buf[18] = '-'; + test = (int)(Data4[0] >> 4 & 0x0F); + buf[19] = inttoHex[test]; + test = (int)(Data4[0] & 0x0F); + buf[20] = inttoHex[test]; + test = (int)(Data4[1] >> 4 & 0x0F); + buf[21] = inttoHex[test]; + test = (int)(Data4[1] & 0x0F); + buf[22] = inttoHex[test]; + buf[23] = '-'; + test = (int)(Data4[2] >> 4 & 0x0F); + buf[24] = inttoHex[test]; + test = (int)(Data4[2] & 0x0F); + buf[25] = inttoHex[test]; + test = (int)(Data4[3] >> 4 & 0x0F); + buf[26] = inttoHex[test]; + test = (int)(Data4[3] & 0x0F); + buf[27] = inttoHex[test]; + test = (int)(Data4[4] >> 4 & 0x0F); + buf[28] = inttoHex[test]; + test = (int)(Data4[4] & 0x0F); + buf[29] = inttoHex[test]; + test = (int)(Data4[5] >> 4 & 0x0F); + buf[30] = inttoHex[test]; + test = (int)(Data4[5] & 0x0F); + buf[31] = inttoHex[test]; + test = (int)(Data4[6] >> 4 & 0x0F); + buf[32] = inttoHex[test]; + test = (int)(Data4[6] & 0x0F); + buf[33] = inttoHex[test]; + test = (int)(Data4[7] >> 4 & 0x0F); + buf[34] = inttoHex[test]; + test = (int)(Data4[7] & 0x0F); + buf[35] = inttoHex[test]; + buf[36] = 0; + + return std::string(buf); + } + + /// + /// Calculates the size of this UUID object. + /// The output from this method is compatible with std::unordered_map. + /// + /// The size of the UUID object in bytes. + size_t Hash() const + { + // Compute individual hash values for Data1, Data2, Data3, and parts of Data4 + size_t res = 17; + res = res * 31 + Data1; + res = res * 31 + Data2; + res = res * 31 + Data3; + res = res * 31 + (Data4[0] << 24 | Data4[1] << 16 | Data4[6] << 8 | Data4[7]); + return res; + } + + /// + /// Tests to determine whether two UUID objects are equivalent (needed for maps). + /// + /// A boolean value that indicates success or failure. + bool operator==(UUID const &other) const + { + return Data1 == other.Data1 && Data2 == other.Data2 && Data3 == other.Data3 && + (0 == memcmp(Data4, other.Data4, sizeof(Data4))); + } + + /// + /// Tests to determine how to sort 2 UUID objects + /// + /// A boolean value that indicates success or failure. + bool operator<(UUID const &other) const + { + return Data1 < other.Data1 || Data2 < other.Data2 || Data3 == other.Data3 || + (memcmp(Data4, other.Data4, sizeof(Data4)) < 0); + } +}; +#pragma pack(pop) /* restore original alignment from stack */ + +/// +/// Declare UUIDComparer as the Comparer when using UUID as a key in a map or set +/// +struct UUIDComparer : std::less +{ + inline size_t operator()(UUID const &key) const { return key.Hash(); } + + inline bool operator()(UUID const &lhs, UUID const &rhs) const { return lhs.Hash() < rhs.Hash(); } +}; + +} // namespace event + +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/api/include/opentelemetry/trace/key_value_iterable.h b/api/include/opentelemetry/trace/key_value_iterable.h new file mode 100644 index 0000000000..94caf64749 --- /dev/null +++ b/api/include/opentelemetry/trace/key_value_iterable.h @@ -0,0 +1,51 @@ +#pragma once + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/nostd/function_ref.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +/** + * Supports internal iteration over a collection of key-value pairs. + */ +class KeyValueIterable +{ +public: + virtual ~KeyValueIterable() = default; + + /** + * Iterate over key-value pairs + * @param callback a callback to invoke for each key-value. If the callback returns false, + * the iteration is aborted. + * @return true if every key-value pair was iterated over + */ + virtual bool ForEachKeyValue(nostd::function_ref + callback) const noexcept = 0; + + /** + * @return the number of key-value pairs + */ + virtual size_t size() const noexcept = 0; +}; + +// +// NULL object pattern empty iterable. +// +class NullKeyValueIterable : public KeyValueIterable +{ +public: + NullKeyValueIterable(){}; + + virtual bool ForEachKeyValue( + nostd::function_ref) const noexcept + { + return true; + }; + + virtual size_t size() const noexcept { return 0; } +}; + +} // namespace trace +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/api/include/opentelemetry/trace/key_value_iterable_view.h b/api/include/opentelemetry/trace/key_value_iterable_view.h new file mode 100644 index 0000000000..0f2eced602 --- /dev/null +++ b/api/include/opentelemetry/trace/key_value_iterable_view.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/trace/key_value_iterable.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +namespace detail +{ +inline void take_key_value(nostd::string_view, common::AttributeValue) {} + +template +auto is_key_value_iterable_impl(T iterable) + -> decltype(take_key_value(std::begin(iterable)->first, std::begin(iterable)->second), + nostd::size(iterable), + std::true_type{}); + +std::false_type is_key_value_iterable_impl(...); + +template +struct is_key_value_iterable +{ + static const bool value = decltype(detail::is_key_value_iterable_impl(std::declval()))::value; +}; +} // namespace detail + +template +class KeyValueIterableView final : public KeyValueIterable +{ +#if 0 // TODO: [MG] - confirm if we really need this + static_assert(detail::is_key_value_iterable::value, "Must be a key-value iterable"); +#endif + +public: + explicit KeyValueIterableView(const T &container) noexcept : container_{&container} {}; + + // KeyValueIterable + bool ForEachKeyValue(nostd::function_ref + callback) const noexcept override + { + auto iter = std::begin(*container_); + auto last = std::end(*container_); + for (; iter != last; ++iter) + { + if (!callback(iter->first, iter->second)) + { + return false; + } + } + return true; + } + + size_t size() const noexcept override { return nostd::size(*container_); } + +private: + const T *container_; +}; +} // namespace trace +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index f324658740..f537a53056 100644 --- a/api/include/opentelemetry/trace/span.h +++ b/api/include/opentelemetry/trace/span.h @@ -5,9 +5,11 @@ #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/key_value_iterable_view.h" #include "opentelemetry/core/timestamp.h" +#include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/nostd/type_traits.h" #include "opentelemetry/trace/canonical_code.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/version.h" @@ -167,5 +169,14 @@ class Span // AddEvent). virtual bool IsRecording() const noexcept = 0; }; + +template +nostd::shared_ptr to_span_ptr(TracerType *objPtr, + nostd::string_view name, + const trace::StartSpanOptions &options) +{ + return nostd::shared_ptr{new (std::nothrow) SpanType{*objPtr, name, options}}; +} + } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/span_context.h b/api/include/opentelemetry/trace/span_context.h index 6add265eed..5c9941a9c6 100644 --- a/api/include/opentelemetry/trace/span_context.h +++ b/api/include/opentelemetry/trace/span_context.h @@ -33,6 +33,10 @@ namespace trace_api = opentelemetry::trace; class SpanContext final { public: + // An invalid SpanContext. + SpanContext() noexcept + : trace_flags_(trace::TraceFlags((uint8_t) false)), remote_parent_(false){}; + /* A temporary constructor for an invalid SpanContext. * Trace id and span id are set to invalid (all zeros). * diff --git a/api/include/opentelemetry/trace/tracer.h b/api/include/opentelemetry/trace/tracer.h index 9ca8b94b8c..7025bfc424 100644 --- a/api/include/opentelemetry/trace/tracer.h +++ b/api/include/opentelemetry/trace/tracer.h @@ -43,11 +43,13 @@ class Tracer template ::value> * = nullptr> + //template ::value> * = nullptr> nostd::shared_ptr StartSpan(nostd::string_view name, const T &attributes, const StartSpanOptions &options = {}) noexcept { return this->StartSpan(name, common::KeyValueIterableView(attributes), options); + //return this->StartSpan(name, KeyValueIterableView(attributes), options); } nostd::shared_ptr StartSpan( diff --git a/exporters/etw/BUILD b/exporters/etw/BUILD index 6731fc650c..fabc10cfb7 100644 --- a/exporters/etw/BUILD +++ b/exporters/etw/BUILD @@ -7,6 +7,7 @@ cc_library( ], hdrs = [ "include/opentelemetry/exporters/etw/etw_tracer_exporter.h", + "include/opentelemetry/exporters/etw/utils.h", ], strip_include_prefix = "include", deps = [ @@ -21,6 +22,7 @@ cc_library( ], hdrs = [ "include/opentelemetry/exporters/etw/etw_provider_exporter.h", + "include/opentelemetry/exporters/etw/utils.h", ] strip_include_prefix = "include", deps = [ diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_provider_exporter.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_provider_exporter.h index e69de29bb2..8f1b9567e8 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_provider_exporter.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_provider_exporter.h @@ -0,0 +1,644 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef _MSC_VER +// evntprov.h(838) : warning C4459 : declaration of 'Version' hides global declaration +# pragma warning(disable : 4459) +// needed for Unit Testing with krabs.hpp +# pragma warning(disable : 4018) +#endif + +#include +#include +#include + +#include "opentelemetry/exporters/etw/utils.h" + +#ifdef HAVE_MSGPACK +// This option requires INCLUDE_DIR=$(ProjectDir)\..\..\third_party\json\include;... +# include "nlohmann/json.hpp" +#endif + +#ifndef HAVE_NO_TLD +// Allow to opt-out from `TraceLoggingDynamic.h` header usage +# define HAVE_TLD +# include "TraceLoggingDynamic.h" +#endif + +#include +#include +#include +#include + +#ifdef HAVE_KRABS_TESTS +// krabs.hpp requires this definition of min macro from Windows.h +# ifndef min +# define min(a, b) (((a) < (b)) ? (a) : (b)) +# endif +#endif + +#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000 + +OPENTELEMETRY_BEGIN_NAMESPACE + +class ETWProvider +{ + +public: + const unsigned long STATUS_OK = 0; + const unsigned long STATUS_ERROR = ULONG_MAX; + const unsigned long STATUS_EFBIG = ULONG_MAX - 1; + + enum EventFormat + { + ETW_MANIFEST = 0, + ETW_MSGPACK = 1, + ETW_XML = 2 + }; + + /// + /// Entry that contains Provider Handle, Provider MetaData and Provider GUID + /// + struct Handle + { + REGHANDLE providerHandle; + std::vector providerMetaVector; + GUID providerGuid; + }; + + /// + /// Check if given provider is registered. + /// + /// + /// + bool is_registered(const std::string &providerId) + { + std::lock_guard lock(m_providerMapLock); + auto it = providers().find(providerId); + if (it != providers().end()) + { + if (it->second.providerHandle != INVALID_HANDLE) + { + return true; + } + } + return false; + } + + /// + /// Get Provider by Name or string representation of GUID + /// + /// + /// + Handle &open(const std::string &providerId, EventFormat format = EventFormat::ETW_MANIFEST) + { + std::lock_guard lock(m_providerMapLock); + +#ifdef HAVE_NO_TLD + // Fallback to MessagePack-encoded ETW events + format = EventFormat::ETW_MSGPACK; +#endif + + // Check and return if provider is already registered + auto it = providers().find(providerId); + if (it != providers().end()) + { + if (it->second.providerHandle != INVALID_HANDLE) + { + return it->second; + } + } + + // Register provider if necessary + auto &data = providers()[providerId]; + data.providerMetaVector.clear(); + + event::UUID guid = (providerId.rfind("{", 0) == 0) ? event::UUID(providerId.c_str()) + : // It's a ProviderGUID + utils::GetProviderGuid(providerId.c_str()); // It's a ProviderName + + data.providerGuid = guid.to_GUID(); + + // TODO: currently we do not allow to specify a custom group GUID + GUID providerGroupGuid = NULL_GUID; + + switch (format) + { +#ifdef HAVE_TLD + // Register with TraceLoggingDynamic facility - dynamic manifest ETW events. + case EventFormat::ETW_MANIFEST: { + tld::ProviderMetadataBuilder> providerMetaBuilder( + data.providerMetaVector); + + // Use Tenant ID as provider Name + providerMetaBuilder.Begin(providerId.c_str()); + providerMetaBuilder.AddTrait(tld::ProviderTraitType::ProviderTraitGroupGuid, + (void *)&providerGroupGuid, sizeof(GUID)); + providerMetaBuilder.End(); + + REGHANDLE hProvider = 0; + if (0 != + tld::RegisterProvider(&hProvider, &data.providerGuid, data.providerMetaVector.data())) + { + // There was an error registering the ETW provider + data.providerHandle = INVALID_HANDLE; + } + else + { + data.providerHandle = hProvider; + }; + }; + break; +#endif + +#ifdef HAVE_MSGPACK + // Register for MsgPack payload ETW events. + case EventFormat::ETW_MSGPACK: { + REGHANDLE hProvider = 0; + if (EventRegister(&data.providerGuid, NULL, NULL, &hProvider) != ERROR_SUCCESS) + { + // There was an error registering the ETW provider + data.providerHandle = INVALID_HANDLE; + } + else + { + data.providerHandle = hProvider; + } + }; + break; +#endif + + default: + // TODO: other protocols, e.g. XML events - not supported yet + break; + } + + // We always return an entry even if we failed to register. + // Caller should check whether the hProvider handle is valid. + return data; + } + + /// + /// Unregister Provider + /// + /// + /// + unsigned long close(Handle data) + { + std::lock_guard lock(m_providerMapLock); + + auto m = providers(); + auto it = m.begin(); + while (it != m.end()) + { + if (it->second.providerHandle == data.providerHandle) + { + auto result = EventUnregister(data.providerHandle); + m.erase(it); + return result; + } + }; + return STATUS_ERROR; + } + +#ifdef HAVE_MSGPACK + template + unsigned long writeMsgPack(Handle &providerData, T eventData) + { + + // Make sure you stop sending event before register unregistering providerData + if (providerData.providerHandle == INVALID_HANDLE) + { + // Provider not registered! + return STATUS_ERROR; + }; + + const std::string EVENT_NAME = "name"; + std::string eventName = "NoName"; + auto nameField = eventData[EVENT_NAME]; + switch (nameField.index()) + { + case common::AttributeType::TYPE_STRING: + eventName = + (char *)(nostd::get(nameField).data()); // must be 0-terminated! + break; + case common::AttributeType::TYPE_CSTRING: + eventName = (char *)(nostd::get(nameField)); + break; + default: + // This is user error. Invalid event name! + // We supply default 'NoName' event name in this case. + break; + } + + /* clang-format off */ + nlohmann::json jObj = + { + { "env_name", "Span" }, + { "env_ver", "4.0" }, + + // TODO: remove these fields. Provided for illustrative purposes only. + { "env_cloud_role", "BusyWorker" }, + { "env_cloud_roleInstance", "CY1SCH030021417" }, + { "env_cloud_roleVer", "9.0.15289.2" }, + // + + // TODO: compute time in MessagePack-friendly format + // TODO: should we consider uint64_t format with Unix timestamps for ELK stack? + { "env_time", + { + { "TypeCode", 255 }, + { "Body","0xFFFFFC60000000005F752C2C" } + } + }, + // + + // TODO: follow JSON implementation of OTLP or place protobuf for non-Microsoft flows + { "env_dt_traceId", "6dcdae7b9b0c7643967d74ee54056178" }, + { "env_dt_spanId", "5866c4322919e641" }, + // + + { "name", eventName }, + { "kind", 0 }, + { "startTime", + { + // TODO: timestamp + { "TypeCode", 255 }, + { "Body", "0xFFFF87CC000000005F752C2C" } + } + } + }; + /* clang-format on */ + + for (auto &kv : eventData) + { + const char *name = kv.first.data(); + // Don't include event name field in the payload + if (EVENT_NAME == name) + continue; + auto &value = kv.second; + switch (value.index()) + { + case common::AttributeType::TYPE_BOOL: { + UINT8 temp = static_cast(nostd::get(value)); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_INT: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_INT64: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_UINT: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_UINT64: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_DOUBLE: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_STRING: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } + case common::AttributeType::TYPE_CSTRING: { + auto temp = nostd::get(value); + jObj[name] = temp; + break; + } +# if HAVE_TYPE_GUID + // TODO: consider adding UUID/GUID to spec + case common::AttributeType::TYPE_GUID: { + auto temp = nostd::get(value); + // TODO: add transform from GUID type to string? + jObj[name] = temp; + break; + } +# endif + // TODO: arrays are not supported yet +# if 0 + // TODO: array of uint8_t is not supported by OT spec + case common::AttributeType::TYPE_SPAN_BYTE: +# endif + case common::AttributeType::TYPE_SPAN_BOOL: + case common::AttributeType::TYPE_SPAN_INT: + case common::AttributeType::TYPE_SPAN_INT64: + case common::AttributeType::TYPE_SPAN_UINT: + case common::AttributeType::TYPE_SPAN_UINT64: + case common::AttributeType::TYPE_SPAN_DOUBLE: + case common::AttributeType::TYPE_SPAN_STRING: + default: + // TODO: unsupported type + break; + } + }; + + // Layer 1 + nlohmann::json l1 = nlohmann::json::array(); + // Layer 2 + nlohmann::json l2 = nlohmann::json::array(); + // Layer 3 + nlohmann::json l3 = nlohmann::json::array(); + + l1.push_back("Span"); + + { + // TODO: clarify why this is needed + // TODO: fix time here + nlohmann::json j; + j["TypeCode"] = 255; + j["Body"] = "0xFFFFFC60000000005F752C2C"; + l3.push_back(j); + }; + + // Actual value object goes here + l3.push_back(jObj); + + l2.push_back(l3); + l1.push_back(l2); + + { + // Another time field again, but at the top + // TODO: fix time here + nlohmann::json j; + j["TypeCode"] = 255; + j["Body"] = "0xFFFFFC60000000005F752C2C"; + l1.push_back(j); + }; + + std::vector v = nlohmann::json::to_msgpack(l1); + + // NUL-terminator and padding for odd-sized buffers + v.push_back(0); + v.push_back(0); + if (v.size() % 2) + { + v.push_back(0); + }; + void *buff = v.data(); + + UCHAR level = 0; // LogAlways + auto writeResponse = EventWriteString(providerData.providerHandle, level, 0, (PCWSTR)buff); + + switch (writeResponse) + { + case ERROR_INVALID_PARAMETER: + break; + case ERROR_INVALID_HANDLE: + break; + case ERROR_ARITHMETIC_OVERFLOW: + break; + case ERROR_MORE_DATA: + break; + case ERROR_NOT_ENOUGH_MEMORY: + break; + default: + break; + }; + + if (writeResponse == ERROR_ARITHMETIC_OVERFLOW) + { + return STATUS_EFBIG; + }; + return (unsigned long)(writeResponse); + } +#endif + + /// + /// Send event to Provider Id + /// + /// + /// + /// + template + unsigned long writeTld(Handle &providerData, T eventData) + { +#ifdef HAVE_TLD + // Make sure you stop sending event before register unregistering providerData + if (providerData.providerHandle == INVALID_HANDLE) + { + // Provider not registered! + return STATUS_ERROR; + }; + + UINT32 eventTags = MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE; + + std::vector byteVector; + std::vector byteDataVector; + tld::EventMetadataBuilder> builder(byteVector); + tld::EventDataBuilder> dbuilder(byteDataVector); + + const std::string EVENT_NAME = "name"; + std::string eventName = "NoName"; + auto nameField = eventData[EVENT_NAME]; + switch (nameField.index()) + { + case common::AttributeType::TYPE_STRING: + eventName = + (char *)(nostd::get(nameField).data()); // must be 0-terminated! + break; + case common::AttributeType::TYPE_CSTRING: + eventName = (char *)(nostd::get(nameField)); + break; + default: + // This is user error. Invalid event name! + // We supply default 'NoName' event name in this case. + break; + } + + builder.Begin(eventName.c_str(), eventTags); + + for (auto &kv : eventData) + { + const char *name = kv.first.data(); + // Don't include event name field in the payload + if (EVENT_NAME == name) + continue; + auto &value = kv.second; + switch (value.index()) + { + case common::AttributeType::TYPE_BOOL: { + builder.AddField(name, tld::TypeBool8); + UINT8 temp = static_cast(nostd::get(value)); + dbuilder.AddByte(temp); + break; + } + case common::AttributeType::TYPE_INT: { + builder.AddField(name, tld::TypeInt32); + auto temp = nostd::get(value); + dbuilder.AddValue(temp); + break; + } + case common::AttributeType::TYPE_INT64: { + builder.AddField(name, tld::TypeInt64); + auto temp = nostd::get(value); + dbuilder.AddValue(temp); + break; + } + case common::AttributeType::TYPE_UINT: { + builder.AddField(name, tld::TypeUInt32); + auto temp = nostd::get(value); + dbuilder.AddValue(temp); + break; + } + case common::AttributeType::TYPE_UINT64: { + builder.AddField(name, tld::TypeUInt64); + auto temp = nostd::get(value); + dbuilder.AddValue(temp); + break; + } + case common::AttributeType::TYPE_DOUBLE: { + builder.AddField(name, tld::TypeDouble); + auto temp = nostd::get(value); + dbuilder.AddValue(temp); + break; + } + case common::AttributeType::TYPE_STRING: { + builder.AddField(name, tld::TypeUtf8String); + auto temp = nostd::get(value); + dbuilder.AddString(temp.data()); + break; + } + case common::AttributeType::TYPE_CSTRING: { + builder.AddField(name, tld::TypeUtf8String); + auto temp = nostd::get(value); + dbuilder.AddString(temp); + break; + } + +# if HAVE_TYPE_GUID + // TODO: consider adding UUID/GUID to spec + case common::AttributeType::TYPE_GUID: { + builder.AddField(name.c_str(), TypeGuid); + auto temp = nostd::get(value); + dbuilder.AddBytes(&temp, sizeof(GUID)); + break; + } +# endif + + // TODO: arrays are not supported +# if 0 + case common::AttributeType::TYPE_SPAN_BYTE: +# endif + case common::AttributeType::TYPE_SPAN_BOOL: + case common::AttributeType::TYPE_SPAN_INT: + case common::AttributeType::TYPE_SPAN_INT64: + case common::AttributeType::TYPE_SPAN_UINT: + case common::AttributeType::TYPE_SPAN_UINT64: + case common::AttributeType::TYPE_SPAN_DOUBLE: + case common::AttributeType::TYPE_SPAN_STRING: + default: + // TODO: unsupported type + break; + } + }; + + if (!builder.End()) // Returns false if the metadata is too large. + { + return STATUS_EFBIG; // if event is too big for UTC to handle + } + + tld::EventDescriptor eventDescriptor; + // eventDescriptor.Keyword = MICROSOFT_KEYWORD_CRITICAL_DATA; + // eventDescriptor.Keyword = MICROSOFT_KEYWORD_TELEMETRY; + // eventDescriptor.Keyword = MICROSOFT_KEYWORD_MEASURES; + + EVENT_DATA_DESCRIPTOR pDataDescriptors[3]; + + EventDataDescCreate(&pDataDescriptors[2], byteDataVector.data(), + static_cast(byteDataVector.size())); + + // Event size detection is needed + int64_t eventByteSize = byteDataVector.size() + byteVector.size(); + int64_t eventKBSize = (eventByteSize + 1024 - 1) / 1024; + // bool isLargeEvent = eventKBSize >= LargeEventSizeKB; + + // TODO: extract + // - GUID ActivityId + // - GUID RelatedActivityId + + HRESULT writeResponse = tld::WriteEvent(providerData.providerHandle, eventDescriptor, + providerData.providerMetaVector.data(), + byteVector.data(), 3, pDataDescriptors); + + if (writeResponse == HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)) + { + return STATUS_EFBIG; + }; + + return (unsigned long)(writeResponse); +#else + return STATUS_ERROR; +#endif + } + + template + unsigned long write(Handle &providerData, + T eventData, + ETWProvider::EventFormat format = ETWProvider::EventFormat::ETW_MANIFEST) + { + if (format == ETWProvider::EventFormat::ETW_MANIFEST) + { + return writeTld(providerData, eventData); + } + if (format == ETWProvider::EventFormat::ETW_MSGPACK) + { + return writeMsgPack(providerData, eventData); + } + if (format == ETWProvider::EventFormat::ETW_XML) + { + // TODO: not implemented + return STATUS_ERROR; + } + return STATUS_ERROR; + }; + + static const REGHANDLE INVALID_HANDLE = _UI64_MAX; + +protected: + const unsigned int LargeEventSizeKB = 62; + + const GUID NULL_GUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; + + mutable std::mutex m_providerMapLock; + + using ProviderMap = std::map; + + ProviderMap &providers() + { + static std::map providers; + return providers; + }; +}; + +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h index e69de29bb2..7db1af321d 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h @@ -0,0 +1,407 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "opentelemetry/exporters/etw/etw_provider_exporter.h" + +#include "opentelemetry/exporters/etw/utils.h" + +namespace core = opentelemetry::core; +namespace trace = opentelemetry::trace; + +OPENTELEMETRY_BEGIN_NAMESPACE + +/// +/// ETW namespace provides no-exporter header-only implementation of ETW Trace Provider +/// +namespace ETW +{ + +class Span; + +/// +/// stream::Tracer class that allows to send spans to ETW +/// +class Tracer : public trace::Tracer +{ + /// + /// Parent provider of this Tracer + /// + trace::TracerProvider &parent; + + /// + /// Provider Id (Name or GUID) + /// + std::string provId; + + /// + /// Provider Handle + /// + ETWProvider::Handle provHandle; + + /// + /// Encoding (Manifest, MessagePack or XML) + /// + ETWProvider::EventFormat encoding; + + /// + /// ETWProvider is a singleton that aggregates all ETW writes. + /// + /// + static ETWProvider &etwProvider() + { + static ETWProvider instance; // C++11 magic static + return instance; + }; + +public: + /// + /// Tracer constructor + /// + /// Parent TraceProvider + /// providerId + /// Tracer instance + Tracer(trace::TracerProvider &parent, + nostd::string_view providerId = "", + ETWProvider::EventFormat encoding = ETWProvider::EventFormat::ETW_MANIFEST) + : trace::Tracer(), + parent(parent), + provId(providerId.data(), providerId.size()), + encoding(encoding) + { + provHandle = etwProvider().open(provId, encoding); + }; + + /// + /// Start Span + /// + /// Span name + /// Span options + /// Span + virtual nostd::shared_ptr StartSpan( + nostd::string_view name, + const trace::KeyValueIterable &attributes, + const trace::StartSpanOptions &options = {}) noexcept override + { + // TODO: support attributes + UNREFERENCED_PARAMETER(attributes); + return trace::to_span_ptr(this, name, options); + }; + + /// + /// Force flush data to Tracer, spending up to given amount of microseconds to flush. + /// + /// Allow Tracer to drop data if timeout is reached + /// void + virtual void ForceFlushWithMicroseconds(uint64_t) noexcept override{}; + + /// + /// Close tracer, spending up to given amount of microseconds to flush and close. + /// + /// Allow Tracer to drop data if timeout is reached + /// + virtual void CloseWithMicroseconds(uint64_t) noexcept override + { + etwProvider().close(provHandle); + }; + + /// + /// Add event data to span associated with tracer + /// + /// + /// + /// + /// + /// + void AddEvent(Span &span, + nostd::string_view name, + core::SystemTimestamp timestamp, + const trace::KeyValueIterable &attributes) noexcept + { + // TODO: associate events with span + (void)span; + // TODO: respect original timestamp + (void)timestamp; + + // Unfortunately we copy right now since we don't have the knowledge of original map object :-( + std::map m; + attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { + m[key] = value; + return true; + }); + m["name"] = name.data(); + +#ifdef HAVE_TLD + if (encoding == ETWProvider::ETW_MANIFEST) + { + etwProvider().writeTld(provHandle, m); + return; + } +#endif + +#ifdef HAVE_MSGPACK + if (encoding == ETWProvider::ETW_MSGPACK) + { + etwProvider().writeMsgPack(provHandle, m); + return; + } +#endif + }; + + /// + /// Add event data to span associated with tracer + /// + /// + /// + /// + /// + void AddEvent(Span &span, nostd::string_view name, core::SystemTimestamp timestamp) noexcept + { + AddEvent(span, name, timestamp, trace::NullKeyValueIterable()); + }; + + /// + /// Add event data to span associated with tracer + /// + /// + /// + void AddEvent(Span &span, nostd::string_view name) + { + AddEvent(span, name, std::chrono::system_clock::now(), trace::NullKeyValueIterable()); + }; + + virtual ~Tracer() { CloseWithMicroseconds(0); }; +}; + +/// +/// stream::Span allows to send event data to stream +/// +class Span : public trace::Span +{ + +protected: + /// + /// Parent (Owner) Tracer of this Span + /// + Tracer &owner; + +public: + /// + /// Span constructor + /// + /// Owner Tracer + /// Span name + /// Span options + /// Span + Span(Tracer &owner, nostd::string_view name, const trace::StartSpanOptions &options) noexcept + : trace::Span(), owner(owner) + { + UNREFERENCED_PARAMETER(name); + UNREFERENCED_PARAMETER(options); + }; + + ~Span() { End(); } + + /// + /// Add named event with no attributes + /// + /// + /// + virtual void AddEvent(nostd::string_view name) noexcept override { owner.AddEvent(*this, name); } + + /// + /// Add named event with custom timestamp + /// + /// + /// + /// + virtual void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp) noexcept override + { + owner.AddEvent(*this, name, timestamp); + } + + /// + /// Add named event with custom timestamp and attributes + /// + /// + /// + /// + /// + void AddEvent(nostd::string_view name, + core::SystemTimestamp timestamp, + const trace::KeyValueIterable &attributes) noexcept override + { + owner.AddEvent(*this, name, timestamp, attributes); + } + + /// + /// Set Span status + /// + /// + /// + /// + virtual void SetStatus(trace::CanonicalCode code, + nostd::string_view description) noexcept override + { + // TODO: not implemented + UNREFERENCED_PARAMETER(code); + UNREFERENCED_PARAMETER(description); + }; + + // Sets an attribute on the Span. If the Span previously contained a mapping for + // the key, the old value is replaced. + virtual void SetAttribute(nostd::string_view key, + const common::AttributeValue &value) noexcept override + { + // TODO: not implemented + UNREFERENCED_PARAMETER(key); + UNREFERENCED_PARAMETER(value); + }; + + /// + /// Update Span name + /// + /// + /// + virtual void UpdateName(nostd::string_view name) noexcept override + { + // TODO: not implemented + UNREFERENCED_PARAMETER(name); + } + + /// + /// End Span + /// + /// + virtual void End(const trace::EndSpanOptions & = {}) noexcept override + { + // TODO: signal this to owner + } + + virtual trace::SpanContext GetContext() const noexcept + { + // TODO: not implemented + static trace::SpanContext nullContext; + return nullContext; + } + + /// + /// Check if Span is recording data + /// + /// + virtual bool IsRecording() const noexcept override + { + // TODO: not implemented + return true; + } + + virtual void SetToken(nostd::unique_ptr &&token) noexcept + { + // TODO: not implemented + UNREFERENCED_PARAMETER(token); + } + + /// + /// Get Owner tracer of this Span + /// + /// + trace::Tracer &tracer() const noexcept { return this->owner; }; +}; + +/// +/// stream::TraceProvider +/// +class TracerProvider : public trace::TracerProvider +{ +public: + /// + /// Obtain a Tracer of given type (name) and supply extra argument arg2 to it. + /// + /// Tracer Type + /// Tracer arguments + /// + virtual nostd::shared_ptr GetTracer(nostd::string_view name, + nostd::string_view args = "") + { + // TODO: describe possible args. Currently supported: + // "MSGPACK" - use MessagePack + // "XML" - use XML + // "ETW" - use 'classic' Trace Logging Dynamic manifest ETW event + // +#if defined(HAVE_NO_TLD) && defined(HAVE_MSGPACK) + ETWProvider::EventFormat evtFmt = ETWProvider::EventFormat::ETW_MSGPACK; +#else + ETWProvider::EventFormat evtFmt = ETWProvider::EventFormat::ETW_MANIFEST; +#endif + +#pragma warning(push) +#pragma warning(disable : 4307) /* Integral constant overflow - OK while computing hash */ + auto h = utils::hashCode(args.data()); + switch (h) + { + case CONST_HASHCODE(MSGPACK): + // nobrk + case CONST_HASHCODE(MsgPack): + // nobrk + case CONST_HASHCODE(MessagePack): + evtFmt = ETWProvider::EventFormat::ETW_MSGPACK; + break; + + case CONST_HASHCODE(XML): + // nobrk + case CONST_HASHCODE(xml): + evtFmt = ETWProvider::EventFormat::ETW_XML; + break; + + case CONST_HASHCODE(TLD): + // nobrk + case CONST_HASHCODE(tld): + // nobrk + evtFmt = ETWProvider::EventFormat::ETW_MANIFEST; + break; + + default: + break; + } +#pragma warning(pop) + return nostd::shared_ptr{new (std::nothrow) Tracer(*this, name, evtFmt)}; + } +}; + +} // namespace ETW +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/exporters/etw/include/opentelemetry/exporters/etw/utils.h b/exporters/etw/include/opentelemetry/exporters/etw/utils.h new file mode 100644 index 0000000000..bafdc45814 --- /dev/null +++ b/exporters/etw/include/opentelemetry/exporters/etw/utils.h @@ -0,0 +1,227 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _WIN32 +# include +# include +# include +# pragma comment(lib, "Advapi32.lib") +# pragma comment(lib, "Rpcrt4.lib") +#endif + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace utils +{ + +#if 0 +/// +/// Convert from time_point to ISO string +/// +static std::string to_string(std::chrono::system_clock::time_point &tp) +{ + int64_t millis = + std::chrono::duration_cast(tp.time_since_epoch()).count(); + auto in_time_t = std::chrono::system_clock::to_time_t(tp); + std::stringstream ss; + // TODO: this is expected to be UTC time + ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%dT%H:%M:%S"); + ss << "." << std::setfill('0') << std::setw(3) << (unsigned)(millis % 1000); + ss << "Z"; + return ss.str(); +} +#endif + +/// +/// Compile-time constexpr djb2 hash function for strings +/// +static constexpr uint32_t hashCode(const char *str, uint32_t h = 0) +{ + return (uint32_t)(!str[h] ? 5381 : ((uint32_t)hashCode(str, h + 1) * (uint32_t)33) ^ str[h]); +} + +#define CONST_UINT32_T(x) std::integral_constant::value + +#define CONST_HASHCODE(name) CONST_UINT32_T(OPENTELEMETRY_NAMESPACE::utils::hashCode(#name)) + +#ifdef _WIN32 + +/// +/// Compute SHA-1 hash of input buffer and save to output +/// +/// Input buffer +/// Input buffer size +/// Output buffer +/// Output buffer size +/// +static inline bool sha1(const BYTE *pData, DWORD nData, BYTE *pHashedData, DWORD &nHashedData) +{ + bool bRet = false; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + + if (!CryptAcquireContext(&hProv, // handle of the CSP + NULL, // key container name + NULL, // CSP name + PROV_RSA_FULL, // provider type + CRYPT_VERIFYCONTEXT)) // no key access is requested + { + bRet = false; + goto CleanUp; + } + + if (!CryptCreateHash(hProv, // handle of the CSP + CALG_SHA1, // hash algorithm to use + 0, // hash key + 0, // reserved + &hHash)) // + { + bRet = false; + goto CleanUp; + } + + if (!CryptHashData(hHash, // handle of the HMAC hash object + pData, // message to hash + nData, // number of bytes of data to add + 0)) // flags + { + bRet = false; + goto CleanUp; + } + + if (!CryptGetHashParam(hHash, // handle of the HMAC hash object + HP_HASHVAL, // query on the hash value + pHashedData, // filled on second call + &nHashedData, // length, in bytes,of the hash + 0)) + { + bRet = false; + goto CleanUp; + } + + bRet = true; + +CleanUp: + + if (hHash) + { + CryptDestroyHash(hHash); + } + + if (hProv) + { + CryptReleaseContext(hProv, 0); + } + return bRet; +} + +/// +/// Convert UTF-8 string to UTF-16 wide string. +/// +/// FIXME: this conversion is marked deprecated after C++17: +/// https://en.cppreference.com/w/cpp/locale/codecvt_utf8_utf16 +/// It works well with Visual C++, but may not work with clang. +/// Best long-term solution is to use Win32 API instead. +/// +/// +/// +/// +static inline std::wstring to_utf16_string(const std::string &in) +{ + std::wstring_convert, wchar_t> converter; + return converter.from_bytes(in); +} + +/// +/// Transform ETW provider name to provider GUID as described here: +/// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/ +/// +/// +/// +static inline GUID GetProviderGuid(const char *providerName) +{ + std::string name(providerName); + std::transform(name.begin(), name.end(), name.begin(), + [](unsigned char c) { return (char)::toupper(c); }); + + size_t len = name.length() * 2 + 0x10; + uint8_t *buffer = new uint8_t[len]; + uint32_t num = 0x482c2db2; + uint32_t num2 = 0xc39047c8; + uint32_t num3 = 0x87f81a15; + uint32_t num4 = 0xbfc130fb; + + for (int i = 3; i >= 0; i--) + { + buffer[i] = (uint8_t)num; + num = num >> 8; + buffer[i + 4] = (uint8_t)num2; + num2 = num2 >> 8; + buffer[i + 8] = (uint8_t)num3; + num3 = num3 >> 8; + buffer[i + 12] = (uint8_t)num4; + num4 = num4 >> 8; + } + + for (size_t j = 0; j < name.length(); j++) + { + buffer[((2 * j) + 0x10) + 1] = (uint8_t)name[j]; + buffer[(2 * j) + 0x10] = (uint8_t)(name[j] >> 8); + } + + const size_t sha1_hash_size = 21; + uint8_t *buffer2 = new uint8_t[sha1_hash_size]; + DWORD len2 = sha1_hash_size; + sha1((const BYTE *)buffer, (DWORD)len, (BYTE *)buffer2, len2); + + unsigned long a = (((((buffer2[3] << 8) + buffer2[2]) << 8) + buffer2[1]) << 8) + buffer2[0]; + unsigned short b = (unsigned short)((buffer2[5] << 8) + buffer2[4]); + unsigned short num9 = (unsigned short)((buffer2[7] << 8) + buffer2[6]); + + GUID guid; + guid.Data1 = a; + guid.Data2 = b; + guid.Data3 = (unsigned short)((num9 & 0xfff) | 0x5000); + guid.Data4[0] = buffer2[8]; + guid.Data4[1] = buffer2[9]; + guid.Data4[2] = buffer2[10]; + guid.Data4[3] = buffer2[11]; + guid.Data4[4] = buffer2[12]; + guid.Data4[5] = buffer2[13]; + guid.Data4[6] = buffer2[14]; + guid.Data4[7] = buffer2[15]; + + delete buffer; + delete buffer2; + + return guid; +} +#endif + +}; // namespace utils + +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/exporters/etw/src/etw_provider_exporter.cc b/exporters/etw/src/etw_provider_exporter.cc index e69de29bb2..79a88ac7c6 100644 --- a/exporters/etw/src/etw_provider_exporter.cc +++ b/exporters/etw/src/etw_provider_exporter.cc @@ -0,0 +1,3 @@ +#define HAVE_NO_TLD + +#include "opentelemetry/exporters/etw/etw_provider_exporter.h" \ No newline at end of file diff --git a/exporters/etw/src/etw_tracer_exporter.cc b/exporters/etw/src/etw_tracer_exporter.cc index e69de29bb2..b752c2bc5d 100644 --- a/exporters/etw/src/etw_tracer_exporter.cc +++ b/exporters/etw/src/etw_tracer_exporter.cc @@ -0,0 +1,3 @@ +#define HAVE_NO_TLD + +#include "opentelemetry/exporters/etw/etw_tracer_exporter.h" \ No newline at end of file