Skip to content

Commit 3c1a633

Browse files
committed
feat(profiling)!: take sample types by enum
1 parent 99be5d7 commit 3c1a633

File tree

19 files changed

+577
-360
lines changed

19 files changed

+577
-360
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ libtest.so
2727
libtest_cpp.so
2828
examples/cxx/crashinfo
2929
examples/cxx/crashinfo.exe
30+
examples/cxx/exporter_manager
3031
examples/cxx/profiling
3132
examples/cxx/profiling.exe
3233
profile.pprof

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

datadog-profiling-replayer/src/replayer.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ pub struct Replayer<'pprof> {
2424
pub start_time: SystemTime,
2525
pub duration: Duration,
2626
pub end_time: SystemTime, // start_time + duration
27-
pub sample_types: Vec<api::ValueType<'pprof>>,
28-
pub period: Option<api::Period<'pprof>>,
27+
pub sample_types: Vec<api::SampleType>,
28+
pub period: Option<api::Period>,
2929
pub endpoints: Vec<(u64, &'pprof str)>,
3030
pub samples: Vec<(Option<Timestamp>, api::Sample<'pprof>)>,
3131
}
@@ -57,29 +57,27 @@ impl<'pprof> Replayer<'pprof> {
5757

5858
fn sample_types<'a>(
5959
profile_index: &'a ProfileIndex<'pprof>,
60-
) -> anyhow::Result<Vec<api::ValueType<'pprof>>> {
60+
) -> anyhow::Result<Vec<api::SampleType>> {
6161
let mut sample_types = Vec::with_capacity(profile_index.pprof.sample_types.len());
6262
for sample_type in profile_index.pprof.sample_types.iter() {
63-
sample_types.push(api::ValueType::new(
64-
profile_index.get_string(sample_type.r#type)?,
65-
profile_index.get_string(sample_type.unit)?,
66-
))
63+
let type_str = profile_index.get_string(sample_type.r#type)?;
64+
let unit = profile_index.get_string(sample_type.unit)?;
65+
let vt = api::ValueType::new(type_str, unit);
66+
sample_types.push(vt.try_into()?);
6767
}
6868
Ok(sample_types)
6969
}
7070

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

7674
match profile_index.pprof.period_type {
7775
Some(period_type) => {
78-
let r#type = api::ValueType::new(
79-
profile_index.get_string(period_type.r#type)?,
80-
profile_index.get_string(period_type.unit)?,
81-
);
82-
Ok(Some(api::Period { r#type, value }))
76+
let type_str = profile_index.get_string(period_type.r#type)?;
77+
let unit = profile_index.get_string(period_type.unit)?;
78+
let vt = api::ValueType::new(type_str, unit);
79+
let sample_type = vt.try_into()?;
80+
Ok(Some(api::Period { sample_type, value }))
8381
}
8482
None => Ok(None),
8583
}

examples/cxx/profiling.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,15 @@ int main() {
1616
std::cout << "=== Datadog Profiling CXX Bindings Example ===" << std::endl;
1717
std::cout << "\nCreating Profile..." << std::endl;
1818

19-
ValueType wall_time{
20-
.type_ = "wall-time",
21-
.unit = "nanoseconds"
22-
};
23-
2419
Period period{
25-
.value_type = wall_time,
20+
.value_type = SampleType::WallTime,
2621
.value = 60
2722
};
2823

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

3230
std::cout << "Adding upscaling rules..." << std::endl;

examples/ffi/exporter.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ int main(int argc, char *argv[]) {
3131

3232
const auto service = argv[1];
3333

34-
const ddog_prof_ValueType wall_time = {
35-
.type_ = DDOG_CHARSLICE_C_BARE("wall-time"),
36-
.unit = DDOG_CHARSLICE_C_BARE("nanoseconds"),
34+
// Use the SampleType enum instead of ValueType struct
35+
const ddog_prof_SampleType wall_time = DDOG_PROF_SAMPLE_TYPE_WALL_TIME;
36+
const ddog_prof_Slice_SampleType sample_types = {&wall_time, 1};
37+
const ddog_prof_Period period = {
38+
.sample_type = wall_time,
39+
.value = 60,
3740
};
38-
39-
const ddog_prof_Slice_ValueType sample_types = {&wall_time, 1};
40-
const ddog_prof_Period period = {wall_time, 60};
4141
ddog_prof_Profile_NewResult profile_new_result = ddog_prof_Profile_new(sample_types, &period);
4242
if (profile_new_result.tag != DDOG_PROF_PROFILE_NEW_RESULT_OK) {
4343
print_error("Failed to make new profile: ", profile_new_result.err);

examples/ffi/profile_intern.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,13 @@ void wait_for_user(std::string s) {
6969
}
7070

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

7980
ddog_prof_Profile_NewResult new_result = ddog_prof_Profile_new(sample_types, &period);
8081
if (new_result.tag != DDOG_PROF_PROFILE_NEW_RESULT_OK) {

examples/ffi/profiles.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
#define NUM_SAMPLES 5000000
1111

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

2021
// Create a ProfilesDictionary for the new API
2122
ddog_prof_ProfilesDictionaryHandle dict = {0};

libdd-profiling-ffi/src/profiles/datatypes.rs

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -96,27 +96,14 @@ impl From<anyhow::Result<internal::EncodedProfile>> for SerializeResult {
9696
}
9797
}
9898

99-
#[repr(C)]
100-
#[derive(Copy, Clone)]
101-
pub struct ValueType<'a> {
102-
pub type_: CharSlice<'a>,
103-
pub unit: CharSlice<'a>,
104-
}
105-
106-
impl<'a> ValueType<'a> {
107-
pub fn new(type_: &'a str, unit: &'a str) -> Self {
108-
Self {
109-
type_: type_.into(),
110-
unit: unit.into(),
111-
}
112-
}
113-
}
99+
/// Sample types supported by Datadog profilers.
100+
/// These correspond to the ValueType entries used in pprof profiles.
101+
/// Re-exported from libdd_profiling::api for FFI use.
102+
pub use api::SampleType;
114103

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

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

286-
impl<'a> From<&'a ValueType<'a>> for api::ValueType<'a> {
287-
fn from(vt: &'a ValueType<'a>) -> Self {
288-
Self::new(
289-
vt.type_.try_to_utf8().unwrap_or(""),
290-
vt.unit.try_to_utf8().unwrap_or(""),
291-
)
292-
}
293-
}
294-
295-
impl<'a> From<&'a Period<'a>> for api::Period<'a> {
296-
fn from(period: &'a Period<'a>) -> Self {
297-
Self {
298-
r#type: api::ValueType::from(&period.type_),
299-
value: period.value,
300-
}
301-
}
302-
}
303-
304273
impl<'a> TryFrom<&'a Function<'a>> for api::Function<'a> {
305274
type Error = Utf8Error;
306275

@@ -423,7 +392,7 @@ impl<'a> From<Sample<'a>> for api::StringIdSample<'a> {
423392
/// `ddog_prof_Profile_drop` when you are done with the profile.
424393
///
425394
/// # Arguments
426-
/// * `sample_types`
395+
/// * `sample_types` - A slice of SampleType enums
427396
/// * `period` - Optional period of the profile. Passing None/null translates to zero values.
428397
/// * `start_time` - Optional time the profile started at. Passing None/null will use the current
429398
/// time.
@@ -434,7 +403,7 @@ impl<'a> From<Sample<'a>> for api::StringIdSample<'a> {
434403
#[no_mangle]
435404
#[must_use]
436405
pub unsafe extern "C" fn ddog_prof_Profile_new(
437-
sample_types: Slice<ValueType>,
406+
sample_types: Slice<SampleType>,
438407
period: Option<&Period>,
439408
) -> ProfileNewResult {
440409
profile_new(sample_types, period, None)
@@ -446,7 +415,7 @@ pub unsafe extern "C" fn ddog_prof_Profile_new(
446415
/// # Arguments
447416
/// * `out` - a non-null pointer to an uninitialized Profile.
448417
/// * `dict`: a valid reference to a ProfilesDictionary handle.
449-
/// * `sample_types`
418+
/// * `sample_types` - A slice of SampleType enums
450419
/// * `period` - Optional period of the profile. Passing None/null translates to zero values.
451420
///
452421
/// # Safety
@@ -463,7 +432,7 @@ pub unsafe extern "C" fn ddog_prof_Profile_new(
463432
pub unsafe extern "C" fn ddog_prof_Profile_with_dictionary(
464433
out: *mut Profile,
465434
dict: &ArcHandle<ProfilesDictionary>,
466-
sample_types: Slice<ValueType>,
435+
sample_types: Slice<SampleType>,
467436
period: Option<&Period>,
468437
) -> ProfileStatus {
469438
ensure_non_null_out_parameter!(out);
@@ -481,18 +450,15 @@ pub unsafe extern "C" fn ddog_prof_Profile_with_dictionary(
481450

482451
unsafe fn profile_with_dictionary(
483452
dict: &ArcHandle<ProfilesDictionary>,
484-
sample_types: Slice<ValueType>,
453+
sample_types: Slice<SampleType>,
485454
period: Option<&Period>,
486455
) -> Result<Profile, ProfileError> {
487456
let sample_types = sample_types.try_as_slice()?;
488457
let dict = dict.try_clone_into_arc()?;
489458

490-
let mut types = Vec::new();
491-
types.try_reserve_exact(sample_types.len())?;
492-
types.extend(sample_types.iter().map(api::ValueType::from));
493-
let period = period.map(Into::into);
459+
let period = period.copied();
494460

495-
match internal::Profile::try_new_with_dictionary(&types, period, dict) {
461+
match internal::Profile::try_new_with_dictionary(sample_types, period, dict) {
496462
Ok(ok) => Ok(Profile::new(ok)),
497463
Err(err) => Err(ProfileError::from(err)),
498464
}
@@ -503,30 +469,30 @@ unsafe fn profile_with_dictionary(
503469
#[must_use]
504470
/// TODO: @ivoanjo Should this take a `*mut ManagedStringStorage` like Profile APIs do?
505471
pub unsafe extern "C" fn ddog_prof_Profile_with_string_storage(
506-
sample_types: Slice<ValueType>,
472+
sample_types: Slice<SampleType>,
507473
period: Option<&Period>,
508474
string_storage: ManagedStringStorage,
509475
) -> ProfileNewResult {
510476
profile_new(sample_types, period, Some(string_storage))
511477
}
512478

513479
unsafe fn profile_new(
514-
sample_types: Slice<ValueType>,
480+
sample_types: Slice<SampleType>,
515481
period: Option<&Period>,
516482
string_storage: Option<ManagedStringStorage>,
517483
) -> ProfileNewResult {
518-
let types: Vec<api::ValueType> = sample_types.into_slice().iter().map(Into::into).collect();
519-
let period = period.map(Into::into);
484+
let types = sample_types.into_slice();
485+
let period = period.copied();
520486

521487
let result = match string_storage {
522-
None => internal::Profile::try_new(&types, period)
488+
None => internal::Profile::try_new(types, period)
523489
.context("failed to initialize a profile without managed string storage"),
524490
Some(s) => {
525491
let string_storage = match get_inner_string_storage(s, true) {
526492
Ok(string_storage) => string_storage,
527493
Err(err) => return ProfileNewResult::Err(err.into()),
528494
};
529-
internal::Profile::try_with_string_storage(&types, period, string_storage)
495+
internal::Profile::try_with_string_storage(types, period, string_storage)
530496
.context("failed to initialize a profile with managed string storage")
531497
}
532498
};
@@ -937,9 +903,9 @@ mod tests {
937903
#[test]
938904
fn ctor_and_dtor() -> Result<(), Error> {
939905
unsafe {
940-
let sample_type: *const ValueType = &ValueType::new("samples", "count");
906+
let sample_type = SampleType::CpuSamples;
941907
let mut profile = Result::from(ddog_prof_Profile_new(
942-
Slice::from_raw_parts(sample_type, 1),
908+
Slice::from_raw_parts(&sample_type, 1),
943909
None,
944910
))?;
945911
ddog_prof_Profile_drop(&mut profile);
@@ -950,9 +916,9 @@ mod tests {
950916
#[test]
951917
fn add_failure() -> Result<(), Error> {
952918
unsafe {
953-
let sample_type: *const ValueType = &ValueType::new("samples", "count");
919+
let sample_type = SampleType::CpuSamples;
954920
let mut profile = Result::from(ddog_prof_Profile_new(
955-
Slice::from_raw_parts(sample_type, 1),
921+
Slice::from_raw_parts(&sample_type, 1),
956922
None,
957923
))?;
958924

@@ -977,9 +943,9 @@ mod tests {
977943
#[cfg_attr(miri, ignore)]
978944
fn aggregate_samples() -> anyhow::Result<()> {
979945
unsafe {
980-
let sample_type: *const ValueType = &ValueType::new("samples", "count");
946+
let sample_type = SampleType::CpuSamples;
981947
let mut profile = Result::from(ddog_prof_Profile_new(
982-
Slice::from_raw_parts(sample_type, 1),
948+
Slice::from_raw_parts(&sample_type, 1),
983949
None,
984950
))?;
985951

@@ -1039,9 +1005,9 @@ mod tests {
10391005
}
10401006

10411007
unsafe fn provide_distinct_locations_ffi() -> Profile {
1042-
let sample_type: *const ValueType = &ValueType::new("samples", "count");
1008+
let sample_type = SampleType::CpuSamples;
10431009
let mut profile = Result::from(ddog_prof_Profile_new(
1044-
Slice::from_raw_parts(sample_type, 1),
1010+
Slice::from_raw_parts(&sample_type, 1),
10451011
None,
10461012
))
10471013
.unwrap();

libdd-profiling/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ bolero = "0.13"
6666
criterion = "0.5.1"
6767
libdd-profiling = { path = ".", features = ["test-utils"] }
6868
proptest = "1"
69+
strum = { version = "0.26", features = ["derive"] }
6970

7071
[build-dependencies]
7172
cxx-build = { version = "1.0", optional = true }

libdd-profiling/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ Core profiling library for collecting, aggregating, and exporting profiling data
2929
## Example Usage
3030

3131
```rust
32-
use libdd_profiling::api::{Profile, ValueType};
32+
use libdd_profiling::api::{Profile, SampleType};
3333

3434
// Create a profile
35-
let value_types = vec![
36-
ValueType::new("samples", "count"),
37-
ValueType::new("cpu", "nanoseconds"),
35+
let sample_types = vec![
36+
SampleType::CpuSamples,
37+
SampleType::CpuTime,
3838
];
3939

4040
// Add samples with stack traces

0 commit comments

Comments
 (0)