Skip to content

Commit

Permalink
refactor(profiling)!: require sample types on profile resets
Browse files Browse the repository at this point in the history
  • Loading branch information
morrisonlevi committed May 1, 2024
1 parent b2ddf1e commit e102e07
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 45 deletions.
2 changes: 1 addition & 1 deletion examples/ffi/exporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) {
}

ddog_prof_Profile_SerializeResult serialize_result =
ddog_prof_Profile_serialize(profile.get(), nullptr, nullptr, nullptr);
ddog_prof_Profile_serialize(profile.get(), nullptr, nullptr, sample_types, &period, nullptr);
if (serialize_result.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
print_error("Failed to serialize profile: ", serialize_result.err);
ddog_Error_drop(&serialize_result.err);
Expand Down
2 changes: 1 addition & 1 deletion examples/ffi/profiles.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ int main(void) {
// printf("Press any key to reset and drop...");
// getchar();

ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(profile, NULL);
ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(profile, sample_types, &period, NULL);
if (reset_result.tag != DDOG_PROF_PROFILE_RESULT_OK) {
ddog_CharSlice message = ddog_Error_message(&reset_result.err);
fprintf(stderr, "%.*s", (int)message.len, message.ptr);
Expand Down
15 changes: 13 additions & 2 deletions profiling-ffi/src/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,11 +689,17 @@ pub unsafe extern "C" fn ddog_prof_Profile_serialize(
profile: *mut Profile,
end_time: Option<&Timespec>,
duration_nanos: Option<&i64>,
sample_types: Slice<ValueType>,
period: Option<&Period>,
start_time: Option<&Timespec>,
) -> SerializeResult {
(|| {
let profile = profile_ptr_to_inner(profile)?;
let old_profile = profile.reset_and_return_previous(start_time.map(SystemTime::from))?;

let start_time = start_time.map(SystemTime::from);
let types: Vec<api::ValueType> = sample_types.into_slice().iter().map(Into::into).collect();
let period = period.map(Into::into);
let old_profile = profile.reset_and_return_previous(start_time, &types, period)?;
let end_time = end_time.map(SystemTime::from);
let duration = match duration_nanos {
None => None,
Expand Down Expand Up @@ -728,11 +734,16 @@ pub unsafe extern "C" fn ddog_Vec_U8_as_slice(vec: &ddcommon_ffi::Vec<u8>) -> Sl
#[must_use]
pub unsafe extern "C" fn ddog_prof_Profile_reset(
profile: *mut Profile,
sample_types: Slice<ValueType>,
period: Option<&Period>,
start_time: Option<&Timespec>,
) -> ProfileResult {
(|| {
let profile = profile_ptr_to_inner(profile)?;
profile.reset_and_return_previous(start_time.map(SystemTime::from))?;

let types: Vec<api::ValueType> = sample_types.into_slice().iter().map(Into::into).collect();
let period = period.map(Into::into);
profile.reset_and_return_previous(start_time.map(SystemTime::from), &types, period)?;
anyhow::Ok(())
})()
.context("ddog_prof_Profile_reset failed")
Expand Down
75 changes: 34 additions & 41 deletions profiling/src/internal/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,6 @@ impl Profile {
Ok(())
}

pub fn get_string(&self, id: StringId) -> &str {
self.strings
.get_index(id.to_offset())
.expect("StringId to have a valid interned index")
}

/// Creates a profile with `start_time`.
/// Initializes the string table to hold:
/// - "" (the empty string)
Expand Down Expand Up @@ -194,23 +188,11 @@ impl Profile {
pub fn reset_and_return_previous(
&mut self,
start_time: Option<SystemTime>,
sample_types: &[api::ValueType],
period: Option<api::Period>,
) -> anyhow::Result<Profile> {
/* We have to map over the types because the order of the strings is
* not generally guaranteed, so we can't just copy the underlying
* structures.
*/
let sample_types: Vec<api::ValueType> = self.extract_api_sample_types()?;

let period = self.period.map(|t| api::Period {
r#type: api::ValueType {
r#type: self.get_string(t.1.r#type),
unit: self.get_string(t.1.unit),
},
value: t.0,
});

let start_time = start_time.unwrap_or_else(SystemTime::now);
let mut profile = Profile::new(start_time, &sample_types, period);
let mut profile = Profile::new(start_time, sample_types, period);

std::mem::swap(&mut *self, &mut profile);
Ok(profile)
Expand Down Expand Up @@ -374,26 +356,13 @@ impl Profile {
self.stack_traces.dedup(StackTrace { locations })
}

fn extract_api_sample_types(&self) -> anyhow::Result<Vec<api::ValueType>> {
let sample_types = self
.sample_types
.iter()
.map(|sample_type| api::ValueType {
r#type: self.get_string(sample_type.r#type),
unit: self.get_string(sample_type.unit),
})
.collect();
Ok(sample_types)
}

/// Fetches the endpoint information for the label. There may be errors,
/// but there may also be no endpoint information for a given endpoint.
/// Hence, the return type of Result<Option<_>, _>.
fn get_endpoint_for_label(&self, label: &Label) -> anyhow::Result<Option<Label>> {
anyhow::ensure!(
label.get_key() == self.endpoints.local_root_span_id_label,
"bug: get_endpoint_for_label should only be called on labels with the key \"local root span id\", called on label with key \"{}\"",
self.get_string(label.get_key())
"bug: get_endpoint_for_label should only be called on labels with the key \"local root span id\""
);

anyhow::ensure!(
Expand Down Expand Up @@ -827,7 +796,14 @@ mod api_test {
assert!(profile.endpoints.stats.is_empty());

let prev = profile
.reset_and_return_previous(None)
.reset_and_return_previous(
None,
&[api::ValueType {
r#type: "samples",
unit: "count",
}],
None,
)
.expect("reset to succeed");

// These should all be empty now
Expand All @@ -846,7 +822,9 @@ mod api_test {

// The string table should have at least the empty string.
assert!(!profile.strings.is_empty());
assert_eq!("", profile.get_string(StringId::ZERO));

// todo: how to test strings?
// assert_eq!("", profile.get_string(StringId::ZERO));
}

#[test]
Expand All @@ -865,17 +843,32 @@ mod api_test {
);
profile.period = Some(period);

let sample_types = &[api::ValueType {
r#type: "samples",
unit: "count",
}];
let prev = profile
.reset_and_return_previous(None)
.reset_and_return_previous(
None,
sample_types,
Some(api::Period {
r#type: api::ValueType {
r#type: "wall-time",
unit: "nanoseconds",
},
value: 10_000_000,
}),
)
.expect("reset to succeed");
assert_eq!(Some(period), prev.period);

// Resolve the string values to check that they match (their string
// table offsets may not match).
let (value, period_type) = profile.period.expect("profile to have a period");
let (value, _period_type) = profile.period.expect("profile to have a period");
assert_eq!(value, period.0);
assert_eq!(profile.get_string(period_type.r#type), "wall-time");
assert_eq!(profile.get_string(period_type.unit), "nanoseconds");
// todo: how should we assert these?
// assert_eq!(profile.get_string(period_type.r#type), "wall-time");
// assert_eq!(profile.get_string(period_type.unit), "nanoseconds");
}

#[test]
Expand Down

0 comments on commit e102e07

Please sign in to comment.