Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Testing/
__pycache__/
*.pyc
*.egg-info/
.env
23 changes: 23 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,29 @@ build-windows:
cmd /S /C "cmake -DCMAKE_BUILD_TYPE=${env:BUILD_CONFIGURATION} -DBUILD_SHARED_LIBS=${env:BUILD_SHARED_LIBS} -DDD_HTTP_USE_SYSTEM_LIBCURL=${env:USE_SYSTEM_LIBCURL} -DDD_CRASH_MODE=${env:DD_CRASH_MODE} -DDD_RUN_CRASHPAD_TESTS=${env:DD_RUN_CRASHPAD_TESTS} -DMSVC_RUNTIME_LIBRARY=${env:MSVC_RUNTIME_LIBRARY} -S . -B build && cmake --build build --config ${env:BUILD_CONFIGURATION} --parallel ${env:NUM_PARALLEL_BUILD_JOBS}"
- If ($LASTEXITCODE -ne "0") { throw "docker run (cmake build) returned $LASTEXITCODE" }

# Build with profiler enabled (dd-win-prof fetched via FetchContent) and verify
# that the profiling-test example compiles and links successfully.
build-windows-profiler:
stage: build
dependencies: []
tags: [windows-v2:2022]
variables:
OVERRIDE_GIT_STRATEGY: clone # Required for Windows runners
TOOLCHAIN_TARGET: vs2022
script:
- $ErrorActionPreference = "Stop"
- if (Test-Path build) { remove-item -recurse -force build }
- $env:TOOLCHAIN_IMAGE = "${env:DISTRIBUTION_REGISTRY}/${env:TOOLCHAIN_IMAGE_PATH}:${env:TOOLCHAIN_TARGET}-${env:WINDOWS_TOOLCHAIN_IMAGE_VERSION}"
- >
docker run --rm
-m 8192M
-e NUM_PARALLEL_BUILD_JOBS
-v "$(Get-Location):C:\mnt"
-w C:\mnt
${env:TOOLCHAIN_IMAGE}
cmd /S /C "cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DDD_HTTP_USE_SYSTEM_LIBCURL=OFF -DDD_CRASH_MODE=inprocess -DMSVC_RUNTIME_LIBRARY=MultiThreadedDLL -DDD_ENABLE_PROFILER=ON -DDD_BUILD_EXAMPLES=ON -S . -B build && cmake --build build --config Release --target dd_profiling_test --parallel %NUM_PARALLEL_BUILD_JOBS%"
- If ($LASTEXITCODE -ne "0") { throw "docker run (cmake build) returned $LASTEXITCODE" }

# Stage 'package' generates release artifacts for the toolchains/configurations that we
# support with precompiled binaries

Expand Down
10 changes: 7 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ endif()
set(DD_ENABLE_SANITIZERS "${DD_ENABLE_SANITIZERS}" CACHE STRING "Comma-separated list of sanitizers to enable, e.g. 'ASan,UBSan', 'MSan,UBSan', 'TSan'")
option(DD_ENABLE_ASSERTS "Enable assertions in Datadog SDK implementation" ${DD_DEVELOPMENT})
option(DD_ENABLE_TEST_ALLOCATION_TRACKING "Enable allocation tracking (via instrumented new/delete) in test binaries" ON)
option(DD_ENABLE_PROFILER "Enable integration with Datadog Windows Profiler" OFF)
if(DD_ENABLE_PROFILER AND NOT WIN32)
message(FATAL_ERROR "DD_ENABLE_PROFILER is only supported on Windows")
endif()

# C API targets C99; C++ API targets C++17
if(DD_IS_TOP_LEVEL)
Expand All @@ -57,9 +61,9 @@ if(DD_IS_TOP_LEVEL)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Crashpad requires C++20, so building the SDK with crashpad support requires
# targeting C++20
if(DD_CRASH_MODE STREQUAL "crashpad")
# Crashpad and dd-win-prof both require C++20, so building the SDK with either
# enabled requires targeting C++20
if(DD_CRASH_MODE STREQUAL "crashpad" OR DD_ENABLE_PROFILER)
set(CMAKE_CXX_STANDARD 20)
endif()
endif()
Expand Down
4 changes: 4 additions & 0 deletions cmake/DatadogConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ if(DATADOG_BUILT_WITH_DD_HTTP_USE_SYSTEM_LIBCURL)
find_dependency(CURL REQUIRED)
endif()

# If the SDK was built with profiler support, expose this to consumers so that
# datadog_enable() can invoke dd_win_prof_copy_runtime_deps() as needed
set(DATADOG_BUILT_WITH_DD_ENABLE_PROFILER @DD_ENABLE_PROFILER@)

