summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/exporters/etw
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/exporters/etw')
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD69
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt63
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md226
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE23
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h3458
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h186
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h142
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h297
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h30
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h456
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h629
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h15
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h995
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h28
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h274
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h412
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc101
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc190
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc64
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc388
20 files changed, 8046 insertions, 0 deletions
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD
new file mode 100644
index 000000000..c2328ed4e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD
@@ -0,0 +1,69 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "etw_exporter",
+ hdrs = glob([
+ "include/opentelemetry/exporters/etw/*.h",
+ ]),
+ includes = ["include"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = ["etw"],
+ deps = [
+ "//api",
+ "//sdk/src/trace",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "etw_provider_test",
+ srcs = ["test/etw_provider_test.cc"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = [
+ "etw",
+ "test",
+ ],
+ deps = [
+ ":etw_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "etw_tracer_test",
+ srcs = ["test/etw_tracer_test.cc"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = [
+ "etw",
+ "test",
+ ],
+ deps = [
+ ":etw_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "etw_logger_test",
+ srcs = ["test/etw_logger_test.cc"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = [
+ "etw",
+ "test",
+ ],
+ deps = [
+ ":etw_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@github_nlohmann_json//:json",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt
new file mode 100644
index 000000000..47791b4ab
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt
@@ -0,0 +1,63 @@
+add_library(opentelemetry_exporter_etw INTERFACE)
+
+target_include_directories(
+ opentelemetry_exporter_etw
+ INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+set_target_properties(opentelemetry_exporter_etw PROPERTIES EXPORT_NAME
+ etw_exporter)
+
+target_link_libraries(opentelemetry_exporter_etw
+ INTERFACE nlohmann_json::nlohmann_json)
+if(nlohmann_json_clone)
+ add_dependencies(opentelemetry_exporter_etw nlohmann_json::nlohmann_json)
+endif()
+
+install(
+ TARGETS opentelemetry_exporter_etw
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/etw
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h")
+
+if(BUILD_TESTING)
+ add_executable(etw_provider_test test/etw_provider_test.cc)
+ add_executable(etw_tracer_test test/etw_tracer_test.cc)
+ add_executable(etw_logger_test test/etw_logger_test.cc)
+
+ add_executable(etw_perf_test test/etw_perf_test.cc)
+
+ target_link_libraries(etw_provider_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ target_link_libraries(etw_tracer_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ target_link_libraries(etw_logger_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ target_link_libraries(
+ etw_perf_test benchmark::benchmark ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ gtest_add_tests(
+ TARGET etw_provider_test
+ TEST_PREFIX exporter.
+ TEST_LIST etw_provider_test)
+ gtest_add_tests(
+ TARGET etw_tracer_test
+ TEST_PREFIX exporter.
+ TEST_LIST etw_tracer_test)
+ gtest_add_tests(
+ TARGET etw_logger_test
+ TEST_PREFIX exporter.
+ TEST_LIST etw_logger_test)
+
+endif() # BUILD_TESTING
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md b/src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md
new file mode 100644
index 000000000..29dc3de43
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md
@@ -0,0 +1,226 @@
+# Getting Started with OpenTelemetry C++ SDK and ETW exporter on Windows
+
+Event Tracing for Windows (ETW) is an efficient kernel-level tracing facility
+that lets you log kernel or application-defined events to a log file. You can
+consume the events in real time or from a log file and use them to debug an
+application or to determine where performance issues are occurring in the
+application.
+
+OpenTelemetry C++ SDK ETW exporter allows the code instrumented using
+OpenTelemetry API to forward events to out-of-process ETW listener, for
+subsequent data recording or forwarding to alternate pipelines and flows.
+Windows Event Tracing infrastructure is available to any vendor or application
+being deployed on Windows.
+
+## API support
+
+These are the features planned to be supported by ETW exporter:
+
+- [x] OpenTelemetry Tracing API and SDK headers are **stable** and moving
+ towards GA.
+- [ ] OpenTelemetry Logging API is work-in-progress, pending implementation of
+ [Latest Logging API spec
+ here](https://github.com/open-telemetry/oteps/pull/150)
+- [ ] OpenTelemetry Metrics API is not implemented yet.
+
+Implementation of OpenTelemetry C++ SDK ETW exporter on Windows OS is `header
+only` :
+
+- full definitions of all macros, functions and classes comprising the library
+are visible to the compiler in a header file form.
+- implementation does not need to be separately compiled, packaged and installed
+ in order to be used.
+
+All that is required is to point the compiler at the location of the headers,
+and then `#include` the header files into the application source. Compiler's
+optimizer can do a much better job when all the library's source code is
+available. Several options below may be turned on to optimize the code with the
+usage of standard C++ library, Microsoft Guidelines Support library, Google
+Abseil Variant library. Or enabling support for non-standard features, such as
+8-bit byte arrays support that enables performance-efficient representation of
+binary blobs on ETW wire.
+
+## Example project
+
+The following include directories are required, relative to the top-level source
+tree of OpenTelemetry C++ repo:
+
+- api/include/
+- exporters/etw/include/
+- sdk/include/
+
+Code that instantiates ETW TracerProvider, subsequently obtaining a Tracer bound
+to `OpenTelemetry-ETW-Provider`, and emitting a span named `MySpan` with
+attributes on it, as well as `MyEvent` within that span.
+
+```cpp
+
+#include <map>
+#include <string>
+
+#include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+using namespace opentelemetry::exporter::etw;
+
+// Supply unique instrumentation name (ETW Provider Name) here:
+std::string providerName = "OpenTelemetry-ETW-Provider";
+
+exporter::etw::TracerProvider tp;
+
+int main(int argc, const char* argv[])
+{
+ // Obtain a Tracer object for instrumentation name.
+ // Each Tracer is associated with unique TraceId.
+ auto tracer = tp.GetTracer(providerName, "TLD");
+
+ // Properties is a helper class in ETW namespace that is otherwise compatible
+ // with Key-Value Iterable accepted by OpenTelemetry API. Using Properties
+ // should enable more efficient data transfer without unnecessary memcpy.
+
+ // Span attributes
+ Properties attribs =
+ {
+ {"attrib1", 1},
+ {"attrib2", 2}
+ };
+
+ // Start Span with attributes
+ auto span = tracer->StartSpan("MySpan", attribs);
+
+ // Emit an event on Span
+ std::string eventName = "MyEvent1";
+ Properties event =
+ {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}
+ };
+ span->AddEvent(eventName, event);
+
+ // End Span.
+ span->End();
+
+ // Close the Tracer on application stop.
+ tracer->CloseWithMicroseconds(0);
+
+ return 0;
+}
+```
+
+Note that different `Tracer` objects may be bound to different ETW destinations.
+
+## Build options and Compiler Defines
+
+While including OpenTelemetry C++ SDK with ETW exporter, the customers are in
+complete control of what options they would like to enable for their project
+using `Preprocessor Definitions`.
+
+These options affect how "embedded in application" OpenTelemetry C++ SDK code is
+compiled:
+
+| Name | Description |
+|---------------------|------------------------------------------------------------------------------------------------------------------------|
+| HAVE_CPP_STDLIB | Use STL classes for API surface. This option requires at least C++17. C++20 is recommended. Some customers may benefit from STL library provided with the compiler instead of using custom OpenTelemetry `nostd::` implementation due to security and performance considerations. |
+| HAVE_GSL | Use [Microsoft GSL](https://github.com/microsoft/GSL) for `gsl::span` implementation. Library must be in include path. Microsoft GSL claims to be the most feature-complete implementation of `std::span`. It may be used instead of `nostd::span` implementation in projects that statically link OpenTelemetry SDK. |
+| HAVE_TLD | Use ETW/TraceLogging Dynamic protocol. This is the default implementation compatible with existing C# "listeners" / "decoders" of ETW events. This option requires an additional optional Microsoft MIT-licensed `TraceLoggingDynamic.h` header. |
+
+## Debugging
+
+### ETW TraceLogging Dynamic Events
+
+ETW supports a mode that is called "Dynamic Manifest", where event may contain
+strongly-typed key-value pairs, with primitive types such as `integer`,
+`double`, `string`, etc. This mode requires `TraceLoggingDynamic.h` header.
+Please refer to upstream repository containining [Microsoft TraceLogging Dynamic
+framework](https://github.com/microsoft/tracelogging-dynamic-windows) licensed
+under [MIT
+License](https://github.com/microsoft/tracelogging-dynamic-windows/blob/main/LICENSE).
+
+Complete [list of ETW
+types](https://docs.microsoft.com/en-us/windows/win32/wes/eventmanifestschema-outputtype-complextype#remarks).
+
+OpenTelemetry C++ ETW exporter implements the following type mapping:
+
+| OpenTelemetry C++ API type | ETW type |
+|----------------------------|-----------------|
+| bool | xs:byte |
+| int (32-bit) | xs:int |
+| int (64-bit) | xs:long |
+| uint (32-bit) | xs:unsignedInt |
+| uint (64-bit) | xs:unsignedLong |
+| double | xs:double |
+| string | win:Utf8 |
+
+Support for arrays of primitive types is not implemented yet.
+
+Visual Studio 2019 allows to use `View -> Other Windows -> Diagnostic Events` to
+capture events that are emitted by instrumented application and sent to ETW
+provider in a live view. Instrumentation name passed to `GetTracer` API above
+corresponds to `ETW Provider Name`. If Instrumentation name contains a GUID -
+starts with a curly brace, e.g. `{deadbeef-fade-dead-c0de-cafebabefeed}` then
+the parameter is assumed to be `ETW Provider GUID`.
+
+Click on `Settings` and add the provider to monitor either by its Name or by
+GUID. In above example, the provider name is `OpenTelemetry-ETW-Provider`.
+Please refer to Diagnostic Events usage instructions
+[here](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-how-to-monitor-and-diagnose-services-locally#view-service-fabric-system-events-in-visual-studio)
+to learn more. Note that running ETW Listener in Visual Studio requires
+Elevation, i.e. Visual Studio would prompt you to confirm that you accept to run
+the ETW Listener process as Administrator. This is a limitation of ETW
+Listeners, they must be run as privileged process.
+
+### ETW events encoded in MessagePack
+
+OpenTelemetry ETW exporter optionally allows to encode the incoming event
+payload using [MessagePack](https://msgpack.org/index.html) compact binary
+protocol. ETW/MsgPack encoding requires
+[nlohmann/json](https://github.com/nlohmann/json) library to be included in the
+build of OpenTelemetry ETW exporter. Any recent version of `nlohmann/json` is
+compatible with ETW exporter. For example, the version included in
+`third_party/nlohmann-json` directory may be used.
+
+There is currently **no built-in decoder available** for this format. However,
+there is ongoing effort to include the ETW/MsgPack decoder in
+[Azure/diagnostics-eventflow](https://github.com/Azure/diagnostics-eventflow)
+project, which may be used as a side-car listener to forward incoming
+ETW/MsgPack events to many other destinations, such as:
+
+- StdOutput (console output)
+- HTTP (json via http)
+- Application Insights
+- Azure EventHub
+- Elasticsearch
+- Azure Monitor Logs
+
+And community-contributed exporters:
+
+- Google Big Query output
+- SQL Server output
+- ReflectInsight output
+- Splunk output
+
+[This PR](https://github.com/Azure/diagnostics-eventflow/pull/382) implements
+the `Input adapter` for OpenTelemetry ETW/MsgPack protocol encoded events for
+Azure EventFlow.
+
+Other standard tools for processing ETW events on Windows OS, such as:
+
+- [PerfView](https://github.com/microsoft/perfview)
+- [PerfViewJS](https://github.com/microsoft/perfview/tree/main/src/PerfViewJS)
+
+will be augmented in future with support for ETW/MsgPack encoding.
+
+## Addendum
+
+This document needs to be supplemented with additional information:
+
+- [ ] mapping between OpenTelemetry fields and concepts and their corresponding
+ ETW counterparts
+- [ ] links to E2E instrumentation example and ETW listener
+- [ ] Logging API example
+- [ ] Metrics API example (once Metrics spec is finalized)
+- [ ] example how ETW Listener may employ OpenTelemetry .NET SDK to 1-1
+ transform from ETW events back to OpenTelemetry flow
+- [ ] links to NuGet package that contains the source code of SDK that includes
+ OpenTelemetry SDK and ETW exporter
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE
new file mode 100644
index 000000000..cfc21fd2c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE
@@ -0,0 +1,23 @@
+TraceLogging Dynamic for Windows
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h
new file mode 100644
index 000000000..17ee108c6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h
@@ -0,0 +1,3458 @@
+/* ++
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+SPDX-License-Identifier: MIT
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ TraceLoggingDynamic.h
+
+Abstract:
+
+ Dynamic TraceLogging Provider API for C++.
+
+Environment:
+
+ User mode.
+
+--*/
+
+#pragma once
+#include <windows.h>
+#include <evntprov.h>
+#include <stdlib.h> // byteswap
+
+namespace tld
+{
+#pragma region Public interface
+
+ /*
+ GENERAL:
+
+ - This implementation of manifest-free ETW supports more functionality
+ than the implementation in TraceLoggingProvider.h, but it also has
+ higher runtime costs. This implementation is intended for use only when
+ the set of events is not known at compile-time. For example,
+ TraceLoggingDynamic.h might be used to implement a library providing
+ manifest-free ETW to a scripting language like JavaScript or Perl.
+ - This header is not optimized for direct use by developers adding events
+ to their code. There is no way to make an optimal solution that
+ works for all of the intended target users. Instead, this header
+ provides various pieces that you can build upon to create a user-friendly
+ implementation of manifest-free ETW tailored for a specific scenario.
+
+ HIGH-LEVEL API:
+
+ The high-level API provides an easy way to get up and running with
+ TraceLogging ETW events.
+
+ Contents:
+ - Class: tld::Provider
+ - Class: tld::Event
+ - Class: tld::EventBuilder
+ - Enum: tld::Type
+
+ Basic usage:
+ - Create a Provider object.
+ - Check provider.IsEnabled(...) so that you don't do the remaining steps
+ if nobody is listening for your event.
+ - Create an Event<std::vector<BYTE>> object.
+ - Add fields definitions (metadata) and values (data) using methods on
+ Event. (You are responsible for making sure that the metadata you add
+ matches the data you add -- the Event object does not validate this.)
+ - Some methods on the Event object return EventBuilder objects, which are
+ used to build the fields of nested structures. Note that Event inherits
+ from EventBuilder, so if you write a function that accepts an
+ EventBuilder&, it will also accept an Event&.
+ - Once you've added all data and metadata, call event.Write() to send the
+ event to ETW.
+
+ LOW-LEVEL API:
+
+ The low-level API provides components that you can mix and match to build
+ your own solution when the high-level components don't precisely meet your
+ needs. For example, you might use the high-level Provider class to manage
+ your REGHANDLE and the ETW callbacks, but you might use
+ EventMetadataBuilder and EventDataBuilder directly instead of using Event
+ so that you can cache event definitions.
+
+ Contents:
+ - Function: tld::RegisterProvider
+ - Function: tld::UnregisterProvider
+ - Function: tld::GetGuidForName
+ - Function: tld::SetInformation
+ - Class: tld::ProviderMetadataBuilder
+ - Class: tld::EventMetadataBuilder
+ - Class: tld::EventDataBuilder
+ - Struct: tld::EventDescriptor
+ - Class: tld::ByteArrayWrapper
+ - Enum: tld::InType
+ - Enum: tld::OutType
+ - Enum: tld::ProviderTraitType
+ - Function: tld::WriteEvent
+ - Function: tld::WriteEventEx
+ - Function: tld::PushBackAsUtf8
+ - Function: tld::MakeType
+ - Macro: TLD_HAVE_EVENT_SET_INFORMATION
+ - Macro: TLD_HAVE_EVENT_WRITE_EX
+
+ Notes:
+ - EventDataDescCreate is a native ETW API from <evntprov.h>. It is not
+ part of this API, but you may want to use it to initialize
+ EVENT_DATA_DESCRIPTOR structures instead of doing it directly.
+ - If you directly initialize your EVENT_DATA_DESCRIPTOR structures instead
+ of using EventDataDescCreate, be sure to properly initialize the
+ EVENT_DATA_DESCRIPTOR.Reserved field (e.g. by setting it to 0).
+ Initializing the Reserved field is NOT optional. (EventDataDescCreate
+ does correctly initialize the Reserved field.)
+ - When the API asks for a byte-buffer, you can use std::vector<BYTE> or
+ another similar type. If you don't want to use a vector, the provided
+ ByteArrayWrapper type allows you to use your own allocation strategy for
+ the buffer.
+ - By default, TraceLogging events have Id=0 and Version=0, indicating
+ that the event does not have an assigned Id. However, events can have
+ Id and Version assigned (typically assigned manually). If you don't want
+ to manage event IDs, set both Id and Version to 0. If you do assign
+ IDs to your events, the Id must be non-zero and there should be a
+ one-to-one mapping between {provider.Id + event.Id + event.Version} and
+ {event metadata}. In other words, any event with a given non-zero
+ Id+Version combination must always have exactly the same event metadata.
+ (Note to decoders: this can be used as an optimization, but do not rely
+ on providers to follow this rule.)
+ - TraceLogging events default to channel 11 (WINEVENT_CHANNEL_TRACELOGGING).
+ This channel has no effect other than to mark the event as TraceLogging-
+ compatible. Other channels can be used, but channels other than 11 will
+ only work if the provider is running on a version of Windows that
+ understands TraceLogging (Windows 7sp1 with latest updates, Windows 8.1
+ with latest updates, Windows 10, or later). If your provider is running
+ on a version of Windows that does not understand TraceLogging and you use
+ a channel other than 11, the resulting events will not decode correctly.
+
+ Low-level provider management:
+ - Use tld::ProviderMetadataBuilder to build a provider metadata blob.
+ - If you don't have a specific provider GUID already selected, use
+ tld::GetGuidForName to compute your provider GUID.
+ - Use tld::RegisterProvider to open the REGHANDLE.
+ - Use the REGHANDLE in calls to tld::WriteEvent.
+ - Use tld::UnregisterProvider to close the REGHANDLE.
+
+ Low-level event management:
+ - Use tld::EventMetadataBuilder to build an event metadata blob.
+ - Use tld::Type values to define field types, or create non-standard
+ types using tld::MakeType, tld::InType, and tld::OutType.
+ - Use tld::EventDataBuilder to build an event data blob.
+ - Create an EVENT_DESCRIPTOR for your event.
+ - Optionally use the tld::EventDescriptor wrapper class to simplify
+ initialization of EVENT_DESCRIPTOR structures.
+ - Allocate an array of EVENT_DATA_DESCRIPTORs. Size should be two more
+ than you need for your event payload (the first two are reserved for the
+ provider and event metadata).
+ - Use EventDataDescCreate to fill in the data descriptor array (skipping
+ the first two) to reference your event payload data.
+ - Use tld::WriteEvent to write your event.
+ */
+
+ /*
+ class Provider (high-level API):
+
+ Manages an ETW REGHANDLE and the provider metadata blob.
+ - Constructor creates the provider metadata blob and registers the provider.
+ Note that this includes configuring ETW callbacks.
+ - Destructor unregisters the provider and frees the metadata blob.
+ - IsEnabled APIs provide very efficient checks for whether an event needs
+ to be written.
+ - Write APIs call WriteEvent, automatically providing the correct REGHANDLE
+ and provider metadata blobs.
+
+ Typical usage:
+
+ tld::Provider provider("ProviderName", ...);
+ if (provider.IsEnabled(eventLevel, eventKeywords))
+ {
+ (prepare event);
+ provider.Write(...);
+ }
+
+ It is not a problem to call provider.Write without checking
+ provider.IsEnabled(...), but it is more efficient to check
+ provider.IsEnabled(...) so that you can skip the process of preparing the
+ event if the event is not enabled.
+ */
+ class Provider;
+
+ /*
+ class Event (high-level API):
+
+ Manages the data and metadata for an event. You create an Event object,
+ set properties, add field definitions, add field values, and then Write
+ the event.
+
+ ByteVectorTy will usually be std::vector<BYTE>, though other similar
+ types could be used instead.
+
+ Typical usage:
+
+ if (provider.IsEnabled(eventLevel, eventKeywords))
+ {
+ Event event(provider, szEventName, eventLevel, eventKeywords);
+ (add fields definitions and values);
+ event.Write();
+ }
+
+ You can reuse an Event object by calling event.Reset(...).
+ */
+ template<class ByteVectorTy>
+ class Event;
+
+ /*
+ class EventBuilder (high-level API):
+
+ This class exposes an interface for adding field metadata/data to an event.
+ The Event class inherits from EventBuilder so that it can expose this
+ interface. In addition, each call to eventBuilder.AddStruct(...) returns a
+ new EventBuilder that can be used to add fields to a nested structure.
+ You won't directly create instances of this type -- you'll use instances
+ returned by Event.AddStruct or by EventBuilder.AddStruct.
+
+ You can use EventBuilder& as the type of a parameter to a function, and
+ then pass either an Event or an EventBuilder to that function (allowing
+ for recursive processing of event fields).
+
+ Note that order is important but not checked when using multiple Event or
+ EventBuilder objects. Fields are always added to the end of the overall
+ event, regardless of which Event or EventBuilder you use to add the field.
+ The separate Event and EventBuilder objects are used for the sole purpose
+ of tracking the number of fields in each struct.
+
+ Assume that you have obtained EventBuilder object b from object a, i.e.
+ EventBuilder b = a.AddStruct(...). You must complete all operations using
+ b before resuming any operations using a. If this rule is not followed,
+ fields may end up nesting in unexpected ways.
+ */
+ template<class ByteVectorTy>
+ class EventBuilder;
+
+ /*
+ enum Type (high-level API and low-level API):
+
+ Types for event fields, corresponding to a combination of TDH_INTYPE and
+ TDH_OUTTYPE values. The Type enumeration contains predefined combinations
+ of InType and OutType that are known to work well and are recognized by
+ decoders such as TDH and xperf.
+
+ Advanced use: It is possible to create other Type values by combining
+ values from the InType and OutType enumerations using the tld::MakeType
+ function. (Valid Type values consist of one InType combined with one
+ OutType.) However, custom combinations not already present in the Type
+ enumeration are unlikely to be recognized by xperf or other decoders,
+ and will usually end up being decoded as if they had used OutTypeDefault.
+
+ When providing payload for a field, the payload data must match the InType.
+ Payload is always packed tightly with no alignment or padding.
+
+ ****
+
+ How to pack single values:
+
+ Primitive values (int, GUID) are packed directly, with no padding or
+ alignment. Just reserve sizeof(value) bytes in the buffer, then memcpy the
+ value into the buffer. Note that there should be no alignment or padding
+ between items -- the payload is always tightly-packed.
+
+ NUL-terminated strings are also simply memcpy'ed into the buffer. Be sure
+ to include the NUL termination when reserving space and copying the value.
+ There is no special encoding reserved for a NULL string. The encoding
+ helpers in this header simply treat a NULL string the same as an empty
+ string, i.e. AddString((char*)NULL) is the same as AppendString("").
+
+ Binary, CountedString, and CountedAnsiString scalars are all encoded as a
+ UINT16 byte-count followed by the string data. In this case, no NUL
+ termination should be included. Remember that the size is a byte count,
+ not a character count.
+
+ ****
+
+ How to pack arrays:
+
+ Assume you have a function Pack(payloadVec, item) that correctly appends an
+ item to the end of payloadVec.
+
+ Packing a variable-length array of N items is done by appending N (encoded
+ as UINT16) to payloadVec followed by calling Pack(...) N times.
+
+ Packing a constant-length array of N items is done by calling Pack(...) N
+ times. The value of N is encoded in the metadata (it was provided in the
+ field's declaration) so it does not need to be provided in the payload.
+
+ Note: while this header allows you to create arrays of anything, you
+ should avoid creating arrays of the following, as they might not decode
+ correctly:
+ - TypeNone (or anything based on InTypeNull).
+ - TypeBinary (or anything based on InTypeBinary).
+
+ It is ok to create an array of nested structures where fields in the
+ structure have TypeNone or TypeBinary.
+ */
+ enum Type : UINT16;
+
+ /*
+ Used for composing Type values.
+ Normally you'll use a precomposed Type... value, but you can compose a
+ custom Type... value by combining a value from InType with a value from
+ OutType using MakeType.
+
+ The InType tells the pipeline how to encode the field's payload (primarily
+ how to determine payload size). In addition, each InType has an implicit
+ default formatting behavior that will be used if not overridden by an
+ OutType.
+
+ The comments for each InType value indicate the payload encoding rules and
+ the OutTypes that are most likely to be usable with this InType.
+ */
+ enum InType : UINT8;
+
+ /*
+ Used for composing Type values.
+ Normally you'll use a precomposed Type... value, but you can compose a
+ custom Type... value by combining a value from InType with a value from
+ OutType using MakeType.
+
+ The OutType gives the pipeline a formatting hint that the trace consumer
+ may use to override the InType's default formatting behavior. If no
+ OutType is specified (i.e. if OutTypeDefault is used) or if the trace
+ consumer does not recognize the specified InType+OutType combination, the
+ trace consumer will perform default formatting based solely on InType.
+
+ The comments for each OutType indicate the InTypes for which the OutType
+ might have valid semantics. However, most trace consumer only recognize a
+ small subset of the "valid" InType+OutType combinations, so even if the
+ semantics are valid, the OutType might still be ignored by the trace
+ consumer. The most commonly-supported combinations are the combinations
+ with corresponding precomposed Type... values.
+ */
+ enum OutType : UINT8;
+
+ /*
+ enum ProviderTraitType (low-level API):
+
+ The type of a provider trait. Used when building up provider metadata.
+ */
+ enum ProviderTraitType : UINT8;
+
+ /*
+ macro TLD_HAVE_EVENT_SET_INFORMATION (low-level API):
+
+ Configuration macro for controlling the behavior of SetInformation.
+
+ Not all versions of Windows support the EventSetInformation API. The
+ tld::SetInformation wrapper provides default behavior that works well in
+ most cases, but may not meet the needs of all users. The configuration
+ macros can be used to control how the tld::SetInformation function invokes
+ the EventSetInformation API. (Note that tld::SetInformation is called
+ automatically by the Provider class's constructor to register the
+ provider's traits with ETW.)
+
+ When TLD_HAVE_EVENT_SET_INFORMATION is not defined and WINVER < 0x0602:
+ SetInformation uses GetModuleHandleExW+GetProcAddress to find the
+ EventSetInformation function. If found, SetInformation calls it and
+ returns whatever EventSetInformation returns. Otherwise, SetInformation
+ returns an error.
+
+ When TLD_HAVE_EVENT_SET_INFORMATION is not defined and WINVER >= 0x0602:
+ SetInformation directly calls the EventSetInformation(...) function and
+ returns whatever EventSetInformation returns.
+
+ If you set TLD_HAVE_EVENT_SET_INFORMATION:
+ - If TLD_HAVE_EVENT_SET_INFORMATION == 0, SetInformation always returns an
+ error.
+ - If TLD_HAVE_EVENT_SET_INFORMATION == 1, SetInformation calls
+ EventSetInformation.
+ - If TLD_HAVE_EVENT_SET_INFORMATION == 2, SetInformation locates
+ EventSetInformation via GetProcAddress.
+ */
+#ifndef TLD_HAVE_EVENT_SET_INFORMATION
+ #if WINVER < 0x0602 // If targeting Windows before Windows 8
+ #define TLD_HAVE_EVENT_SET_INFORMATION 2 // Find "EventSetInformation" via GetModuleHandleExW/GetProcAddress
+ #else
+ #define TLD_HAVE_EVENT_SET_INFORMATION 1 // Directly invoke EventSetInformation(...)
+ #endif
+#endif // TLD_HAVE_EVENT_SET_INFORMATION
+
+ /*
+ macro TLD_HAVE_EVENT_WRITE_EX (low-level API):
+
+ Configuration macro for enabling/disabling WriteEventEx, WriteEx.
+
+ If TLD_HAVE_EVENT_WRITE_EX is not defined and WINVER < 0x0601, or
+ if TLD_HAVE_EVENT_WRITE_EX is defined as 0,
+ then the WriteEventEx, WriteEx, and related APIs will not be available.
+
+ If TLD_HAVE_EVENT_WRITE_EX is not defined and WINVER >= 0x0601, or
+ if TLD_HAVE_EVENT_WRITE_EX is defined as 1,
+ then the WriteEventEx, WriteEx, and related APIs will be available.
+ */
+#ifndef TLD_HAVE_EVENT_WRITE_EX
+ #if WINVER < 0x0601 // If targeting Windows before Windows 7
+ #define TLD_HAVE_EVENT_WRITE_EX 0
+ #else
+ #define TLD_HAVE_EVENT_WRITE_EX 1
+ #endif
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+
+ /*
+ class ProviderMetadataBuilder (low-level API):
+
+ Helper for building the provider metadata blob.
+ The type provided for ByteBufferTy must behave like a std::vector<BYTE>.
+
+ Example usage:
+
+ std::vector<BYTE> byteVector;
+ tld::ProviderMetadataBuilder<std::vector<BYTE>> builder(byteVector);
+ builder.Begin(szProviderName); // Note: calls byteVector.clear().
+ if (builder.End()) // Returns false if the metadata is too large.
+ {
+ // byteVector now contains a valid provider metadata blob.
+ }
+ */
+ template<class ByteBufferTy>
+ class ProviderMetadataBuilder;
+
+ /*
+ class EventMetadataBuilder (low-level API):
+
+ Helper for building the event metadata blob, including field definitions.
+ The type provided for ByteBufferTy must behave like a std::vector<BYTE>.
+
+ Example usage:
+
+ std::vector<BYTE> byteVector;
+ tld::EventMetadataBuilder<std::vector<BYTE>> builder(byteVector);
+ builder.Begin(eventName); // Note: calls byteVector.clear().
+ builder.AddField("Field1", TypeBOOL32); // top-level field
+ auto struct1 = builder.AddStruct("Struct1");
+ struct1.AddField("Nested1", TypeINT32) // nested field
+ struct1.AddField("Nested2", TypeANSISTRING); // nested field
+ if (builder.End()) // Returns false if the metadata is too large.
+ {
+ // byteVector now contains a valid event metadata blob.
+ }
+ */
+ template<class ByteBufferTy>
+ class EventMetadataBuilder;
+
+ /*
+ class EventDataBuilder (low-level API):
+
+ Helpers for adding event data to an event data blob.
+ The type provided for ByteBufferTy must behave like a std::vector<BYTE>.
+ */
+ template<class ByteBufferTy>
+ class EventDataBuilder;
+
+ /*
+ class EventDescriptor (low-level API):
+
+ Derives from EVENT_DESCRIPTOR. Adds convenient constructor and Reset
+ members. Contains an event's Id/Version/Level/Opcode/Task/Keyword
+ settings. Use of this class is optional - you can use this class if you
+ want to use the constructor or Reset methods, or you can use
+ EVENT_DESCRIPTOR directly if you don't need the convenience methods.
+ */
+ struct EventDescriptor;
+
+ /*
+ class ByteArrayWrapper (low-level API):
+
+ Adapter that allows a regular BYTE array (stack-allocated or
+ manually-allocated) to be used as the buffer in MetadataBuilder
+ operations. If the underlying BYTE array is too small, or if no array is
+ provided at all, this adapter will stop writing data beyond the end of the
+ array, but will continue to "accept" data (and will increment it size
+ member accordingly). This allows you to determine the size of the required
+ array so that the operation can be retried with a correctly-sized array.
+
+ Example usage:
+
+ // Create a wrapper with a default (0-length) buffer.
+ tld::ByteArrayWrapper wrapper;
+
+ // Create a metadata builder attached to the wrapper.
+ tld::ProviderMetadataBuilder<tld::ByteArrayWrapper> builder(wrapper);
+
+ // Determine the size needed for provider metadata.
+ builder.Begin(providerName);
+ builder.End();
+
+ // Allocate a real buffer and reset the wrapper to use it.
+ BYTE* pBuffer = new BYTE[wrapper.size()];
+ wrapper.reset(pBuffer, wrapper.size());
+
+ // Redo the operation with the real buffer.
+ builder.Begin(providerName);
+ if (builder.End())
+ {
+ // pBuffer now contains a valid provider metadata blob.
+ }
+ */
+ class ByteArrayWrapper;
+
+ /*
+ function RegisterProvider (low-level API):
+
+ Calls EventRegister. If EventRegister succeeds, calls EventSetInformation
+ to register your provider traits with ETW. Use this instead of calling
+ EventRegister directly to ensure that your provider traits are properly
+ registered.
+ */
+ inline HRESULT RegisterProvider(
+ _Out_ REGHANDLE* phProvider,
+ _In_ GUID const* pProviderId,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_opt_ PENABLECALLBACK pEnableCallback = 0,
+ _In_opt_ void* pEnableCallbackContext = 0);
+
+ /*
+ function UnregisterProvider (low-level API):
+
+ Calls EventUnregister.
+ */
+ inline HRESULT UnregisterProvider(
+ REGHANDLE hProvider);
+
+ /*
+ function WriteEvent (low-level API):
+
+ Calls EventWriteTransfer. You must provide 2 more data descriptors than
+ are required for your data. The first two elements in pDataDescriptors
+ will be used for provider and event metadata. The actual payload (if any)
+ is expected to be in the remaining elements of pDataDescriptors.
+
+ Example usage:
+
+ tld::EventDescriptor eventDescriptor(
+ level,
+ opcode,
+ task,
+ keywords);
+ EVENT_DATA_DESCRIPTOR pDataDescriptors[cFields + 2];
+ for (int i = 0; i < cFields; i++)
+ {
+ EventDataDescCreate(&pDataDescriptors[i + 2], ...);
+ }
+ tld::WriteEvent(
+ hProvider,
+ eventDescriptor,
+ pProviderMetadata,
+ pEventMetadata,
+ cFields + 2,
+ pDataDescriptors);
+ */
+ inline HRESULT WriteEvent(
+ _In_ REGHANDLE hProvider,
+ _In_ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ _In_opt_ GUID const* pActivityId = 0,
+ _In_opt_ GUID const* pRelatedActivityId = 0);
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ /*
+ function WriteEventEx (low-level API):
+ Calls EventWriteEx. Otherwise works like the WriteEvent function.
+ */
+ inline HRESULT WriteEventEx(
+ _In_ REGHANDLE hProvider,
+ _In_ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ ULONG64 filter = 0,
+ ULONG flags = 0,
+ _In_opt_ GUID const* pActivityId = 0,
+ _In_opt_ GUID const* pRelatedActivityId = 0);
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+ /*
+ function SetInformation (low-level API):
+
+ Wrapper for ETW API EventSetInformation.
+ If TraceLoggingDynamic.cpp was compiled to require Win8 or later (as
+ determined by WINVER), this directly calls EventSetInformation. Otherwise,
+ this attempts to dynamically load the EventSetInformation API via
+ GetModuleHandleExW.
+
+ The behavior of this function (e.g. to override the WINVER check) can be
+ adjusted by setting the TLD_HAVE_EVENT_SET_INFORMATION macro as described
+ below.
+ */
+ inline HRESULT SetInformation(
+ _In_ REGHANDLE hProvider,
+ EVENT_INFO_CLASS informationClass,
+ _In_reads_bytes_opt_(cbInformation) void* pbInformation,
+ ULONG cbInformation);
+
+ /*
+ function GetGuidForName (low-level API):
+
+ Hashes a provider name to generate a GUID. Uses the same GUID generation
+ algorithm as System.Diagnostics.Tracing.EventSource (from .NET) and
+ Windows.Foundation.Diagnostics.LoggingChannel (from Windows Runtime).
+ */
+ inline GUID GetGuidForName(
+ _In_z_ char const* szUtf8Name);
+
+ /*
+ function GetGuidForName (low-level API):
+
+ Hashes a provider name to generate a GUID. Uses the same GUID generation
+ algorithm as .NET System.Diagnostics.Tracing.EventSource and Windows
+ Runtime Windows.Foundation.Diagnostics.LoggingChannel.
+ */
+ inline GUID GetGuidForName(
+ _In_z_ wchar_t const* szUtf16Name);
+
+ /*
+ function PushBackAsUtf8 (low-level API):
+
+ Transcodes a NUL-terminated UTF-16LE string to a NUL-terminated UTF-8
+ string. Intended for use when appending provider/event/field names to
+ metadata buffers.
+ */
+ template<class ByteBufferTy>
+ void PushBackAsUtf8(
+ ByteBufferTy& buffer,
+ _In_z_ wchar_t const* szUtf16);
+
+#pragma endregion
+
+ namespace detail
+ {
+#pragma region Internal macros
+
+#define _tld_MAKE_TYPE(inType, outType) \
+ static_cast<::tld::Type>((inType) | (static_cast<int>(outType) << 8))
+
+#ifndef TLD_DEBUG
+ #if (DBG || defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG)
+ #define TLD_DEBUG 1
+ #else // DBG
+ #define TLD_DEBUG 0
+ #endif // DBG
+#endif // TLD_DEBUG
+
+#ifndef _tld_ASSERT
+ #if TLD_DEBUG
+ #define _tld_ASSERT(exp, str) ((void)(!(exp) ? (__annotation(L"Debug", L"AssertFail", L"TraceLogging: " L#exp L" : " L##str), DbgRaiseAssertionFailure(), 0) : 0))
+ #else // TLD_DEBUG
+ #define _tld_ASSERT(exp, str) ((void)0)
+ #endif // TLD_DEBUG
+#endif // _tld_ASSERT
+
+#pragma endregion
+
+#pragma region MetadataBuilderBase
+
+ template<class ByteBufferTy>
+ class MetadataBuilderBase
+ {
+ ByteBufferTy& m_buffer;
+
+ MetadataBuilderBase(MetadataBuilderBase const&); // = delete
+ void operator=(MetadataBuilderBase const&); // = delete
+
+ protected:
+
+ explicit MetadataBuilderBase(ByteBufferTy& buffer)
+ : m_buffer(buffer)
+ {
+ return;
+ }
+
+ void BaseBegin()
+ {
+ m_buffer.clear();
+ AddU16(0); // Metadata size filled in by BaseEnd
+ }
+
+ bool BaseEnd()
+ {
+ auto size = m_buffer.size();
+ _tld_ASSERT(2 <= size, "Begin was not called");
+ SetU16(0, static_cast<UINT16>(size));
+ return size < 32768;
+ }
+
+ void AddBytes(
+ _In_reads_bytes_(cb) void const* p,
+ unsigned cb)
+ {
+ auto pb = static_cast<UINT8 const*>(p);
+ for (unsigned i = 0; i != cb; i++)
+ {
+ m_buffer.push_back(pb[i]);
+ }
+ }
+
+ void AddString(
+ _In_z_ char const* szUtf8)
+ {
+ for (unsigned i = 0;; i++)
+ {
+ m_buffer.push_back(szUtf8[i]);
+ if (szUtf8[i] == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ void AddString(
+ _In_z_ wchar_t const* szUtf16)
+ {
+ PushBackAsUtf8(m_buffer, szUtf16);
+ }
+
+ void AddU8(
+ UINT8 val)
+ {
+ m_buffer.push_back(val);;
+ }
+
+ void AddU16(
+ UINT16 val)
+ {
+ m_buffer.push_back(static_cast<UINT8>(val));
+ m_buffer.push_back(static_cast<UINT8>(val >> 8));
+ }
+
+ UINT32 GetBookmark()
+ {
+ return static_cast<UINT32>(m_buffer.size());
+ }
+
+ void SetU8(
+ UINT32 bookmark,
+ UINT8 val)
+ {
+ _tld_ASSERT(bookmark < m_buffer.size(), "bookmark out of range");
+ m_buffer[bookmark] = val;
+ }
+
+ void SetU16(
+ UINT32 bookmark,
+ UINT16 val)
+ {
+ _tld_ASSERT(1 <= bookmark + 1, "bookmark out of range");
+ SetU8(bookmark + 0, static_cast<UINT8>(val));
+ SetU8(bookmark + 1, static_cast<UINT8>(val >> 8));
+ }
+
+ void IncrementU7(
+ UINT32 bookmark)
+ {
+ _tld_ASSERT(bookmark < m_buffer.size(), "bookmark out of range");
+ UINT8 result;
+ result = ++m_buffer[bookmark];
+ _tld_ASSERT((result & 0x7f) != 0, "too many fields in struct");
+ }
+
+ public:
+
+ ByteBufferTy& GetBuffer()
+ {
+ return m_buffer;
+ }
+
+ ByteBufferTy const& GetBuffer() const
+ {
+ return m_buffer;
+ }
+ };
+
+#pragma endregion
+
+#pragma region Sha1ForNonSecretPurposes
+
+ /*
+ Implements the SHA1 hashing algorithm. Note that this implementation is
+ for hashing public information. Do not use this code to hash private data,
+ as this implementation does not take any steps to avoid information
+ disclosure (i.e. does not scrub its buffers).
+ */
+ class Sha1ForNonSecretPurposes
+ {
+ Sha1ForNonSecretPurposes(Sha1ForNonSecretPurposes const&); // = delete
+ void operator=(Sha1ForNonSecretPurposes const&); // = delete
+
+ UINT64 m_length; // Total message length in bits
+ unsigned m_pos; // Length of current chunk in bytes
+ UINT32 m_results[5];
+ UINT32 m_w[80]; // Workspace
+
+ public:
+
+ Sha1ForNonSecretPurposes()
+ : m_length()
+ , m_pos()
+ {
+ m_results[0] = 0x67452301;
+ m_results[1] = 0xEFCDAB89;
+ m_results[2] = 0x98BADCFE;
+ m_results[3] = 0x10325476;
+ m_results[4] = 0xC3D2E1F0;
+ }
+
+ void Append(
+ _In_reads_bytes_(cbInput) void const* pInput,
+ unsigned cbInput)
+ {
+ for (unsigned i = 0; i != cbInput; i++)
+ {
+ Append(static_cast<UINT8 const*>(pInput)[i]);
+ }
+ }
+
+ void Append(UINT8 input)
+ {
+ reinterpret_cast<UINT8*>(m_w)[m_pos++] = input;
+ if (64 == m_pos)
+ {
+ Drain();
+ }
+ }
+
+ _Ret_bytecount_(20) UINT8* Finish()
+ {
+ UINT64 totalBitCount = m_length + 8 * m_pos;
+ Append(0x80);
+ while (m_pos != 56)
+ {
+ Append(0x00);
+ }
+
+ *reinterpret_cast<UINT64*>(m_w + 14) = _byteswap_uint64(totalBitCount);
+ Drain();
+
+ for (unsigned i = 0; i != 5; i++)
+ {
+ m_results[i] = _byteswap_ulong(m_results[i]);
+ }
+
+ return reinterpret_cast<UINT8*>(m_results);
+ }
+
+ private:
+
+ void Drain()
+ {
+ for (unsigned i = 0; i != 16; i++)
+ {
+ m_w[i] = _byteswap_ulong(m_w[i]);
+ }
+
+ for (unsigned i = 16; i != 80; i++)
+ {
+ m_w[i] = _rotl((m_w[i - 3] ^ m_w[i - 8] ^ m_w[i - 14] ^ m_w[i - 16]), 1);
+ }
+
+ UINT32 a = m_results[0];
+ UINT32 b = m_results[1];
+ UINT32 c = m_results[2];
+ UINT32 d = m_results[3];
+ UINT32 e = m_results[4];
+
+ for (unsigned i = 0; i != 20; i++)
+ {
+ UINT32 const k = 0x5A827999;
+ UINT32 f = (b & c) | ((~b) & d);
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ for (unsigned i = 20; i != 40; i++)
+ {
+ const UINT32 k = 0x6ED9EBA1;
+ UINT32 f = b ^ c ^ d;
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ for (unsigned i = 40; i != 60; i++)
+ {
+ const UINT32 k = 0x8F1BBCDC;
+ UINT32 f = (b & c) | (b & d) | (c & d);
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ for (unsigned i = 60; i != 80; i++)
+ {
+ const UINT32 k = 0xCA62C1D6;
+ UINT32 f = b ^ c ^ d;
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ m_results[0] += a;
+ m_results[1] += b;
+ m_results[2] += c;
+ m_results[3] += d;
+ m_results[4] += e;
+
+ m_length += 512; // 64 bytes == 512 bits
+ m_pos = 0;
+ }
+ };
+
+#pragma endregion
+
+#pragma region CopyUtfCodePoint
+
+ /*
+ Transcodes one code point from UTF-8 to UTF-16LE.
+
+ Note that this function requires the input buffer to be nul-terminated.
+ This function may try to read several bytes from the input buffer, and the
+ nul-termination is required to ensure that it doesn't read off the end of
+ the valid buffer when decoding what appears to be a multi-byte code point.
+
+ The UTF-8 validation is very permissive -- anything that is not valid
+ UTF-8 is treated as if it were ISO-8859-1 (i.e. the BYTE value is cast
+ to wchar_t and assumed to be a valid Unicode code point). This usually
+ works well - it has a reasonable probability of doing what the developer
+ expects even when the input is CP1252/Latin1 instead of UTF-8, and doesn't
+ fail too badly when it doesn't do what the developer expects.
+
+ pchUtf16Output:
+ Buffer that receives a single code point, encoded in UTF-16-LE as one or
+ two 16-bit wchar_t values.
+
+ pszUtf8Input:
+ Pointer into a nul-terminated UTF-8 byte sequence. On entry, this points
+ to the next char to be consumed. This function will advance the pointer
+ past the consumed data.
+
+ returns:
+ The number of 16-bit wchar_t values added to pchUtf16Output (one or two).
+ */
+ static unsigned CopyUtfCodePoint(
+ _Out_writes_to_(2, return) wchar_t* pchUtf16Output,
+ char const*& pszUtf8Input)
+ {
+ unsigned cchOutput = 1;
+ unsigned ch = static_cast<unsigned char>(*pszUtf8Input);
+ pszUtf8Input += 1;
+ if (ch <= 0xbf)
+ {
+ // 0x01..0x7f: ASCII - pass through.
+ // 0x80..0xbf: Invalid - pass through.
+ }
+ else if ((pszUtf8Input[0] & 0xc0) != 0x80)
+ {
+ // Invalid trail byte - pass through.
+ }
+ else if (ch <= 0xdf)
+ {
+ // 0xc0..0xdf: Two-byte encoding for 0x0080..0x07ff.
+ unsigned ch2 = ((ch & 0x1f) << 6)
+ | (pszUtf8Input[0] & 0x3f);
+ if (0x0080 <= ch2)
+ {
+ ch = ch2;
+ pszUtf8Input += 1;
+ }
+ }
+ else if ((pszUtf8Input[1] & 0xc0) != 0x80)
+ {
+ // Invalid trail byte - pass through.
+ }
+ else if (ch <= 0xef)
+ {
+ // 0xe0..0xef: Three-byte encoding for 0x0800..0xffff.
+ unsigned ch2 = ((ch & 0x0f) << 12)
+ | ((pszUtf8Input[0] & 0x3f) << 6)
+ | (pszUtf8Input[1] & 0x3f);
+ if (0x0800 <= ch2)
+ {
+ ch = ch2;
+ pszUtf8Input += 2;
+ }
+ }
+ else if ((pszUtf8Input[2] & 0xc0) != 0x80)
+ {
+ // Invalid trail byte - pass through.
+ }
+ else if (ch <= 0xf4)
+ {
+ // 0xf0..0xf4: Four-byte encoding for 0x010000..0x10ffff.
+ unsigned ch2 = ((ch & 0x07) << 18)
+ | ((pszUtf8Input[0] & 0x3f) << 12)
+ | ((pszUtf8Input[1] & 0x3f) << 6)
+ | (pszUtf8Input[2] & 0x3f);
+ ch2 -= 0x10000;
+ if (ch2 <= 0xfffff)
+ {
+ // Decode into surrogate pair (2 wchar_t units)
+ pchUtf16Output[1] = static_cast<wchar_t>(0xdc00 | (ch2 & 0x3ff));
+ ch = 0xd800 | (ch2 >> 10);
+ pszUtf8Input += 3;
+ cchOutput = 2;
+ }
+ }
+
+ pchUtf16Output[0] = static_cast<wchar_t>(ch);
+ return cchOutput;
+ }
+
+ /*
+ Copies one code point of UTF-16LE data.
+
+ Note that this function requires the input buffer to be nul-terminated.
+ This function may try to read several words from the input buffer, and the
+ nul-termination is required to ensure that it doesn't read off the end of
+ the valid buffer when copying what appears to be a surrogate pair.
+
+ pchUtf16Output:
+ Buffer that receives a single code point, encoded in UTF-16-LE as one or
+ two 16-bit wchar_t values.
+
+ pszUtf16Input:
+ Pointer into a nul-terminated UTF-16-LE wchar_t sequence. On entry, this
+ points to the next wchar_t to be consumed. This function will advance the
+ pointer past the consumed data.
+
+ returns:
+ The number of 16-bit wchar_t values added to pchUtf16Output (one or two).
+ */
+ static unsigned CopyUtfCodePoint(
+ _Out_writes_to_(2, return) wchar_t* pchUtf16Output,
+ wchar_t const*& pszUtf16Input)
+ {
+ unsigned cchOutput = 1;
+ wchar_t ch = *pszUtf16Input;
+ pszUtf16Input += 1;
+ if (0xd800 <= ch && ch < 0xdc00 &&
+ 0xdc00 <= *pszUtf16Input && *pszUtf16Input < 0xe000)
+ {
+ pchUtf16Output[1] = *pszUtf16Input;
+ pszUtf16Input += 1;
+ cchOutput = 2;
+ }
+ pchUtf16Output[0] = ch;
+ return cchOutput;
+ }
+
+#pragma endregion
+
+#pragma region GetGuidForNameImpl
+
+ __declspec(selectany) extern UINT32 const NamespaceValue[] = {
+ 0xB22D2C48, 0xC84790C3, 0x151AF887, 0xFB30C1Bf
+ };
+
+ template<class CharTy>
+ GUID GetGuidForNameImpl(
+ _In_z_ CharTy const* szUtfName)
+ {
+ /*
+ Algorithm:
+ szNameUpperBE = ByteSwap(ToUpper(szName));
+ hash = SHA1(namespaceBytes + szNameUpperBE); // Does not include zero-termination in hash.
+ hash[7] = (hash[7] & 0x0F) | 0x50;
+ guid = First16Bytes(hash);
+ */
+
+ WCHAR buffer[16];
+ unsigned const cchBuffer = ARRAYSIZE(buffer);
+ Sha1ForNonSecretPurposes sha1;
+
+ sha1.Append(NamespaceValue, sizeof(NamespaceValue));
+ if (szUtfName[0] != 0)
+ {
+ CharTy const* pszName = szUtfName;
+ unsigned iBuffer = 0;
+ for (;;)
+ {
+ // CopyUtfCodePoint may add up to 2 wchar_t units to buffer.
+ iBuffer += CopyUtfCodePoint(buffer + iBuffer, pszName);
+ _tld_ASSERT(iBuffer <= cchBuffer, "buffer overflow");
+
+ bool const bDone = *pszName == 0;
+ if (bDone || cchBuffer - 1 <= iBuffer)
+ {
+ #pragma warning(suppress: 26035) // string is counted, not nul-terminated.
+ LCMapStringEx(
+ LOCALE_NAME_INVARIANT,
+ LCMAP_UPPERCASE | LCMAP_BYTEREV,
+ buffer,
+ iBuffer,
+ buffer,
+ iBuffer,
+ NULL,
+ NULL,
+ 0);
+ sha1.Append(buffer, iBuffer * 2);
+
+ if (bDone)
+ {
+ break;
+ }
+
+ iBuffer = 0;
+ }
+ }
+ }
+
+ UINT8* pSha1 = sha1.Finish();
+
+ // Set high 4 bits of octet 7 to 5, as per RFC 4122
+ pSha1[7] = (pSha1[7] & 0x0f) | 0x50;
+
+ return *reinterpret_cast<GUID*>(pSha1);
+ }
+
+#pragma endregion
+ }
+ // namespace detail
+
+#pragma region Enumerations
+
+ enum InType : UINT8
+ {
+ /*
+ A field with no value (0-length payload).
+ Arrays of InTypeNull are illegal.
+ */
+ InTypeNull,
+
+ /*
+ A nul-terminated CHAR16 string.
+ Useful OutTypes: Xml, Json.
+ */
+ InTypeUnicodeString,
+
+ /*
+ A nul-terminated CHAR8 string.
+ Useful OutTypes: Xml, Json, Utf8.
+ */
+ InTypeAnsiString,
+
+ /*
+ INT8.
+ */
+ InTypeInt8,
+
+ /*
+ UINT8.
+ Useful OutTypes: String, Boolean, Hex.
+ */
+ InTypeUInt8,
+
+ /*
+ INT16.
+ */
+ InTypeInt16,
+
+ /*
+ UINT16.
+ Useful OutTypes: String, Hex, Port.
+ */
+ InTypeUInt16,
+
+ /*
+ INT32.
+ Useful OutTypes: HResult.
+ */
+ InTypeInt32,
+
+ /*
+ UINT32.
+ Useful OutTypes: Pid, Tid, IPv4, Win32Error, NTStatus.
+ */
+ InTypeUInt32,
+
+ /*
+ INT64.
+ */
+ InTypeInt64,
+
+ /*
+ UINT64.
+ */
+ InTypeUInt64,
+
+ /*
+ FLOAT.
+ */
+ InTypeFloat,
+
+ /*
+ DOUBLE.
+ */
+ InTypeDouble,
+
+ /*
+ BOOL.
+ */
+ InTypeBool32,
+
+ /*
+ UINT16 byte-count followed by binary data.
+ Useful OutTypes: IPv6, SocketAddress.
+ Arrays of InTypeBinary are not supported. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ InTypeBinary,
+
+ /*
+ GUID.
+ */
+ InTypeGuid,
+
+ /*
+ Not supported. Use InTypePointer. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ InTypePointer_PlatformSpecific,
+
+ /*
+ FILETIME.
+ */
+ InTypeFileTime,
+
+ /*
+ SYSTEMTIME.
+ */
+ InTypeSystemTime,
+
+ /*
+ SID. Size is determined from the number of subauthorities.
+ */
+ InTypeSid,
+
+ /*
+ INT32.
+ */
+ InTypeHexInt32,
+
+ /*
+ INT64.
+ */
+ InTypeHexInt64,
+
+ /*
+ UINT16 byte-count followed by UTF-16 data.
+ */
+ InTypeCountedString,
+
+ /*
+ UINT16 byte-count followed by MBCS data.
+ */
+ InTypeCountedAnsiString,
+
+ /*
+ This field has no value by itself, but it causes the decoder to treat
+ the next N fields as part of this field. The value of N is taken from
+ the field's OutType.
+
+ Note that it is valid to have an array of structs. The array length
+ will apply to the entire group of fields contained in the array.
+
+ The Struct type is special. Do not use it directly. Use helper APIs to
+ create nested structures.
+ */
+ InTypeStruct,
+
+ /*
+ INT_PTR.
+ InTypeIntPtr is an alias for either InTypeInt32 or InTypeInt64.
+ */
+ InTypeIntPtr = sizeof(void*) == 8 ? InTypeInt64 : InTypeInt32,
+
+ /*
+ UINT_PTR.
+ InTypeUIntPtr is an alias for either InTypeUInt32 or InTypeUInt64.
+ */
+ InTypeUIntPtr = sizeof(void*) == 8 ? InTypeUInt64 : InTypeUInt32,
+
+ /*
+ LPVOID.
+ InTypePointer is an alias for either InTypeHexInt32 or InTypeHexInt64.
+ */
+ InTypePointer = sizeof(void*) == 8 ? InTypeHexInt64 : InTypeHexInt32,
+
+ /*
+ InType must fit in 5 bits.
+ */
+ InTypeMask = 31
+ };
+
+ enum OutType : UINT8
+ {
+ OutTypeDefault = 0x00,
+ OutTypeNoPrint = 0x01,
+ OutTypeString = 0x02, // affects 8-bit and 16-bit integral types.
+ OutTypeBoolean = 0x03, // affects 8-bit and 32-bit integral types.
+ OutTypeHex = 0x04, // affects integral types.
+ OutTypePid = 0x05, // affects 32-bit integral types.
+ OutTypeTid = 0x06, // affects 32-bit integral types.
+ OutTypePort = 0x07, // affects 16-bit integral types.
+ OutTypeIPv4 = 0x08, // affects 32-bit integral types.
+ OutTypeIPv6 = 0x09, // affects arrays of 8-bit integral types, e.g. UInt8[].
+ OutTypeSocketAddress = 0x0a, // affects arrays of 8-bit integral types, e.g. UInt8[].
+ OutTypeXml = 0x0b, // affects strings; affects arrays of 8-bit and 16-bit integral types.
+ OutTypeJson = 0x0c, // affects strings; affects arrays of 8-bit and 16-bit integral types.
+ OutTypeWin32Error = 0x0d,// affects 32-bit integral types.
+ OutTypeNTStatus = 0x0e, // affects 32-bit integral types.
+ OutTypeHResult = 0x0f, // affects 32-bit integral types.
+ OutTypeFileTime = 0x10, // affects 64-bit integral types.
+ OutTypeSigned = 0x11, // affects integral types.
+ OutTypeUnsigned = 0x12, // affects integral types.
+ OutTypeDateTimeCultureInsensitive = 0x21, // affects FileTime, SystemTime.
+ OutTypeUtf8 = 0x23, // affects AnsiString types.
+ OutTypePkcs7WithTypeInfo = 0x24, // affects binary types.
+ OutTypeCodePointer = 0x25, // affects UInt32, UInt64, HexInt32, HexInt64.
+ OutTypeDateTimeUtc = 0x26, // affects FileTime, SystemTime.
+ OutTypeMask = 0x7f // OutType must fit into 7 bits.
+ };
+
+ enum Type : UINT16
+ {
+ /*
+ Field with no data (empty). 0-length payload.
+ Can only be used on scalar fields. Illegal for arrays.
+ NOTE: Not well-supported by decoders.
+ */
+ TypeNone = InTypeNull,
+
+ /*
+ Encoding assumes 0-terminated CHAR16 string.
+ Formatting treats as UTF-16LE string.
+ */
+ TypeUtf16String = InTypeUnicodeString,
+
+ /*
+ Encoding assumes 0-terminated CHAR8 string.
+ Formatting treats as MBCS string.
+ */
+ TypeMbcsString = InTypeAnsiString,
+
+ /*
+ Encoding assumes nul-terminated CHAR8 string.
+ Formatting treats as UTF-8 string.
+ */
+ TypeUtf8String = _tld_MAKE_TYPE(InTypeAnsiString, OutTypeUtf8),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt8 = InTypeInt8,
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt8 = InTypeUInt8,
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt16 = InTypeInt16,
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt16 = InTypeUInt16,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt32 = InTypeInt32,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt32 = InTypeUInt32,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt64 = InTypeInt64,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt64 = InTypeUInt64,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as Float32.
+ */
+ TypeFloat = InTypeFloat,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as Float64.
+ */
+ TypeDouble = InTypeDouble,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as Boolean.
+ */
+ TypeBool32 = InTypeBool32,
+
+ /*
+ Encoding assumes 2-byte length followed by binary data.
+ Formatting treats as binary blob.
+ Arrays of TypeBinary are not supported. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ TypeBinary = InTypeBinary,
+
+ /*
+ Encoding assumes 16-byte value.
+ Formatting treats as GUID.
+ */
+ TypeGuid = InTypeGuid,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as FILETIME.
+ */
+ TypeFileTime = InTypeFileTime,
+
+ /*
+ Encoding assumes 16-byte value.
+ Formatting treats as SYSTEMTIME.
+ */
+ TypeSystemTime = InTypeSystemTime,
+
+ /*
+ Encoding assumes 16-byte value.
+ Formatting treats as UTC SYSTEMTIME.
+ */
+ TypeSystemTimeUtc = _tld_MAKE_TYPE(InTypeSystemTime, OutTypeDateTimeUtc),
+
+ /*
+ Encoding assumes SID. Length is determined from number of subauthorities.
+ Formatting treats as a SID.
+ */
+ TypeSid = InTypeSid,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt32 = InTypeHexInt32,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt64 = InTypeHexInt64,
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR16 data.
+ Formatting treats as UTF-16LE string.
+ */
+ TypeCountedUtf16String = InTypeCountedString,
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as MBCS string.
+ */
+ TypeCountedMbcsString = InTypeCountedAnsiString,
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as UTF-8 string.
+ */
+ TypeCountedUtf8String = _tld_MAKE_TYPE(InTypeCountedAnsiString, OutTypeUtf8),
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as signed integer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is an alias for TypeInt32 when compiled for a 32-bit target.
+ This is an alias for TypeInt64 when compiled for a 64-bit target.
+ */
+ TypeIntPtr = InTypeIntPtr,
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as unsigned integer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is an alias for TypeUInt32 when compiled for a 32-bit target.
+ This is an alias for TypeUInt64 when compiled for a 64-bit target.
+ */
+ TypeUIntPtr = InTypeUIntPtr,
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as hexadecimal unsigned integer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is an alias for TypeHexInt32 when compiled for a 32-bit target.
+ This is an alias for TypeHexInt64 when compiled for a 64-bit target.
+ */
+ TypePointer = InTypePointer,
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as code pointer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is a subtype of TypeHexInt32 when compiled for a 32-bit target.
+ This is a subtype of TypeHexInt64 when compiled for a 64-bit target.
+ */
+ TypeCodePointer = _tld_MAKE_TYPE(InTypePointer, OutTypeCodePointer),
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as UTF-16LE character.
+ */
+ TypeChar16 = _tld_MAKE_TYPE(InTypeUInt16, OutTypeString),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as ANSI character.
+ */
+ TypeChar8 = _tld_MAKE_TYPE(InTypeUInt8, OutTypeString),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as Boolean.
+ */
+ TypeBool8 = _tld_MAKE_TYPE(InTypeUInt8, OutTypeBoolean),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt8 = _tld_MAKE_TYPE(InTypeUInt8, OutTypeHex),
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt16 = _tld_MAKE_TYPE(InTypeUInt16, OutTypeHex),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as process identifier.
+ */
+ TypePid = _tld_MAKE_TYPE(InTypeUInt32, OutTypePid),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as thread identifier.
+ */
+ TypeTid = _tld_MAKE_TYPE(InTypeUInt32, OutTypeTid),
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as big-endian IP port(s).
+ */
+ TypePort = _tld_MAKE_TYPE(InTypeUInt16, OutTypePort),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as IPv4 address.
+ */
+ TypeIPv4 = _tld_MAKE_TYPE(InTypeUInt32, OutTypeIPv4),
+
+ /*
+ Encoding assumes 2-byte length followed by binary data.
+ Formatting treats as IPv6 address.
+ Arrays of TypeIPv6 are not supported. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ TypeIPv6 = _tld_MAKE_TYPE(InTypeBinary, OutTypeIPv6),
+
+ /*
+ Encoding assumes 2-byte length followed by binary data.
+ Formatting treats as SOCKADDR.
+ Arrays of TypeSocketAddress are not supported. (No error will be
+ reported, but decoding tools will probably not be able to decode them).
+ */
+ TypeSocketAddress = _tld_MAKE_TYPE(InTypeBinary, OutTypeSocketAddress),
+
+ /*
+ Encoding assumes nul-terminated CHAR16 string.
+ Formatting treats as UTF-16LE XML.
+ */
+ TypeUtf16Xml = _tld_MAKE_TYPE(InTypeUnicodeString, OutTypeXml),
+
+ /*
+ Encoding assumes nul-terminated CHAR8 string.
+ Formatting treats as UTF-8 XML.
+ */
+ TypeMbcsXml = _tld_MAKE_TYPE(InTypeAnsiString, OutTypeXml),
+
+ /*
+ Encoding assumes nul-terminated CHAR16 string.
+ Formatting treats as UTF-16LE JSON.
+ */
+ TypeUtf16Json = _tld_MAKE_TYPE(InTypeUnicodeString, OutTypeJson),
+
+ /*
+ Encoding assumes nul-terminated CHAR8 string.
+ Formatting treats as UTF-8 JSON.
+ */
+ TypeMbcsJson = _tld_MAKE_TYPE(InTypeAnsiString, OutTypeJson),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR16 data.
+ Formatting treats as UTF-16LE XML.
+ */
+ TypeCountedUtf16Xml = _tld_MAKE_TYPE(InTypeCountedString, OutTypeXml),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as UTF-8 XML.
+ */
+ TypeCountedMbcsXml = _tld_MAKE_TYPE(InTypeCountedAnsiString, OutTypeXml),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR16 data.
+ Formatting treats as UTF-16LE JSON.
+ */
+ TypeCountedUtf16Json = _tld_MAKE_TYPE(InTypeCountedString, OutTypeJson),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as UTF-8 JSON.
+ */
+ TypeCountedMbcsJson = _tld_MAKE_TYPE(InTypeCountedAnsiString, OutTypeJson),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as Win32 error(s).
+ */
+ TypeWin32Error = _tld_MAKE_TYPE(InTypeUInt32, OutTypeWin32Error),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as NTSTATUS(s).
+ */
+ TypeNTStatus = _tld_MAKE_TYPE(InTypeUInt32, OutTypeNTStatus),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as HRESULT(s).
+ */
+ TypeHResult = _tld_MAKE_TYPE(InTypeInt32, OutTypeHResult)
+ };
+
+ enum ProviderTraitType : UINT8
+ {
+ ProviderTraitNone = 0, // Invalid value
+ ProviderTraitGroupGuid = 1 // Data is the group GUID.
+ };
+
+#pragma endregion
+
+#pragma region EventDescriptor
+
+ /*
+ Simple wrapper around the ETW EVENT_DESCRIPTOR structure.
+ Provides a constructor to make initialization easier.
+ Can be used anywhere an EVENT_DESCRIPTOR is required.
+
+ Notes:
+ - Channel defaults to 11 (WINEVENT_CHANNEL_TRACELOGGING). Other channels
+ will only work if the provider is running on a TraceLogging-enabled OS
+ (Windows 10 or later, or Windows 7 sp1 with updates, or Windows 8.1 with
+ updates). If the provider is running on an earlier version of Windows and
+ you use a channel other than 11, the events will not decode correctly.
+ - If you are not assigning unique Ids for your events, set both Id and
+ Version to 0.
+ */
+ struct EventDescriptor
+ : EVENT_DESCRIPTOR
+ {
+ explicit EventDescriptor(
+ UCHAR level = 5, // 5 = WINEVENT_LEVEL_VERBOSE
+ ULONGLONG keyword = 0, // 0 = no keywords
+ UCHAR opcode = 0, // 0 = WINEVENT_OPCODE_INFO
+ USHORT task = 0) // 0 = WINEVENT_TASK_NONE
+ {
+ Reset(level, keyword, opcode, task);
+ }
+
+ void Reset(
+ UCHAR level = 5, // 5 = WINEVENT_LEVEL_VERBOSE
+ ULONGLONG keyword = 0, // 0 = no keywords
+ UCHAR opcode = 0, // 0 = WINEVENT_OPCODE_INFO
+ USHORT task = 0) // 0 = WINEVENT_TASK_NONE
+ {
+ Id = 0;
+ Version = 0;
+ Channel = 11; // WINEVENT_CHANNEL_TRACELOGGING
+ Level = level;
+ Opcode = opcode;
+ Task = task;
+ Keyword = keyword;
+ }
+ };
+
+#pragma endregion
+
+#pragma region Support functions
+
+#pragma warning(push)
+#pragma warning(disable: 26018) // PREfast: Potential read overflow. (Analysis is incorrect.)
+
+ template<class ByteBufferTy>
+ void PushBackAsUtf8(
+ ByteBufferTy& buffer,
+ _In_z_ wchar_t const* szUtf16)
+ {
+ for (unsigned i = 0;; i++)
+ {
+ unsigned ch = szUtf16[i];
+ if (ch < 0x80)
+ {
+ buffer.push_back(static_cast<UINT8>(ch));
+ if (ch == 0)
+ {
+ break;
+ }
+ }
+ else if (ch < 0x800)
+ {
+ buffer.push_back(static_cast<UINT8>(((ch >> 6)) | 0xc0));
+ buffer.push_back(static_cast<UINT8>(((ch)& 0x3f) | 0x80));
+ }
+ else if (
+ 0xd800 <= ch && ch < 0xdc00 &&
+ 0xdc00 <= szUtf16[i + 1] && szUtf16[i + 1] < 0xe000)
+ {
+ // Decode surrogate pair.
+ ++i;
+ ch = 0x010000 + (((ch - 0xd800) << 10) | (szUtf16[i] - 0xdc00));
+ buffer.push_back(static_cast<UINT8>(((ch >> 18)) | 0xf0));
+ buffer.push_back(static_cast<UINT8>(((ch >> 12) & 0x3f) | 0x80));
+ buffer.push_back(static_cast<UINT8>(((ch >> 6) & 0x3f) | 0x80));
+ buffer.push_back(static_cast<UINT8>(((ch)& 0x3f) | 0x80));
+ }
+ else
+ {
+ buffer.push_back(static_cast<UINT8>(((ch >> 12)) | 0xe0));
+ buffer.push_back(static_cast<UINT8>(((ch >> 6) & 0x3f) | 0x80));
+ buffer.push_back(static_cast<UINT8>(((ch)& 0x3f) | 0x80));
+ }
+ }
+ }
+
+#pragma warning(pop)
+
+ inline ::tld::Type MakeType(
+ ::tld::InType inType,
+ ::tld::OutType outType)
+ {
+ return _tld_MAKE_TYPE(inType & InTypeMask, outType & OutTypeMask);
+ }
+
+ _Use_decl_annotations_
+ inline GUID GetGuidForName(
+ wchar_t const* szUtf16Name)
+ {
+ return ::tld::detail::GetGuidForNameImpl(szUtf16Name);
+ }
+
+ _Use_decl_annotations_
+ inline GUID GetGuidForName(
+ char const* szUtf8Name)
+ {
+ return ::tld::detail::GetGuidForNameImpl(szUtf8Name);
+ }
+
+ inline HRESULT RegisterProvider(
+ _Out_ REGHANDLE* phProvider,
+ _In_ GUID const* pProviderId,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_opt_ PENABLECALLBACK pEnableCallback,
+ _In_opt_ void* pEnableCallbackContext)
+ {
+ HRESULT hr;
+ REGHANDLE hProvider = 0;
+ ULONG status = ::EventRegister(
+ pProviderId,
+ pEnableCallback,
+ pEnableCallbackContext,
+ &hProvider);
+ if (status == 0)
+ {
+ auto cbMetadata = *reinterpret_cast<UINT16 const*>(pProviderMetadata);
+ status = tld::SetInformation(
+ hProvider,
+ static_cast<EVENT_INFO_CLASS>(2), // EventProviderSetTraits
+ const_cast<UINT8*>(pProviderMetadata),
+ cbMetadata);
+ hr = S_OK; // Ignore any failures from SetInformation.
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(status);
+ }
+
+ *phProvider = hProvider;
+ return hr;
+ }
+
+ inline HRESULT UnregisterProvider(
+ REGHANDLE hProvider)
+ {
+ ULONG status = ::EventUnregister(hProvider);
+ return HRESULT_FROM_WIN32(status);
+ }
+
+ _Use_decl_annotations_
+ inline HRESULT SetInformation(
+ REGHANDLE hProvider,
+ EVENT_INFO_CLASS informationClass,
+ void* pvInformation,
+ ULONG cbInformation)
+ {
+ ULONG status;
+
+#if TLD_HAVE_EVENT_SET_INFORMATION == 1
+
+#pragma warning(suppress: 6387) // It's ok for pvInformation to be null if cbInformation is 0.
+ status = ::EventSetInformation(
+ hProvider,
+ informationClass,
+ pvInformation,
+ cbInformation);
+
+#elif TLD_HAVE_EVENT_SET_INFORMATION == 2
+
+ HMODULE hEventing;
+ status = ERROR_NOT_SUPPORTED;
+ if (GetModuleHandleExW(0, L"api-ms-win-eventing-provider-l1-1-0", &hEventing) ||
+ GetModuleHandleExW(0, L"advapi32.dll", &hEventing))
+ {
+ typedef ULONG(WINAPI* PFEventSetInformation)(
+ _In_ REGHANDLE RegHandle,
+ _In_ EVENT_INFO_CLASS InformationClass,
+ _In_reads_bytes_opt_(InformationLength) PVOID EventInformation,
+ _In_ ULONG InformationLength);
+ PFEventSetInformation pfEventSetInformation =
+ (PFEventSetInformation)GetProcAddress(hEventing, "EventSetInformation");
+ if (pfEventSetInformation)
+ {
+ status = pfEventSetInformation(
+ hProvider,
+ informationClass,
+ pvInformation,
+ cbInformation);
+ }
+
+ FreeLibrary(hEventing);
+ }
+
+#else // TLD_HAVE_EVENT_SET_INFORMATION == 0
+
+ (void)hProvider;
+ (void)informationClass;
+ (void)pvInformation;
+ (void)cbInformation;
+
+ status = ERROR_NOT_SUPPORTED;
+
+#endif // TLD_HAVE_EVENT_SET_INFORMATION
+
+ return HRESULT_FROM_WIN32(status);
+ }
+
+ _Use_decl_annotations_
+ inline HRESULT WriteEvent(
+ REGHANDLE hProvider,
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ UINT8 const* pProviderMetadata,
+ UINT8 const* pEventMetadata,
+ ULONG cDataDescriptors,
+ EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId)
+ {
+ _tld_ASSERT(3 <= *(UINT16*)pProviderMetadata, "Invalid provider metadata.");
+ _tld_ASSERT(4 <= *(UINT16*)pEventMetadata, "Invalid event metadata.");
+ pDataDescriptors[0].Ptr = (ULONG_PTR)pProviderMetadata;
+ pDataDescriptors[0].Size = *(UINT16*)pProviderMetadata;
+ pDataDescriptors[0].Reserved = 2; // Descriptor contains provider metadata.
+ pDataDescriptors[1].Ptr = (ULONG_PTR)pEventMetadata;
+ pDataDescriptors[1].Size = *(UINT16*)pEventMetadata;
+ pDataDescriptors[1].Reserved = 1; // Descriptor contains event metadata.
+ ULONG status = ::EventWriteTransfer(
+ hProvider,
+ &eventDescriptor,
+ pActivityId,
+ pRelatedActivityId,
+ cDataDescriptors,
+ pDataDescriptors);
+ return HRESULT_FROM_WIN32(status);
+ }
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ _Use_decl_annotations_
+ inline HRESULT WriteEventEx(
+ REGHANDLE hProvider,
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ UINT8 const* pProviderMetadata,
+ UINT8 const* pEventMetadata,
+ ULONG cDataDescriptors,
+ EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ ULONG64 filter,
+ ULONG flags,
+ GUID const* pActivityId,
+ GUID const* pRelatedActivityId)
+ {
+ _tld_ASSERT(3 <= *(UINT16*)pProviderMetadata, "Invalid provider metadata.");
+ _tld_ASSERT(4 <= *(UINT16*)pEventMetadata, "Invalid event metadata.");
+ pDataDescriptors[0].Ptr = (ULONG_PTR)pProviderMetadata;
+ pDataDescriptors[0].Size = *(UINT16*)pProviderMetadata;
+ pDataDescriptors[0].Reserved = 2; // Descriptor contains provider metadata.
+ pDataDescriptors[1].Ptr = (ULONG_PTR)pEventMetadata;
+ pDataDescriptors[1].Size = *(UINT16*)pEventMetadata;
+ pDataDescriptors[1].Reserved = 1; // Descriptor contains event metadata.
+ ULONG status = ::EventWriteEx(
+ hProvider,
+ &eventDescriptor,
+ filter,
+ flags,
+ pActivityId,
+ pRelatedActivityId,
+ cDataDescriptors,
+ pDataDescriptors);
+ return HRESULT_FROM_WIN32(status);
+ }
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+#pragma endregion
+
+#pragma region ByteArrayWrapper
+
+ class ByteArrayWrapper
+ {
+ UINT8* m_pb;
+ UINT32 m_cbMax;
+ UINT32 m_cbCur;
+
+ public:
+
+ ByteArrayWrapper()
+ {
+ reset();
+ }
+
+ ByteArrayWrapper(
+ _Out_writes_(cbMax) UINT8* pb,
+ UINT32 cbMax)
+ {
+ reset(pb, cbMax);
+ }
+
+ void reset()
+ {
+ m_pb = 0;
+ m_cbMax = 0;
+ m_cbCur = 0;
+ }
+
+ void reset(
+ _Inout_updates_bytes_(cbMax) UINT8* pb,
+ UINT32 cbMax)
+ {
+ m_pb = pb;
+ m_cbMax = cbMax;
+ m_cbCur = 0;
+ }
+
+ void clear()
+ {
+ m_cbCur = 0;
+ }
+
+ UINT32 size() const
+ {
+ return m_cbCur;
+ }
+
+ BYTE const& front() const
+ {
+ return *m_pb;
+ }
+
+ BYTE const* data() const
+ {
+ return m_pb;
+ }
+
+ UINT32 capacity() const
+ {
+ return m_cbMax;
+ }
+
+ void push_back(UINT8 val)
+ {
+ if (m_cbCur < m_cbMax)
+ {
+ m_pb[m_cbCur] = val;
+ }
+
+ m_cbCur += 1;
+ }
+
+ UINT8& operator[](UINT32 index)
+ {
+ static UINT8 dummy;
+ return index < m_cbMax
+ ? m_pb[index]
+ : dummy;
+ }
+ };
+
+#pragma endregion
+
+#pragma region ProviderMetadataBuilder
+
+ template<class ByteBufferTy>
+ class ProviderMetadataBuilder
+ : public detail::MetadataBuilderBase<ByteBufferTy>
+ {
+ /*
+ ProviderMetadata =
+ Size : UINT16; // Size counts the whole data structure, including the Size field.
+ Name : Nul-terminated UTF-8.
+ Traits[]: ProviderTrait (parse until you have consumed Size bytes)
+
+ ProviderTrait =
+ TraitSize: UINT16;
+ TraitType: UINT8;
+ TraitData: UINT8[TraitSize - 3];
+ */
+
+ public:
+
+ explicit ProviderMetadataBuilder(ByteBufferTy& buffer)
+ : detail::MetadataBuilderBase<ByteBufferTy>(buffer)
+ {
+ return;
+ }
+
+ template<class CharTy>
+ void Begin(
+ _In_z_ CharTy const* szUtfProviderName)
+ {
+ this->BaseBegin();
+ this->AddString(szUtfProviderName);
+ }
+
+ void AddTrait(
+ ProviderTraitType type,
+ _In_reads_bytes_(cbData) void const* pData,
+ unsigned cbData)
+ {
+ this->AddU16(static_cast<UINT16>(cbData + 3));
+ this->AddU8(static_cast<UINT8>(type));
+ this->AddBytes(pData, cbData);
+ }
+
+ bool End()
+ {
+ return this->BaseEnd();
+ }
+ };
+
+#pragma endregion
+
+#pragma region EventMetadataBuilder
+
+ template<class ByteBufferTy>
+ class EventMetadataBuilder
+ : public detail::MetadataBuilderBase<ByteBufferTy>
+ {
+ /*
+ EventMetadata =
+ Size : UINT16; // Size counts the whole data structure, including the Size field.
+ Tags[] : UINT8 (read bytes until you encounter a byte with high bit unset);
+ Name : Nul-terminated UTF-8;
+ Fields[]: FieldMetadata (parse until you get to end of EventMetadata);
+
+ FieldMetadata =
+ Name : Nul-terminated UTF-8.
+ InMeta : UINT8.
+ OutMeta : UINT8 (only present if InMeta & InMetaChain);
+ Tags[] : UINT8 (only present if OutMeta & OutMetaChain, read bytes until you encounter a byte with high bit unset);
+ Ccount : UINT16 (only present if InMeta & InMetaCountMask == InMetaCcount);
+ CbSchema: UINT16 (only present if InMeta & InMetaCountMask == InMetaCustom);
+ Schema : UINT8[CbSchema]
+ */
+
+ static const UINT8 InMetaScalar = 0;
+ static const UINT8 InMetaCcount = 32;
+ static const UINT8 InMetaVcount = 64;
+ static const UINT8 InMetaCustom = 96;
+ static const UINT8 InMetaCountMask = 96;
+ static const UINT8 InMetaChain = 128;
+ static const UINT8 OutMetaChain = 128;
+
+ UINT32 const m_bookmark;
+
+ EventMetadataBuilder(
+ ByteBufferTy& buffer,
+ UINT32 bookmark)
+ : detail::MetadataBuilderBase<ByteBufferTy>(buffer)
+ , m_bookmark(bookmark)
+ {
+ return;
+ }
+
+ void AddTags(UINT32 tags)
+ {
+ _tld_ASSERT(0 == (tags & 0xf0000000), "Tags only supports 28-bit values.");
+ for (;;)
+ {
+ auto val = static_cast<UINT8>(tags >> 21);
+ if ((tags & 0x1fffff) == 0)
+ {
+ this->AddU8(val & 0x7f);
+ break;
+ }
+
+ this->AddU8(val | 0x80);
+ tags = tags << 7;
+ }
+ }
+
+ void AddFieldInfo(UINT8 arity, Type type, UINT32 tags)
+ {
+ _tld_ASSERT((type & InTypeMask) == (type & 0xff), "InType out of range");
+ _tld_ASSERT((type & _tld_MAKE_TYPE(0, OutTypeMask)) == (Type)(type & 0xffffff00), "OutType out of range");
+
+ UINT8 inMeta = arity | static_cast<UINT8>(type);
+ UINT8 outMeta = static_cast<UINT8>(type >> 8);
+ if (tags != 0)
+ {
+ this->AddU8(static_cast<UINT8>(inMeta | InMetaChain));
+ this->AddU8(static_cast<UINT8>(outMeta | OutMetaChain));
+ AddTags(tags);
+ }
+ else if (outMeta != 0)
+ {
+ this->AddU8(static_cast<UINT8>(inMeta | InMetaChain));
+ this->AddU8(static_cast<UINT8>(outMeta));
+ }
+ else
+ {
+ this->AddU8(inMeta);
+ }
+
+ if (m_bookmark != 0)
+ {
+ this->IncrementU7(m_bookmark);
+ }
+ }
+
+ UINT32 AddStructInfo(UINT8 arity, UINT32 tags)
+ {
+ UINT8 inMeta = arity | InTypeStruct;
+ this->AddU8(static_cast<UINT8>(inMeta | InMetaChain));
+ UINT32 bookmark = this->GetBookmark();
+ if (tags == 0)
+ {
+ this->AddU8(0);
+ }
+ else
+ {
+ this->AddU8(OutMetaChain);
+ AddTags(tags);
+ }
+
+ if (m_bookmark != 0)
+ {
+ this->IncrementU7(m_bookmark);
+ }
+
+ return bookmark;
+ }
+
+ public:
+
+ explicit EventMetadataBuilder(
+ ByteBufferTy& buffer)
+ : detail::MetadataBuilderBase<ByteBufferTy>(buffer)
+ , m_bookmark(0)
+ {
+ return;
+ }
+
+ EventMetadataBuilder(EventMetadataBuilder& other)
+ : detail::MetadataBuilderBase<ByteBufferTy>(other.GetBuffer())
+ , m_bookmark(other.m_bookmark)
+ {
+ return;
+ }
+
+ EventMetadataBuilder(EventMetadataBuilder&& other)
+ : detail::MetadataBuilderBase<ByteBufferTy>(other.GetBuffer())
+ , m_bookmark(other.m_bookmark)
+ {
+ return;
+ }
+
+ template<class CharTy>
+ void Begin(
+ _In_z_ CharTy const* szUtfEventName,
+ UINT32 eventTags = 0)
+ {
+ _tld_ASSERT(m_bookmark == 0, "Must not call Begin on a struct builder");
+ this->BaseBegin();
+ AddTags(eventTags);
+ this->AddString(szUtfEventName);
+ }
+
+ bool End()
+ {
+ _tld_ASSERT(m_bookmark == 0, "Must not call End on a struct builder");
+ return this->BaseEnd();
+ }
+
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ EventMetadataBuilder AddStruct(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaScalar, fieldTags);
+ return EventMetadataBuilder(this->GetBuffer(), bookmark);
+ }
+
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ UINT32 AddStructRaw(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaScalar, fieldTags);
+ return bookmark;
+ }
+
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ EventMetadataBuilder AddStructArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaVcount, fieldTags);
+ return EventMetadataBuilder(this->GetBuffer(), bookmark);
+ }
+
+ // Note: Do not use 0 for itemCount.
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ EventMetadataBuilder AddStructFixedArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT16 itemCount,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaCcount, fieldTags);
+ this->AddU16(itemCount);
+ return EventMetadataBuilder(this->GetBuffer(), bookmark);
+ }
+
+ template<class CharTy>
+ void AddField(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaScalar, type, fieldTags);
+ }
+
+ template<class CharTy>
+ void AddFieldArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaVcount, type, fieldTags);
+ }
+
+ // Note: Do not use 0 for itemCount.
+ template<class CharTy>
+ void AddFieldFixedArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ UINT16 itemCount,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaCcount, type, fieldTags);
+ this->AddU16(itemCount);
+ }
+
+ template<class CharTy>
+ void AddFieldCustom(
+ _In_z_ CharTy const* szUtfFieldName,
+ _In_reads_bytes_(cbSchema) void const* pSchema,
+ UINT16 cbSchema,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaCustom, type, fieldTags);
+ this->AddU16(cbSchema);
+ auto pbSchema = static_cast<UINT8 const*>(pSchema);
+ for (UINT32 i = 0; i != cbSchema; i++)
+ {
+ this->AddU8(pbSchema[i]);
+ }
+ }
+ };
+
+#pragma endregion
+
+#pragma region EventDataBuilder
+
+ template<class ByteBufferTy>
+ class EventDataBuilder
+ {
+ void operator=(EventDataBuilder const&); // = delete
+ ByteBufferTy& m_buffer;
+
+ public:
+
+ explicit EventDataBuilder(ByteBufferTy& buffer)
+ : m_buffer(buffer)
+ {
+ return;
+ }
+
+ EventDataBuilder(EventDataBuilder& other)
+ : m_buffer(other.m_buffer)
+ {
+ return;
+ }
+
+ EventDataBuilder(EventDataBuilder&& other)
+ : m_buffer(other.m_buffer)
+ {
+ return;
+ }
+
+ ByteBufferTy& Buffer()
+ {
+ return m_buffer;
+ }
+
+ ByteBufferTy const& Buffer() const
+ {
+ return m_buffer;
+ }
+
+ void Clear()
+ {
+ m_buffer.clear();
+ }
+
+ UINT32 Size() const
+ {
+ return static_cast<UINT32>(m_buffer.size());
+ }
+
+ UINT8& operator[](UINT32 index)
+ {
+ return m_buffer[index];
+ }
+
+ void AddArrayCount(UINT16 cItems)
+ {
+ AddBytes(&cItems, sizeof(cItems));
+ }
+
+ /*
+ Adds a value to the payload.
+ Note: should only be used for blittable POD types with no padding.
+ */
+ template<class T>
+ void AddValue(T const& value)
+ {
+ AddBytes(&reinterpret_cast<const char&>(value), sizeof(T));
+ }
+
+ /*
+ Adds an array of values to the payload.
+ Note: should only be used for blittable POD types with no padding.
+ */
+ template<class T>
+ void AddValues(_In_reads_(cValues) T const* pValues, unsigned cValues)
+ {
+ AddBytes(pValues, sizeof(T) * cValues);
+ }
+
+ /*
+ Appends an InTypeBinary field to the payload.
+ Compatible with: TypeBinary, TypeIPv6, TypeSocketAddress.
+ */
+ void AddBinary(_In_reads_bytes_(cbData) void const* pbData, UINT16 cbData)
+ {
+ AddBytes(&cbData, sizeof(cbData));
+ AddBytes(pbData, cbData);
+ }
+
+ /*
+ Appends an InTypeCountedAnsiString field to the payload.
+ Compatible with: TypeCountedMbcsString, TypeCountedMbcsXml, TypeCountedMbcsJson.
+ */
+ void AddCountedString(_In_reads_(cch) char const* pch, UINT16 cch)
+ {
+ AddBinary(pch, cch * sizeof(*pch));
+ }
+
+ /*
+ Appends an InTypeCountedString field to the payload.
+ Compatible with: TypeCountedUtf16String, TypeCountedUtf16Xml, TypeCountedUtf16Json.
+ */
+ void AddCountedString(_In_reads_(cch) wchar_t const* pch, UINT16 cch)
+ {
+ AddBinary(pch, cch * sizeof(*pch));
+ }
+
+ /*
+ Appends an InTypeAnsiString field to the payload.
+ Compatible with: TypeMbcsString, TypeMbcsXml, TypeMbcsJson.
+ */
+ void AddString(_In_z_ char const* psz)
+ {
+ AddBytes(psz, static_cast<unsigned>(strlen(psz) + 1));
+ }
+
+ /*
+ Appends a InTypeUnicodeString field to the payload.
+ Compatible with: TypeUtf16String, TypeUtf16Xml, TypeUtf16Json
+ */
+ void AddString(_In_z_ wchar_t const* psz)
+ {
+ AddBytes(psz, static_cast<unsigned>(2 * (wcslen(psz) + 1)));
+ }
+
+ /*
+ Appends a raw byte to the payload. (Calls buffer.push_back)
+ */
+ void AddByte(UINT8 val)
+ {
+ m_buffer.push_back(val);
+ }
+
+ /*
+ Appends raw bytes to the payload. (Calls buffer.insert)
+ */
+ void AddBytes(_In_reads_bytes_(cbData) void const* pbData, unsigned cbData)
+ {
+ auto pb = static_cast<UINT8 const*>(pbData);
+ m_buffer.insert(m_buffer.end(), pb + 0, pb + cbData);
+ }
+ };
+
+#pragma endregion
+
+#pragma region Provider
+
+ class Provider
+ {
+ Provider(Provider const&); // = delete
+ void operator=(Provider const&); // = delete
+
+ UINT32 m_levelPlus1;
+ HRESULT m_hrInitialization;
+ ULONGLONG m_keywordsAny;
+ ULONGLONG m_keywordsAll;
+ REGHANDLE m_hProvider;
+ UINT8 const* m_pbMetadata;
+ PENABLECALLBACK m_pfCallback;
+ LPVOID m_pCallbackContext;
+ GUID m_id;
+
+ /*
+ Shortest possible valid provider metadata blob:
+ Size = 0x0003, Name = "", no options.
+ */
+ static _Ret_bytecount_(3) UINT8 const* NullMetadata()
+ {
+ return reinterpret_cast<UINT8 const*>("\03\0");
+ }
+
+ static VOID NTAPI EnableCallback(
+ _In_ LPCGUID pSourceId,
+ _In_ ULONG callbackType,
+ _In_ UCHAR level,
+ _In_ ULONGLONG keywordsAny,
+ _In_ ULONGLONG keywordsAll,
+ _In_opt_ PEVENT_FILTER_DESCRIPTOR pFilterData,
+ _Inout_opt_ PVOID pCallbackContext)
+ {
+ if (pCallbackContext)
+ {
+ Provider* pProvider = static_cast<Provider*>(pCallbackContext);
+ switch (callbackType)
+ {
+ case 0: // EVENT_CONTROL_CODE_DISABLE_PROVIDER
+ pProvider->m_levelPlus1 = 0;
+ break;
+ case 1: // EVENT_CONTROL_CODE_ENABLE_PROVIDER
+ pProvider->m_levelPlus1 = level != 0 ? (UINT32)level + 1u : 256u;
+ pProvider->m_keywordsAny = keywordsAny;
+ pProvider->m_keywordsAll = keywordsAll;
+ break;
+ }
+
+ if (pProvider->m_pfCallback)
+ {
+ pProvider->m_pfCallback(
+ pSourceId,
+ callbackType,
+ level,
+ keywordsAny,
+ keywordsAll,
+ pFilterData,
+ pProvider->m_pCallbackContext);
+ }
+ }
+ }
+
+ _Ret_opt_bytecount_(cbMetadata)
+ static UINT8* MetadataAlloc(
+ UINT32 cbMetadata)
+ {
+ return static_cast<UINT8*>(HeapAlloc(GetProcessHeap(), 0, cbMetadata));
+ }
+
+ static void MetadataFree(
+ _Post_invalid_ UINT8* pbMetadata)
+ {
+ if (pbMetadata)
+ {
+ HeapFree(GetProcessHeap(), 0, pbMetadata);
+ }
+ }
+
+ void InitFail(
+ HRESULT hr)
+ {
+ _tld_ASSERT(m_pbMetadata != NullMetadata(), "InitFail called with m_pbMetadata == NullMetadata()");
+ MetadataFree(const_cast<UINT8*>(m_pbMetadata));
+ m_hrInitialization = hr;
+ m_pbMetadata = NullMetadata();
+ }
+
+ template<class CharTy>
+ void Init(
+ _In_z_ CharTy const* szName,
+ _In_opt_ GUID const* pGroupId)
+ {
+ // Already initialized: m_pfCallback, m_pCallbackContext, m_id.
+ m_levelPlus1 = 0;
+ m_hrInitialization = 0;
+ m_keywordsAny = 0;
+ m_keywordsAll = 0;
+ m_hProvider = 0;
+ m_pbMetadata = 0;
+
+ ByteArrayWrapper wrapper(0, 0);
+ ProviderMetadataBuilder<ByteArrayWrapper> builder(wrapper);
+ builder.Begin(szName);
+ if (pGroupId)
+ {
+ builder.AddTrait(ProviderTraitGroupGuid, pGroupId, sizeof(GUID));
+ }
+ if (!builder.End())
+ {
+ InitFail(E_INVALIDARG); // Metadata > 64KB.
+ goto Done;
+ }
+
+ {
+ UINT32 const cbMetadata = wrapper.size();
+ UINT8* pbMetadata = MetadataAlloc(cbMetadata);
+ if (!pbMetadata)
+ {
+ InitFail(E_OUTOFMEMORY);
+ goto Done;
+ }
+
+ m_pbMetadata = pbMetadata;
+ wrapper.reset(pbMetadata, cbMetadata);
+ builder.Begin(szName);
+ if (pGroupId)
+ {
+ builder.AddTrait(ProviderTraitGroupGuid, pGroupId, sizeof(GUID));
+ }
+ if (!builder.End() || cbMetadata < wrapper.size())
+ {
+ InitFail(0x8000000CL); // E_CHANGED_STATE
+ goto Done;
+ }
+
+ {
+ HRESULT hr = ::tld::RegisterProvider(&m_hProvider, &m_id, m_pbMetadata, &EnableCallback, this);
+ if (FAILED(hr))
+ {
+ InitFail(hr);
+ }
+ }
+ }
+
+ Done:
+
+ return;
+ }
+
+ bool IsEnabledForKeywords(ULONGLONG keywords) const
+ {
+ return keywords == 0 || (
+ (keywords & m_keywordsAny) != 0 &&
+ (keywords & m_keywordsAll) == m_keywordsAll);
+ }
+
+ public:
+
+ ~Provider()
+ {
+ EventUnregister(m_hProvider);
+ if (m_pbMetadata != NullMetadata())
+ {
+ MetadataFree(const_cast<UINT8*>(m_pbMetadata));
+ }
+ }
+
+ Provider(
+ _In_z_ wchar_t const* szUtf16Name,
+ GUID const& providerId,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(providerId)
+ {
+ Init(szUtf16Name, pGroupId);
+ }
+
+ Provider(
+ _In_z_ char const* szUtf8Name,
+ GUID const& providerId,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(providerId)
+ {
+ Init(szUtf8Name, pGroupId);
+ }
+
+ explicit Provider(
+ _In_z_ wchar_t const* szUtf16Name,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(GetGuidForName(szUtf16Name))
+ {
+ Init(szUtf16Name, pGroupId);
+ }
+
+ explicit Provider(
+ _In_z_ char const* szUtf8Name,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(GetGuidForName(szUtf8Name))
+ {
+ Init(szUtf8Name, pGroupId);
+ }
+
+ HRESULT InitializationResult() const
+ {
+ return m_hrInitialization;
+ }
+
+ _Ret_z_ char const* Name() const
+ {
+ return reinterpret_cast<char const*>(m_pbMetadata + 2);
+ }
+
+ GUID const& Id() const
+ {
+ return m_id;
+ }
+
+ _Ret_notnull_ UINT8 const* GetMetadata() const
+ {
+ return m_pbMetadata;
+ }
+
+ UINT16 GetMetadataSize() const
+ {
+ return *reinterpret_cast<UINT16 const*>(m_pbMetadata);
+ }
+
+ bool IsEnabled() const
+ {
+ return m_levelPlus1 != 0;
+ }
+
+ bool IsEnabled(UCHAR level) const
+ {
+ return level < m_levelPlus1;
+ }
+
+ bool IsEnabled(UCHAR level, ULONGLONG keywords) const
+ {
+ return level < m_levelPlus1 && IsEnabledForKeywords(keywords);
+ }
+
+ HRESULT SetInformation(
+ EVENT_INFO_CLASS informationClass,
+ _In_reads_bytes_opt_(cbInformation) void* pbInformation,
+ ULONG cbInformation
+ ) const
+ {
+ return ::tld::SetInformation(m_hProvider, informationClass, pbInformation, cbInformation);
+ }
+
+ HRESULT WriteGather(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ return ::tld::WriteEvent(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cDataDescriptors,
+ pDataDescriptors,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+ HRESULT Write(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_reads_bytes_(cbData) LPCVOID pbData,
+ UINT32 cbData,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ EventDataDescCreate(dataDesc + 2, pbData, cbData);
+ return ::tld::WriteEvent(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cbData ? 3 : 2,
+ dataDesc,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ HRESULT WriteGatherEx(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ ULONG64 filter,
+ ULONG flags,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ return ::tld::WriteEventEx(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cDataDescriptors,
+ pDataDescriptors,
+ filter,
+ flags,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+ HRESULT WriteEx(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_reads_bytes_(cbData) LPCVOID pbData,
+ UINT32 cbData,
+ ULONG64 filter,
+ ULONG flags,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ EventDataDescCreate(dataDesc + 2, pbData, cbData);
+ return ::tld::WriteEventEx(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cbData ? 3 : 2,
+ dataDesc,
+ filter,
+ flags,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+ };
+
+#pragma endregion
+
+#pragma region EventBuilder
+
+ enum EventState
+ {
+ EventStateOpen,
+ EventStateClosed,
+ EventStateError
+ };
+
+ template<class ByteBufferTy>
+ class EventBuilder
+ {
+ void operator=(EventBuilder const&); // = delete
+
+ EventMetadataBuilder<ByteBufferTy> m_metaBuilder;
+ EventDataBuilder<ByteBufferTy> m_dataBuilder;
+ EventState& m_eventState;
+
+ EventBuilder(EventBuilder& other, EventMetadataBuilder<ByteBufferTy>&& metaBuilder)
+ : m_metaBuilder(metaBuilder)
+ , m_dataBuilder(other.m_dataBuilder)
+ , m_eventState(other.m_eventState)
+ {
+ return;
+ }
+
+ protected:
+
+ EventBuilder(
+ ByteBufferTy& metaBuffer,
+ ByteBufferTy& dataBuffer,
+ EventState& eventState)
+ : m_metaBuilder(metaBuffer)
+ , m_dataBuilder(dataBuffer)
+ , m_eventState(eventState)
+ {
+ return;
+ }
+
+ public:
+
+ EventBuilder(EventBuilder& other)
+ : m_metaBuilder(other.m_metaBuilder)
+ , m_dataBuilder(other.m_dataBuilder)
+ , m_eventState(other.m_eventState)
+ {
+ return;
+ }
+
+ EventBuilder(EventBuilder&& other)
+ : m_metaBuilder(other.m_metaBuilder)
+ , m_dataBuilder(other.m_dataBuilder)
+ , m_eventState(other.m_eventState)
+ {
+ return;
+ }
+
+ /*
+ Returns a const reference to the event metadata's underlying buffer.
+ */
+ ByteBufferTy const& MetaBuffer() const
+ {
+ return m_metaBuilder.GetBuffer();
+ }
+
+ /*
+ Returns a const reference to the event payload data's underlying buffer.
+ */
+ ByteBufferTy const& DataBuffer() const
+ {
+ return m_dataBuilder.Buffer();
+ }
+
+ /*
+ Adds a nested struct to the event's metadata.
+ Use the returned EventBuilder to add data and metadata for the fields
+ of the nested struct.
+ Note: do not call any Add methods on this builder object until you are
+ done calling Add methods on the nested builder object.
+ Note: Do not create structs with 0 fields.
+ */
+ template<class CharTy>
+ EventBuilder AddStruct(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ return EventBuilder(*this, m_metaBuilder.AddStruct(szUtfStructName, fieldTags));
+ }
+
+ /*
+ Adds a nested array of struct to the event's metadata.
+ Use the returned EventBuilder to add data and metadata for the fields
+ of the nested struct.
+ Note: do not call any Add methods on this builder object until you are
+ done calling Add methods on the nested builder object.
+ Note: Do not create structs with 0 fields.
+ */
+ template<class CharTy>
+ EventBuilder AddStructArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ return EventBuilder(*this, m_metaBuilder.AddStructArray(szUtfStructName, fieldTags));
+ }
+
+ /*
+ Adds a nested array of struct to the event's metadata.
+ Use the returned EventBuilder to add data and metadata for the fields
+ of the nested struct.
+ Note: do not call any Add methods on this builder object until you are
+ done calling Add methods on the nested builder object.
+ Note: Do not use 0 for itemCount.
+ Note: Do not create structs with 0 fields.
+ */
+ template<class CharTy>
+ EventBuilder AddStructFixedArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT16 itemCount,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ return EventBuilder(*this, m_metaBuilder.AddStructFixedArray(szUtfStructName, itemCount, fieldTags));
+ }
+
+ /*
+ Adds a scalar field to the event's metadata.
+ */
+ template<class CharTy>
+ void AddField(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddField(szUtfFieldName, type, fieldTags);
+ }
+
+ /*
+ Adds a variable-length array field to the event's metadata.
+ The length (item count) must be added to the event's payload
+ immediately before the array item values.
+ */
+ template<class CharTy>
+ void AddFieldArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddFieldArray(szUtfFieldName, type, fieldTags);
+ }
+
+ /*
+ Adds a fixed-length array field to the event's metadata.
+ The length (item count) is encoded in the metadata, so it does not
+ need to be included in the event's payload.
+ Note: Do not use 0 for itemCount.
+ */
+ template<class CharTy>
+ void AddFieldFixedArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ UINT16 itemCount,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddFieldFixedArray(szUtfFieldName, itemCount, type, fieldTags);
+ }
+
+ /*
+ Adds a custom-encoded field to the event's metadata.
+ Custom-encoded means that you are using some other serialization
+ system (e.g. Bond, Protocol Buffers) to produce the schema and data.
+ The payload is encoded as a UINT16 byte count followed by the data.
+ */
+ template<class CharTy>
+ void AddFieldCustom(
+ _In_z_ CharTy const* szUtfFieldName,
+ _In_reads_bytes_(cbSchema) void const* pSchema,
+ UINT16 cbSchema,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddFieldCustom(szUtfFieldName, pSchema, cbSchema, type, fieldTags);
+ }
+
+ /*
+ Advanced use: adds raw metadata bytes.
+ */
+ void AddRawMetadata(
+ _In_reads_bytes_(cbMetadata) void const* pMetadata,
+ UINT32 cbMetadata)
+ {
+ auto& buf = m_metaBuilder.GetBuffer();
+ auto pb = static_cast<BYTE const*>(pMetadata);
+ buf.insert(buf.end(), pb + 0u, pb + cbMetadata);
+ }
+
+ /*
+ Adds an array length (item count) to the payload. This is for
+ variable-length arrays only (not fixed-length), and must occur
+ immediately before the array item values are written to the payload.
+ */
+ void AddArrayCount(UINT16 cItems)
+ {
+ m_dataBuilder.AddArrayCount(cItems);
+ }
+
+ /*
+ Adds a value to the payload.
+ Note: should only be used for blittable POD types with no padding,
+ e.g. INT32, FILETIME, GUID, not for strings or structs.
+ */
+ template<class T>
+ void AddValue(T const& value)
+ {
+ m_dataBuilder.AddValue(value);
+ }
+
+ /*
+ Adds an array of values to the payload.
+ Note: should only be used for blittable POD types with no padding,
+ e.g. INT32, FILETIME, GUID, not for strings or structs.
+ */
+ template<class T>
+ void AddValues(_In_reads_(cValues) T const* pValues, unsigned cValues)
+ {
+ m_dataBuilder.AddValues(pValues, cValues);
+ }
+
+ /*
+ Appends an InTypeBinary field to the payload.
+ Compatible with: TypeBinary, TypeIPv6, TypeSocketAddress.
+ */
+ void AddBinary(_In_reads_bytes_(cbData) void const* pbData, UINT16 cbData)
+ {
+ m_dataBuilder.AddBinary(pbData, cbData);
+ }
+
+ /*
+ Appends an InTypeCountedAnsiString field to the payload.
+ Compatible with: TypeCountedMbcsString, TypeCountedMbcsXml, TypeCountedMbcsJson.
+ */
+ void AddCountedString(_In_reads_(cch) char const* pch, UINT16 cch)
+ {
+ m_dataBuilder.AddCountedString(pch, cch);
+ }
+
+ /*
+ Appends an InTypeCountedString field to the payload.
+ Compatible with: TypeCountedUtf16String, TypeCountedUtf16Xml, TypeCountedUtf16Json.
+ */
+ void AddCountedString(_In_reads_(cch) wchar_t const* pch, UINT16 cch)
+ {
+ m_dataBuilder.AddCountedString(pch, cch);
+ }
+
+ /*
+ Appends an InTypeAnsiString field to the payload.
+ Compatible with: TypeMbcsString, TypeMbcsXml, TypeMbcsJson.
+ */
+ void AddString(_In_z_ char const* psz)
+ {
+ m_dataBuilder.AddString(psz);
+ }
+
+ /*
+ Appends a InTypeUnicodeString field to the payload.
+ Compatible with: TypeUtf16String, TypeUtf16Xml, TypeUtf16Json
+ */
+ void AddString(_In_z_ wchar_t const* psz)
+ {
+ m_dataBuilder.AddString(psz);
+ }
+
+ /*
+ Appends a raw byte to the payload.
+ */
+ void AddByte(UINT8 val)
+ {
+ m_dataBuilder.AddByte(val);
+ }
+
+ /*
+ Appends raw bytes to the payload.
+ */
+ void AddBytes(_In_reads_bytes_(cbData) void const* pbData, unsigned cbData)
+ {
+ m_dataBuilder.AddBytes(pbData, cbData);
+ }
+ };
+
+#pragma endregion
+
+#pragma region Event
+
+ template<class ByteBufferTy>
+ class Event
+ : public EventBuilder<ByteBufferTy>
+ {
+ Event(Event const&); // = delete
+ void operator=(Event const&); // = delete
+
+ ByteBufferTy m_metaBuffer;
+ ByteBufferTy m_dataBuffer;
+ EventDescriptor m_descriptor;
+ GUID const* m_pActivityId;
+ GUID const* m_pRelatedActivityId;
+ EventState m_state;
+
+ public:
+
+ Event(
+ _In_z_ char const* szUtf8Name,
+ UCHAR level = 5,
+ ULONGLONG keywords = 0,
+ UINT32 tags = 0,
+ UCHAR opcode = 0,
+ USHORT task = 0,
+ GUID const* pActivityId = 0,
+ GUID const* pRelatedActivityId = 0)
+ : EventBuilder<ByteBufferTy>(m_metaBuffer, m_dataBuffer, m_state)
+ , m_descriptor(level, keywords, opcode, task)
+ , m_pActivityId(pActivityId)
+ , m_pRelatedActivityId(pRelatedActivityId)
+ , m_state(EventStateOpen)
+ {
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).Begin(szUtf8Name, tags);
+ }
+
+ Event(
+ _In_z_ wchar_t const* szUtf16Name,
+ UCHAR level = 5,
+ ULONGLONG keywords = 0,
+ UINT32 tags = 0,
+ UCHAR opcode = 0,
+ USHORT task = 0,
+ GUID const* pActivityId = 0,
+ GUID const* pRelatedActivityId = 0)
+ : EventBuilder<ByteBufferTy>(m_metaBuffer, m_dataBuffer, m_state)
+ , m_descriptor(level, keywords, opcode, task)
+ , m_pActivityId(pActivityId)
+ , m_pRelatedActivityId(pRelatedActivityId)
+ , m_state(EventStateOpen)
+ {
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).Begin(szUtf16Name, tags);
+ }
+
+ /*
+ Clears the metadata and the payload.
+ */
+ template<class CharTy>
+ void Reset(
+ _In_z_ CharTy const* szUtfName,
+ UCHAR level = 5,
+ ULONGLONG keywords = 0,
+ UINT32 tags = 0,
+ UCHAR opcode = 0,
+ USHORT task = 0,
+ GUID* pActivityId = 0,
+ GUID* pRelatedActivityId = 0)
+ {
+ m_metaBuffer.clear();
+ m_dataBuffer.clear();
+ m_descriptor.Reset(level, keywords, opcode, task);
+ m_pActivityId = pActivityId;
+ m_pRelatedActivityId = pRelatedActivityId;
+ m_state = EventStateOpen;
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).Begin(szUtfName, tags);
+ }
+
+ /*
+ Clears the payload without clearing the metadata.
+ */
+ void ResetData()
+ {
+ m_dataBuffer.clear();
+ }
+
+ /*
+ Sends the event to ETW using EventWriteTransfer.
+ */
+ HRESULT Write(Provider const& provider)
+ {
+ HRESULT hr = E_INVALIDARG;
+ if (PrepareEvent())
+ {
+ hr = provider.Write(
+ m_descriptor,
+ &m_metaBuffer.front(),
+ m_dataBuffer.empty() ? NULL : &m_dataBuffer.front(),
+ static_cast<UINT32>(m_dataBuffer.size()),
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+ return hr;
+ }
+
+ /*
+ Sends the event to ETW using EventWriteTransfer.
+ */
+ HRESULT Write(
+ REGHANDLE hProvider,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata)
+ {
+ HRESULT hr = E_INVALIDARG;
+
+ if (PrepareEvent())
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ ULONG cDataDesc = 2;
+ if (!m_dataBuffer.empty())
+ {
+ EventDataDescCreate(
+ &dataDesc[cDataDesc++],
+ &m_dataBuffer.front(),
+ static_cast<ULONG>(m_dataBuffer.size()));
+ }
+
+ hr = ::tld::WriteEvent(
+ hProvider,
+ m_descriptor,
+ pProviderMetadata,
+ &m_metaBuffer.front(),
+ cDataDesc,
+ dataDesc,
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+
+ return hr;
+ }
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ /*
+ Sends the event to ETW using EventWriteEx.
+ */
+ HRESULT WriteEx(
+ Provider const& provider,
+ ULONG64 filter,
+ ULONG flags)
+ {
+ HRESULT hr = E_INVALIDARG;
+ if (PrepareEvent())
+ {
+ hr = provider.WriteEx(
+ m_descriptor,
+ &m_metaBuffer.front(),
+ m_dataBuffer.empty() ? NULL : &m_dataBuffer.front(),
+ static_cast<UINT32>(m_dataBuffer.size()),
+ filter,
+ flags,
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+ return hr;
+ }
+
+ /*
+ Sends the event to ETW using EventWriteEx.
+ */
+ HRESULT WriteEx(
+ REGHANDLE hProvider,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pProviderMetadata,
+ ULONG64 filter,
+ ULONG flags)
+ {
+ HRESULT hr = E_INVALIDARG;
+
+ if (PrepareEvent())
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ ULONG cDataDesc = 2;
+ if (!m_dataBuffer.empty())
+ {
+ EventDataDescCreate(
+ &dataDesc[cDataDesc++],
+ &m_dataBuffer.front(),
+ static_cast<ULONG>(m_dataBuffer.size()));
+ }
+
+ hr = ::tld::WriteEventEx(
+ hProvider,
+ m_descriptor,
+ pProviderMetadata,
+ &m_metaBuffer.front(),
+ cDataDesc,
+ dataDesc,
+ filter,
+ flags,
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+
+ return hr;
+ }
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+#pragma region Basic properties
+
+ UCHAR Channel() const
+ {
+ return m_descriptor.Channel;
+ }
+
+ /*
+ Note: the default channel is 11 (WINEVENT_CHANNEL_TRACELOGGING).
+ Other channels are only supported if the provider is running on
+ Windows 10 or later. If a provider is running on an earlier version
+ of Windows and it uses a channel other than 11, TDH will not be able
+ to decode the events.
+ */
+ void Channel(UCHAR channel)
+ {
+ m_descriptor.Channel = channel;
+ }
+
+ UCHAR Level() const
+ {
+ return m_descriptor.Level;
+ }
+
+ void Level(UCHAR value)
+ {
+ m_descriptor.Level = value;
+ }
+
+ ULONGLONG Keywords() const
+ {
+ return m_descriptor.Keyword;
+ }
+
+ void Keywords(ULONGLONG value)
+ {
+ m_descriptor.Keyword = value;
+ }
+
+ UCHAR Opcode() const
+ {
+ return m_descriptor.Opcode;
+ }
+
+ void Opcode(UCHAR value)
+ {
+ m_descriptor.Opcode = value;
+ }
+
+ USHORT Task() const
+ {
+ return m_descriptor.Task;
+ }
+
+ void Task(USHORT value)
+ {
+ m_descriptor.Task = value;
+ }
+
+ const GUID* ActivityId() const
+ {
+ return m_pActivityId;
+ }
+
+ void ActivityId(GUID* pValue)
+ {
+ m_pActivityId = pValue;
+ }
+
+ const GUID* RelatedActivityId() const
+ {
+ return m_pRelatedActivityId;
+ }
+
+ void RelatedActivityId(GUID* pValue)
+ {
+ m_pRelatedActivityId = pValue;
+ }
+
+ bool IsEnabledFor(Provider const& provider) const
+ {
+ return provider.IsEnabled(m_descriptor.Level, m_descriptor.Keyword);
+ }
+
+#pragma endregion
+
+ private:
+
+ bool PrepareEvent()
+ {
+ if (m_state == EventStateOpen)
+ {
+ PrepareEventImpl();
+ }
+ return m_state == EventStateClosed;
+ }
+
+ void PrepareEventImpl()
+ {
+ m_state =
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).End()
+ ? EventStateClosed
+ : EventStateError;
+ }
+ };
+
+#pragma endregion
+}
+// namespace tld
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h
new file mode 100644
index 000000000..60cac48a5
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h
@@ -0,0 +1,186 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#include <map>
+
+#include "opentelemetry/nostd/shared_ptr.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/nostd/unique_ptr.h"
+#include "opentelemetry/nostd/variant.h"
+#include "opentelemetry/trace/span_id.h"
+
+#include "opentelemetry/exporters/etw/etw_provider.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+/**
+ * @brief TelemetryProvider Options passed via SDK API.
+ */
+using TelemetryProviderOptions =
+ std::map<std::string, nostd::variant<std::string, uint64_t, float, bool>>;
+
+/**
+ * @brief TelemetryProvider runtime configuration class. Internal representation
+ * of TelemetryProviderOptions used by various components of SDK.
+ */
+typedef struct
+{
+ bool enableTraceId; // Set `TraceId` on ETW events
+ bool enableSpanId; // Set `SpanId` on ETW events
+ bool enableActivityId; // Assign `SpanId` to `ActivityId`
+ bool enableActivityTracking; // Emit TraceLogging events for Span/Start and Span/Stop Not used
+ // for Logs
+ bool enableRelatedActivityId; // Assign parent `SpanId` to `RelatedActivityId`
+ bool enableAutoParent; // Start new spans as children of current active span, Not used for Logs
+ ETWProvider::EventFormat
+ encoding; // Event encoding to use for this provider (TLD, MsgPack, XML, etc.).
+} TelemetryProviderConfiguration;
+
+/**
+ * @brief Helper template to convert a variant value from TelemetryProviderOptions to
+ * LoggerProviderConfiguration
+ *
+ * @param options TelemetryProviderOptions passed on API surface
+ * @param key Option name
+ * @param value Reference to destination value
+ * @param defaultValue Default value if option is not supplied
+ */
+template <typename T>
+static inline void GetOption(const TelemetryProviderOptions &options,
+ const char *key,
+ T &value,
+ T defaultValue)
+{
+ auto it = options.find(key);
+ if (it != options.end())
+ {
+ auto val = it->second;
+ value = nostd::get<T>(val);
+ }
+ else
+ {
+ value = defaultValue;
+ }
+}
+
+/**
+ * @brief Helper template to convert encoding config option to EventFormat.
+ * Configuration option passed as `options["encoding"] = "MsgPack"`.
+ * Default encoding is TraceLogging Dynamic Manifest (TLD).
+ *
+ * Valid encoding names listed below.
+ *
+ * For MessagePack encoding:
+ * - "MSGPACK"
+ * - "MsgPack"
+ * - "MessagePack"
+ *
+ * For XML encoding:
+ * - "XML"
+ * - "xml"
+ *
+ * For TraceLogging Dynamic encoding:
+ * - "TLD"
+ * - "tld"
+ *
+ */
+static inline ETWProvider::EventFormat GetEncoding(const TelemetryProviderOptions &options)
+{
+ ETWProvider::EventFormat evtFmt = ETWProvider::EventFormat::ETW_MANIFEST;
+
+ auto it = options.find("encoding");
+ if (it != options.end())
+ {
+ auto varValue = it->second;
+ std::string val = nostd::get<std::string>(varValue);
+
+#pragma warning(push)
+#pragma warning(disable : 4307) /* Integral constant overflow - OK while computing hash */
+ auto h = utils::hashCode(val.c_str());
+ 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 evtFmt;
+}
+
+/**
+ * @brief Utility template to obtain etw::TracerProvider._config or etw::LoggerProvider._config
+ *
+ * @tparam T etw::TracerProvider
+ * @param t etw::TracerProvider ref
+ * @return TelemetryProviderConfiguration ref
+ */
+template <class T>
+TelemetryProviderConfiguration &GetConfiguration(T &t)
+{
+ return t.config_;
+}
+
+/**
+ * @brief Utility template to convert SpanId or TraceId to hex.
+ * @param id - value of SpanId or TraceId
+ * @return Hexadecimal representation of Id as string.
+ */
+template <class T>
+static inline std::string ToLowerBase16(const T &id)
+{
+ char buf[2 * T::kSize] = {0};
+ id.ToLowerBase16(buf);
+ return std::string(buf, sizeof(buf));
+}
+
+/**
+ * @brief Utility method to convert span_id (8 byte) to ActivityId GUID (16 bytes)
+ * @param span OpenTelemetry Span Id object
+ * @return GUID struct containing 8-bytes of SpanId + 8 NUL bytes.
+ */
+static inline bool CopySpanIdToActivityId(const opentelemetry::trace::SpanId &span_id,
+ GUID &outGuid)
+{
+ memset(&outGuid, 0, sizeof(outGuid));
+ if (!span_id.IsValid())
+ {
+ return false;
+ }
+ auto spanId = span_id.Id().data();
+ uint8_t *guidPtr = reinterpret_cast<uint8_t *>(&outGuid);
+ for (size_t i = 0; i < 8; i++)
+ {
+ guidPtr[i] = spanId[i];
+ }
+ return true;
+}
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h
new file mode 100644
index 000000000..a2fd6c727
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h
@@ -0,0 +1,142 @@
+/* // Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <unordered_map>
+
+#include "opentelemetry/exporters/etw/utils.h"
+
+/* clang-format off */
+#ifdef CUSTOM_ETW_FIELDS_H
+/* Customers may redefine the default field names by including CUSTOM_ETW_FIELDS_H header */
+# include CUSTOM_ETW_FIELDS_H
+#else
+
+/**
+
+ List of configurable Field Name constants:
+
+ Version - Schema version (optional for ETW exporter).
+ _name - Built-in ETW name at envelope level (dedicated ETW field).
+ _time - Built-in ETW time at envelope level (dedicated ETW field).
+ SpanId - OT SpanId
+ TraceId - OT TraceId
+ StartTime - OT Span start time
+ Kind - OT Span kind
+ Name - OT Span name in ETW 'Payload["Name"]'
+ ParentId - OT Span parentId
+ Links - OT Span links array
+
+ Other standard fields (reserved names) that may be appended by ETW channel:
+
+ Level - a 1-byte integer that enables filtering based on the severity or verbosity of events
+ ProviderGuid - ETW Provider Guid
+ ProviderName - ETW Provider Name
+ OpcodeName - Name of Opcode (e.g. Start, Stop)
+ KeywordName - Name of Keyword
+ TaskName - TaskName, could be handled as an alias to Payload['name']
+ ChannelName - ETW Channel Name
+ EventMessage - ETW Event Message string for unstructured events
+ ActivityId - ActivityId for EventSource parenting (current event)
+ RelatedActivityId - RelatedActivityId for EventSource parenting (parent event)
+ Pid - Process Id
+ Tid - Thread Id
+
+ Example "Span" as shown in Visual Studio "Diagnostic Events" view. EventName="Span":
+
+ {
+ "Timestamp": "2021-04-01T00:33:25.5876605-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 20,
+ "Message": null,
+ "ProcessId": 10424,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "56f2366b-5475-496f-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 0,
+ "Name": "B.max",
+ "ParentId": "8ad900d0587fad4a",
+ "SpanId": "6b36f25675546f49",
+ "StartTime": "2021-04-01T07:33:25.587Z",
+ "TraceId": "8f8ac710c37c5a419f0fe574f335e986"
+ }
+ }
+
+ Example named Event on Span. Note that EventName="MyEvent2" in this case:
+
+ {
+ "Timestamp": "2021-04-01T00:33:22.5848789-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 15,
+ "Message": null,
+ "ProcessId": 10424,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "MyEvent2",
+ "ActivityID": null,
+ "RelatedActivityID": null,
+ "Payload": {
+ "SpanId": "0da9f6bf7524a449",
+ "TraceId": "7715c9d490f54f44a5d0c6b62570f1b2",
+ "strKey": "anotherValue",
+ "uint32Key": 9876,
+ "uint64Key": 987654321
+ }
+ }
+
+ */
+# define ETW_FIELD_VERSION "Version" /* Event version */
+# define ETW_FIELD_TYPE "Type" /* Event type */
+# define ETW_FIELD_NAME "_name" /* Event name */
+# define ETW_FIELD_TIME "_time" /* Event time */
+# define ETW_FIELD_OPCODE "OpCode" /* OpCode for TraceLogging */
+
+# define ETW_FIELD_TRACE_ID "TraceId" /* Trace Id */
+# define ETW_FIELD_SPAN_ID "SpanId" /* Span Id */
+# define ETW_FIELD_SPAN_PARENTID "ParentId" /* Span ParentId */
+# define ETW_FIELD_SPAN_KIND "Kind" /* Span Kind */
+# define ETW_FIELD_SPAN_LINKS "Links" /* Span Links array */
+
+# define ETW_FIELD_PAYLOAD_NAME "Name" /* ETW Payload["Name"] */
+
+/* Span option constants */
+# define ETW_FIELD_STARTTIME "StartTime" /* Operation start time */
+# define ETW_FIELD_DURATION "Duration" /* Operation duration */
+# define ETW_FIELD_STATUSCODE "StatusCode" /* Span status code */
+# define ETW_FIELD_STATUSMESSAGE "StatusMessage" /* Span status message */
+# define ETW_FIELD_SUCCESS "Success" /* Span success */
+# define ETW_FIELD_TIMESTAMP "Timestamp" /* Log timestamp */
+
+/* Value constants */
+# define ETW_VALUE_SPAN "Span" /* ETW event name for Span */
+# define ETW_VALUE_LOG "Log" /* ETW event name for Log */
+
+# define ETW_VALUE_SPAN_START "SpanStart" /* ETW for Span Start */
+# define ETW_VALUE_SPAN_END "SpanEnd" /* ETW for Span Start */
+
+
+/* Log specific */
+# define ETW_FIELD_LOG_BODY "body" /* Log body */
+# define ETW_FIELD_LOG_SEVERITY_TEXT "severityText" /* Sev text */
+# define ETW_FIELD_LOG_SEVERITY_NUM "severityNumber" /* Sev num */
+
+
+#endif
+
+/* clang-format on */
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h
new file mode 100644
index 000000000..bd2478224
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h
@@ -0,0 +1,297 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <algorithm>
+
+# include <cstdint>
+# include <cstdio>
+# include <cstdlib>
+# include <sstream>
+# include <type_traits>
+
+# include <fstream>
+
+# include <map>
+
+# include "opentelemetry/nostd/shared_ptr.h"
+# include "opentelemetry/nostd/string_view.h"
+# include "opentelemetry/nostd/unique_ptr.h"
+# include "opentelemetry/nostd/variant.h"
+
+# include "opentelemetry/common/key_value_iterable_view.h"
+
+# include "opentelemetry/logs/logger_provider.h"
+# include "opentelemetry/trace/span_id.h"
+# include "opentelemetry/trace/trace_id.h"
+
+# include "opentelemetry/exporters/etw/etw_config.h"
+# include "opentelemetry/exporters/etw/etw_fields.h"
+# include "opentelemetry/exporters/etw/etw_properties.h"
+# include "opentelemetry/exporters/etw/etw_provider.h"
+# include "opentelemetry/exporters/etw/utils.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+
+class LoggerProvider;
+
+/**
+ * @brief Logger class that allows to send logs to ETW Provider.
+ */
+class Logger : public opentelemetry::logs::Logger
+{
+
+ /**
+ * @brief Parent provider of this Tracer
+ */
+ etw::LoggerProvider &loggerProvider_;
+
+ /**
+ * @brief ProviderId (Name or GUID)
+ */
+ std::string provId;
+
+ /**
+ * @brief Encoding (Manifest, MessagePack or XML)
+ */
+ ETWProvider::EventFormat encoding;
+
+ /**
+ * @brief Provider Handle
+ */
+ ETWProvider::Handle &provHandle;
+
+ /**
+ * @brief ETWProvider is a singleton that aggregates all ETW writes.
+ * @return
+ */
+ static ETWProvider &etwProvider()
+ {
+ static ETWProvider instance; // C++11 magic static
+ return instance;
+ }
+
+ /**
+ * @brief Init a reference to etw::ProviderHandle
+ * @return Provider Handle
+ */
+ ETWProvider::Handle &initProvHandle() { return etwProvider().open(provId, encoding); }
+
+public:
+ /**
+ * @brief Tracer constructor
+ * @param parent Parent LoggerProvider
+ * @param providerId ProviderId - Name or GUID
+ * @param encoding ETW encoding format to use.
+ */
+ Logger(etw::LoggerProvider &parent,
+ nostd::string_view providerId = "",
+ ETWProvider::EventFormat encoding = ETWProvider::EventFormat::ETW_MANIFEST)
+ : opentelemetry::logs::Logger(),
+ loggerProvider_(parent),
+ provId(providerId.data(), providerId.size()),
+ encoding(encoding),
+ provHandle(initProvHandle())
+ {}
+
+ void Log(opentelemetry::logs::Severity severity,
+ nostd::string_view body,
+ const common::KeyValueIterable &attributes,
+ opentelemetry::trace::TraceId trace_id,
+ opentelemetry::trace::SpanId span_id,
+ opentelemetry::trace::TraceFlags trace_flags,
+ common::SystemTimestamp timestamp) noexcept override
+ {
+
+# ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ // Properties *res = dynamic_cast<Properties *>(&resr);
+
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return Log(severity, provId, body, *evt, trace_id, span_id, trace_flags, timestamp);
+ }
+# endif
+ Properties evtCopy = attributes;
+ return Log(severity, provId, body, evtCopy, trace_id, span_id, trace_flags, timestamp);
+ }
+
+ void Log(opentelemetry::logs::Severity severity,
+ nostd::string_view name,
+ nostd::string_view body,
+ const common::KeyValueIterable &attributes,
+ opentelemetry::trace::TraceId trace_id,
+ opentelemetry::trace::SpanId span_id,
+ opentelemetry::trace::TraceFlags trace_flags,
+ common::SystemTimestamp timestamp) noexcept override
+ {
+
+# ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ // Properties *res = dynamic_cast<Properties *>(&resr);
+
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return Log(severity, name, body, *evt, trace_id, span_id, trace_flags, timestamp);
+ }
+# endif
+ Properties evtCopy = attributes;
+ return Log(severity, name, body, evtCopy, trace_id, span_id, trace_flags, timestamp);
+ }
+
+ virtual void Log(opentelemetry::logs::Severity severity,
+ nostd::string_view name,
+ nostd::string_view body,
+ Properties &evt,
+ opentelemetry::trace::TraceId trace_id,
+ opentelemetry::trace::SpanId span_id,
+ opentelemetry::trace::TraceFlags trace_flags,
+ common::SystemTimestamp timestamp) noexcept
+ {
+ // Populate Etw.EventName attribute at envelope level
+ evt[ETW_FIELD_NAME] = ETW_VALUE_LOG;
+
+# ifdef HAVE_FIELD_TIME
+ {
+ auto timeNow = std::chrono::system_clock::now().time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow).count();
+ evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis);
+ }
+# endif
+ const auto &cfg = GetConfiguration(loggerProvider_);
+ if (cfg.enableSpanId)
+ {
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(span_id);
+ }
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(trace_id);
+ }
+ // Populate ActivityId if enabled
+ GUID ActivityId;
+ LPGUID ActivityIdPtr = nullptr;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(span_id, ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+ evt[ETW_FIELD_PAYLOAD_NAME] = std::string(name.data(), name.size());
+ std::chrono::system_clock::time_point ts = timestamp;
+ int64_t tsMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(ts.time_since_epoch()).count();
+ evt[ETW_FIELD_TIMESTAMP] = utils::formatUtcTimestampMsAsISO8601(tsMs);
+ int severity_index = static_cast<int>(severity);
+ if (severity_index < 0 ||
+ severity_index >= std::extent<decltype(opentelemetry::logs::SeverityNumToText)>::value)
+ {
+ std::stringstream sout;
+ sout << "Invalid severity(" << severity_index << ")";
+ evt[ETW_FIELD_LOG_SEVERITY_TEXT] = sout.str();
+ }
+ else
+ {
+ evt[ETW_FIELD_LOG_SEVERITY_TEXT] =
+ opentelemetry::logs::SeverityNumToText[severity_index].data();
+ }
+ evt[ETW_FIELD_LOG_SEVERITY_NUM] = static_cast<uint32_t>(severity);
+ evt[ETW_FIELD_LOG_BODY] = std::string(body.data(), body.length());
+ etwProvider().write(provHandle, evt, nullptr, nullptr, 0, encoding);
+ }
+
+ const nostd::string_view GetName() noexcept override { return std::string(); }
+ // TODO : Flush and Shutdown method in main Logger API
+ ~Logger() { etwProvider().close(provHandle); }
+};
+
+/**
+ * @brief ETW LoggerProvider
+ */
+class LoggerProvider : public opentelemetry::logs::LoggerProvider
+{
+public:
+ /**
+ * @brief LoggerProvider options supplied during initialization.
+ */
+ TelemetryProviderConfiguration config_;
+
+ /**
+ * @brief Construct instance of LoggerProvider with given options
+ * @param options Configuration options
+ */
+ LoggerProvider(TelemetryProviderOptions options) : opentelemetry::logs::LoggerProvider()
+ {
+ GetOption(options, "enableTraceId", config_.enableTraceId, true);
+ GetOption(options, "enableSpanId", config_.enableSpanId, true);
+ GetOption(options, "enableActivityId", config_.enableActivityId, false);
+
+ // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc.
+ config_.encoding = GetEncoding(options);
+ }
+
+ LoggerProvider() : opentelemetry::logs::LoggerProvider()
+ {
+ config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST;
+ }
+
+ nostd::shared_ptr<opentelemetry::logs::Logger> GetLogger(
+ nostd::string_view logger_name,
+ nostd::string_view options,
+ nostd::string_view library_name,
+ nostd::string_view version = "",
+ nostd::string_view schema_url = "") override
+ {
+ UNREFERENCED_PARAMETER(options);
+ UNREFERENCED_PARAMETER(library_name);
+ UNREFERENCED_PARAMETER(version);
+ UNREFERENCED_PARAMETER(schema_url);
+ ETWProvider::EventFormat evtFmt = config_.encoding;
+ return nostd::shared_ptr<opentelemetry::logs::Logger>{
+ new (std::nothrow) etw::Logger(*this, logger_name, evtFmt)};
+ }
+
+ /**
+ * @brief Obtain ETW Tracer.
+ * @param name ProviderId (instrumentation name) - Name or GUID
+ * @param args Additional arguments that controls `codec` of the provider.
+ * Possible values are:
+ * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events.
+ * - "MSGPACK" - MessagePack-encoded binary payload ETW events.
+ * - "XML" - XML events (reserved for future use)
+ * @param library_name Library name
+ * @param version Library version
+ * @param schema_url schema URL
+ * @return
+ */
+ nostd::shared_ptr<opentelemetry::logs::Logger> GetLogger(
+ nostd::string_view logger_name,
+ nostd::span<nostd::string_view> args,
+ nostd::string_view library_name,
+ nostd::string_view version = "",
+ nostd::string_view schema_url = "") override
+ {
+ UNREFERENCED_PARAMETER(args);
+ UNREFERENCED_PARAMETER(library_name);
+ UNREFERENCED_PARAMETER(version);
+ UNREFERENCED_PARAMETER(schema_url);
+ ETWProvider::EventFormat evtFmt = config_.encoding;
+ return nostd::shared_ptr<opentelemetry::logs::Logger>{
+ new (std::nothrow) etw::Logger(*this, logger_name, evtFmt)};
+ }
+};
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h
new file mode 100644
index 000000000..9b96cb27a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h
@@ -0,0 +1,30 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#ifdef ENABLE_LOGS_PREVIEW
+# include <cstdint>
+# include <cstdio>
+# include <cstdlib>
+
+# include <mutex>
+
+# include "opentelemetry/nostd/shared_ptr.h"
+# include "opentelemetry/nostd/string_view.h"
+# include "opentelemetry/nostd/unique_ptr.h"
+
+# include "opentelemetry/common/key_value_iterable_view.h"
+
+# include "opentelemetry/logs/logger_provider.h"
+# include "opentelemetry/trace/span_id.h"
+# include "opentelemetry/trace/trace_id.h"
+
+# include "opentelemetry/sdk/logs/exporter.h"
+
+# include "opentelemetry/exporters/etw/etw_config.h"
+# include "opentelemetry/exporters/etw/etw_logger.h"
+# include "opentelemetry/exporters/etw/etw_provider.h"
+
+# include "opentelemetry/exporters/etw/utils.h"
+
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h
new file mode 100644
index 000000000..3cf365c8a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h
@@ -0,0 +1,456 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include "opentelemetry/version.h"
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+
+#include <opentelemetry/nostd/span.h>
+#include <map>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+# include <Windows.h>
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+
+/**
+ * @brief PropertyVariant provides:
+ * - a constructor to initialize from initializer lists
+ * - an owning wrapper around `common::AttributeValue`
+ */
+using PropertyVariant =
+ nostd::variant<bool,
+ int32_t,
+ int64_t,
+ uint32_t,
+ uint64_t,
+ double,
+ std::string,
+ const char *,
+ // 8-bit byte arrays / binary blobs are not part of OT spec yet!
+ // Ref: https://github.com/open-telemetry/opentelemetry-specification/issues/780
+ std::vector<uint8_t>,
+ std::vector<bool>,
+ std::vector<int32_t>,
+ std::vector<int64_t>,
+ std::vector<uint32_t>,
+ std::vector<uint64_t>,
+ std::vector<double>,
+ std::vector<std::string>>;
+
+enum PropertyType
+{
+ kTypeBool,
+ kTypeInt,
+ kTypeInt64,
+ kTypeUInt,
+ kTypeUInt64,
+ kTypeDouble,
+ kTypeString,
+ kTypeCString,
+ kTypeSpanByte,
+ kTypeSpanBool,
+ kTypeSpanInt,
+ kTypeSpanInt64,
+ kTypeSpanUInt,
+ kTypeSpanUInt64,
+ kTypeSpanDouble,
+ kTypeSpanString
+};
+
+/**
+ * @brief PropertyValue class that holds PropertyVariant and
+ * provides converter for non-owning common::AttributeValue
+ */
+class PropertyValue : public PropertyVariant
+{
+
+ /**
+ * @brief Convert span<T> to vector<T>
+ * @tparam T
+ * @param source
+ * @return
+ */
+ template <typename T>
+ static std::vector<T> to_vector(const nostd::span<const T, nostd::dynamic_extent> &source)
+ {
+ return std::vector<T>(source.begin(), source.end());
+ }
+
+ /**
+ * @brief Convert span<string_view> to vector<string>
+ * @param source Span of non-owning string views.
+ * @return Vector of owned strings.
+ */
+ std::vector<std::string> static to_vector(const nostd::span<const nostd::string_view> &source)
+ {
+ std::vector<std::string> result(source.size());
+ for (const auto &item : source)
+ {
+ result.push_back(std::string(item.data()));
+ }
+ return result;
+ }
+
+ /**
+ * @brief Convert vector<INTEGRAL> to span<INTEGRAL>.
+ * @tparam T Integral type
+ * @param vec Vector of integral type primitives to convert to span.
+ * @return Span of integral type primitives.
+ */
+ template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+ static nostd::span<const T> to_span(const std::vector<T> &vec)
+ {
+ nostd::span<const T> result(vec.data(), vec.size());
+ return result;
+ }
+
+ /**
+ * @brief Convert vector<FLOAT> to span<const FLOAT>.
+ * @tparam T Float type
+ * @param vec Vector of float type primitives to convert to span.
+ * @return Span of float type primitives.
+ */
+ template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+ static nostd::span<const T> to_span(const std::vector<T> &vec)
+ {
+ nostd::span<const T> result(vec.data(), vec.size());
+ return result;
+ }
+
+public:
+ /**
+ * @brief PropertyValue from bool
+ * @param v
+ * @return
+ */
+ PropertyValue(bool value) : PropertyVariant(value) {}
+
+ /**
+ * @brief PropertyValue from integral.
+ * @param v
+ * @return
+ */
+ template <typename TInteger, std::enable_if_t<std::is_integral<TInteger>::value, bool> = true>
+ PropertyValue(TInteger number) : PropertyVariant(number)
+ {}
+
+ /**
+ * @brief PropertyValue from floating point.
+ * @param v
+ * @return
+ */
+ template <typename TFloat, std::enable_if_t<std::is_floating_point<TFloat>::value, bool> = true>
+ PropertyValue(TFloat number) : PropertyVariant(double(number))
+ {}
+
+ /**
+ * @brief Default PropertyValue (int32_t=0)
+ * @param v
+ * @return
+ */
+ PropertyValue() : PropertyVariant(int32_t(0)) {}
+
+ /**
+ * @brief PropertyValue from array of characters as string.
+ *
+ * @param v
+ * @return
+ */
+ PropertyValue(char value[]) : PropertyVariant(std::string(value)) {}
+
+ /**
+ * @brief PropertyValue from array of characters as string.
+ *
+ * @param v
+ * @return
+ */
+ PropertyValue(const char *value) : PropertyVariant(std::string(value)) {}
+
+ /**
+ * @brief PropertyValue from string.
+ *
+ * @param v
+ * @return
+ */
+ PropertyValue(const std::string &value) : PropertyVariant(value) {}
+
+ /**
+ * @brief PropertyValue from vector as array.
+ * @return
+ */
+ template <typename T>
+ PropertyValue(std::vector<T> value) : PropertyVariant(value)
+ {}
+
+ /**
+ * @brief Convert non-owning common::AttributeValue to owning PropertyValue.
+ * @return
+ */
+ PropertyValue &FromAttributeValue(const common::AttributeValue &v)
+ {
+ switch (v.index())
+ {
+ case common::AttributeType::kTypeBool:
+ PropertyVariant::operator=(nostd::get<bool>(v));
+ break;
+ case common::AttributeType::kTypeInt:
+ PropertyVariant::operator=(nostd::get<int32_t>(v));
+ break;
+ case common::AttributeType::kTypeInt64:
+ PropertyVariant::operator=(nostd::get<int64_t>(v));
+ break;
+ case common::AttributeType::kTypeUInt:
+ PropertyVariant::operator=(nostd::get<uint32_t>(v));
+ break;
+ case common::AttributeType::kTypeUInt64:
+ PropertyVariant::operator=(nostd::get<uint64_t>(v));
+ break;
+ case common::AttributeType::kTypeDouble:
+ PropertyVariant::operator=(nostd::get<double>(v));
+ break;
+ case common::AttributeType::kTypeCString: {
+ PropertyVariant::operator=(nostd::get<const char *>(v));
+ break;
+ }
+ case common::AttributeType::kTypeString: {
+ PropertyVariant::operator=
+ (std::string{nostd::string_view(nostd::get<nostd::string_view>(v)).data()});
+ break;
+ }
+
+ case common::AttributeType::kTypeSpanByte:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const uint8_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanBool:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const bool>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanInt:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const int32_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanInt64:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const int64_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanUInt:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const uint32_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanUInt64:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const uint64_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanDouble:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const double>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanString:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const nostd::string_view>>(v)));
+ break;
+
+ default:
+ break;
+ }
+ return (*this);
+ }
+
+ /**
+ * @brief Convert owning PropertyValue to non-owning common::AttributeValue
+ * @param other
+ */
+ common::AttributeValue ToAttributeValue() const
+ {
+ common::AttributeValue value;
+
+ switch (this->index())
+ {
+ case PropertyType::kTypeBool:
+ value = nostd::get<bool>(*this);
+ break;
+ case PropertyType::kTypeInt:
+ value = nostd::get<int32_t>(*this);
+ break;
+ case PropertyType::kTypeInt64:
+ value = nostd::get<int64_t>(*this);
+ break;
+ case PropertyType::kTypeUInt:
+ value = nostd::get<uint32_t>(*this);
+ break;
+ case PropertyType::kTypeUInt64:
+ value = nostd::get<uint64_t>(*this);
+ break;
+ case PropertyType::kTypeDouble:
+ value = nostd::get<double>(*this);
+ break;
+ case PropertyType::kTypeString: {
+ const std::string &str = nostd::get<std::string>(*this);
+ return nostd::string_view(str.data(), str.size());
+ break;
+ }
+ case PropertyType::kTypeCString: {
+ const char *data = nostd::get<const char *>(*this);
+ return nostd::string_view(data, (data) ? strlen(data) : 0);
+ break;
+ }
+ case PropertyType::kTypeSpanByte: {
+ value = to_span(nostd::get<std::vector<uint8_t>>(*this));
+ break;
+ }
+ case PropertyType::kTypeSpanBool: {
+ const auto &vec = nostd::get<std::vector<bool>>(*this);
+ // FIXME: sort out how to remap from vector<bool> to span<bool>
+ UNREFERENCED_PARAMETER(vec);
+ break;
+ }
+ case PropertyType::kTypeSpanInt:
+ value = to_span(nostd::get<std::vector<int32_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanInt64:
+ value = to_span(nostd::get<std::vector<int64_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanUInt:
+ value = to_span(nostd::get<std::vector<uint32_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanUInt64:
+ value = to_span(nostd::get<std::vector<uint64_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanDouble:
+ value = to_span(nostd::get<std::vector<double>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanString:
+ // FIXME: sort out how to remap from vector<string> to span<string_view>
+ // value = to_span(nostd::get<std::vector<std::string>>(self));
+ break;
+
+ default:
+ break;
+ }
+ return value;
+ }
+};
+
+/**
+ * @brief Map of PropertyValue
+ */
+using PropertyValueMap = std::map<std::string, PropertyValue>;
+
+/**
+ * @brief Map of PropertyValue with common::KeyValueIterable interface.
+ */
+class Properties : public common::KeyValueIterable, public PropertyValueMap
+{
+
+ /**
+ * @brief Helper tyoe for map constructor.
+ */
+ using PropertyValueType = std::pair<const std::string, PropertyValue>;
+
+public:
+ /**
+ * @brief PropertyValueMap constructor.
+ */
+ Properties() : PropertyValueMap() {}
+
+ /**
+ * @brief PropertyValueMap constructor from initializer list.
+ */
+ Properties(const std::initializer_list<PropertyValueType> properties) : PropertyValueMap()
+ {
+ (*this) = (properties);
+ }
+
+ /**
+ * @brief PropertyValueMap assignment operator from initializer list.
+ */
+ Properties &operator=(std::initializer_list<PropertyValueType> properties)
+ {
+ PropertyValueMap::operator=(properties);
+ return (*this);
+ }
+
+ /**
+ * @brief PropertyValueMap constructor from map.
+ */
+ Properties(const PropertyValueMap &properties) : PropertyValueMap() { (*this) = properties; }
+
+ /**
+ * @brief PropertyValueMap assignment operator from map.
+ */
+ Properties &operator=(const PropertyValueMap &properties)
+ {
+ PropertyValueMap::operator=(properties);
+ return (*this);
+ }
+
+ /**
+ * @brief PropertyValueMap constructor from KeyValueIterable
+ * allows to convert non-Owning KeyValueIterable to owning
+ * container.
+ *
+ */
+ Properties(const common::KeyValueIterable &other) { (*this) = other; }
+
+ /**
+ * @brief PropertyValueMap assignment operator.
+ */
+ Properties &operator=(const common::KeyValueIterable &other)
+ {
+ clear();
+ other.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
+ std::string k(key.data(), key.length());
+ (*this)[k].FromAttributeValue(value);
+ return true;
+ });
+ return (*this);
+ }
+
+ /**
+ * @brief PropertyValueMap property accessor.
+ */
+ PropertyValue &operator[](const std::string &k) { return PropertyValueMap::operator[](k); }
+
+ /**
+ * 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
+ */
+ bool ForEachKeyValue(nostd::function_ref<bool(nostd::string_view, common::AttributeValue)>
+ callback) const noexcept override
+ {
+ for (const auto &kv : (*this))
+ {
+ const common::AttributeValue &value = kv.second.ToAttributeValue();
+ if (!callback(nostd::string_view{kv.first}, value))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the number of key-value pairs
+ */
+ size_t size() const noexcept override { return PropertyValueMap::size(); }
+};
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h
new file mode 100644
index 000000000..51cca03a6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h
@@ -0,0 +1,629 @@
+// // Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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
+# pragma warning(push)
+# pragma warning(disable : 4459)
+# pragma warning(disable : 4018)
+# pragma warning(disable : 5054)
+#endif
+
+#include "opentelemetry/exporters/etw/etw_properties.h"
+#include "opentelemetry/exporters/etw/uuid.h"
+#include "opentelemetry/version.h"
+
+#include "opentelemetry/exporters/etw/etw_fields.h"
+#include "opentelemetry/exporters/etw/utils.h"
+
+#ifdef HAVE_MSGPACK
+# include "nlohmann/json.hpp"
+#endif
+
+#include "opentelemetry/exporters/etw/etw_traceloggingdynamic.h"
+
+#include <map>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#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
+
+using namespace OPENTELEMETRY_NAMESPACE::exporter::etw;
+
+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
+ };
+
+ /// <summary>
+ /// Entry that contains Provider Handle, Provider MetaData and Provider GUID
+ /// </summary>
+ struct Handle
+ {
+ uint64_t refCount;
+ REGHANDLE providerHandle;
+ std::vector<BYTE> providerMetaVector;
+ GUID providerGuid;
+ };
+
+ /// <summary>
+ /// Check if given provider is registered.
+ /// </summary>
+ /// <param name="providerId"></param>
+ /// <returns></returns>
+ bool is_registered(const std::string &providerId)
+ {
+ std::lock_guard<std::mutex> lock(m_providerMapLock);
+ auto it = providers().find(providerId);
+ if (it != providers().end())
+ {
+ if (it->second.providerHandle != INVALID_HANDLE)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Get Provider by Name or string representation of GUID
+ /// </summary>
+ /// <param name="providerId"></param>
+ /// <returns></returns>
+ Handle &open(const std::string &providerId, EventFormat format = EventFormat::ETW_MSGPACK)
+ {
+ std::lock_guard<std::mutex> lock(m_providerMapLock);
+
+ // Check and return if provider is already registered
+ auto it = providers().find(providerId);
+ if (it != providers().end())
+ {
+ if (it->second.providerHandle != INVALID_HANDLE)
+ {
+ it->second.refCount++;
+ return it->second;
+ }
+ }
+
+ // Register provider if necessary
+ auto &data = providers()[providerId];
+ data.providerMetaVector.clear();
+
+ utils::UUID guid = (providerId.rfind("{", 0) == 0) ? utils::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)
+ {
+ // Register with TraceLoggingDynamic facility - dynamic manifest ETW events.
+ case EventFormat::ETW_MANIFEST: {
+ tld::ProviderMetadataBuilder<std::vector<BYTE>> 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.refCount = 0;
+ data.providerHandle = INVALID_HANDLE;
+ }
+ else
+ {
+ data.refCount = 1;
+ data.providerHandle = hProvider;
+ }
+ }
+ break;
+
+#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.refCount = 0;
+ data.providerHandle = INVALID_HANDLE;
+ }
+ else
+ {
+ data.refCount = 1;
+ 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;
+ }
+
+ /**
+ * @brief Unregister provider
+ * @param data Provider Handle
+ * @return status code
+ */
+ unsigned long close(Handle handle)
+ {
+ std::lock_guard<std::mutex> lock(m_providerMapLock);
+
+ // use reference to provider list, NOT it' copy.
+ auto &m = providers();
+ auto it = m.begin();
+ while (it != m.end())
+ {
+ if (it->second.providerHandle == handle.providerHandle)
+ {
+ // reference to item in the map of open provider handles
+ auto &data = it->second;
+ unsigned long result = STATUS_OK;
+
+ data.refCount--;
+ if (data.refCount == 0)
+ {
+ if (data.providerMetaVector.size())
+ {
+ // ETW/TraceLoggingDynamic provider
+ result = tld::UnregisterProvider(data.providerHandle);
+ }
+ else
+ {
+ // Other provider types, e.g. ETW/MsgPack
+ result = EventUnregister(data.providerHandle);
+ }
+
+ it->second.providerHandle = INVALID_HANDLE;
+ if (result == STATUS_OK)
+ {
+ m.erase(it);
+ }
+ }
+ return result;
+ }
+ }
+ return STATUS_ERROR;
+ }
+
+ unsigned long writeMsgPack(Handle &providerData,
+ exporter::etw::Properties &eventData,
+ LPCGUID ActivityId = nullptr,
+ LPCGUID RelatedActivityId = nullptr,
+ uint8_t Opcode = 0)
+ {
+#ifdef HAVE_MSGPACK
+ // Events can only be sent if provider is registered
+ if (providerData.providerHandle == INVALID_HANDLE)
+ {
+ // Provider not registered!
+ return STATUS_ERROR;
+ }
+
+ std::string eventName = "NoName";
+ auto nameField = eventData[ETW_FIELD_NAME];
+
+# ifdef HAVE_FIELD_TIME
+ // Event time is appended by ETW layer itself by default. This code allows
+ // to override the timestamp by millisecond timestamp, in case if it has
+ // not been already provided by the upper layer.
+ if (!eventData.count(ETW_FIELD_TIME))
+ {
+ // TODO: if nanoseconds resolution is required, then we can populate it in 96-bit MsgPack
+ // spec. auto nanos =
+ // std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
+ eventData[ETW_FIELD_TIME] = opentelemetry::utils::getUtcSystemTimeMs();
+ }
+# endif
+
+ switch (nameField.index())
+ {
+ case PropertyType::kTypeString:
+ eventName = (char *)(nostd::get<std::string>(nameField).data()); // must be 0-terminated!
+ break;
+ case PropertyType::kTypeCString:
+ eventName = (char *)(nostd::get<const char *>(nameField));
+ break;
+ default:
+ // If invalid event name is supplied, then we replace it with 'NoName'
+ break;
+ }
+
+ /* clang-format off */
+ nlohmann::json jObj =
+ {
+ { ETW_FIELD_NAME, eventName },
+ { ETW_FIELD_OPCODE, Opcode }
+ };
+ /* clang-format on */
+
+ std::string eventFieldName(ETW_FIELD_NAME);
+ for (auto &kv : eventData)
+ {
+ const char *name = kv.first.data();
+
+ // Don't include event name field in the Payload section
+ if (eventFieldName == name)
+ continue;
+
+ auto &value = kv.second;
+ switch (value.index())
+ {
+ case PropertyType::kTypeBool: {
+ UINT8 temp = static_cast<UINT8>(nostd::get<bool>(value));
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeInt: {
+ auto temp = nostd::get<int32_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeInt64: {
+ auto temp = nostd::get<int64_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeUInt: {
+ auto temp = nostd::get<uint32_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeUInt64: {
+ auto temp = nostd::get<uint64_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeDouble: {
+ auto temp = nostd::get<double>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeString: {
+ jObj[name] = nostd::get<std::string>(value);
+ break;
+ }
+ case PropertyType::kTypeCString: {
+ auto temp = nostd::get<const char *>(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<GUID>(value);
+ // TODO: add transform from GUID type to string?
+ jObj[name] = temp;
+ break;
+ }
+# endif
+
+ // TODO: arrays are not supported yet
+ case PropertyType::kTypeSpanByte:
+ case PropertyType::kTypeSpanBool:
+ case PropertyType::kTypeSpanInt:
+ case PropertyType::kTypeSpanInt64:
+ case PropertyType::kTypeSpanUInt:
+ case PropertyType::kTypeSpanUInt64:
+ case PropertyType::kTypeSpanDouble:
+ case PropertyType::kTypeSpanString:
+ default:
+ // TODO: unsupported type
+ break;
+ }
+ }
+
+ std::vector<uint8_t> v = nlohmann::json::to_msgpack(jObj);
+
+ EVENT_DESCRIPTOR evtDescriptor;
+ // TODO: event descriptor may be populated with additional values as follows:
+ // Id - if 0, auto-incremented sequence number that uniquely identifies event in a trace
+ // Version - event version
+ // Channel - 11 for TraceLogging
+ // Level - verbosity level
+ // Task - TaskId
+ // Opcode - described in evntprov.h:259 : 0 - info, 1 - activity start, 2 - activity stop.
+ EventDescCreate(&evtDescriptor, 0, 0x1, 0, 0, 0, Opcode, 0);
+ EVENT_DATA_DESCRIPTOR evtData[1];
+ EventDataDescCreate(&evtData[0], v.data(), static_cast<ULONG>(v.size()));
+ ULONG writeResponse = 0;
+ if ((ActivityId != nullptr) || (RelatedActivityId != nullptr))
+ {
+ writeResponse = EventWriteTransfer(providerData.providerHandle, &evtDescriptor, ActivityId,
+ RelatedActivityId, 1, evtData);
+ }
+ else
+ {
+ writeResponse = EventWrite(providerData.providerHandle, &evtDescriptor, 1, evtData);
+ }
+
+ 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);
+#else
+ UNREFERENCED_PARAMETER(providerData);
+ UNREFERENCED_PARAMETER(eventData);
+ UNREFERENCED_PARAMETER(ActivityId);
+ UNREFERENCED_PARAMETER(RelatedActivityId);
+ UNREFERENCED_PARAMETER(Opcode);
+ return STATUS_ERROR;
+#endif
+ }
+
+ /// <summary>
+ /// Send event to Provider Id
+ /// </summary>
+ /// <param name="providerId"></param>
+ /// <param name="eventData"></param>
+ /// <returns></returns>
+ unsigned long writeTld(Handle &providerData,
+ Properties &eventData,
+ LPCGUID ActivityId = nullptr,
+ LPCGUID RelatedActivityId = nullptr,
+ uint8_t Opcode = 0 /* Information */)
+ {
+ // 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<BYTE> byteVector;
+ std::vector<BYTE> byteDataVector;
+ tld::EventMetadataBuilder<std::vector<BYTE>> builder(byteVector);
+ tld::EventDataBuilder<std::vector<BYTE>> dbuilder(byteDataVector);
+
+ const std::string EVENT_NAME = ETW_FIELD_NAME;
+ std::string eventName = "NoName";
+ auto nameField = eventData[EVENT_NAME];
+ switch (nameField.index())
+ {
+ case PropertyType::kTypeString:
+ eventName = (char *)(nostd::get<std::string>(nameField).data());
+ break;
+ case PropertyType::kTypeCString:
+ eventName = (char *)(nostd::get<const char *>(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();
+ auto &value = kv.second;
+ switch (value.index())
+ {
+ case PropertyType::kTypeBool: {
+ builder.AddField(name, tld::TypeBool8);
+ UINT8 temp = static_cast<UINT8>(nostd::get<bool>(value));
+ dbuilder.AddByte(temp);
+ break;
+ }
+ case PropertyType::kTypeInt: {
+ builder.AddField(name, tld::TypeInt32);
+ auto temp = nostd::get<int32_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeInt64: {
+ builder.AddField(name, tld::TypeInt64);
+ auto temp = nostd::get<int64_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeUInt: {
+ builder.AddField(name, tld::TypeUInt32);
+ auto temp = nostd::get<uint32_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeUInt64: {
+ builder.AddField(name, tld::TypeUInt64);
+ auto temp = nostd::get<uint64_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeDouble: {
+ builder.AddField(name, tld::TypeDouble);
+ auto temp = nostd::get<double>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeString: {
+ builder.AddField(name, tld::TypeUtf8String);
+ dbuilder.AddString(nostd::get<std::string>(value).data());
+ break;
+ }
+ case PropertyType::kTypeCString: {
+ builder.AddField(name, tld::TypeUtf8String);
+ auto temp = nostd::get<const char *>(value);
+ dbuilder.AddString(temp);
+ break;
+ }
+#if HAVE_TYPE_GUID
+ // TODO: consider adding UUID/GUID to spec
+ case PropertyType::kGUID: {
+ builder.AddField(name.c_str(), TypeGuid);
+ auto temp = nostd::get<GUID>(value);
+ dbuilder.AddBytes(&temp, sizeof(GUID));
+ break;
+ }
+#endif
+
+ // TODO: arrays are not supported
+ case PropertyType::kTypeSpanByte:
+ case PropertyType::kTypeSpanBool:
+ case PropertyType::kTypeSpanInt:
+ case PropertyType::kTypeSpanInt64:
+ case PropertyType::kTypeSpanUInt:
+ case PropertyType::kTypeSpanUInt64:
+ case PropertyType::kTypeSpanDouble:
+ case PropertyType::kTypeSpanString:
+ 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.Opcode = Opcode;
+ eventDescriptor.Level = 0; /* FIXME: Always on for now */
+
+ EVENT_DATA_DESCRIPTOR pDataDescriptors[3];
+ EventDataDescCreate(&pDataDescriptors[2], byteDataVector.data(),
+ static_cast<ULONG>(byteDataVector.size()));
+
+ HRESULT writeResponse = 0;
+ if ((ActivityId != nullptr) || (RelatedActivityId != nullptr))
+ {
+ writeResponse = tld::WriteEvent(providerData.providerHandle, eventDescriptor,
+ providerData.providerMetaVector.data(), byteVector.data(), 3,
+ pDataDescriptors, ActivityId, RelatedActivityId);
+ }
+ else
+ {
+ writeResponse = tld::WriteEvent(providerData.providerHandle, eventDescriptor,
+ providerData.providerMetaVector.data(), byteVector.data(), 3,
+ pDataDescriptors);
+ }
+
+ // Event is larger than ETW max sized of 64KB
+ if (writeResponse == HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW))
+ {
+ return STATUS_EFBIG;
+ }
+
+ return (unsigned long)(writeResponse);
+ }
+
+ unsigned long write(Handle &providerData,
+ Properties &eventData,
+ LPCGUID ActivityId,
+ LPCGUID RelatedActivityId,
+ uint8_t Opcode,
+ ETWProvider::EventFormat format)
+ {
+ if (format == ETWProvider::EventFormat::ETW_MANIFEST)
+ {
+ return writeTld(providerData, eventData, ActivityId, RelatedActivityId, Opcode);
+ }
+ if (format == ETWProvider::EventFormat::ETW_MSGPACK)
+ {
+ return writeMsgPack(providerData, eventData, ActivityId, RelatedActivityId, Opcode);
+ }
+ 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<std::string, Handle>;
+
+ ProviderMap &providers()
+ {
+ static std::map<std::string, Handle> providers;
+ return providers;
+ }
+};
+
+OPENTELEMETRY_END_NAMESPACE
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h
new file mode 100644
index 000000000..a6243c46d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifdef __has_include
+# if __has_include("TraceLoggingDynamic.h")
+# include "TraceLoggingDynamic.h"
+# ifndef HAVE_TLD
+# define HAVE_TLD
+# endif
+# endif
+#else
+# ifndef HAVE_TLD
+# define HAVE_TLD
+# endif
+# include "TraceLoggingDynamic.h"
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
new file mode 100644
index 000000000..5db31a7a7
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
@@ -0,0 +1,995 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <algorithm>
+#include <atomic>
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <vector>
+
+#include "opentelemetry/nostd/shared_ptr.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/nostd/unique_ptr.h"
+#include "opentelemetry/nostd/variant.h"
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+
+#include "opentelemetry/trace/span.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+#include "opentelemetry/trace/span_id.h"
+#include "opentelemetry/trace/trace_id.h"
+#include "opentelemetry/trace/tracer_provider.h"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/etw/etw_config.h"
+#include "opentelemetry/exporters/etw/etw_fields.h"
+#include "opentelemetry/exporters/etw/etw_properties.h"
+#include "opentelemetry/exporters/etw/etw_provider.h"
+#include "opentelemetry/exporters/etw/utils.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+
+class Span;
+
+/**
+ * @brief Template that allows to instantiate new Span object for header-only forward-declared
+ * etw::Span type
+ *
+ * @tparam SpanType Expected to be etw::Span
+ * @tparam TracerType expected to be etw::Tracer
+ * @param objPtr Pointer to parent
+ * @param name Span Name
+ * @param options Span Options
+ * @return Span instance
+ */
+template <class SpanType, class TracerType>
+SpanType *new_span(TracerType *objPtr,
+ nostd::string_view name,
+ const opentelemetry::trace::StartSpanOptions &options)
+{
+ return new (std::nothrow) SpanType{*objPtr, name, options};
+}
+
+/**
+ * @brief Template that allows to convert etw::Span pointer to smart shared pointer to
+ * `opentelemetry::trace::Span`
+ * @tparam SpanType Expected to be etw::Span
+ * @param ptr Pointer to etw::Span
+ * @return Smart shared pointer to `opentelemetry::trace::Span`
+ */
+template <class SpanType>
+nostd::shared_ptr<opentelemetry::trace::Span> to_span_ptr(SpanType *ptr)
+{
+ return nostd::shared_ptr<opentelemetry::trace::Span>{ptr};
+}
+
+class TracerProvider;
+
+/**
+ * @brief Utility template for obtaining Span Name
+ * @tparam T etw::Span
+ * @param t instance of etw::Span
+ * @return Span Name
+ */
+template <class T>
+std::string GetName(T &t)
+{
+ auto sV = t.GetName();
+ return std::string(sV.data(), sV.length());
+}
+
+/**
+ * @brief Utility template to obtain Span start time
+ * @tparam T etw::Span
+ * @param t instance of etw::Span
+ * @return Span Start timestamp
+ */
+template <class T>
+common::SystemTimestamp GetStartTime(T &t)
+{
+ return t.GetStartTime();
+}
+
+/**
+ * @brief Utility template to obtain Span end time
+ * @tparam T etw::Span
+ * @param t instance of etw::Span
+ * @return Span Stop timestamp
+ */
+template <class T>
+common::SystemTimestamp GetEndTime(T &t)
+{
+ return t.GetEndTime();
+}
+
+class Properties;
+
+/**
+ * @brief Utility template to store Attributes on Span
+ * @tparam T etw::Span
+ * @param instance instance of etw::Span
+ * @param t Properties to store as Attributes
+ */
+template <class T>
+void SetSpanAttributes(T &instance, Properties &t)
+{
+ instance.SetAttributes(t);
+}
+
+/**
+ * @brief Utility template to obtain Span Attributes
+ * @tparam T etw::Span
+ * @param instance instance of etw::Span
+ * @return ref to Span Attributes
+ */
+template <class T>
+Properties &GetSpanAttributes(T &instance)
+{
+ return instance.GetAttributes();
+}
+
+template <class T>
+void UpdateStatus(T &t, Properties &props)
+{
+ t.UpdateStatus(props);
+}
+
+/**
+ * @brief Tracer class that allows to send spans to ETW Provider.
+ */
+class Tracer : public opentelemetry::trace::Tracer
+{
+
+ /**
+ * @brief Parent provider of this Tracer
+ */
+ etw::TracerProvider &tracerProvider_;
+
+ /**
+ * @brief ProviderId (Name or GUID)
+ */
+ std::string provId;
+
+ /**
+ * @brief Encoding (Manifest, MessagePack or XML)
+ */
+ ETWProvider::EventFormat encoding;
+
+ /**
+ * @brief Provider Handle
+ */
+ ETWProvider::Handle &provHandle;
+
+ opentelemetry::trace::TraceId traceId_;
+
+ std::atomic<bool> isClosed_{true};
+
+ /**
+ * @brief ETWProvider is a singleton that aggregates all ETW writes.
+ * @return
+ */
+ static ETWProvider &etwProvider()
+ {
+ static ETWProvider instance; // C++11 magic static
+ return instance;
+ }
+
+ /**
+ * @brief Internal method that allows to populate Links to other Spans.
+ * Span links are in hexadecimal representation, comma-separated in their
+ * order of appearance.
+ *
+ * @param attributes
+ * @param links
+ */
+ virtual void DecorateLinks(Properties &attributes,
+ const opentelemetry::trace::SpanContextKeyValueIterable &links) const
+ {
+ // Add `SpanLinks` attribute if the list is not empty
+ if (links.size())
+ {
+ size_t idx = 0;
+ std::string linksValue;
+ links.ForEachKeyValue(
+ [&](opentelemetry::trace::SpanContext ctx, const common::KeyValueIterable &) {
+ if (!linksValue.empty())
+ {
+ linksValue += ',';
+ linksValue += ToLowerBase16(ctx.span_id());
+ }
+ idx++;
+ return true;
+ });
+ attributes[ETW_FIELD_SPAN_LINKS] = linksValue;
+ }
+ }
+
+ /**
+ * @brief Allow our friendly etw::Span to end itself on Tracer.
+ * @param span
+ * @param
+ */
+ virtual void EndSpan(const Span &span,
+ const opentelemetry::trace::Span *parentSpan = nullptr,
+ const opentelemetry::trace::EndSpanOptions & = {})
+ {
+ const auto &cfg = GetConfiguration(tracerProvider_);
+ const opentelemetry::trace::Span &spanBase =
+ reinterpret_cast<const opentelemetry::trace::Span &>(span);
+ auto spanContext = spanBase.GetContext();
+
+ // Populate Span with presaved attributes
+ Span &currentSpan = const_cast<Span &>(span);
+ Properties evt = GetSpanAttributes(currentSpan);
+ evt[ETW_FIELD_NAME] = GetName(span);
+
+ if (cfg.enableSpanId)
+ {
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
+ }
+
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
+ }
+
+ // Populate ActivityId if enabled
+ GUID ActivityId;
+ LPGUID ActivityIdPtr = nullptr;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(spanBase.GetContext().span_id(), ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+
+ // Populate RelatedActivityId if enabled
+ GUID RelatedActivityId;
+ LPGUID RelatedActivityIdPtr = nullptr;
+ if (cfg.enableRelatedActivityId)
+ {
+ if (parentSpan != nullptr)
+ {
+ if (CopySpanIdToActivityId(parentSpan->GetContext().span_id(), RelatedActivityId))
+ {
+ RelatedActivityIdPtr = &RelatedActivityId;
+ }
+ }
+ }
+
+ if (cfg.enableActivityTracking)
+ {
+ // TODO: check what EndSpanOptions should be supported for this exporter.
+ // The only option available currently (end_steady_time) does not apply.
+ //
+ // This event on Span Stop enables generation of "non-transactional"
+ // OpCode=Stop in alignment with TraceLogging Activity "EventSource"
+ // spec.
+ etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 2, encoding);
+ }
+
+ {
+ // Now since the span has ended, we need to emit the "Span" event that
+ // contains the entire span information, attributes, time, etc. on it.
+ evt[ETW_FIELD_NAME] = ETW_VALUE_SPAN;
+ evt[ETW_FIELD_PAYLOAD_NAME] = GetName(span);
+
+ // Add timing details in ISO8601 format, which adequately represents
+ // the actual time, taking Timezone into consideration. This is NOT
+ // local time, but rather UTC time (Z=0).
+ std::chrono::system_clock::time_point startTime = GetStartTime(currentSpan);
+ std::chrono::system_clock::time_point endTime = GetEndTime(currentSpan);
+ int64_t startTimeMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(startTime.time_since_epoch())
+ .count();
+ int64_t endTimeMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(endTime.time_since_epoch()).count();
+
+ // It may be more optimal to enable passing timestamps as UTC milliseconds
+ // since Unix epoch instead of string, but that implies additional tooling
+ // is needed to convert it, rendering it NOT human-readable.
+ evt[ETW_FIELD_STARTTIME] = utils::formatUtcTimestampMsAsISO8601(startTimeMs);
+#ifdef ETW_FIELD_ENDTTIME
+ // ETW has its own precise timestamp at envelope layer for every event.
+ // However, in some scenarios it is easier to deal with ISO8601 strings.
+ // In that case we convert the app-created timestamp and place it into
+ // Payload[$ETW_FIELD_TIME] field. The option configurable at compile-time.
+ evt[ETW_FIELD_ENDTTIME] = utils::formatUtcTimestampMsAsISO8601(endTimeMs);
+#endif
+ // Duration of Span in milliseconds
+ evt[ETW_FIELD_DURATION] = endTimeMs - startTimeMs;
+ // Presently we assume that all spans are server spans
+ evt[ETW_FIELD_SPAN_KIND] = uint32_t(opentelemetry::trace::SpanKind::kServer);
+ UpdateStatus(currentSpan, evt);
+ etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 0, encoding);
+ }
+ }
+
+ const opentelemetry::trace::TraceId &trace_id() { return traceId_; }
+
+ friend class Span;
+
+ /**
+ * @brief Init a reference to etw::ProviderHandle
+ * @return Provider Handle
+ */
+ ETWProvider::Handle &initProvHandle()
+ {
+ isClosed_ = false;
+ return etwProvider().open(provId, encoding);
+ }
+
+public:
+ /**
+ * @brief Tracer constructor
+ * @param parent Parent TraceProvider
+ * @param providerId ProviderId - Name or GUID
+ * @param encoding ETW encoding format to use.
+ */
+ Tracer(etw::TracerProvider &parent,
+ nostd::string_view providerId = "",
+ ETWProvider::EventFormat encoding = ETWProvider::EventFormat::ETW_MANIFEST)
+ : opentelemetry::trace::Tracer(),
+ tracerProvider_(parent),
+ provId(providerId.data(), providerId.size()),
+ encoding(encoding),
+ provHandle(initProvHandle())
+ {
+ // Generate random GUID
+ GUID trace_id;
+ CoCreateGuid(&trace_id);
+ // Populate TraceId of the Tracer with the above GUID
+ const auto *traceIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(trace_id));
+ nostd::span<const uint8_t, opentelemetry::trace::TraceId::kSize> traceIdBytes(
+ traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize);
+ traceId_ = opentelemetry::trace::TraceId(traceIdBytes);
+ }
+
+ /**
+ * @brief Start Span
+ * @param name Span name
+ * @param attributes Span attributes
+ * @param links Span links
+ * @param options Span options
+ * @return
+ */
+ nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
+ nostd::string_view name,
+ const common::KeyValueIterable &attributes,
+ const opentelemetry::trace::SpanContextKeyValueIterable &links,
+ const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
+ {
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return StartSpan(name, *evt, links, options);
+ }
+#endif
+ Properties evtCopy = attributes;
+ return StartSpan(name, evtCopy, links, options);
+ }
+
+ /**
+ * @brief Start Span
+ * @param name Span name
+ * @param attributes Span attributes
+ * @param links Span links
+ * @param options Span options
+ * @return
+ */
+ virtual nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
+ nostd::string_view name,
+ Properties &evt,
+ const opentelemetry::trace::SpanContextKeyValueIterable &links,
+ const opentelemetry::trace::StartSpanOptions &options = {}) noexcept
+ {
+ const auto &cfg = GetConfiguration(tracerProvider_);
+
+ // Parent Context:
+ // - either use current span
+ // - or attach to parent SpanContext specified in options
+ opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
+ if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
+ {
+ auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
+ if (span_context.IsValid())
+ {
+ parentContext = span_context;
+ }
+ }
+
+ // Populate Etw.RelatedActivityId at envelope level if enabled
+ GUID RelatedActivityId;
+ LPCGUID RelatedActivityIdPtr = nullptr;
+ if (cfg.enableAutoParent)
+ {
+ if (cfg.enableRelatedActivityId)
+ {
+ if (CopySpanIdToActivityId(parentContext.span_id(), RelatedActivityId))
+ {
+ RelatedActivityIdPtr = &RelatedActivityId;
+ }
+ }
+ }
+
+ // This template pattern allows us to forward-declare the etw::Span,
+ // create an instance of it, then assign it to tracer::Span result.
+ auto currentSpan = new_span<Span, Tracer>(this, name, options);
+ nostd::shared_ptr<opentelemetry::trace::Span> result = to_span_ptr<Span>(currentSpan);
+
+ auto spanContext = result->GetContext();
+
+ // Decorate with additional standard fields
+ std::string eventName = name.data();
+
+ // Populate Etw.EventName attribute at envelope level
+ evt[ETW_FIELD_NAME] = eventName;
+
+ // Populate Payload["SpanId"] attribute
+ // Populate Payload["ParentSpanId"] attribute if parent Span is valid
+ if (cfg.enableSpanId)
+ {
+ if (parentContext.IsValid())
+ {
+ evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id());
+ }
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
+ }
+
+ // Populate Etw.Payload["TraceId"] attribute
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
+ }
+
+ // Populate Etw.ActivityId at envelope level if enabled
+ GUID ActivityId;
+ LPCGUID ActivityIdPtr = nullptr;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(result.get()->GetContext().span_id(), ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+
+ // Links
+ DecorateLinks(evt, links);
+
+ // Remember Span attributes to be passed down to ETW on Span end
+ SetSpanAttributes(*currentSpan, evt);
+
+ if (cfg.enableActivityTracking)
+ {
+ // TODO: add support for options that are presently ignored :
+ // - options.kind
+ // - options.start_steady_time
+ // - options.start_system_time
+ etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding);
+ }
+
+ return result;
+ }
+
+ /**
+ * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush.
+ * NOTE: this method has no effect for the realtime streaming Tracer.
+ *
+ * @param timeout Allow Tracer to drop data if timeout is reached
+ * @return
+ */
+ void ForceFlushWithMicroseconds(uint64_t) noexcept override {}
+
+ /**
+ * @brief Close tracer, spending up to given amount of microseconds to flush and close.
+ * NOTE: This method decrements the reference count on current ETW Provider Handle and
+ * closes it if reference count on that provider handle is zero.
+ *
+ * @param timeout Allow Tracer to drop data if timeout is reached.
+ * @return
+ */
+ void CloseWithMicroseconds(uint64_t) noexcept override
+ {
+ // Close once only
+ if (!isClosed_.exchange(true))
+ {
+ etwProvider().close(provHandle);
+ }
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Parent span.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @param attributes Event attributes.
+ * @return
+ */
+ void AddEvent(opentelemetry::trace::Span &span,
+ nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept
+ {
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return AddEvent(span, name, timestamp, *evt);
+ }
+#endif
+ // Pass a copy converted to Properties object on stack
+ Properties evtCopy = attributes;
+ return AddEvent(span, name, timestamp, evtCopy);
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Parent span.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @param attributes Event attributes.
+ * @return
+ */
+ void AddEvent(opentelemetry::trace::Span &span,
+ nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ Properties &evt) noexcept
+ {
+ // TODO: respect originating timestamp. Do we need to reserve
+ // a special 'Timestamp' field or is it an overkill? The delta
+ // between when `AddEvent` API is called and when ETW layer
+ // timestamp is appended is nanos- to micros-, thus handling
+ // the explicitly provided timestamp is only necessary in case
+ // if a process wants to submit back-dated or future-dated
+ // timestamp. Unless there is a strong ask from any ETW customer
+ // to have it, this feature (custom timestamp) remains unimplemented.
+ (void)timestamp;
+
+ const auto &cfg = GetConfiguration(tracerProvider_);
+
+ evt[ETW_FIELD_NAME] = name.data();
+
+ const auto &spanContext = span.GetContext();
+ if (cfg.enableSpanId)
+ {
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
+ }
+
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
+ }
+
+ LPGUID ActivityIdPtr = nullptr;
+ GUID ActivityId;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(spanContext.span_id(), ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+
+#ifdef HAVE_FIELD_TIME
+ {
+ auto timeNow = std::chrono::system_clock::now().time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow).count();
+ evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis);
+ }
+#endif
+
+ etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding);
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Span.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @return
+ */
+ void AddEvent(opentelemetry::trace::Span &span,
+ nostd::string_view name,
+ common::SystemTimestamp timestamp) noexcept
+ {
+ AddEvent(span, name, timestamp, sdk::GetEmptyAttributes());
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Spab.
+ * @param name Event name.
+ */
+ void AddEvent(opentelemetry::trace::Span &span, nostd::string_view name)
+ {
+ AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes());
+ }
+
+ /**
+ * @brief Tracer destructor.
+ */
+ virtual ~Tracer() { CloseWithMicroseconds(0); }
+};
+
+/**
+ * @brief etw::Span allows to send event data to ETW listener.
+ */
+class Span : public opentelemetry::trace::Span
+{
+protected:
+ friend class Tracer;
+
+ /**
+ * @brief Span properties are attached on "Span" event on end of Span.
+ */
+ Properties attributes_;
+
+ common::SystemTimestamp start_time_;
+ common::SystemTimestamp end_time_;
+
+ opentelemetry::trace::StatusCode status_code_{opentelemetry::trace::StatusCode::kUnset};
+ std::string status_description_;
+
+ /**
+ * @brief Owner Tracer of this Span
+ */
+ Tracer &owner_;
+
+ /**
+ * @brief Span name.
+ */
+ nostd::string_view name_;
+
+ /**
+ * @brief Attribute indicating that the span has ended.
+ */
+ std::atomic<bool> has_ended_{false};
+
+ /**
+ * @brief Attribute indicating that the span has started.
+ */
+ std::atomic<bool> has_started_{false};
+
+ /**
+ * @brief Parent Span of this nested Span (optional)
+ */
+ Span *parent_{nullptr};
+
+ /**
+ * @brief Get Parent Span of this nested Span.
+ * @return Pointer to Parent or nullptr if no Parent.
+ */
+ Span *GetParent() const { return parent_; }
+
+ opentelemetry::trace::SpanContext context_;
+
+ const opentelemetry::trace::SpanContext CreateContext()
+ {
+ GUID activity_id;
+ // Generate random GUID
+ CoCreateGuid(&activity_id);
+ const auto *activityIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(activity_id));
+
+ // Populate SpanId with that GUID
+ nostd::span<const uint8_t, opentelemetry::trace::SpanId::kSize> spanIdBytes(
+ activityIdPtr, activityIdPtr + opentelemetry::trace::SpanId::kSize);
+ const opentelemetry::trace::SpanId spanId(spanIdBytes);
+
+ // Inherit trace_id from Tracer
+ const opentelemetry::trace::TraceId traceId{owner_.trace_id()};
+ // TODO: TraceFlags are not supported by ETW exporter.
+ const opentelemetry::trace::TraceFlags flags{0};
+ // TODO: Remote parent is not supported by ETW exporter.
+ const bool hasRemoteParent = false;
+ return opentelemetry::trace::SpanContext{traceId, spanId, flags, hasRemoteParent};
+ }
+
+public:
+ /**
+ * @brief Update Properties object with current Span status
+ * @param evt
+ */
+ void UpdateStatus(Properties &evt)
+ {
+ /* Should we avoid populating this extra field if status is unset? */
+ if ((status_code_ == opentelemetry::trace::StatusCode::kUnset) ||
+ (status_code_ == opentelemetry::trace::StatusCode::kOk))
+ {
+ evt[ETW_FIELD_SUCCESS] = "True";
+ evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_);
+ evt[ETW_FIELD_STATUSMESSAGE] = status_description_;
+ }
+ else
+ {
+ evt[ETW_FIELD_SUCCESS] = "False";
+ evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_);
+ evt[ETW_FIELD_STATUSMESSAGE] = status_description_;
+ }
+ }
+
+ /**
+ * @brief Get start time of this Span.
+ * @return
+ */
+ common::SystemTimestamp GetStartTime() { return start_time_; }
+
+ /**
+ * @brief Get end time of this Span.
+ * @return
+ */
+ common::SystemTimestamp GetEndTime() { return end_time_; }
+
+ /**
+ * @brief Get Span Name.
+ * @return Span Name.
+ */
+ nostd::string_view GetName() const { return name_; }
+
+ /**
+ * @brief Span constructor
+ * @param owner Owner Tracer
+ * @param name Span name
+ * @param options Span options
+ * @param parent Parent Span (optional)
+ * @return
+ */
+ Span(Tracer &owner,
+ nostd::string_view name,
+ const opentelemetry::trace::StartSpanOptions &options,
+ Span *parent = nullptr) noexcept
+ : opentelemetry::trace::Span(),
+ start_time_(std::chrono::system_clock::now()),
+ owner_(owner),
+ parent_(parent),
+ context_(CreateContext())
+ {
+ name_ = name;
+ UNREFERENCED_PARAMETER(options);
+ }
+
+ /**
+ * @brief Span Destructor
+ */
+ ~Span() { End(); }
+
+ /**
+ * @brief Add named event with no attributes.
+ * @param name Event name.
+ * @return
+ */
+ void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); }
+
+ /**
+ * @brief Add named event with custom timestamp.
+ * @param name
+ * @param timestamp
+ * @return
+ */
+ void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override
+ {
+ owner_.AddEvent(*this, name, timestamp);
+ }
+
+ /**
+ * @brief Add named event with custom timestamp and attributes.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @param attributes Event attributes.
+ * @return
+ */
+ void AddEvent(nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept override
+ {
+ owner_.AddEvent(*this, name, timestamp, attributes);
+ }
+
+ /**
+ * @brief Set Span status
+ * @param code Span status code.
+ * @param description Span description.
+ * @return
+ */
+ void SetStatus(opentelemetry::trace::StatusCode code,
+ nostd::string_view description) noexcept override
+ {
+ status_code_ = code;
+ status_description_ = description.data();
+ }
+
+ void SetAttributes(Properties attributes) { attributes_ = attributes; }
+
+ /**
+ * @brief Obtain span attributes specified at Span start.
+ * NOTE: please consider that this method is NOT thread-safe.
+ *
+ * @return ref to Properties collection
+ */
+ Properties &GetAttributes() { return attributes_; }
+
+ /**
+ * @brief Sets an attribute on the Span. If the Span previously contained a mapping
+ * for the key, the old value is replaced.
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override
+ {
+ // don't override fields propagated from span data.
+ if (key == ETW_FIELD_NAME || key == ETW_FIELD_SPAN_ID || key == ETW_FIELD_TRACE_ID)
+ {
+ return;
+ }
+ attributes_[std::string{key}].FromAttributeValue(value);
+ }
+
+ /**
+ * @brief Update Span name.
+ *
+ * NOTE: this method is a no-op for streaming implementation.
+ * We cannot change the Span name after it started streaming.
+ *
+ * @param name
+ * @return
+ */
+ void UpdateName(nostd::string_view) noexcept override
+ {
+ // We can't do that!
+ // name_ = name;
+ }
+
+ /**
+ * @brief End Span.
+ * @param EndSpanOptions
+ * @return
+ */
+ void End(const opentelemetry::trace::EndSpanOptions &options = {}) noexcept override
+ {
+ end_time_ = std::chrono::system_clock::now();
+
+ if (!has_ended_.exchange(true))
+ {
+ owner_.EndSpan(*this, parent_, options);
+ }
+ }
+
+ /**
+ * @brief Obtain SpanContext
+ * @return
+ */
+ opentelemetry::trace::SpanContext GetContext() const noexcept override { return context_; }
+
+ /**
+ * @brief Check if Span is recording data.
+ * @return
+ */
+ bool IsRecording() const noexcept override
+ {
+ // For streaming implementation this should return the state of ETW Listener.
+ // In certain unprivileged environments, ex. containers, it is impossible
+ // to determine if a listener is registered. Thus, we always return true.
+ return true;
+ }
+
+ virtual void SetToken(nostd::unique_ptr<context::Token> &&token) noexcept
+ {
+ // TODO: not implemented
+ UNREFERENCED_PARAMETER(token);
+ }
+
+ /// <summary>
+ /// Get Owner tracer of this Span
+ /// </summary>
+ /// <returns></returns>
+ opentelemetry::trace::Tracer &tracer() const noexcept { return this->owner_; }
+};
+
+/**
+ * @brief ETW TracerProvider
+ */
+class TracerProvider : public opentelemetry::trace::TracerProvider
+{
+public:
+ /**
+ * @brief TracerProvider options supplied during initialization.
+ */
+ TelemetryProviderConfiguration config_;
+
+ /**
+ * @brief Construct instance of TracerProvider with given options
+ * @param options Configuration options
+ */
+ TracerProvider(TelemetryProviderOptions options) : opentelemetry::trace::TracerProvider()
+ {
+ // By default we ensure that all events carry their with TraceId and SpanId
+ GetOption(options, "enableTraceId", config_.enableTraceId, true);
+ GetOption(options, "enableSpanId", config_.enableSpanId, true);
+
+ // Backwards-compatibility option that allows to reuse ETW-specific parenting described here:
+ // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid
+ // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer
+
+ // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop
+ // format for every Span emitted.
+ GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false);
+
+ // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL,
+ // ETW gets the identifier from the thread local storage. For details on getting this
+ // identifier, see EventActivityIdControl.
+ GetOption(options, "enableActivityId", config_.enableActivityId, false);
+
+ // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous
+ // component. Use this parameter to link your component's events to the previous component's
+ // events.
+ GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false);
+
+ // When a new Span is started, the current span automatically becomes its parent.
+ GetOption(options, "enableAutoParent", config_.enableAutoParent, false);
+
+ // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc.
+ config_.encoding = GetEncoding(options);
+ }
+
+ TracerProvider() : opentelemetry::trace::TracerProvider()
+ {
+ config_.enableTraceId = true;
+ config_.enableSpanId = true;
+ config_.enableActivityId = false;
+ config_.enableActivityTracking = false;
+ config_.enableRelatedActivityId = false;
+ config_.enableAutoParent = false;
+ config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST;
+ }
+
+ /**
+ * @brief Obtain ETW Tracer.
+ * @param name ProviderId (instrumentation name) - Name or GUID
+ *
+ * @param args Additional arguments that controls `codec` of the provider.
+ * Possible values are:
+ * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events.
+ * - "MSGPACK" - MessagePack-encoded binary payload ETW events.
+ * - "XML" - XML events (reserved for future use)
+ * @return
+ */
+ nostd::shared_ptr<opentelemetry::trace::Tracer> GetTracer(
+ nostd::string_view name,
+ nostd::string_view args = "",
+ nostd::string_view schema_url = "") noexcept override
+ {
+ UNREFERENCED_PARAMETER(args);
+ UNREFERENCED_PARAMETER(schema_url);
+ ETWProvider::EventFormat evtFmt = config_.encoding;
+ return nostd::shared_ptr<opentelemetry::trace::Tracer>{new (std::nothrow)
+ Tracer(*this, name, evtFmt)};
+ }
+};
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h
new file mode 100644
index 000000000..b28f1a021
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h
@@ -0,0 +1,28 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include <mutex>
+
+#include "opentelemetry/nostd/shared_ptr.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/nostd/unique_ptr.h"
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+#include "opentelemetry/trace/span.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+#include "opentelemetry/trace/span_id.h"
+#include "opentelemetry/trace/trace_id.h"
+#include "opentelemetry/trace/tracer_provider.h"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/etw/etw_config.h"
+#include "opentelemetry/exporters/etw/etw_provider.h"
+#include "opentelemetry/exporters/etw/etw_tracer.h"
+
+#include "opentelemetry/exporters/etw/utils.h"
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h
new file mode 100644
index 000000000..4d38c1fb6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h
@@ -0,0 +1,274 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <algorithm>
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <string>
+
+#include "opentelemetry/common/macros.h"
+#include "opentelemetry/exporters/etw/uuid.h"
+#include "opentelemetry/version.h"
+
+#ifdef _WIN32
+# include <Windows.h>
+# include <evntprov.h>
+# include <wincrypt.h>
+# pragma comment(lib, "Advapi32.lib")
+# pragma comment(lib, "Rpcrt4.lib")
+# include <Objbase.h>
+# pragma comment(lib, "Ole32.Lib")
+#else
+# include <codecvt>
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace utils
+{
+
+/// <summary>
+/// Compile-time constexpr djb2 hash function for strings
+/// </summary>
+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<uint32_t, (uint32_t)x>::value
+
+#define CONST_HASHCODE(name) CONST_UINT32_T(OPENTELEMETRY_NAMESPACE::utils::hashCode(#name))
+
+#ifdef _WIN32
+
+/// <summary>
+/// Compute SHA-1 hash of input buffer and save to output
+/// </summary>
+/// <param name="pData">Input buffer</param>
+/// <param name="nData">Input buffer size</param>
+/// <param name="pHashedData">Output buffer</param>
+/// <param name="nHashedData">Output buffer size</param>
+/// <returns></returns>
+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;
+}
+
+/// <summary>
+/// Convert UTF-8 string to UTF-16 wide string.
+///
+/// </summary>
+/// <param name="in"></param>
+/// <returns></returns>
+static inline std::wstring to_utf16_string(const std::string &in)
+{
+# ifdef _WIN32
+ int in_length = static_cast<int>(in.size());
+ int out_length = MultiByteToWideChar(CP_UTF8, 0, &in[0], in_length, NULL, 0);
+ std::wstring result(out_length, '\0');
+ MultiByteToWideChar(CP_UTF8, 0, &in[0], in_length, &result[0], out_length);
+ return result;
+# else
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
+ return converter.from_bytes(in);
+# endif
+}
+
+/// <summary>
+/// Transform ETW provider name to provider GUID as described here:
+/// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
+/// </summary>
+/// <param name="providerName"></param>
+/// <returns></returns>
+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
+
+static inline int64_t getUtcSystemTimeMs()
+{
+#ifdef _WIN32
+ ULARGE_INTEGER now;
+ ::GetSystemTimeAsFileTime(reinterpret_cast<FILETIME *>(&now));
+ return (now.QuadPart - 116444736000000000ull) / 10000;
+#else
+ return std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
+#endif
+}
+
+static inline int64_t getUtcSystemTimeinTicks()
+{
+#ifdef _WIN32
+ FILETIME tocks;
+ ::GetSystemTimeAsFileTime(&tocks);
+ ULONGLONG ticks = (ULONGLONG(tocks.dwHighDateTime) << 32) | tocks.dwLowDateTime;
+ // number of days from beginning to 1601 multiplied by ticks per day
+ return ticks + 0x701ce1722770000ULL;
+#else
+ // On Un*x systems system_clock de-facto contains UTC time. Ref:
+ // https://en.cppreference.com/w/cpp/chrono/system_clock
+ // This UTC epoch contract has been signed in blood since C++20
+ std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
+ auto duration = now.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
+ uint64_t ticks = millis;
+ ticks *= 10000; // convert millis to ticks (1 tick = 100ns)
+ ticks += 0x89F7FF5F7B58000ULL; // UTC time 0 in .NET ticks
+ return ticks;
+#endif
+}
+
+/**
+ * @brief Convert local system milliseconds time to ISO8601 string UTC time
+ *
+ * @param timestampMs Milliseconds since epoch in system time
+ *
+ * @return ISO8601 UTC string with microseconds part set to 000
+ */
+static inline std::string formatUtcTimestampMsAsISO8601(int64_t timestampMs)
+{
+ char buf[sizeof("YYYY-MM-DDTHH:MM:SS.ssssssZ") + 1] = {0};
+#ifdef _WIN32
+ __time64_t seconds = static_cast<__time64_t>(timestampMs / 1000);
+ int milliseconds = static_cast<int>(timestampMs % 1000);
+ tm tm;
+ if (::_gmtime64_s(&tm, &seconds) != 0)
+ {
+ memset(&tm, 0, sizeof(tm));
+ }
+ ::_snprintf_s(buf, _TRUNCATE, "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm.tm_year,
+ 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 1000 * milliseconds);
+#else
+ time_t seconds = static_cast<time_t>(timestampMs / 1000);
+ int milliseconds = static_cast<int>(timestampMs % 1000);
+ tm tm;
+ bool valid = (gmtime_r(&seconds, &tm) != NULL);
+ if (!valid)
+ {
+ memset(&tm, 0, sizeof(tm));
+ }
+ (void)snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm.tm_year,
+ 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 1000 * milliseconds);
+#endif
+ return buf;
+}
+
+}; // namespace utils
+
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h
new file mode 100644
index 000000000..a004aec9e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h
@@ -0,0 +1,412 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/version.h"
+
+#include <cstddef>
+#include <cstring>
+#include <functional>
+#include <initializer_list>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+# include "Windows.h"
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace utils
+{
+
+/// <summary>
+/// The UUID structure represents the portable cross-platform implementation of a GUID (Globally
+/// Unique ID).
+/// </summary>
+/// <remarks>
+/// 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.
+/// </remarks>
+#pragma pack(push) /* push current alignment to stack */
+#pragma pack(1) /* set alignment to 1 byte boundary */
+struct UUID
+{
+ /// <summary>
+ /// Specifies the first eight hexadecimal digits of the GUID.
+ /// </summary>
+ uint32_t Data1;
+
+ /// <summary>
+ /// Specifies the first group of four hexadecimal digits.
+ ///</summary>
+ uint16_t Data2;
+
+ /// <summary>
+ /// Specifies the second group of four hexadecimal digits.
+ /// </summary>
+ uint16_t Data3;
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ uint8_t Data4[8];
+
+ /// <summary>
+ /// The default UUID constructor.
+ /// Creates a null instance of the UUID object (initialized to all zeros).
+ /// {00000000-0000-0000-0000-000000000000}.
+ /// </summary>
+ UUID() : Data1(0), Data2(0), Data3(0)
+ {
+ for (size_t i = 0; i < 8; i++)
+ {
+ Data4[i] = 0;
+ }
+ }
+
+ /// <summary>
+ /// A constructor that creates a UUID object from a hyphenated string as defined by
+ /// https://tools.ietf.org/html/rfc4122#page-4
+ /// </summary>
+ /// <param name="uuid_string">A hyphenated string that contains the UUID (curly braces
+ /// optional).</param>
+ 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_s(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_s(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<uint32_t>(p0);
+ Data2 = static_cast<uint16_t>(p1);
+ Data3 = static_cast<uint16_t>(p2);
+ Data4[0] = static_cast<uint8_t>(p3);
+ Data4[1] = static_cast<uint8_t>(p4);
+ Data4[2] = static_cast<uint8_t>(p5);
+ Data4[3] = static_cast<uint8_t>(p6);
+ Data4[4] = static_cast<uint8_t>(p7);
+ Data4[5] = static_cast<uint8_t>(p8);
+ Data4[6] = static_cast<uint8_t>(p9);
+ Data4[7] = static_cast<uint8_t>(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;
+ }
+ }
+
+ /// <summary>
+ /// A constructor that creates a UUID object from a byte array.
+ /// </summary>
+ /// <param name="guid_bytes">A byte array.</param>
+ /// <param name="bigEndian">
+ /// A boolean value that specifies the byte order.<br>
+ /// A value of <i>true</i> specifies the more natural human-readable order.<br>
+ /// A value of <i>false</i> (the default) specifies the same order as the .NET GUID constructor.
+ /// </param>
+ 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];
+ }
+ }
+
+ /// <summary>
+ /// A constructor that creates a UUID object from three integers and a byte array.
+ /// </summary>
+ /// <param name="d1">An integer that specifies the first eight hexadecimal digits of the
+ /// UUID.</param> <param name="d2">An integer that specifies the first group of four hexadecimal
+ /// digits.</param> <param name="d3">An integer that specifies the second group of four
+ /// hexadecimal digits.</param> <param name="v">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.</param>
+ UUID(int d1, int d2, int d3, const std::initializer_list<uint8_t> &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++;
+ }
+ }
+
+ /// <summary>
+ /// The UUID copy constructor.
+ /// </summary>
+ /// <param name="uuid">A UUID object.</param>
+ 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
+
+ /// <summary>
+ /// A constructor that creates a UUID object from a Windows GUID object.
+ /// </summary>
+ /// <param name="guid">A Windows GUID object.</param>
+ 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));
+ }
+
+ /// <summary>
+ /// Converts a standard vector of bytes into a Windows GUID object.
+ /// </summary>
+ /// <param name="bytes">A standard vector of bytes.</param>
+ /// <returns>A GUID.</returns>
+ static GUID to_GUID(std::vector<uint8_t> 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
+
+ /// <summary>
+ /// Converts this UUID to an array of bytes.
+ /// </summary>
+ /// <param name="guid_bytes">A uint8_t array of 16 bytes.</param>
+ 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];
+ }
+ }
+
+ /// <summary>
+ /// Convert this UUID object to a string.
+ /// </summary>
+ /// <returns>This UUID object in a string.</returns>
+ 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);
+ }
+
+ /// <summary>
+ /// Calculates the size of this UUID object.
+ /// The output from this method is compatible with std::unordered_map.
+ /// </summary>
+ /// <returns>The size of the UUID object in bytes.</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Tests to determine whether two UUID objects are equivalent (needed for maps).
+ /// </summary>
+ /// <returns>A boolean value that indicates success or failure.</returns>
+ bool operator==(UUID const &other) const
+ {
+ return Data1 == other.Data1 && Data2 == other.Data2 && Data3 == other.Data3 &&
+ (0 == memcmp(Data4, other.Data4, sizeof(Data4)));
+ }
+
+ /// <summary>
+ /// Tests to determine how to sort 2 UUID objects
+ /// </summary>
+ /// <returns>A boolean value that indicates success or failure.</returns>
+ 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 */
+
+/// <summary>
+/// Declare UUIDComparer as the Comparer when using UUID as a key in a map or set
+/// </summary>
+struct UUIDComparer : std::less<UUID>
+{
+ 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 utils
+
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc
new file mode 100644
index 000000000..4e2210118
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc
@@ -0,0 +1,101 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+# ifdef _WIN32
+
+# include <gtest/gtest.h>
+# include <map>
+# include <string>
+
+# include "opentelemetry/exporters/etw/etw_logger_exporter.h"
+# include "opentelemetry/sdk/trace/simple_processor.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+using namespace opentelemetry::exporter::etw;
+
+const char *kGlobalProviderName = "OpenTelemetry-ETW-TLD";
+
+/**
+ * @brief Logger test with name and unstructured text
+ * {
+ * "Timestamp": "2021-09-30T16:40:40.0820563-07:00",
+ * "ProviderName": "OpenTelemetry-ETW-TLD",
+ * "Id": 2,
+ * "Message": null,
+ * "ProcessId": 23180,
+ * "Level": "Always",
+ * "Keywords": "0x0000000000000000",
+ * "EventName": "Log",
+ * "ActivityID": null,
+ * "RelatedActivityID": null,
+ * "Payload": {
+ * "Name": "test",
+ * "SpanId": "0000000000000000",
+ * "Timestamp": "2021-09-30T23:40:40.081000Z",
+ * "TraceId": "00000000000000000000000000000000",
+ * "_name": "Log",
+ * "body": "This is test message",
+ * "severityNumber": 5,
+ * "severityText": "DEBUG"
+ * }
+ * }
+ */
+
+TEST(ETWLogger, LoggerCheckWithBody)
+{
+ std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
+ exporter::etw::LoggerProvider lp;
+
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"};
+ auto logger = lp.GetLogger(providerName, "", schema_url);
+ Properties attribs = {{"attrib1", 1}, {"attrib2", 2}};
+ EXPECT_NO_THROW(logger->Log(opentelemetry::logs::Severity::kDebug, "This is test log body"));
+}
+
+/**
+ * @brief Logger Test with structured attributes
+ *
+ * Example Event for below test:
+ * {
+ * "Timestamp": "2021-09-30T15:04:15.4227815-07:00",
+ * "ProviderName": "OpenTelemetry-ETW-TLD",
+ * "Id": 1,
+ * "Message": null,
+ * "ProcessId": 33544,
+ * "Level": "Always",
+ * "Keywords": "0x0000000000000000",
+ * "EventName": "Log",
+ * "ActivityID": null,
+ * "RelatedActivityID": null,
+ * "Payload": {
+ * "Name": "test",
+ * "SpanId": "0000000000000000",
+ * "Timestamp": "2021-09-30T22:04:15.422000Z",
+ * "TraceId": "00000000000000000000000000000000",
+ * "_name": "Log",
+ * "attrib1": 1,
+ * "attrib2": 2,
+ * "body": "",
+ * "severityNumber": 5,
+ * "severityText": "DEBUG"
+ * }
+ * }
+ *
+ */
+
+TEST(ETWLogger, LoggerCheckWithAttributes)
+{
+ std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
+ exporter::etw::LoggerProvider lp;
+
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"};
+ auto logger = lp.GetLogger(providerName, "", schema_url);
+ // Log attributes
+ Properties attribs = {{"attrib1", 1}, {"attrib2", 2}};
+ EXPECT_NO_THROW(logger->Log(opentelemetry::logs::Severity::kDebug, attribs));
+}
+
+# endif // _WIN32
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc
new file mode 100644
index 000000000..79052464e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc
@@ -0,0 +1,190 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _WIN32
+
+# include <benchmark/benchmark.h>
+# include <gtest/gtest.h>
+# include <map>
+# include <string>
+# include <unordered_map>
+
+# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
+# include "opentelemetry/sdk/trace/simple_processor.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+using namespace opentelemetry::exporter::etw;
+
+namespace
+{
+static constexpr const char *providerName = "OpenTelemetry-ETW-StressTest";
+
+static exporter::etw::TelemetryProviderOptions providerOptions = {
+ {"enableTraceId", false},
+ {"enableSpanId", false},
+ {"enableActivityId", false},
+ {"enableRelatedActivityId", false},
+ {"enableAutoParent", false}};
+
+class ETWProviderStressTest
+{
+ exporter::etw::TracerProvider provider_;
+ std::string mode_;
+ nostd::shared_ptr<trace::Tracer> tracer_;
+ nostd::shared_ptr<trace::Span> span_;
+
+public:
+ /**
+ * @brief Construct ETW Provider stress test object
+ * @param mode Operational mode: "TLD" or "MsgPack"
+ */
+ ETWProviderStressTest(std::string mode = "TLD") : mode_(mode), provider_(providerOptions) {}
+
+ /**
+ * @brief Initializer tracer and start a Span
+ */
+ void Initialize()
+ {
+ tracer_ = provider_.GetTracer(providerName, mode_);
+ span_ = tracer_->StartSpan("Span");
+ }
+
+ /**
+ * @brief Obtain the tracer
+ */
+ nostd::shared_ptr<trace::Tracer> GetTracer() { return tracer_; }
+
+ /**
+ * @brief Add event using Properties container
+ */
+ bool AddProperties()
+ {
+ std::string eventName = "MyEvent";
+ Properties event = {{"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}};
+ span_->AddEvent(eventName, event);
+ return true;
+ }
+
+ /**
+ * @brief Add event using static preallocated "reusable" Properties container.
+ * This approach works well for single-threaded flows, but may not be safe in
+ * some multithreaded scenarios in case if reusable `Properties` get concurrently
+ * modified by different threads (writes to Properties are not thread-safe).
+ */
+ bool AddPropertiesStatic()
+ {
+ std::string eventName = "MyEvent";
+ static Properties event = {{"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}};
+ span_->AddEvent(eventName, event);
+ return true;
+ }
+
+ /**
+ * @brief Add event using initializer list
+ */
+ bool AddInitList()
+ {
+ std::string eventName = "MyEvent";
+ span_->AddEvent(eventName, {{"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}});
+ return true;
+ }
+
+ /**
+ * @brief Add event using unordered_map
+ */
+ bool AddMap()
+ {
+ std::string eventName = "MyEvent";
+ std::unordered_map<const char *, common::AttributeValue> m = {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}};
+ span_->AddEvent(eventName, m);
+ return true;
+ }
+
+ /**
+ * @brief End Span and close tracer.
+ */
+ void Teardown()
+ {
+ span_->End();
+ tracer_->CloseWithMicroseconds(0);
+ }
+};
+
+ETWProviderStressTest provider;
+
+/**
+ * @brief Create Properties and AddEvent(Properties) to Tracer
+ * @param state Benchmark state.
+ */
+void BM_AddPropertiesToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddProperties());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddPropertiesToTracer);
+
+/**
+ * @brief Create static Properties and AddEvent(Properties) to Tracer
+ * @param state Benchmark state.
+ */
+void BM_AddPropertiesStaticToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddPropertiesStatic());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddPropertiesStaticToTracer);
+
+/**
+ * @brief Create event via initializer list and AddEvent({...}) to Tracer
+ * @param state Benchmark state.
+ */
+void BM_AddInitListToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddInitList());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddInitListToTracer);
+
+/**
+ * @brief Create event as `std::map<std::string, common::AttributeValue>`
+ * and AddEvent(event) to Tracer.
+ * @param state Benchmark state.
+ */
+void BM_AddMapToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddMap());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddMapToTracer);
+
+} // namespace
+
+BENCHMARK_MAIN();
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc
new file mode 100644
index 000000000..d5ebbcad4
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc
@@ -0,0 +1,64 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _WIN32
+
+# include <gtest/gtest.h>
+# include <string>
+
+# include "opentelemetry/exporters/etw/etw_provider.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+TEST(ETWProvider, ProviderIsRegisteredSuccessfully)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider";
+ static ETWProvider etw;
+ auto handle = etw.open(providerName.c_str());
+
+ bool registered = etw.is_registered(providerName);
+ ASSERT_TRUE(registered);
+ etw.close(handle);
+}
+
+TEST(ETWProvider, ProviderIsNotRegisteredSuccessfully)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider-NULL";
+ static ETWProvider etw;
+
+ bool registered = etw.is_registered(providerName);
+ ASSERT_FALSE(registered);
+}
+
+TEST(ETWProvider, CheckOpenGUIDDataSuccessfully)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider";
+
+ // get GUID from the handle returned
+ static ETWProvider etw;
+ auto handle = etw.open(providerName.c_str());
+
+ utils::UUID uuid_handle(handle.providerGuid);
+ auto guidStrHandle = uuid_handle.to_string();
+
+ // get GUID from the providerName
+ auto guid = utils::GetProviderGuid(providerName.c_str());
+ utils::UUID uuid_name(guid);
+ auto guidStrName = uuid_name.to_string();
+
+ ASSERT_STREQ(guidStrHandle.c_str(), guidStrName.c_str());
+ etw.close(handle);
+}
+
+TEST(ETWProvider, CheckCloseSuccess)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider";
+
+ static ETWProvider etw;
+ auto handle = etw.open(providerName.c_str(), ETWProvider::EventFormat::ETW_MANIFEST);
+ auto result = etw.close(handle);
+ ASSERT_NE(result, etw.STATUS_ERROR);
+ ASSERT_FALSE(etw.is_registered(providerName));
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc
new file mode 100644
index 000000000..1c9c1f09b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc
@@ -0,0 +1,388 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _WIN32
+
+# include <gtest/gtest.h>
+# include <map>
+# include <string>
+
+# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
+# include "opentelemetry/sdk/trace/simple_processor.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+using namespace opentelemetry::exporter::etw;
+
+const char *kGlobalProviderName = "OpenTelemetry-ETW-TLD";
+
+std::string getTemporaryValue()
+{
+ return std::string("Value from Temporary std::string");
+}
+
+/* clang-format off */
+TEST(ETWTracer, TracerCheck)
+{
+ // SDK customer specifies their unique ETW ProviderName. Every component or library
+ // is assumed to have its own instrumentation name. Traces are routed to dedicated
+ // provider. Standard hash function maps from ProviderName to ProviderGUID.
+ //
+ // Prominent naming examples from `logman query providers` :
+ //
+ // [Docker] {a3693192-9ed6-46d2-a981-f8226c8363bd}
+ // ...
+ // Intel-Autologger-iclsClient {B8D7E9A0-65D5-40BE-AFEA-83593FC0164E}
+ // Intel-Autologger-iclsProxy {301B773F-50F3-4C8E-83F0-53BA9590A13E}
+ // Intel-Autologger-PTTEKRecertification {F33E9E07-8792-47E8-B3FA-2C92AB32C5B3}
+ // ...
+ // NodeJS-ETW-provider {77754E9B-264B-4D8D-B981-E4135C1ECB0C}
+ // ...
+ // OpenSSH {C4B57D35-0636-4BC3-A262-370F249F9802}
+ // ...
+ // Windows Connect Now {C100BECE-D33A-4A4B-BF23-BBEF4663D017}
+ // Windows Defender Firewall API {28C9F48F-D244-45A8-842F-DC9FBC9B6E92}
+ // Windows Defender Firewall API - GP {0EFF663F-8B6E-4E6D-8182-087A8EAA29CB}
+ // Windows Defender Firewall Driver {D5E09122-D0B2-4235-ADC1-C89FAAAF1069}
+
+ std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
+ exporter::etw::TracerProvider tp;
+
+ auto tracer = tp.GetTracer(providerName);
+
+ // Span attributes
+ Properties attribs =
+ {
+ {"attrib1", 1},
+ {"attrib2", 2}
+ };
+ {
+ auto topSpan = tracer->StartSpan("MySpanTop");
+ auto topScope = tracer->WithActiveSpan(topSpan);
+ {
+ auto outerSpan = tracer->StartSpan("MySpanL2", attribs);
+ auto outerScope = tracer->WithActiveSpan(outerSpan);
+
+ // Create nested span. Note how we share the attributes here.
+ // It is Okay to either reuse/share or have your own attributes.
+ {
+ auto innerSpan = tracer->StartSpan("MySpanL3", attribs);
+ auto innerScope = tracer->WithActiveSpan(innerSpan);
+
+ // Add span attribute
+ EXPECT_NO_THROW(outerSpan->SetAttribute("AttrName1", "AttrValue1"));
+
+ // Add first event
+ std::string eventName1 = "MyEvent1";
+ Properties event1 =
+ {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}
+ };
+ EXPECT_NO_THROW(outerSpan->AddEvent(eventName1, event1));
+
+ // Add second event
+ std::string eventName2 = "MyEvent2";
+ Properties event2 =
+ {
+ {"uint32Key", (uint32_t)9876},
+ {"uint64Key", (uint64_t)987654321},
+ {"strKey", "anotherValue"}
+ };
+ EXPECT_NO_THROW(outerSpan->AddEvent(eventName2, event2));
+
+ std::string eventName3= "MyEvent3";
+ Properties event3 =
+ {
+ /* Extra metadata that allows event to flow to A.I. pipeline */
+ {"metadata", "ai_event"},
+ {"uint32Key", (uint32_t)9876},
+ {"uint64Key", (uint64_t)987654321},
+ // {"int32array", {{-1,0,1,2,3}} },
+ {"tempString", getTemporaryValue() }
+ };
+ EXPECT_NO_THROW(innerSpan->AddEvent(eventName3, event3));
+ EXPECT_NO_THROW(innerSpan->End());
+
+ }
+ EXPECT_NO_THROW(outerSpan->End());
+
+ }
+ EXPECT_NO_THROW(topSpan->End());
+ }
+
+ EXPECT_NO_THROW(tracer->CloseWithMicroseconds(0));
+}
+
+// Lowest decoration level -> smaller ETW event size.
+// Expected output in C# listener on the other side:
+// no ActivityID GUID, no SpanId, no TraceId.
+/*
+{
+ "Timestamp": "2021-03-19T21:04:38.411193-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 13,
+ "Message": null,
+ "ProcessId": 15120,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "C.min/Stop",
+ "ActivityID": null,
+ "RelatedActivityID": null,
+ "Payload": {}
+}
+*/
+TEST(ETWTracer, TracerCheckMinDecoration)
+{
+ std::string providerName = kGlobalProviderName;
+ exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", false},
+ {"enableSpanId", false},
+ {"enableActivityId", false},
+ {"enableActivityTracking", true},
+ {"enableRelatedActivityId", false},
+ {"enableAutoParent", false}
+ });
+ auto tracer = tp.GetTracer(providerName);
+ {
+ auto aSpan = tracer->StartSpan("A.min");
+ auto aScope = tracer->WithActiveSpan(aSpan);
+ {
+ auto bSpan = tracer->StartSpan("B.min");
+ auto bScope = tracer->WithActiveSpan(bSpan);
+ {
+ auto cSpan = tracer->StartSpan("C.min");
+ auto cScope = tracer->WithActiveSpan(cSpan);
+ EXPECT_NO_THROW(cSpan->End());
+ }
+ EXPECT_NO_THROW(bSpan->End());
+ }
+ EXPECT_NO_THROW(aSpan->End());
+}
+ tracer->CloseWithMicroseconds(0);
+}
+
+// Highest decoration level -> larger ETW event size
+// Expected output in C# listener on the other side:
+// ActivityID GUID (==SpanId), SpanId, TraceId.
+/*
+{
+ "Timestamp": "2021-03-19T21:04:38.4120274-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 21,
+ "Message": null,
+ "ProcessId": 15120,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "C.max/Stop",
+ "ActivityID": "d55a2c25-8033-40ab-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "SpanId": "252c5ad53380ab40",
+ "TraceId": "4dea2a63c188894ea5ab979e5cd7ec36"
+ }
+}
+*/
+TEST(ETWTracer, TracerCheckMaxDecoration)
+{
+ std::string providerName = kGlobalProviderName;
+ exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", true},
+ {"enableSpanId", true},
+ {"enableActivityId", true},
+ {"enableRelatedActivityId", true},
+ {"enableAutoParent", true}
+ });
+ auto tracer = tp.GetTracer(providerName);
+ {
+ auto aSpan = tracer->StartSpan("A.max");
+ auto aScope = tracer->WithActiveSpan(aSpan);
+ {
+ auto bSpan = tracer->StartSpan("B.max");
+ auto bScope = tracer->WithActiveSpan(bSpan);
+ {
+ auto cSpan = tracer->StartSpan("C.max");
+ auto cScope = tracer->WithActiveSpan(cSpan);
+ EXPECT_NO_THROW(cSpan->End());
+ }
+ EXPECT_NO_THROW(bSpan->End());
+ }
+ EXPECT_NO_THROW(aSpan->End());
+ }
+ tracer->CloseWithMicroseconds(0);
+}
+
+TEST(ETWTracer, TracerCheckMsgPack)
+{
+ std::string providerName = "OpenTelemetry-ETW-MsgPack";
+ exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", true},
+ {"enableSpanId", true},
+ {"enableActivityId", true},
+ {"enableRelatedActivityId", true},
+ {"enableAutoParent", true}
+ });
+ auto tracer = tp.GetTracer(providerName);
+ {
+ auto aSpan = tracer->StartSpan("A.max");
+ auto aScope = tracer->WithActiveSpan(aSpan);
+ {
+ auto bSpan = tracer->StartSpan("B.max");
+ auto bScope = tracer->WithActiveSpan(bSpan);
+ {
+ auto cSpan = tracer->StartSpan("C.max");
+ auto cScope = tracer->WithActiveSpan(cSpan);
+ std::string eventName = "MyMsgPackEvent";
+ Properties event =
+ {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}
+ };
+ cSpan->AddEvent(eventName, event);
+ EXPECT_NO_THROW(cSpan->End());
+ }
+ EXPECT_NO_THROW(bSpan->End());
+ }
+ EXPECT_NO_THROW(aSpan->End());
+ }
+ tracer->CloseWithMicroseconds(0);
+}
+
+/**
+ * @brief Global Tracer singleton may be placed in .h header and
+ * shared across different compilation units. All would get the
+ * same object.
+ *
+ * @return Single global tracer instance.
+*/
+static OPENTELEMETRY_NAMESPACE::trace::TracerProvider& GetGlobalTracerProvider()
+{
+ static exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", true},
+ {"enableSpanId", true},
+ {"enableActivityId", true},
+ {"enableRelatedActivityId", true},
+ {"enableAutoParent", true}
+ });
+ return tp;
+}
+
+static OPENTELEMETRY_NAMESPACE::trace::Tracer& GetGlobalTracer()
+{
+ static auto tracer = GetGlobalTracerProvider().GetTracer(kGlobalProviderName);
+ return (*tracer.get());
+}
+
+TEST(ETWTracer, GlobalSingletonTracer)
+{
+ // Obtain a global tracer using C++11 magic static.
+ auto& globalTracer = GetGlobalTracer();
+ auto s1 = globalTracer.StartSpan("Span1");
+ auto traceId1 = s1->GetContext().trace_id();
+ s1->End();
+/* === Span 1 - "TraceId": "182a64258fb1864ca4e1a542eecbd9bf"
+{
+ "Timestamp": "2021-05-10T11:45:27.028827-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 5,
+ "Message": null,
+ "ProcessId": 23712,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "6ed94703-6b0a-4e76-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 0,
+ "Kind": 1,
+ "Name": "Span1",
+ "SpanId": "0347d96e0a6b764e",
+ "StartTime": "2021-05-10T18:45:27.028000Z",
+ "StatusCode": 0,
+ "StatusMessage": "",
+ "Success": "True",
+ "TraceId": "182a64258fb1864ca4e1a542eecbd9bf",
+ "_name": "Span"
+ }
+}
+*/
+
+ // Obtain a different tracer withs its own trace-id.
+ auto localTracer = GetGlobalTracerProvider().GetTracer(kGlobalProviderName);
+ auto s2 = localTracer->StartSpan("Span2");
+ auto traceId2 = s2->GetContext().trace_id();
+ s2->End();
+/* === Span 2 - "TraceId": "334bf9a1eed98d40a873a606295a9368"
+{
+ "Timestamp": "2021-05-10T11:45:27.0289654-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 5,
+ "Message": null,
+ "ProcessId": 23712,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "3b7b2ecb-2e84-4903-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 0,
+ "Kind": 1,
+ "Name": "Span2",
+ "SpanId": "cb2e7b3b842e0349",
+ "StartTime": "2021-05-10T18:45:27.028000Z",
+ "StatusCode": 0,
+ "StatusMessage": "",
+ "Success": "True",
+ "TraceId": "334bf9a1eed98d40a873a606295a9368",
+ "_name": "Span"
+ }
+}
+*/
+
+ // Obtain the same global tracer with the same trace-id as before.
+ auto& globalTracer2 = GetGlobalTracer();
+ auto s3 = globalTracer2.StartSpan("Span3");
+ auto traceId3 = s3->GetContext().trace_id();
+ s3->End();
+/* === Span 3 - "TraceId": "182a64258fb1864ca4e1a542eecbd9bf"
+{
+ "Timestamp": "2021-05-10T11:45:27.0290936-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 5,
+ "Message": null,
+ "ProcessId": 23712,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "0a970247-ba0e-4d4b-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 1,
+ "Kind": 1,
+ "Name": "Span3",
+ "SpanId": "4702970a0eba4b4d",
+ "StartTime": "2021-05-10T18:45:27.028000Z",
+ "StatusCode": 0,
+ "StatusMessage": "",
+ "Success": "True",
+ "TraceId": "182a64258fb1864ca4e1a542eecbd9bf",
+ "_name": "Span"
+ }
+}
+*/
+ EXPECT_NE(traceId1, traceId2);
+ EXPECT_EQ(traceId1, traceId3);
+
+ localTracer->CloseWithMicroseconds(0);
+ globalTracer.CloseWithMicroseconds(0);
+}
+
+/* clang-format on */
+
+#endif