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
3 changes: 2 additions & 1 deletion examples/cxx/profiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@ int main() {
// Files to compress and attach
{AttachmentFile{
.name = "app_metadata.json",
.data = {metadata_bytes.data(), metadata_bytes.size()}
.data = {metadata_bytes.data(), metadata_bytes.size()},
.mime = MimeType::ApplicationJson
}},
// Additional per-profile tags
{
Expand Down
1 change: 1 addition & 0 deletions libdd-profiling-ffi/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ renaming_overrides_prefixing = true
"VoidResult" = "ddog_VoidResult"

"CbindgenIsDumbStringId" = "ddog_prof_StringId"
"MimeType" = "ddog_prof_MimeType"

"Slice_GenerationalIdLabelId" = "ddog_prof_Slice_LabelId"
"Slice_GenerationalIdLocationId" = "ddog_prof_Slice_LocationId"
Expand Down
6 changes: 4 additions & 2 deletions libdd-profiling-ffi/src/exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use libdd_common::tag::Tag;
use libdd_common_ffi::slice::{AsBytes, ByteSlice, CharSlice, Slice};
use libdd_common_ffi::{wrap_with_ffi_result, Handle, Result, ToInner};
use libdd_profiling::exporter;
use libdd_profiling::exporter::ProfileExporter;
use libdd_profiling::exporter::{MimeType, ProfileExporter};
use libdd_profiling::internal::EncodedProfile;
use std::borrow::Cow;
use std::str::FromStr;
Expand All @@ -29,6 +29,7 @@ pub enum ProfilingEndpoint<'a> {
pub struct File<'a> {
name: CharSlice<'a>,
file: ByteSlice<'a>,
mime: MimeType,
}

#[must_use]
Expand Down Expand Up @@ -184,7 +185,8 @@ unsafe fn into_vec_files<'a>(slice: Slice<'a, File>) -> Vec<exporter::File<'a>>
.map(|file| {
let name = file.name.try_to_utf8().unwrap_or("{invalid utf-8}");
let bytes = file.file.as_slice();
exporter::File { name, bytes }
let mime = file.mime;
exporter::File { name, bytes, mime }
})
.collect()
}
Expand Down
54 changes: 47 additions & 7 deletions libdd-profiling/src/cxx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@ use crate::internal;
// CXX Bridge - C++ Bindings
// ============================================================================

/// cbindgen:ignore
#[cxx::bridge(namespace = "datadog::profiling")]
pub mod ffi {
// Shared enums
#[derive(Debug)]
#[repr(u8)]
enum MimeType {
ApplicationJson,
ApplicationOctetStream,
TextCsv,
TextPlain,
TextXml,
}

// Shared structs - CXX-friendly types
struct ValueType<'a> {
type_: &'a str,
Expand Down Expand Up @@ -68,6 +80,7 @@ pub mod ffi {
struct AttachmentFile<'a> {
name: &'a str,
data: &'a [u8],
mime: MimeType,
}

// Opaque Rust types
Expand Down Expand Up @@ -274,12 +287,30 @@ impl<'a> From<&ffi::Label<'a>> for api::Label<'a> {
}
}

impl<'a> From<&ffi::AttachmentFile<'a>> for exporter::File<'a> {
fn from(file: &ffi::AttachmentFile<'a>) -> Self {
exporter::File {
impl TryFrom<ffi::MimeType> for exporter::MimeType {
type Error = anyhow::Error;

fn try_from(mime: ffi::MimeType) -> Result<Self, Self::Error> {
match mime {
ffi::MimeType::ApplicationJson => Ok(exporter::MimeType::ApplicationJson),
ffi::MimeType::ApplicationOctetStream => Ok(exporter::MimeType::ApplicationOctetStream),
ffi::MimeType::TextCsv => Ok(exporter::MimeType::TextCsv),
ffi::MimeType::TextPlain => Ok(exporter::MimeType::TextPlain),
ffi::MimeType::TextXml => Ok(exporter::MimeType::TextXml),
_ => anyhow::bail!("Unknown MimeType variant: {:?}", mime),
}
}
}

impl<'a> TryFrom<&ffi::AttachmentFile<'a>> for exporter::File<'a> {
type Error = anyhow::Error;

fn try_from(file: &ffi::AttachmentFile<'a>) -> Result<Self, Self::Error> {
Ok(exporter::File {
name: file.name,
bytes: file.data,
}
mime: file.mime.try_into()?,
})
}
}

