Skip to content
Open
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
23 changes: 22 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions components-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ datadog-sidecar = { path = "../libdatadog/datadog-sidecar" }
datadog-sidecar-ffi = { path = "../libdatadog/datadog-sidecar-ffi" }
libdd-tinybytes = { path = "../libdatadog/libdd-tinybytes" }
libdd-trace-utils = { path = "../libdatadog/libdd-trace-utils" }
libdd-trace-stats = { path = "../libdatadog/libdd-trace-stats" }
libdd-crashtracker-ffi = { path = "../libdatadog/libdd-crashtracker-ffi", default-features = false, features = ["collector"] }
libdd-library-config-ffi = { path = "../libdatadog/libdd-library-config-ffi", default-features = false }
spawn_worker = { path = "../libdatadog/spawn_worker" }
Expand Down
92 changes: 92 additions & 0 deletions components-rs/agent_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

//! FFI wrappers that read from the agent /info SHM (`AgentInfoReader`) and propagate
//! the results to the concentrator and trace-filter subsystems.
//!
//! Both functions perform a single `reader.read()` call (advancing the internal SHM
//! position once) and use the returned `changed` flag to decide whether to rebuild
//! the concentrator and filter configuration.

use crate::stats::apply_concentrator_config;
use datadog_sidecar::service::agent_info::AgentInfoReader;
use libdd_common_ffi::slice::CharSlice;
use std::ffi::c_char;

/// Read all agent /info data in one SHM read and apply env, container-hash and concentrator
/// config atomically.
///
/// Fills `env_out` with the agent's `config.default_env` (zero-length slice if absent).
/// Fills `container_hash_out` with `container_tags_hash` (zero-length slice if absent).
/// Both slices borrow from the reader's cached info — valid until the next `reader.read()`.
///
/// Concentrator config (peer tags, span kinds, trace filters) is applied only when the
/// SHM has changed since the last read (`changed == true`). Calling this once at RINIT
/// ensures the config is always applied before the first span is processed, so the
/// per-span `ddog_apply_agent_info_concentrator_config` can safely rely on `changed` alone.
///
/// # Safety
/// `reader` must be a valid pointer to an `AgentInfoReader`.
#[no_mangle]
pub unsafe extern "C" fn ddog_apply_agent_info(
reader: &mut AgentInfoReader,
env_out: &mut CharSlice<'static>,
container_hash_out: &mut CharSlice<'static>,
) {
let (changed, info) = reader.read();
if let Some(info) = info {
if let Some(s) = info
.config
.as_ref()
.and_then(|c| c.default_env.as_deref())
.filter(|s| !s.is_empty())
{
*env_out = CharSlice::from_raw_parts(s.as_ptr() as *const c_char, s.len());
}
if changed {
if let Some(s) = info.container_tags_hash.as_deref().filter(|s| !s.is_empty()) {
*container_hash_out = CharSlice::from_raw_parts(s.as_ptr() as *const c_char, s.len());
} else {
*container_hash_out = CharSlice::empty();
}
apply_concentrator_config(
info.peer_tags.as_deref().unwrap_or(&[]).to_owned(),
info.span_kinds_stats_computed.as_deref().unwrap_or(&[]).to_owned(),
info.filter_tags.as_ref().and_then(|f| f.require.as_deref()).unwrap_or(&[]).to_owned(),
info.filter_tags.as_ref().and_then(|f| f.reject.as_deref()).unwrap_or(&[]).to_owned(),
info.filter_tags_regex.as_ref().and_then(|f| f.require.as_deref()).unwrap_or(&[]).to_owned(),
info.filter_tags_regex.as_ref().and_then(|f| f.reject.as_deref()).unwrap_or(&[]).to_owned(),
info.ignore_resources.as_deref().unwrap_or(&[]).to_owned(),
);
}
}
}

