Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
cc_library(
name = "dd_trace_cpp",
srcs = [
"src/datadog/base64.cpp",
"src/datadog/cerr_logger.cpp",
"src/datadog/clock.cpp",
"src/datadog/config_manager.cpp",
"src/datadog/collector_response.cpp",
# "src/datadog/curl.cpp", no libcurl
"src/datadog/datadog_agent_config.cpp",
Expand All @@ -24,6 +26,7 @@ cc_library(
"src/datadog/propagation_style.cpp",
"src/datadog/random.cpp",
"src/datadog/rate.cpp",
"src/datadog/remote_config.cpp",
"src/datadog/runtime_id.cpp",
"src/datadog/span.cpp",
"src/datadog/span_data.cpp",
Expand All @@ -45,8 +48,11 @@ cc_library(
"src/datadog/w3c_propagation.cpp",
],
hdrs = [
"src/datadog/base64.h",
"src/datadog/cerr_logger.h",
"src/datadog/clock.h",
"src/datadog/config_manager.h",
"src/datadog/config_update.h",
"src/datadog/collector.h",
"src/datadog/collector_response.h",
# "src/datadog/curl.h", no libcurl
Expand Down Expand Up @@ -78,6 +84,7 @@ cc_library(
"src/datadog/propagation_style.h",
"src/datadog/random.h",
"src/datadog/rate.h",
"src/datadog/remote_config.h",
"src/datadog/runtime_id.h",
"src/datadog/sampling_decision.h",
"src/datadog/sampling_mechanism.h",
Expand All @@ -95,6 +102,7 @@ cc_library(
"src/datadog/tags.h",
"src/datadog/threaded_event_scheduler.h",
"src/datadog/tracer_config.h",
"src/datadog/tracer_signature.h",
"src/datadog/tracer_telemetry.h",
"src/datadog/tracer.h",
"src/datadog/trace_id.h",
Expand Down
23 changes: 18 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ project(dd-trace-cpp)
option(BUILD_COVERAGE "Build code with code coverage profiling instrumentation" OFF)
option(BUILD_HASHER_EXAMPLE "Build the example program examples/hasher" OFF)
option(BUILD_TESTING "Build the unit tests (test/)" OFF)
option(BUILD_FUZZERS "Build fuzzers" OFF)
option(BUILD_BENCHMARK "Build benchmark binaries" OFF)
option(SANITIZE "Build with address sanitizer and undefined behavior sanitizer" OFF)
option(FUZZ_W3C_PROPAGATION "Build a fuzzer for W3C propagation" OFF)

set(CMAKE_BUILD_TYPE "RelWithDebInfo")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo")
endif ()

set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
Expand All @@ -32,7 +36,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# But there's one exception: libfuzzer is built with libstdc++ on Ubuntu,
# and so won't link to libc++. So, if any of the FUZZ_* variables are set,
# keep to libstdc++ (the default on most systems).
if (NOT ${FUZZ_W3C_PROPAGATION})
if (NOT ${BUILD_FUZZERS})
add_compile_options(-stdlib=libc++)
add_link_options(-stdlib=libc++)
endif ()
Expand All @@ -45,11 +49,12 @@ function(add_sanitizers)
add_link_options(-fsanitize=undefined)
endfunction()

if(FUZZ_W3C_PROPAGATION)
if(BUILD_FUZZERS)
set(BUILD_TESTING OFF)
add_compile_options(-fsanitize=fuzzer)
add_link_options(-fsanitize=fuzzer)
add_sanitizers()
add_subdirectory(fuzz/w3c-propagation)
add_subdirectory(fuzz)
endif()

if (SANITIZE)
Expand Down Expand Up @@ -82,8 +87,10 @@ endif()

add_library(dd_trace_cpp-objects OBJECT)
target_sources(dd_trace_cpp-objects PRIVATE
src/datadog/base64.cpp
src/datadog/cerr_logger.cpp
src/datadog/clock.cpp
src/datadog/config_manager.cpp
src/datadog/collector_response.cpp
src/datadog/curl.cpp
src/datadog/datadog_agent_config.cpp
Expand All @@ -105,6 +112,7 @@ target_sources(dd_trace_cpp-objects PRIVATE
src/datadog/propagation_style.cpp
src/datadog/random.cpp
src/datadog/rate.cpp
src/datadog/remote_config.cpp
src/datadog/runtime_id.cpp
src/datadog/span.cpp
src/datadog/span_data.cpp
Expand Down Expand Up @@ -132,8 +140,11 @@ target_sources(dd_trace_cpp-objects PUBLIC
TYPE HEADERS
BASE_DIRS src/
FILES
src/datadog/base64.h
src/datadog/cerr_logger.h
src/datadog/clock.h
src/datadog/config_manager.h
src/datadog/config_update.h
src/datadog/collector.h
src/datadog/collector_response.h
# src/datadog/curl.h except for curl.h
Expand Down Expand Up @@ -165,6 +176,7 @@ target_sources(dd_trace_cpp-objects PUBLIC
src/datadog/propagation_style.h
src/datadog/random.h
src/datadog/rate.h
src/datadog/remote_config.h
src/datadog/runtime_id.h
src/datadog/sampling_decision.h
src/datadog/sampling_mechanism.h
Expand All @@ -182,6 +194,7 @@ target_sources(dd_trace_cpp-objects PUBLIC
src/datadog/tags.h
src/datadog/threaded_event_scheduler.h
src/datadog/tracer_config.h
src/datadog/tracer_signature.h
src/datadog/tracer_telemetry.h
src/datadog/tracer.h
src/datadog/trace_id.h
Expand Down
2 changes: 1 addition & 1 deletion bin/cmake-build
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ cd "$(dirname "$0")"/..

mkdir -p .build
cd .build
cmake ..
cmake .. "$@"
make -j "$(nproc)"
2 changes: 2 additions & 0 deletions fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(base64)
add_subdirectory(w3c-propagation)
21 changes: 8 additions & 13 deletions fuzz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,19 @@ Fuzzers
Each subdirectory here contains the source of an executable that [fuzz tests][1]
some part of the library using [LLVM's libfuzzer][2].

There is a toplevel CMake boolean option associated with each fuzzer. The naming
convention is `FUZZ_<SUBDIRECTORY_WITH_UNDERSCORES>`, e.g.
`FUZZ_W3C_PROPAGATION` for the fuzzer defined in
[fuzz/w3c-propagation/](./w3c-propagation/). The resulting binary is called
`fuzz` by convention.
There is a toplevel CMake boolean option that adds all of the fuzzer
executables to the build: `BUILD_FUZZERS`.

When building a fuzzer, the toolchain must be clang-based. For example, this
is how to build the fuzzer in [fuzz/w3c-propagation](./w3c-propagation/) from
the root of the repository:
When building the fuzzers, the toolchain must be clang-based. For example:
```console
$ rm -rf .build && mkdir .build # if toolchain or test setup need clearing
$ cd .build
$ CC=clang CXX=clang++ cmake .. -DFUZZ_W3C_PROPAGATION=ON
$ make -j $(nproc)
$ fuzz/w3c-propagation/fuzz
$ rm -rf .build # if toolchain needs clearing
$ bin/with-toolchain llvm bin/cmake-build -DBUILD_FUZZERS=1
$ .build/fuzz/w3c-propagation/w3c-propagation-fuzz

[... fuzzer output ...]
```

The fuzzer executables are named `.build/fuzz/*/*-fuzz` by convention.

[1]: https://en.wikipedia.org/wiki/Fuzzing
[2]: https://llvm.org/docs/LibFuzzer.html
4 changes: 4 additions & 0 deletions fuzz/base64/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
add_executable(base64-fuzz main.cpp)

add_dependencies(base64-fuzz dd_trace_cpp-static)
target_link_libraries(base64-fuzz dd_trace_cpp-static)
11 changes: 11 additions & 0 deletions fuzz/base64/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <datadog/base64.h>
#include <datadog/string_view.h>

#include <cstdint>

namespace dd = datadog::tracing;

extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, size_t size) {
dd::base64_decode(dd::StringView{(const char*)data, size});
return 0;
}
6 changes: 3 additions & 3 deletions fuzz/w3c-propagation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_executable(fuzz fuzz.cpp)
add_executable(w3c-propagation-fuzz fuzz.cpp)

add_dependencies(fuzz dd_trace_cpp-static)
target_link_libraries(fuzz dd_trace_cpp-static)
add_dependencies(w3c-propagation-fuzz dd_trace_cpp-static)
target_link_libraries(w3c-propagation-fuzz dd_trace_cpp-static)
88 changes: 88 additions & 0 deletions src/datadog/base64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include "base64.h"

#include <cstddef>
#include <cstdint>

namespace datadog {
namespace tracing {

constexpr uint8_t k_sentinel = 255;
constexpr uint8_t _ = k_sentinel; // for brevity
constexpr uint8_t k_eol = 0;

// Invalid inputs are mapped to the value 255. '=' maps to 0.
constexpr uint8_t k_base64_table[] = {
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, 62, _, _, _, 63, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, _, _, _, k_eol, _, _, _, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, _, _, _, _, _, _, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
_, _, _, _};

std::string base64_decode(StringView input) {
const size_t in_size = input.size();

std::string output;
output.reserve(in_size);

size_t i = 0;

for (; i + 4 < in_size;) {
uint32_t c0 = k_base64_table[static_cast<size_t>(input[i++])];
uint32_t c1 = k_base64_table[static_cast<size_t>(input[i++])];
uint32_t c2 = k_base64_table[static_cast<size_t>(input[i++])];
uint32_t c3 = k_base64_table[static_cast<size_t>(input[i++])];

if (c0 == k_sentinel || c1 == k_sentinel || c2 == k_sentinel ||
c3 == k_sentinel) {
return "";
}

output.push_back(c0 << 2 | (c1 & 0xF0) >> 4);
output.push_back((c1 & 0x0F) << 4 | ((c2 & 0x3C) >> 2));
output.push_back(((c2 & 0x03) << 6) | (c3 & 0x3F));
}

// If padding is missing, return the empty string in lieu of an Error.
if ((in_size - i) < 4) return "";

uint32_t c0 = k_base64_table[static_cast<size_t>(input[i++])];
uint32_t c1 = k_base64_table[static_cast<size_t>(input[i++])];
uint32_t c2 = k_base64_table[static_cast<size_t>(input[i++])];
uint32_t c3 = k_base64_table[static_cast<size_t>(input[i++])];

if (c0 == k_sentinel || c1 == k_sentinel || c2 == k_sentinel ||
c3 == k_sentinel) {
return "";
}

if (c2 == k_eol) {
// The last quadruplet is of the form "xx==", where only one character needs
// to be decoded.
output.push_back(c0 << 2 | (c1 & 0xF0) >> 4);
} else if (c3 == k_eol) {
// The last quadruplet is of the form "xxx=", where only two character needs
// to be decoded.
output.push_back(c0 << 2 | (c1 & 0xF0) >> 4);
output.push_back((c1 & 0x0F) << 4 | ((c2 & 0x3C) >> 2));
} else {
// The last quadruplet is not padded -> common use case
output.push_back(c0 << 2 | (c1 & 0xF0) >> 4);
output.push_back((c1 & 0x0F) << 4 | ((c2 & 0x3C) >> 2));
output.push_back(((c2 & 0x03) << 6) | (c3 & 0x3F));
}

return output;
}

} // namespace tracing
} // namespace datadog
15 changes: 15 additions & 0 deletions src/datadog/base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <string>

#include "string_view.h"

namespace datadog {
namespace tracing {

// Return the result of decoding the specified padded base64-encoded `input`. If
// `input` is not padded, then return the empty string instead.
std::string base64_decode(StringView input);

} // namespace tracing
} // namespace datadog
47 changes: 47 additions & 0 deletions src/datadog/config_manager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "config_manager.h"

#include "trace_sampler.h"

namespace datadog {
namespace tracing {

ConfigManager::ConfigManager(const FinalizedTracerConfig& config)
: clock_(config.clock),
default_trace_sampler_(
std::make_shared<TraceSampler>(config.trace_sampler, clock_)),
current_trace_sampler_(default_trace_sampler_) {}

std::shared_ptr<TraceSampler> ConfigManager::get_trace_sampler() {
std::lock_guard<std::mutex> lock(mutex_);
return current_trace_sampler_;
}

void ConfigManager::update(const ConfigUpdate& conf) {
std::lock_guard<std::mutex> lock(mutex_);

if (conf.trace_sampler) {
if (auto finalized_trace_sampler_cfg =
finalize_config(*conf.trace_sampler)) {
current_trace_sampler_ =
std::make_shared<TraceSampler>(*finalized_trace_sampler_cfg, clock_);
} else {
// TODO: report error
}
} else {
current_trace_sampler_ = default_trace_sampler_;
}
}

void ConfigManager::reset() {
std::lock_guard<std::mutex> lock(mutex_);
current_trace_sampler_ = default_trace_sampler_;
}

nlohmann::json ConfigManager::config_json() const {
std::lock_guard<std::mutex> lock(mutex_);
return nlohmann::json{
{"trace_sampler", current_trace_sampler_->config_json()}};
}

} // namespace tracing
} // namespace datadog
43 changes: 43 additions & 0 deletions src/datadog/config_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

// The `ConfigManager` class is designed to handle configuration update
// and provide access to the current configuration.
// It utilizes a mutex to ensure thread safety when updating or accessing
// the configuration.

#include <mutex>

#include "clock.h"
#include "config_update.h"
#include "json.hpp"
#include "tracer_config.h"

namespace datadog {
namespace tracing {

class ConfigManager {
mutable std::mutex mutex_;
Clock clock_;
std::shared_ptr<TraceSampler> default_trace_sampler_;
std::shared_ptr<TraceSampler> current_trace_sampler_;

public:
ConfigManager(const FinalizedTracerConfig& config);

// Return the `TraceSampler` consistent with the most recent configuration.
std::shared_ptr<TraceSampler> get_trace_sampler();

// Apply the specified `conf` update.
void update(const ConfigUpdate& conf);

// Restore the configuration that was passed to this object's constructor,
// overriding any previous calls to `update`.
void reset();

// Return a JSON representation of the current configuration managed by this
// object.
nlohmann::json config_json() const;
};

} // namespace tracing
} // namespace datadog
Loading