# If the SDK was built with Crashpad support, include CrashpadConfig.cmake, ensuring
# that crashpad::client and crashpad::handler targets are defined
set(DATADOG_BUILT_WITH_DD_CRASH_MODE "@DD_CRASH_MODE@")
Expand Down
53 changes: 46 additions & 7 deletions cmake/DatadogConvenience.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,32 @@ function(datadog_enable target)
message(FATAL_ERROR "datadog_enable(): target crashpad::handler does not exist")
endif()
endif()

# If the SDK was built with profiler support, copy the required runtime DLLs
# (dd-win-prof.dll and datadog_profiling_ffi.dll) alongside the application
# binary so they can be found at runtime.
if(DD_ENABLE_PROFILER OR DATADOG_BUILT_WITH_DD_ENABLE_PROFILER)
if(COMMAND dd_win_prof_copy_runtime_deps)
dd_win_prof_copy_runtime_deps(${target})
else()
message(FATAL_ERROR
"datadog_enable(): dd_win_prof_copy_runtime_deps is not available; "
"ensure dd-win-prof was fetched before calling datadog_enable()")
endif()
endif()
endfunction()

# datadog_install(bin):
# datadog_install(destination):
#
# - (if Crashpad support is enabled) ensures that the crashpad_handler executable will
# be copied to bin/ alongside your application
# Installs runtime dependencies required by the SDK alongside your application:
# - (if Crashpad support is enabled) copies the crashpad_handler executable
# - (if Profiler support is enabled) copies dd-win-prof.dll and datadog_profiling_ffi.dll
#
# Call this function after defining `install(TARGETS my-app ...)` for your app,
# specifying the destination directory for your application's binaries in lieu of `bin`.
# You may elect not to call this function if a.) your project does not use CMake
# installation rules, b.) you don't need Crashpad support, or c.) you are ensuring that
# the crashpad_handler exectuable makes its way into your builds through other means.
# specifying the destination directory for your application's binaries (typically "bin").
# You may elect not to call this function if: a.) your project does not use CMake
# installation rules, b.) you don't need Crashpad or Profiler support, or c.) you are
# handling runtime dependencies through other means.
#
function(datadog_install destination)
# If the SDK was built with crashpad support, install the crashpad_handler
Expand All @@ -63,4 +77,29 @@ function(datadog_install destination)
message(FATAL_ERROR "datadog_install(): target crashpad::handler does not exist")
endif()
endif()

# If the SDK was built with profiler support, install the required runtime DLLs
# to the destination directory
if(DD_ENABLE_PROFILER OR DATADOG_BUILT_WITH_DD_ENABLE_PROFILER)
if(TARGET dd-win-prof)
# Try to get imported location (for find_package case)
get_target_property(DD_WIN_PROF_DLL dd-win-prof IMPORTED_LOCATION)

if(DD_WIN_PROF_DLL AND NOT DD_WIN_PROF_DLL MATCHES "-NOTFOUND$")
# Imported target case: install the DLL from its imported location
install(PROGRAMS ${DD_WIN_PROF_DLL} DESTINATION ${destination})

get_filename_component(DD_WIN_PROF_DIR ${DD_WIN_PROF_DLL} DIRECTORY)
install(PROGRAMS "${DD_WIN_PROF_DIR}/datadog_profiling_ffi.dll" DESTINATION ${destination})
else()
# FetchContent case: use generator expressions to get target files
install(FILES "$<TARGET_FILE:dd-win-prof>" DESTINATION ${destination})
if(TARGET libdatadog_dynamic)
install(FILES "$<TARGET_FILE:libdatadog_dynamic>" DESTINATION ${destination})
endif()
endif()
else()
message(FATAL_ERROR "datadog_install(): target dd-win-prof does not exist")
endif()
endif()
endfunction()
40 changes: 40 additions & 0 deletions cmake/profiler.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
include(FetchContent)

# Allow overriding the GitHub fetch with a local clone of dd-win-prof. When
# set, CMake skips the network fetch and uses the local source directory
# directly.
set(DD_WIN_PROF_SOURCE_DIR "" CACHE PATH
"Local path to dd-win-prof source tree (overrides GitHub fetch)")

if(DD_WIN_PROF_SOURCE_DIR)
FetchContent_Declare(
dd-win-prof
SOURCE_DIR "${DD_WIN_PROF_SOURCE_DIR}"
)
else()
# TODO: Pin to a released tag once the split RUM context API is merged.
# For now, this points to a specific commit on main.
FetchContent_Declare(
dd-win-prof
GIT_REPOSITORY https://github.com/DataDog/dd-win-prof.git
GIT_TAG 75f644a
)
endif()

FetchContent_MakeAvailable(dd-win-prof)

# Link dd-win-prof into the SDK as a public dependency so that consumers can
# include dd-win-prof.h directly if needed.
target_link_libraries(dd_native PUBLIC dd-win-prof)