/// Apply concentrator config changes from the agent /info SHM.
///
/// Cheap no-op when the SHM has not changed (`changed == false`). Only applies when
/// new data has arrived mid-request — `ddog_apply_agent_info` at RINIT guarantees the
/// initial configuration is already in place, so `changed` alone is sufficient here.
///
/// # Safety
/// `reader` must be a valid pointer to an `AgentInfoReader`.
#[no_mangle]
pub unsafe extern "C" fn ddog_apply_agent_info_concentrator_config(
reader: &mut AgentInfoReader,
) {
let (changed, info) = reader.read();
if !changed {
return;
}
if let Some(info) = info {
apply_concentrator_config(
info.peer_tags.as_deref().unwrap_or(&[]).to_owned(),
info.span_kinds_stats_computed.as_deref().unwrap_or(&[]).to_owned(),
info.filter_tags.as_ref().and_then(|f| f.require.as_deref()).unwrap_or(&[]).to_owned(),
info.filter_tags.as_ref().and_then(|f| f.reject.as_deref()).unwrap_or(&[]).to_owned(),
info.filter_tags_regex.as_ref().and_then(|f| f.require.as_deref()).unwrap_or(&[]).to_owned(),
info.filter_tags_regex.as_ref().and_then(|f| f.reject.as_deref()).unwrap_or(&[]).to_owned(),
info.ignore_resources.as_deref().unwrap_or(&[]).to_owned(),
);
}
}
114 changes: 112 additions & 2 deletions components-rs/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ typedef struct _zend_string _zend_string;

#define ddog_LOG_ONCE (1 << 3)

/**
* Number of gRPC status-code keys checked by the stats aggregation (must match
* `GRPC_STATUS_CODE_FIELD` in libdd-trace-stats/src/span_concentrator/aggregation.rs).
*/
#define ddog_PHP_GRPC_KEY_COUNT 4

#define ddog_MultiTargetFetcher_DEFAULT_CLIENTS_LIMIT 100

