Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETW example and tests clean-up #882

Merged
merged 17 commits into from
Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/include/opentelemetry/context/runtime_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ inline Token::~Token()
}

// The ThreadLocalContextStorage class is a derived class from
// RuntimeContextStorage and provides a wrapper for propogating context through
// RuntimeContextStorage and provides a wrapper for propagating context through
// cpp thread locally. This file must be included to use the RuntimeContext
// class if another implementation has not been registered.
class ThreadLocalContextStorage : public RuntimeContextStorage
Expand Down
3 changes: 3 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ if(WITH_OTLP_GRPC OR WITH_OTLP_HTTP)
add_subdirectory(otlp)
add_subdirectory(grpc)
endif()
if(WITH_ETW)
add_subdirectory(etw_threads)
endif()
if(WITH_JAEGER)
add_subdirectory(jaeger)
endif()
Expand Down
6 changes: 6 additions & 0 deletions examples/etw_threads/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project(etw_threadpool)

add_executable(etw_threadpool main.cc)

target_link_libraries(etw_threadpool ${CMAKE_THREAD_LIBS_INIT}
opentelemetry_api opentelemetry_exporter_etw)
32 changes: 32 additions & 0 deletions examples/etw_threads/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ETW Exporter multithreaded context propagation example

## Preface

This example shows how to pass context from main dispatcher thread to worker threads.
While this example is convenient to run in Visual Studio with ETW exporter, the same
logic should apply to any other exporter. Only the initial portion that obtains ETW
Tracer is unique to ETW, the rest can be reused.

## How to debug events in Visual Studio 2019 or newer

Specify your component instrumentation name, which should match the destination ETW
Provider Name or GUID. Example uses "OpenTelemetry-ETW-TLD" for the instrument /
instrumentation name.

In Visual Studio IDE:

- navigate to `View -> Other Windows -> Diagnostic Events...`
- click `Configure...` gear on top.
- specify `OpenTelemetry-ETW-TLD` in the list of providers to monitor.
- run example.
- `Diagnostic Events` view shows you the event flow in realtime.

## Explanation of the code flow

`main` function acts as a dispatcher to manage thread pool called `pool`. `beep_bop`
span is started in the `main`. Then in a loop up to `kMaxThreads` get started
concurrently. Each thread executing `beep` function with a parent scope of `main`.
`beep` subsequently calls into `bop` function, with a parent scope of `beep` span.
Entire execution of all threads is grouped under the main span called `beep_bop`.
At the end of execution, the `main` function joins all pending threads and ends
the main span.
154 changes: 154 additions & 0 deletions examples/etw_threads/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

// Include common Trace Provider API and processor
#include "opentelemetry/sdk/trace/simple_processor.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"
#include "opentelemetry/trace/provider.h"

#include "opentelemetry/exporters/etw/etw_tracer_exporter.h"

#include <iostream>
#include <thread>

#include <cstdio>
#ifndef LOG_DEBUG
# include <mutex>

/**
* @brief Thread-safe logger routine.
* @param format
* @param args
* @return
*/
static inline int log_vprintf(const char *format, va_list args)
{
static std::mutex cout_mutex;
std::lock_guard<std::mutex> lk(cout_mutex);
return vprintf(format, args);
};

/**
* @brief Thread-safe logger routine.
* @param format
* @param
* @return
*/
static inline int log_printf(const char *format, ...)
{
int result;
va_list ap;
va_start(ap, format);
result = log_vprintf(format, ap);
va_end(ap);
return result;
};

// Debug macros
# define LOG_DEBUG(fmt_, ...) log_printf(" " fmt_ "\n", ##__VA_ARGS__)
# define LOG_TRACE(fmt_, ...) log_printf(" " fmt_ "\n", ##__VA_ARGS__)
# define LOG_INFO(fmt_, ...) log_printf(" " fmt_ "\n", ##__VA_ARGS__)
# define LOG_WARN(fmt_, ...) log_printf(" " fmt_ "\n", ##__VA_ARGS__)
# define LOG_ERROR(fmt_, ...) log_printf(" " fmt_ "\n", ##__VA_ARGS__)
#endif

using namespace OPENTELEMETRY_NAMESPACE;

using namespace opentelemetry::exporter::etw;

// Specify destination ETW Provider Name or GUID.
//
// In Visual Studio:
// - View -> Other Windows -> Diagnostic Events...
// - Click Configure (gear on top).
// - Specify "OpenTelemetry-ETW-TLD" in the list of providers.
//
// Event view shows event flow in realtime.
const char *kGlobalProviderName = "OpenTelemetry-ETW-TLD";

std::string providerName = kGlobalProviderName;

// One provider can be used to associate with different destinations.
exporter::etw::TracerProvider provider;

// Code below is generic and may be reused with any other TracerProvider.
// In ETW provider case - instrumentation name must match destination
// ETW provider name.
nostd::shared_ptr<trace::Tracer> tracer = provider.GetTracer(providerName, "1.0");

// Obtain numeric thread id
static inline uint64_t gettid()
{
std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());
return id;
}

// bop gets called by beep.
void bop()
{
LOG_TRACE("bop worker tid=%u", gettid());
auto span = tracer->StartSpan("bop");
span->AddEvent("BopEvent", {{"tid", gettid()}});
span->SetAttribute("attrib", 2);
span->End();
}

// beep gets called in its own thread.
// It is running in a thread pool, invoked via lambda by dispatcher.
void beep()
{
LOG_TRACE("beep worker tid=%u", gettid());
auto span = tracer->StartSpan("beep");
span->AddEvent("BeepEvent", {{"tid", gettid()}});
span->SetAttribute("attrib", 1);
{
auto bopScope = tracer->WithActiveSpan(span);
bop();
}
span->End();
}

// Max number of threads to spawn
constexpr int kMaxThreads = 10;

int main(int arc, char **argv)
{
std::thread pool[kMaxThreads];

// Main dispatcher span: parent of all child thread spans.
auto mainSpan = tracer->StartSpan("beep_bop");
mainSpan->SetAttribute("attrib", 0);

// Start several threads to perform beep-bop actions.
LOG_TRACE("beep-boop dispatcher tid=%u", gettid());
for (auto i = 0; i < kMaxThreads; i++)
{
pool[i] = std::thread([&]() {
// This code runs in its own worker thread.
auto beepScope = tracer->WithActiveSpan(mainSpan);
// call beep -> bop
beep();
});
};

// Join all worker threads in the pool
for (auto &th : pool)
{
try
{
if (th.joinable())
th.join();
}
catch (...)
{
// thread might have been gracefully terminated already
}
}

// End span
mainSpan->End();

return 0;
}
Loading