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
47 changes: 43 additions & 4 deletions datadog-live-debugger-ffi/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use datadog_live_debugger::debugger_defs::{ProbeMetadata, ProbeMetadataLocation, ProbeStatus};
use datadog_live_debugger::{
CaptureConfiguration, DslString, EvaluateAt, InBodyLocation, MetricKind, ProbeCondition,
ProbeValue, SpanProbeTarget,
CaptureConfiguration, CaptureExpression as RustCaptureExpression, DslString, EvaluateAt,
InBodyLocation, MetricKind, ProbeCondition, ProbeValue, SpanProbeTarget,
};
use libdd_common_ffi::slice::AsBytes;
use libdd_common_ffi::{CharSlice, Option};
Expand Down Expand Up @@ -57,27 +57,66 @@ impl<'a> From<&'a datadog_live_debugger::MetricProbe> for MetricProbe<'a> {
}
}

#[repr(C)]
pub struct CaptureExpression<'a> {
pub name: CharSlice<'a>,
pub expr: &'a ProbeValue,
pub capture: &'a CaptureConfiguration,
}

#[repr(C)]
pub struct LogProbe<'a> {
pub segments: &'a DslString,
pub when: &'a ProbeCondition,
pub capture: &'a CaptureConfiguration,
pub capture_snapshot: bool,
pub sampling_snapshots_per_second: u32,
pub capture_expressions: *const CaptureExpression<'a>,
pub capture_expressions_num: usize,
}

impl<'a> From<&'a datadog_live_debugger::LogProbe> for LogProbe<'a> {
fn from(from: &'a datadog_live_debugger::LogProbe) -> Self {
LogProbe {
let exprs: Vec<CaptureExpression<'a>> = from
.capture_expressions
.iter()
.map(|e: &'a RustCaptureExpression| CaptureExpression {
name: e.name.as_str().into(),
expr: &e.expr,
capture: &e.capture,
})
.collect();
let new = LogProbe {
segments: &from.segments,
when: &from.when,
capture: &from.capture,
capture_snapshot: from.capture_snapshot,
sampling_snapshots_per_second: from.sampling_snapshots_per_second,
capture_expressions: exprs.as_ptr(),
capture_expressions_num: exprs.len(),
};
std::mem::forget(exprs);
new
}
}

impl Drop for LogProbe<'_> {
fn drop(&mut self) {
if !self.capture_expressions.is_null() {
unsafe {
Vec::from_raw_parts(
self.capture_expressions as *mut CaptureExpression,
self.capture_expressions_num,
self.capture_expressions_num,
);
}
}
}
}

#[no_mangle]
pub extern "C" fn ddog_drop_log_probe_capture_expressions(_: LogProbe) {}

#[repr(C)]
pub struct Tag<'a> {
pub name: CharSlice<'a>,
Expand Down Expand Up @@ -129,7 +168,7 @@ impl<'a> From<&'a datadog_live_debugger::SpanDecorationProbe> for SpanDecoration
}

#[no_mangle]
extern "C" fn drop_span_decoration_probe(_: SpanDecorationProbe) {}
extern "C" fn ddog_drop_span_decoration_probe(_: SpanDecorationProbe) {}

impl Drop for SpanDecorationProbe<'_> {
fn drop(&mut self) {
Expand Down
21 changes: 12 additions & 9 deletions datadog-live-debugger-ffi/src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl<'e> datadog_live_debugger::Evaluator<'e, c_void> for EvalCtx<'e> {
(self.eval.length)(self.context, value)
}