typedef enum ddog_ConfigurationOrigin {
Expand Down Expand Up @@ -406,6 +412,7 @@ typedef enum ddog_RemoteConfigCapabilities {
DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLE_LIVE_DEBUGGING = 41,
DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_DD_MULTICONFIG = 42,
DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRACE_TAGGING_RULES = 43,
DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_MULTICONFIG = 45,
DDOG_REMOTE_CONFIG_CAPABILITIES_FFE_FLAG_CONFIGURATION_RULES = 46,
} ddog_RemoteConfigCapabilities;

Expand All @@ -426,6 +433,8 @@ typedef enum ddog_SpanProbeTarget {
DDOG_SPAN_PROBE_TARGET_ROOT,
} ddog_SpanProbeTarget;

typedef struct ddog_AgentInfoReader ddog_AgentInfoReader;

typedef struct ddog_DebuggerPayload ddog_DebuggerPayload;

typedef struct ddog_DslString ddog_DslString;
Expand Down Expand Up @@ -455,6 +464,20 @@ typedef struct ddog_SidecarActionsBuffer ddog_SidecarActionsBuffer;
*/
typedef struct ddog_SidecarTransport ddog_SidecarTransport;

/**
* Opaque shared-memory span stats concentrator exposed to C.
*
* Always heap-allocated (as a `Box`) — C holds a raw pointer and must pass it back to
* `ddog_span_concentrator_drop` to free.
*
* When `inner` is `None` this is a *virtual* concentrator: the SHM has not been created by the
* sidecar yet, but peer-tag keys and span-kinds from `DESIRED_CONFIG` are still available so the
* C callback can run eligibility checks and extract peer tags. A virtual concentrator is always
* considered stale (`needs_refresh` returns `true`) so it will be upgraded to a real one on the
* next call once the SHM becomes available.
*/
typedef struct ddog_SpanConcentrator ddog_SpanConcentrator;

/**
* Holds the raw parts of a Rust Vec; it should only be created from Rust,
* never from C.
Expand Down Expand Up @@ -648,8 +671,97 @@ typedef struct ddog_Vec_DebuggerPayload {
*/
typedef uint64_t ddog_QueueId;

/**
* A (key, value) pair for peer-service tags, borrowed from PHP/concentrator memory.
*/
typedef struct ddog_PhpPeerTag {
/**
* Key string — borrows from the concentrator's `peer_tag_keys` Vec<String>.
*/
ddog_CharSlice key;
/**
* Value string — borrows from PHP span meta memory.
*/
ddog_CharSlice value;
} ddog_PhpPeerTag;

/**
* Flat representation of a PHP span's stats-relevant fields, filled by C code in one call.
*
* All `CharSlice` fields borrow from PHP memory (or from the concentrator for peer-tag keys) and
* must remain valid for the duration of `ddog_span_concentrator_add_php_span`.
*
* For absent optional strings pass an empty slice (ptr may be non-null with len == 0).
* For absent optional `f64` values pass `f64::NAN`.
*/
typedef struct ddog_PhpSpanStats {
ddog_CharSlice service;
ddog_CharSlice resource;
ddog_CharSlice name;
ddog_CharSlice type;
int64_t start;
int64_t duration;
bool is_error;
bool is_trace_root;
bool is_measured;
bool has_top_level;
bool is_partial_snapshot;
ddog_CharSlice span_kind;
ddog_CharSlice http_status_code_str;
double http_status_code_f64;
ddog_CharSlice http_method;
ddog_CharSlice http_endpoint;
ddog_CharSlice http_route;
ddog_CharSlice origin;
/**
* Value of the `_dd.svc_src` meta tag; empty slice when absent.
*/
ddog_CharSlice service_source;
/**
* gRPC meta values in order: rpc.grpc.status_code, grpc.code, rpc.grpc.status.code,
* grpc.status.code. Empty slice = absent.
*/
ddog_CharSlice grpc_meta[ddog_PHP_GRPC_KEY_COUNT];
/**
* Same gRPC keys but from metrics (NaN = absent).
*/
double grpc_metrics[ddog_PHP_GRPC_KEY_COUNT];
/**
* Number of (key,value) pairs in `peer_tags`.
*/
uintptr_t peer_tags_count;
/**
* Pointer to an array of `peer_tags_count` `PhpPeerTag` pairs.
* May be null when `peer_tags_count == 0`.
*/
const struct ddog_PhpPeerTag *peer_tags;
} ddog_PhpSpanStats;

typedef struct ddog_HashMap_ShmCacheKey__ShmCache ddog_ShmCacheMap;

/**
* Fast path: exact-key lookup into a root span. Returns null when the key is absent.
*/
typedef const char *(*ddog_RootTagLookupFn)(const void *ctx,
const char *key,
uintptr_t key_len,
uintptr_t *out_len);

/**
* Per-entry callback passed to `RootMetaIterFn`. Return `false` to stop iteration early.
*/
typedef bool (*ddog_MetaEntryCb)(void *iter_ctx,
const char *key,
uintptr_t key_len,
const char *val,
uintptr_t val_len);

/**
* Slow-path meta iterator. `NULL` when no regex-key filter entries are present.
* Iterates all string meta entries, calling `cb` for each; stops when `cb` returns `false`.
*/
typedef void (*ddog_RootMetaIterFn)(const void *ctx, void *iter_ctx, ddog_MetaEntryCb cb);

/**
* A 128-bit (16 byte) buffer containing the UUID.
*
Expand Down Expand Up @@ -1020,8 +1132,6 @@ typedef enum ddog_DynamicInstrumentationConfigState {
DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_NOT_SET,
} ddog_DynamicInstrumentationConfigState;

typedef struct ddog_AgentInfoReader ddog_AgentInfoReader;

typedef struct ddog_AgentRemoteConfigReader ddog_AgentRemoteConfigReader;

typedef struct ddog_AgentRemoteConfigWriter_ShmHandle ddog_AgentRemoteConfigWriter_ShmHandle;
Expand Down
Loading
Loading