|
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | 4 | use crate::string_storage::{get_inner_string_storage, ManagedStringStorage}; |
| 5 | +use crate::{ensure_non_null_out_parameter, ArcHandle, ProfileError, ProfileStatus}; |
5 | 6 | use anyhow::Context; |
6 | 7 | use function_name::named; |
7 | 8 | use libdd_common_ffi::slice::{AsBytes, ByteSlice, CharSlice, Slice}; |
8 | 9 | use libdd_common_ffi::{wrap_with_ffi_result, Error, Handle, Timespec, ToInner}; |
9 | 10 | use libdd_profiling::api::{self, ManagedStringId}; |
10 | | -use libdd_profiling::internal; |
| 11 | +use libdd_profiling::profiles::datatypes::{ProfilesDictionary, StringId2}; |
| 12 | +use libdd_profiling::{api2, internal}; |
11 | 13 | use std::num::NonZeroI64; |
12 | 14 | use std::str::Utf8Error; |
13 | 15 | use std::time::SystemTime; |
@@ -215,6 +217,44 @@ pub struct Sample<'a> { |
215 | 217 | pub labels: Slice<'a, Label<'a>>, |
216 | 218 | } |
217 | 219 |
|
| 220 | +#[derive(Copy, Clone, Debug, Default)] |
| 221 | +#[repr(C)] |
| 222 | +pub struct Label2<'a> { |
| 223 | + pub key: StringId2, |
| 224 | + |
| 225 | + /// At most one of `.str` and `.num` should not be empty. |
| 226 | + pub str: CharSlice<'a>, |
| 227 | + pub num: i64, |
| 228 | + |
| 229 | + /// Should only be present when num is present. |
| 230 | + /// Specifies the units of num. |
| 231 | + /// Use arbitrary string (for example, "requests") as a custom count unit. |
| 232 | + /// If no unit is specified, consumer may apply heuristic to deduce the unit. |
| 233 | + /// Consumers may also interpret units like "bytes" and "kilobytes" as memory |
| 234 | + /// units and units like "seconds" and "nanoseconds" as time units, |
| 235 | + /// and apply appropriate unit conversions to these. |
| 236 | + pub num_unit: CharSlice<'a>, |
| 237 | +} |
| 238 | + |
| 239 | +#[repr(C)] |
| 240 | +#[derive(Copy, Clone)] |
| 241 | +pub struct Sample2<'a> { |
| 242 | + /// The leaf is at locations[0]. |
| 243 | + pub locations: Slice<'a, api2::Location2>, |
| 244 | + |
| 245 | + /// The type and unit of each value is defined by the corresponding |
| 246 | + /// entry in Profile.sample_type. All samples must have the same |
| 247 | + /// number of values, the same as the length of Profile.sample_type. |
| 248 | + /// When aggregating multiple samples into a single sample, the |
| 249 | + /// result has a list of values that is the element-wise sum of the |
| 250 | + /// lists of the originals. |
| 251 | + pub values: Slice<'a, i64>, |
| 252 | + |
| 253 | + /// label includes additional context for this sample. It can include |
| 254 | + /// things like a thread id, allocation size, etc |
| 255 | + pub labels: Slice<'a, Label2<'a>>, |
| 256 | +} |
| 257 | + |
218 | 258 | impl<'a> TryFrom<&'a Mapping<'a>> for api::Mapping<'a> { |
219 | 259 | type Error = Utf8Error; |
220 | 260 |
|
@@ -400,6 +440,64 @@ pub unsafe extern "C" fn ddog_prof_Profile_new( |
400 | 440 | profile_new(sample_types, period, None) |
401 | 441 | } |
402 | 442 |
|
| 443 | +/// Create a new profile with the given sample types. Must call |
| 444 | +/// `ddog_prof_Profile_drop` when you are done with the profile. |
| 445 | +/// |
| 446 | +/// # Arguments |
| 447 | +/// * `out` - a non-null pointer to an uninitialized Profile. |
| 448 | +/// * `dict`: a valid reference to a ProfilesDictionary handle. |
| 449 | +/// * `sample_types` |
| 450 | +/// * `period` - Optional period of the profile. Passing None/null translates to zero values. |
| 451 | +/// |
| 452 | +/// # Safety |
| 453 | +/// All slices must have pointers that are suitably aligned for their type |
| 454 | +/// and must have the correct number of elements for the slice. |
| 455 | +/// |
| 456 | +/// The `dict` reference must be to a valid ProfilesDictionary handle. It may |
| 457 | +/// be an empty handle, but it must be a valid handle. |
| 458 | +/// |
| 459 | +/// The `out` pointer must be non-null and suitable for pointer writes, |
| 460 | +/// including that it has the correct size and alignment. |
| 461 | +#[no_mangle] |
| 462 | +#[must_use] |
| 463 | +pub unsafe extern "C" fn ddog_prof_Profile_with_dictionary( |
| 464 | + out: *mut Profile, |
| 465 | + dict: &ArcHandle<ProfilesDictionary>, |
| 466 | + sample_types: Slice<ValueType>, |
| 467 | + period: Option<&Period>, |
| 468 | +) -> ProfileStatus { |
| 469 | + ensure_non_null_out_parameter!(out); |
| 470 | + match profile_with_dictionary(dict, sample_types, period) { |
| 471 | + // SAFETY: checked that it isn't null above, the rest comes from this |
| 472 | + // function's own safety conditions. Technically, our safety conditions |
| 473 | + // don't require a null check, but we're being safe there. |
| 474 | + Ok(profile) => unsafe { |
| 475 | + out.write(profile); |
| 476 | + ProfileStatus::OK |
| 477 | + }, |
| 478 | + Err(e) => ProfileStatus::from(e), |
| 479 | + } |
| 480 | +} |
| 481 | + |
| 482 | +unsafe fn profile_with_dictionary( |
| 483 | + dict: &ArcHandle<ProfilesDictionary>, |
| 484 | + sample_types: Slice<ValueType>, |
| 485 | + period: Option<&Period>, |
| 486 | +) -> Result<Profile, ProfileError> { |
| 487 | + let sample_types = sample_types.try_as_slice()?; |
| 488 | + let dict = dict.try_clone_into_arc()?; |
| 489 | + |
| 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); |
| 494 | + |
| 495 | + match internal::Profile::try_new_with_dictionary(&types, period, dict) { |
| 496 | + Ok(ok) => Ok(Profile::new(ok)), |
| 497 | + Err(err) => Err(ProfileError::from(err)), |
| 498 | + } |
| 499 | +} |
| 500 | + |
403 | 501 | /// Same as `ddog_profile_new` but also configures a `string_storage` for the profile. |
404 | 502 | #[no_mangle] |
405 | 503 | #[must_use] |
@@ -508,6 +606,42 @@ pub unsafe extern "C" fn ddog_prof_Profile_add( |
508 | 606 | .context("ddog_prof_Profile_add failed") |
509 | 607 | .into() |
510 | 608 | } |
| 609 | +/// # Safety |
| 610 | +/// The `profile` ptr must point to a valid Profile object created by this |
| 611 | +/// module. All pointers inside the `sample` need to be valid for the duration |
| 612 | +/// of this call. |
| 613 | +/// |
| 614 | +/// If successful, it returns the Ok variant. |
| 615 | +/// On error, it holds an error message in the error variant. |
| 616 | +/// |
| 617 | +/// This call is _NOT_ thread-safe. |
| 618 | +#[must_use] |
| 619 | +#[no_mangle] |
| 620 | +pub unsafe extern "C" fn ddog_prof_Profile_add2( |
| 621 | + profile: *mut Profile, |
| 622 | + sample: Sample2, |
| 623 | + timestamp: Option<NonZeroI64>, |
| 624 | +) -> ProfileStatus { |
| 625 | + ProfileStatus::from((|| { |
| 626 | + let profile = profile_ptr_to_inner(profile)?; |
| 627 | + |
| 628 | + let locations = sample.locations.try_as_slice()?; |
| 629 | + let values = sample.values.try_as_slice()?; |
| 630 | + let labels = sample.labels.try_as_slice()?; |
| 631 | + |
| 632 | + let labels_iter = labels.iter().map(|label| -> anyhow::Result<api2::Label> { |
| 633 | + Ok(api2::Label { |
| 634 | + key: label.key, |
| 635 | + str: core::str::from_utf8(label.str.try_as_bytes()?)?, |
| 636 | + num: label.num, |
| 637 | + num_unit: core::str::from_utf8(label.num_unit.try_as_bytes()?)?, |
| 638 | + }) |
| 639 | + }); |
| 640 | + profile |
| 641 | + .try_add_sample2(locations, values, labels_iter, timestamp) |
| 642 | + .context("ddog_prof_Profile_add failed") |
| 643 | + })()) |
| 644 | +} |
511 | 645 |
|
512 | 646 | pub(crate) unsafe fn profile_ptr_to_inner<'a>( |
513 | 647 | profile_ptr: *mut Profile, |
|
0 commit comments