Skip to content
Draft
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
28 changes: 13 additions & 15 deletions datadog-profiling-replayer/src/replayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ pub struct Replayer<'pprof> {
pub start_time: SystemTime,
pub duration: Duration,
pub end_time: SystemTime, // start_time + duration
pub sample_types: Vec<api::ValueType<'pprof>>,
pub period: Option<api::Period<'pprof>>,
pub sample_types: Vec<api::SampleType>,
pub period: Option<api::Period>,
pub endpoints: Vec<(u64, &'pprof str)>,
pub samples: Vec<(Option<Timestamp>, api::Sample<'pprof>)>,
}
Expand Down Expand Up @@ -57,29 +57,27 @@ impl<'pprof> Replayer<'pprof> {

fn sample_types<'a>(
profile_index: &'a ProfileIndex<'pprof>,
) -> anyhow::Result<Vec<api::ValueType<'pprof>>> {
) -> anyhow::Result<Vec<api::SampleType>> {
let mut sample_types = Vec::with_capacity(profile_index.pprof.sample_types.len());
for sample_type in profile_index.pprof.sample_types.iter() {
sample_types.push(api::ValueType::new(
profile_index.get_string(sample_type.r#type)?,
profile_index.get_string(sample_type.unit)?,
))
let type_str = profile_index.get_string(sample_type.r#type)?;
let unit = profile_index.get_string(sample_type.unit)?;
let vt = api::ValueType::new(type_str, unit);
sample_types.push(vt.try_into()?);
}
Ok(sample_types)
}

fn period<'a>(
profile_index: &'a ProfileIndex<'pprof>,
) -> anyhow::Result<Option<api::Period<'pprof>>> {
fn period<'a>(profile_index: &'a ProfileIndex<'pprof>) -> anyhow::Result<Option<api::Period>> {
let value = profile_index.pprof.period;

match profile_index.pprof.period_type {
Some(period_type) => {
let r#type = api::ValueType::new(
profile_index.get_string(period_type.r#type)?,
profile_index.get_string(period_type.unit)?,
);
Ok(Some(api::Period { r#type, value }))
let type_str = profile_index.get_string(period_type.r#type)?;
let unit = profile_index.get_string(period_type.unit)?;
let vt = api::ValueType::new(type_str, unit);
let sample_type = vt.try_into()?;
Ok(Some(api::Period { sample_type, value }))
}
None => Ok(None),
}
Expand Down
12 changes: 5 additions & 7 deletions examples/cxx/profiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ int main() {
std::cout << "=== Datadog Profiling CXX Bindings Example ===" << std::endl;
std::cout << "\nCreating Profile..." << std::endl;

ValueType wall_time{
.type_ = "wall-time",
.unit = "nanoseconds"
};

Period period{
.value_type = wall_time,
.value_type = SampleType::WallTime,
.value = 60
};

auto profile = Profile::create({wall_time}, period);
// Create profile with predefined sample types
// Note: ExperimentalCount, ExperimentalNanoseconds, and ExperimentalBytes
// are available for custom profiling metrics
auto profile = Profile::create({SampleType::WallTime}, period);
std::cout << "✅ Profile created" << std::endl;

std::cout << "Adding upscaling rules..." << std::endl;
Expand Down
12 changes: 6 additions & 6 deletions examples/ffi/exporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ int main(int argc, char *argv[]) {

const auto service = argv[1];

const ddog_prof_ValueType wall_time = {
.type_ = DDOG_CHARSLICE_C_BARE("wall-time"),
.unit = DDOG_CHARSLICE_C_BARE("nanoseconds"),
// Use the SampleType enum instead of ValueType struct
const ddog_prof_SampleType wall_time = DDOG_PROF_SAMPLE_TYPE_WALL_TIME;
const ddog_prof_Slice_SampleType sample_types = {&wall_time, 1};
const ddog_prof_Period period = {
.sample_type = wall_time,
.value = 60,
};

const ddog_prof_Slice_ValueType sample_types = {&wall_time, 1};
const ddog_prof_Period period = {wall_time, 60};
ddog_prof_Profile_NewResult profile_new_result = ddog_prof_Profile_new(sample_types, &period);
if (profile_new_result.tag != DDOG_PROF_PROFILE_NEW_RESULT_OK) {
print_error("Failed to make new profile: ", profile_new_result.err);
Expand Down
11 changes: 6 additions & 5 deletions examples/ffi/profile_intern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ void wait_for_user(std::string s) {
}

int main(void) {
const ddog_prof_ValueType wall_time = {
.type_ = to_slice_c_char("wall-time"),
.unit = to_slice_c_char("nanoseconds"),
// Use the SampleType enum instead of ValueType struct
const ddog_prof_SampleType wall_time = DDOG_PROF_SAMPLE_TYPE_WALL_TIME;
const ddog_prof_Slice_SampleType sample_types = {&wall_time, 1};
const ddog_prof_Period period = {
.sample_type = wall_time,
.value = 60,
};
const ddog_prof_Slice_ValueType sample_types = {&wall_time, 1};
const ddog_prof_Period period = {wall_time, 60};

ddog_prof_Profile_NewResult new_result = ddog_prof_Profile_new(sample_types, &period);
if (new_result.tag != DDOG_PROF_PROFILE_NEW_RESULT_OK) {
Expand Down
11 changes: 6 additions & 5 deletions examples/ffi/profiles.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
#define NUM_SAMPLES 5000000

int main(void) {
const ddog_prof_ValueType wall_time = {
.type_ = DDOG_CHARSLICE_C("wall-time"),
.unit = DDOG_CHARSLICE_C("nanoseconds"),
// Use the SampleType enum instead of ValueType struct
const ddog_prof_SampleType wall_time = DDOG_PROF_SAMPLE_TYPE_WALL_TIME;
const ddog_prof_Slice_SampleType sample_types = {&wall_time, 1};
const ddog_prof_Period period = {
.sample_type = wall_time,
.value = 60,
};
const ddog_prof_Slice_ValueType sample_types = {&wall_time, 1};
const ddog_prof_Period period = {wall_time, 60};

// Create a ProfilesDictionary for the new API
ddog_prof_ProfilesDictionaryHandle dict = {0};
Expand Down
90 changes: 28 additions & 62 deletions libdd-profiling-ffi/src/profiles/datatypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,14 @@ impl From<anyhow::Result<internal::EncodedProfile>> for SerializeResult {
}
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct ValueType<'a> {
pub type_: CharSlice<'a>,
pub unit: CharSlice<'a>,
}

impl<'a> ValueType<'a> {
pub fn new(type_: &'a str, unit: &'a str) -> Self {
Self {
type_: type_.into(),
unit: unit.into(),
}
}
}
/// Sample types supported by Datadog profilers.
/// These correspond to the ValueType entries used in pprof profiles.
/// Re-exported from libdd_profiling::api for FFI use.
pub use api::SampleType;

#[repr(C)]
pub struct Period<'a> {
pub type_: ValueType<'a>,
pub value: i64,
}
/// Re-export Period from the API for consistency.
/// The FFI Period is identical to the API Period.
pub use api::Period;

#[repr(C)]
#[derive(Copy, Clone, Default)]
Expand Down Expand Up @@ -283,24 +270,6 @@ impl<'a> From<&'a Mapping<'a>> for api::StringIdMapping {
}
}

impl<'a> From<&'a ValueType<'a>> for api::ValueType<'a> {
fn from(vt: &'a ValueType<'a>) -> Self {
Self::new(
vt.type_.try_to_utf8().unwrap_or(""),
vt.unit.try_to_utf8().unwrap_or(""),
)
}
}

impl<'a> From<&'a Period<'a>> for api::Period<'a> {
fn from(period: &'a Period<'a>) -> Self {
Self {
r#type: api::ValueType::from(&period.type_),
value: period.value,
}
}
}

impl<'a> TryFrom<&'a Function<'a>> for api::Function<'a> {
type Error = Utf8Error;

Expand Down Expand Up @@ -423,7 +392,7 @@ impl<'a> From<Sample<'a>> for api::StringIdSample<'a> {
/// `ddog_prof_Profile_drop` when you are done with the profile.
///
/// # Arguments
/// * `sample_types`
/// * `sample_types` - A slice of SampleType enums
/// * `period` - Optional period of the profile. Passing None/null translates to zero values.
/// * `start_time` - Optional time the profile started at. Passing None/null will use the current
/// time.
Expand All @@ -434,7 +403,7 @@ impl<'a> From<Sample<'a>> for api::StringIdSample<'a> {
#[no_mangle]
#[must_use]
pub unsafe extern "C" fn ddog_prof_Profile_new(
sample_types: Slice<ValueType>,
sample_types: Slice<SampleType>,
period: Option<&Period>,
) -> ProfileNewResult {
profile_new(sample_types, period, None)
Expand All @@ -446,7 +415,7 @@ pub unsafe extern "C" fn ddog_prof_Profile_new(
/// # Arguments
/// * `out` - a non-null pointer to an uninitialized Profile.
/// * `dict`: a valid reference to a ProfilesDictionary handle.
/// * `sample_types`
/// * `sample_types` - A slice of SampleType enums
/// * `period` - Optional period of the profile. Passing None/null translates to zero values.
///
/// # Safety
Expand All @@ -463,7 +432,7 @@ pub unsafe extern "C" fn ddog_prof_Profile_new(
pub unsafe extern "C" fn ddog_prof_Profile_with_dictionary(
out: *mut Profile,
dict: &ArcHandle<ProfilesDictionary>,
sample_types: Slice<ValueType>,
sample_types: Slice<SampleType>,
period: Option<&Period>,
) -> ProfileStatus {
ensure_non_null_out_parameter!(out);
Expand All @@ -481,18 +450,15 @@ pub unsafe extern "C" fn ddog_prof_Profile_with_dictionary(

unsafe fn profile_with_dictionary(
dict: &ArcHandle<ProfilesDictionary>,
sample_types: Slice<ValueType>,
sample_types: Slice<SampleType>,
period: Option<&Period>,
) -> Result<Profile, ProfileError> {
let sample_types = sample_types.try_as_slice()?;
let dict = dict.try_clone_into_arc()?;

let mut types = Vec::new();
types.try_reserve_exact(sample_types.len())?;
types.extend(sample_types.iter().map(api::ValueType::from));
let period = period.map(Into::into);
let period = period.copied();

match internal::Profile::try_new_with_dictionary(&types, period, dict) {
match internal::Profile::try_new_with_dictionary(sample_types, period, dict) {
Ok(ok) => Ok(Profile::new(ok)),
Err(err) => Err(ProfileError::from(err)),
}
Expand All @@ -503,30 +469,30 @@ unsafe fn profile_with_dictionary(
#[must_use]
/// TODO: @ivoanjo Should this take a `*mut ManagedStringStorage` like Profile APIs do?
pub unsafe extern "C" fn ddog_prof_Profile_with_string_storage(
sample_types: Slice<ValueType>,
sample_types: Slice<SampleType>,
period: Option<&Period>,
string_storage: ManagedStringStorage,
) -> ProfileNewResult {
profile_new(sample_types, period, Some(string_storage))
}

unsafe fn profile_new(
sample_types: Slice<ValueType>,
sample_types: Slice<SampleType>,
period: Option<&Period>,
string_storage: Option<ManagedStringStorage>,
) -> ProfileNewResult {
let types: Vec<api::ValueType> = sample_types.into_slice().iter().map(Into::into).collect();
let period = period.map(Into::into);
let types = sample_types.into_slice();
let period = period.copied();

let result = match string_storage {
None => internal::Profile::try_new(&types, period)
None => internal::Profile::try_new(types, period)
.context("failed to initialize a profile without managed string storage"),
Some(s) => {
let string_storage = match get_inner_string_storage(s, true) {
Ok(string_storage) => string_storage,
Err(err) => return ProfileNewResult::Err(err.into()),
};
internal::Profile::try_with_string_storage(&types, period, string_storage)
internal::Profile::try_with_string_storage(types, period, string_storage)
.context("failed to initialize a profile with managed string storage")
}
};
Expand Down Expand Up @@ -937,9 +903,9 @@ mod tests {
#[test]
fn ctor_and_dtor() -> Result<(), Error> {
unsafe {
let sample_type: *const ValueType = &ValueType::new("samples", "count");
let sample_type = SampleType::CpuSamples;
let mut profile = Result::from(ddog_prof_Profile_new(
Slice::from_raw_parts(sample_type, 1),
Slice::from_raw_parts(&sample_type, 1),
None,
))?;
ddog_prof_Profile_drop(&mut profile);
Expand All @@ -950,9 +916,9 @@ mod tests {
#[test]
fn add_failure() -> Result<(), Error> {
unsafe {
let sample_type: *const ValueType = &ValueType::new("samples", "count");
let sample_type = SampleType::CpuSamples;
let mut profile = Result::from(ddog_prof_Profile_new(
Slice::from_raw_parts(sample_type, 1),
Slice::from_raw_parts(&sample_type, 1),
None,
))?;

Expand All @@ -977,9 +943,9 @@ mod tests {
#[cfg_attr(miri, ignore)]
fn aggregate_samples() -> anyhow::Result<()> {
unsafe {
let sample_type: *const ValueType = &ValueType::new("samples", "count");
let sample_type = SampleType::CpuSamples;
let mut profile = Result::from(ddog_prof_Profile_new(
Slice::from_raw_parts(sample_type, 1),
Slice::from_raw_parts(&sample_type, 1),
None,
))?;

Expand Down Expand Up @@ -1039,9 +1005,9 @@ mod tests {
}

unsafe fn provide_distinct_locations_ffi() -> Profile {
let sample_type: *const ValueType = &ValueType::new("samples", "count");
let sample_type = SampleType::CpuSamples;
let mut profile = Result::from(ddog_prof_Profile_new(
Slice::from_raw_parts(sample_type, 1),
Slice::from_raw_parts(&sample_type, 1),
None,
))
.unwrap();
Expand Down
8 changes: 4 additions & 4 deletions libdd-profiling/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ Core profiling library for collecting, aggregating, and exporting profiling data
## Example Usage

```rust
use libdd_profiling::api::{Profile, ValueType};
use libdd_profiling::api::{Profile, SampleType};

// Create a profile
let value_types = vec![
ValueType::new("samples", "count"),
ValueType::new("cpu", "nanoseconds"),
let sample_types = vec![
SampleType::CpuSamples,
SampleType::CpuTime,
];

// Add samples with stack traces
Expand Down
4 changes: 2 additions & 2 deletions libdd-profiling/benches/add_samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use libdd_profiling::api2::Location2;
use libdd_profiling::profiles::datatypes::{Function, FunctionId2, MappingId2};
use libdd_profiling::{self as profiling, api, api2};

fn make_sample_types() -> Vec<api::ValueType<'static>> {
vec![api::ValueType::new("samples", "count")]
fn make_sample_types() -> Vec<api::SampleType> {
vec![api::SampleType::CpuSamples]
}

fn make_stack_api(frames: &[Frame]) -> (Vec<api::Location<'static>>, Vec<i64>) {
Expand Down
5 changes: 2 additions & 3 deletions libdd-profiling/examples/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ use std::process::exit;

// Keep this in-sync with profiles.c
fn main() {
let walltime = api::ValueType::new("wall-time", "nanoseconds");
let sample_types = [api::ValueType::new("samples", "count"), walltime];
let sample_types = [api::SampleType::CpuSamples, api::SampleType::WallTime];

let period = api::Period {
r#type: walltime,
sample_type: api::SampleType::WallTime,
value: 10000,
};

Expand Down
Loading
Loading