fn try_enumerate(&mut self, value: &'e c_void) -> ResultValue<Vec<&'e c_void>> {
fn try_enumerate(&mut self, value: &'e c_void) -> ResultValue<Vec<(&'e c_void, &'e c_void)>> {
let collection = (self.eval.try_enumerate)(self.context, value);
if collection.count < 0 {
Err(if collection.count == EVALUATOR_RESULT_REDACTED as isize {
Expand All @@ -170,15 +170,18 @@ impl<'e> datadog_live_debugger::Evaluator<'e, c_void> for EvalCtx<'e> {
ResultError::Invalid
})
} else {
// We need to copy, Vec::from_raw_parts with only free in the allocator would be
// unstable...
let mut vec = Vec::with_capacity(collection.count as usize);
// elements are interleaved key-value pairs: [k0, v0, k1, v1, ...]
// count is the number of pairs (not the number of raw pointers)
let count = collection.count as usize;
let mut vec = Vec::with_capacity(count);
unsafe {
vec.extend_from_slice(std::slice::from_raw_parts(
collection.elements as *const &c_void,
collection.count as usize,
))
};
let ptrs = collection.elements as *const *const c_void;
for i in 0..count {
let key = &**ptrs.add(i * 2);
let val = &**ptrs.add(i * 2 + 1);
vec.push((key, val));
}
}
(collection.free)(collection);
Ok(vec)
}
Expand Down
66 changes: 63 additions & 3 deletions datadog-live-debugger-ffi/src/send_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use crate::data::Probe;
use datadog_live_debugger::debugger_defs::{
Capture as DebuggerCaptureAlias, Capture, Captures, DebuggerData, DebuggerPayload, Diagnostics,
DiagnosticsError, Entry, Fields, ProbeMetadata, ProbeMetadataLocation, ProbeStatus, Snapshot,
SnapshotEvaluationError, SnapshotStackFrame, Value as DebuggerValueAlias,
SnapshotEvaluationError, SnapshotStackFrame, Throwable, Value as DebuggerValueAlias,
};
use datadog_live_debugger::sender::generate_new_id;
use datadog_live_debugger::{
add_redacted_name, add_redacted_type, is_redacted_name, is_redacted_type,
add_excluded_name, add_redacted_name, add_redacted_type, is_redacted_name, is_redacted_type,
};
use libdd_common_ffi::slice::AsBytes;

Expand Down Expand Up @@ -126,7 +126,7 @@ pub extern "C" fn ddog_create_exception_snapshot<'a>(
}),
language: language.to_utf8_lossy(),
id: id.to_utf8_lossy(),
exception_capture_id: Some(exception_id.to_utf8_lossy()),
exception_id: Some(exception_id.to_utf8_lossy()),
exception_hash: Some(exception_hash.to_utf8_lossy()),
frame_index: Some(frame_index),
timestamp,
Expand Down Expand Up @@ -155,6 +155,15 @@ pub extern "C" fn ddog_create_exception_snapshot<'a>(
}
}

/// Returns a mutable pointer to the last DebuggerPayload in the Vec.
/// Used to push stack frames to exception replay snapshots after creation.
#[no_mangle]
pub extern "C" fn ddog_vec_last_debugger_payload<'a>(
buffer: &'a mut Vec<DebuggerPayload<'a>>,
) -> Option<&'a mut DebuggerPayload<'a>> {
buffer.last_mut()
}

#[no_mangle]
pub extern "C" fn ddog_create_log_probe_snapshot<'a>(
probe: &'a Probe,
Expand Down Expand Up @@ -265,6 +274,12 @@ pub unsafe extern "C" fn ddog_snapshot_add_redacted_name(name: CharSlice) {
add_redacted_name(name.as_bytes())
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn ddog_snapshot_add_excluded_name(name: CharSlice) {
add_excluded_name(name.as_bytes())
}

#[no_mangle]
pub extern "C" fn ddog_snapshot_redacted_type(name: CharSlice) -> bool {
is_redacted_type(name.as_bytes())
Expand All @@ -276,6 +291,51 @@ pub unsafe extern "C" fn ddog_snapshot_add_redacted_type(name: CharSlice) {
add_redacted_type(name.as_bytes())
}

#[no_mangle]
#[allow(improper_ctypes_definitions)]
pub extern "C" fn ddog_snapshot_set_throwable<'a, 'b: 'a>(
capture: &mut DebuggerCapture<'a>,
throwable_type: CharSlice<'b>,
message: CharSlice<'b>,
) {
capture.0.throwable = Some(Throwable {
r#type: throwable_type.to_utf8_lossy(),
message: message.to_utf8_lossy(),
stacktrace: Vec::new(),
});
}

#[no_mangle]
#[allow(improper_ctypes_definitions)]
pub extern "C" fn ddog_snapshot_throwable_add_frame<'a, 'b: 'a>(
capture: &mut DebuggerCapture<'a>,
file: CharSlice<'b>,
function: CharSlice<'b>,
line: i64,
) {
if let Some(throwable) = &mut capture.0.throwable {
throwable.stacktrace.push(SnapshotStackFrame {
file_name: file.to_utf8_lossy(),
function: function.to_utf8_lossy(),
line_number: line,
column_number: None,
});
}
}