if(DD_BUILD_INSTALL)
# Install dd-win-prof.dll to bin/ so it ships alongside the SDK. Include it in
# the DatadogTargets export set since dd_native depends on it.
install(TARGETS dd-win-prof
EXPORT DatadogTargets
RUNTIME DESTINATION bin
)

# Install datadog_profiling_ffi.dll, which dd-win-prof.dll loads at runtime.
install(FILES "$<TARGET_FILE:libdatadog_dynamic>" DESTINATION bin)
endif()
5 changes: 5 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ target_enable_coverage(dd_native_repl)
target_enable_sanitizers(dd_native_repl)
target_include_directories(dd_native_repl PRIVATE .)
datadog_enable(dd_native_repl)

# Profiling + RUM integration smoke test (requires dd-win-prof)
if(DD_ENABLE_PROFILER)
add_subdirectory(profiling-test)
endif()
11 changes: 11 additions & 0 deletions examples/profiling-test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Profiling + RUM integration smoke test
# Only built when DD_ENABLE_PROFILER is ON (requires dd-win-prof).

add_executable(dd_profiling_test
main.cpp
)
target_compile_definitions(dd_profiling_test PRIVATE _CRT_SECURE_NO_WARNINGS)
target_enable_strict_warnings(dd_profiling_test)
target_enable_coverage(dd_profiling_test)
target_enable_sanitizers(dd_profiling_test)
datadog_enable(dd_profiling_test)
62 changes: 62 additions & 0 deletions examples/profiling-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# profiling-test

Minimal smoke test that exercises the dd-sdk-cpp **Profiling + RUM** integration end-to-end.

## What it does

1. Creates a `datadog::Core` (service=`profiling-test`, env=`dev`, staging endpoint)
2. Registers **Profiling** (agentless, symbolize=true) and **RUM**
3. Calls `core->Start()` which auto-wires RUM context into the profiler
4. Simulates 3 view transitions with ~5s of CPU busy-loop each:
- `HomePage` -> `SettingsPage` -> `ProfilePage`
5. Calls `core->Stop()` and prints a summary

Total runtime: ~15s. The profiler should collect CPU/wall-time samples tagged with the active RUM view.

## Environment variables

| Variable | Read by | Description |
|---|---|---|
| `DD_CLIENT_TOKEN` | Code | Client token (used by Core for RUM) |
| `DD_RUM_APPLICATION_ID` | Code | RUM application ID |
| `DD_API_KEY` | Profiler (env) | Datadog API key for profile upload |
| `DD_SITE` | Profiler (env) | Datadog site, e.g. `datad0g.com` (staging) or `datadoghq.com` (prod) |

Create a `.env` file in this directory (git-ignored):

```
DD_CLIENT_TOKEN=your-client-token-here
DD_RUM_APPLICATION_ID=your-rum-app-id-here
DD_API_KEY=your-api-key-here
DD_SITE=datad0g.com
```

Then source it before running:

```powershell
# Load env vars from .env
. .\load-env.ps1

# Or specify a custom path
. .\load-env.ps1 -Path C:\path\to\.env
```

## Build

From the `dd-sdk-cpp` root:

```powershell
cmake -B build -G "Visual Studio 17 2022" -A x64 `
-DDD_ENABLE_PROFILER=ON `
-DDD_BUILD_EXAMPLES=ON `
-DDD_HTTP_USE_SYSTEM_LIBCURL=OFF `
-DDD_WIN_PROF_SOURCE_DIR="$PWD\..\dd-win-prof"

cmake --build build --config Release --target dd_profiling_test
```

## Run

```powershell
.\build\examples\profiling-test\Release\dd_profiling_test.exe
```
37 changes: 37 additions & 0 deletions examples/profiling-test/load-env.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Load environment variables from a .env file.
# Usage:
# . .\load-env.ps1 # loads .env from current directory
# . .\load-env.ps1 -Path C:\path\to\.env

param(
[string]$Path = (Join-Path $PSScriptRoot ".env")
)

if (-not (Test-Path $Path)) {
Write-Error "File not found: $Path"
return
}

$count = 0
Get-Content $Path | ForEach-Object {
$line = $_.Trim()
# Skip empty lines and comments
if ($line -eq "" -or $line.StartsWith("#")) { return }

$eq = $line.IndexOf("=")
if ($eq -lt 1) { return }

$name = $line.Substring(0, $eq).Trim()
$value = $line.Substring($eq + 1).Trim()
# Strip surrounding quotes if present
if (($value.StartsWith('"') -and $value.EndsWith('"')) -or
($value.StartsWith("'") -and $value.EndsWith("'"))) {
$value = $value.Substring(1, $value.Length - 2)
}

[System.Environment]::SetEnvironmentVariable($name, $value, "Process")
Write-Host " $name = $($value.Substring(0, [Math]::Min(8, $value.Length)))..."
$count++
}

Write-Host "Loaded $count variable(s) from $Path"
Loading