Expand Down Expand Up @@ -620,8 +651,10 @@ impl ProfileExporter {
let end_time = Some(std::time::SystemTime::now());
let encoded = old_profile.serialize_into_compressed_pprof(end_time, None)?;

let files_to_compress_vec: Vec<exporter::File> =
files_to_compress.iter().map(Into::into).collect();
let files_to_compress_vec: Vec<exporter::File> = files_to_compress
.iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?;

let additional_tags_vec: Vec<libdd_common::tag::Tag> = additional_tags
.iter()
Expand Down Expand Up @@ -901,10 +934,16 @@ mod tests {
let file: exporter::File = (&ffi::AttachmentFile {
name: "test.bin",
data: &data,
mime: ffi::MimeType::ApplicationOctetStream,
})
.into();
.try_into()
.expect("Failed to convert AttachmentFile");
assert_eq!(file.name, "test.bin");
assert_eq!(file.bytes, data.as_slice());
assert!(matches!(
file.mime,
exporter::MimeType::ApplicationOctetStream
));

// Tag conversion with special characters
let tag: libdd_common::tag::Tag = (&ffi::Tag {
Expand Down Expand Up @@ -996,6 +1035,7 @@ mod tests {
vec![ffi::AttachmentFile {
name: "metadata.json",
data: &attachment_data,
mime: ffi::MimeType::ApplicationJson,
}],
vec![
ffi::Tag {
Expand Down
23 changes: 23 additions & 0 deletions libdd-profiling/src/exporter/profile_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,32 @@ pub struct ProfileExporter {
runtime: Option<Runtime>,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub enum MimeType {
ApplicationJson,
ApplicationOctetStream,
TextCsv,
TextPlain,
TextXml,
}
Comment on lines +50 to +56
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LMK if there are any other types we need to support at this time

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python currently uses json for code provenance and internal metadata. And pprof.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node.js doesn't use libdatadog for profile uploads, but just wanted to confirm here it only uses two MIME types: application/json for event.json and application/octet-stream for the pprof files.


impl MimeType {
pub fn as_str(&self) -> &'static str {
match self {
MimeType::ApplicationJson => mime::APPLICATION_JSON.as_ref(),
MimeType::ApplicationOctetStream => mime::APPLICATION_OCTET_STREAM.as_ref(),
MimeType::TextCsv => mime::TEXT_CSV.as_ref(),
MimeType::TextPlain => mime::TEXT_PLAIN.as_ref(),
MimeType::TextXml => mime::TEXT_XML.as_ref(),
}
}
}

pub struct File<'a> {
pub name: &'a str,
pub bytes: &'a [u8],
pub mime: MimeType,
}

impl ProfileExporter {
Expand Down
4 changes: 3 additions & 1 deletion libdd-profiling/tests/exporter_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use libdd_profiling::exporter::config;
use libdd_profiling::exporter::utils::parse_http_request;
use libdd_profiling::exporter::{File, ProfileExporter};
use libdd_profiling::exporter::{File, MimeType, ProfileExporter};
use libdd_profiling::internal::EncodedProfile;
use std::collections::HashMap;
use std::path::PathBuf;
Expand Down Expand Up @@ -226,10 +226,12 @@ async fn export_full_profile(
File {
name: "jit.pprof",
bytes: b"fake-jit-data",
mime: MimeType::ApplicationOctetStream,
},
File {
name: "metadata.json",
bytes: b"{\"test\": true}",
mime: MimeType::ApplicationJson,
},
];

Expand Down
Loading