#[no_mangle]
#[allow(improper_ctypes_definitions)]
pub extern "C" fn ddog_snapshot_add_capture_fields<'a, 'b: 'a, 'c: 'a>(
capture: &mut DebuggerCapture<'a>,
name: CharSlice<'b>,
value: CaptureValue<'c>,
) {
capture
.0
.capture_expressions
.insert(name.to_utf8_lossy(), value.into());
}

#[no_mangle]
#[allow(improper_ctypes_definitions)] // Vec has a fixed size, and we care only about that here
pub extern "C" fn ddog_snapshot_add_field<'a, 'b: 'a, 'c: 'a>(
Expand Down
27 changes: 24 additions & 3 deletions datadog-live-debugger/src/debugger_defs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use serde::{Deserialize, Serialize};
use serde::{Deserialize, Serialize, Serializer};
use std::borrow::Cow;
use std::collections::HashMap;

Expand All @@ -11,7 +11,9 @@ pub struct DebuggerPayload<'a> {
pub ddsource: Cow<'static, str>,
pub timestamp: u64,
pub debugger: DebuggerData<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<Cow<'a, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub process_tags: Option<Cow<'a, str>>,
}

Expand Down Expand Up @@ -60,10 +62,11 @@ pub struct Snapshot<'a> {
pub id: Cow<'a, str>,
pub timestamp: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub exception_capture_id: Option<Cow<'a, str>>,
pub exception_id: Option<Cow<'a, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exception_hash: Option<Cow<'a, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(serialize_with = "serialize_option_u32_as_string")]
pub frame_index: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub captures: Option<Captures<'a>>,
Expand All @@ -87,6 +90,7 @@ pub struct Captures<'a> {

pub type Fields<'a> = HashMap<Cow<'a, str>, Value<'a>>;
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Capture<'a> {
#[serde(skip_serializing_if = "HashMap::is_empty")]
#[serde(rename = "staticFields")]
Expand All @@ -96,12 +100,22 @@ pub struct Capture<'a> {
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub locals: Fields<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
pub throwable: Option<Value<'a>>,
pub throwable: Option<Throwable<'a>>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub capture_expressions: Fields<'a>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Entry<'a>(pub Value<'a>, pub Value<'a>);

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Throwable<'a> {
pub r#type: Cow<'a, str>,
pub message: Cow<'a, str>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub stacktrace: Vec<SnapshotStackFrame<'a>>,
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Value<'a> {
Expand Down Expand Up @@ -159,3 +173,10 @@ pub struct DiagnosticsError<'a> {
pub message: Cow<'a, str>,
pub stacktrace: Option<Cow<'a, str>>,
}

fn serialize_option_u32_as_string<S>(val: &Option<u32>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&val.unwrap_or_default().to_string())
}
6 changes: 5 additions & 1 deletion datadog-live-debugger/src/expr_defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ impl Display for CollectionSource {

#[derive(Debug)]
pub enum Reference {
IteratorVariable,
IteratorVariable, // @it — current iteration value
IteratorKey, // @key — current iteration key
IteratorValue, // @value — current iteration value
Base(String),
Index(Box<(CollectionSource, Value)>), // i.e. foo[bar]
Nested(Box<(Reference, Value)>), // i.e. foo.bar
Expand All @@ -33,6 +35,8 @@ impl Display for Reference {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Reference::IteratorVariable => f.write_str("@it"),
Reference::IteratorKey => f.write_str("@key"),
Reference::IteratorValue => f.write_str("@value"),
Reference::Base(s) => s.fmt(f),
Reference::Index(b) => {
let (source, index) = &**b;
Expand Down
Loading
Loading