Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert local root span id to u64, fix clippy lints #80

Merged
merged 10 commits into from
Jan 11, 2023

Conversation

morrisonlevi
Copy link
Contributor

@morrisonlevi morrisonlevi commented Dec 12, 2022

What does this PR do?

  • Fix clippy::uninlined_format_args lints.
  • Fix unused import std::path::Path in cfg(test).
  • Converts local_root_span_id in add_endpoint and related functions to be u64 instead of a string.
  • Removes unnecessary Cow<str> for str in label.
  • Bumps version to v2.0.0.

Motivation

The profiling backend can now accept numeric labels. This PR converts "local root span id" related parameters to use numbers in the endpoint API.

Additional Notes

This should have been multiple PRs. I'm sorry.

How to test the change?

  • Change add_endpoint to use u64 instead of a string.
  • Change your local root span id labels to use the .num variant instead of .str. Transmute from u64 to i64 as necessary. If they aren't changed, then profile.add will fail.

And then test as normal. Double-check that Code Hotspots and Endpoint Profiling both work.

Comment on lines 499 to 511
} else {
/* Sad path: have to fetch the string from the table and convert
* it back into a u64. Clients should change to use numbers!
*/
let string = match self.strings.get_index(label.str as usize) {
None => anyhow::bail!(
"failed to retrieve index {} from the string table when looking for endpoint data",
label.str
),
Some(string) => string,
};
string.as_str().parse()?
};
Copy link
Member

Choose a reason for hiding this comment

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

As this PR changes add_endpoint to take a u64 and not a String, I'm not quite sure we need this.

E.g., what clients would use the new numberic add_endpoint but still send the labels as strings elsewhere?

Copy link
Contributor Author

@morrisonlevi morrisonlevi Dec 16, 2022

Choose a reason for hiding this comment

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

This is coming from a label, and there's nothing that stops a client from attaching the label value as a string instead of a number. So it seems prudent to have the path, although it shouldn't be used.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm... I think having the fallback path is a sharper edge, as it makes things work even when they're not set up the way they should.

E.g., if someone does it wrong, they will still get the right output, which is dangerous...

One suggestion is to consider failing serialization and complaining that this label should not be a string. The serialize operation already returns a Result<EncodedProfile, EncodeError> so clients already need to handle errors, and perhaps modifying from to potentially return an error would allow us to report back what's wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I've started working on fixing this. There will be two changes:

  1. At serialization time, verify that any local root span id labels use .num.
  2. When the sample is added, verify that any local root span id labels use .num.

@morrisonlevi
Copy link
Contributor Author

I confirmed that Code Hotspots and Endpoint profiling work for PHP with this branch.

ivoanjo added a commit that referenced this pull request Jan 6, 2023
**What does this PR do?**:

In #73, we changed a number of types, functions and enums to be
properly scoped inside or outside namespaces.

But while integrating the 1.0.1 release in Ruby, I noticed that by
mistake I forgot to properly configure the renaming for `Vec_U8`,
which resulted in two copies of `Vec_U8` being emitted in the header
files: `ddog_Vec_U8` and `ddof_prof_Vec_U8`, with some APIs using one
and others using the other.

This PR fixes the configuration.

**Motivation**:

Having two copies of the same struct is not correct and is really
annoying for clients as well -- I temporarily needed to duplicate
a helper on the Ruby profiler to have variants for both types
(since often we don't even use a pointer but the struct directly,
you can't even cast between them).

**Additional Notes**:

Here's the diff for the generated headers before/after this change:

```diff
diff -uwr old-include/datadog/common.h build-linux/x86_64-linux/include/datadog/common.h
--- old-include/datadog/common.h	2023-01-06 10:57:32.730644531 +0000
+++ build-linux/x86_64-linux/include/datadog/common.h	2023-01-06 10:58:41.954571430 +0000
@@ -106,16 +106,6 @@
  */
 typedef struct ddog_prof_Exporter_Request ddog_prof_Exporter_Request;

-/**
- * Holds the raw parts of a Rust Vec; it should only be created from Rust,
- * never from C.
- */
-typedef struct ddog_prof_Vec_U8 {
-  const uint8_t *ptr;
-  uintptr_t len;
-  uintptr_t capacity;
-} ddog_prof_Vec_U8;
-
 typedef enum ddog_prof_Exporter_NewResult_Tag {
   DDOG_PROF_EXPORTER_NEW_RESULT_OK,
   DDOG_PROF_EXPORTER_NEW_RESULT_ERR,
@@ -128,7 +118,7 @@
       struct ddog_prof_Exporter *ok;
     };
     struct {
-      struct ddog_prof_Vec_U8 err;
+      struct ddog_Vec_U8 err;
     };
   };
 } ddog_prof_Exporter_NewResult;
@@ -205,7 +195,7 @@
       struct ddog_HttpStatus http_response;
     };
     struct {
-      struct ddog_prof_Vec_U8 err;
+      struct ddog_Vec_U8 err;
     };
   };
 } ddog_prof_Exporter_SendResult;
@@ -399,7 +389,7 @@
 typedef struct ddog_prof_EncodedProfile {
   struct ddog_Timespec start;
   struct ddog_Timespec end;
-  struct ddog_prof_Vec_U8 buffer;
+  struct ddog_Vec_U8 buffer;
   struct ddog_prof_ProfiledEndpointsStats *endpoints_stats;
 } ddog_prof_EncodedProfile;

@@ -415,7 +405,7 @@
       struct ddog_prof_EncodedProfile ok;
     };
     struct {
-      struct ddog_prof_Vec_U8 err;
+      struct ddog_Vec_U8 err;
     };
   };
 } ddog_prof_Profile_SerializeResult;
diff -uwr old-include/datadog/profiling.h build-linux/x86_64-linux/include/datadog/profiling.h
--- old-include/datadog/profiling.h	2023-01-06 10:57:32.730644531 +0000
+++ build-linux/x86_64-linux/include/datadog/profiling.h	2023-01-06 10:58:41.954571430 +0000
@@ -224,7 +224,7 @@

 void ddog_prof_Profile_SerializeResult_drop(struct ddog_prof_Profile_SerializeResult _result);

-DDOG_CHECK_RETURN struct ddog_Slice_U8 ddog_Vec_U8_as_slice(const struct ddog_prof_Vec_U8 *vec);
+DDOG_CHECK_RETURN struct ddog_Slice_U8 ddog_Vec_U8_as_slice(const struct ddog_Vec_U8 *vec);

 /**
  * Resets all data in `profile` except the sample types and period. Returns
```

This is technically a breaking change for users of the C bindings,
since a type gets removed (even if the type was never supposed to
have existed).

Since #80 is already bumping the libdatadog version to 2.0.0,
I suggest only merging this PR after that one, and including it
in the 2.0.0 release.

**How to test the change?**:

This change doesn't actually affect the compiled library, only the
header files. Thus, the only way of noticing and testing this change,
is to compile something that includes the headers and validate that
only `ddog_Vec_U8` gets used.
ivoanjo added a commit to DataDog/dd-trace-rb that referenced this pull request Jan 6, 2023
…tring

This is a newly-added feature to both the profiling backend, as well as
libdatadog.

Instead of sending the local root span ids as strings (that take up
space in the string table, etc), we can now send them as numbers.

We already did a similar migration for span ids in #2476, but at
the time we did not change the local root span ids because the
libdatadog `ddog_prof_Profile_set_endpoint` API could not properly
handle the numeric ids (this has been changed in
DataDog/libdatadog#80 ).

As far as testing goes, I could have chosen to abstract away this
change from our specs BUT I chose to make it explicit and changed the
specs to match. I like having visibility in the specs of the values
being encoded and passed around as numbers and not strings.
@ivoanjo
Copy link
Member

ivoanjo commented Jan 6, 2023

🎉 I too was able to check that Code Hotspots and Endpoint profiling work fine for Ruby with this branch.

Copy link
Member

@ivoanjo ivoanjo left a comment

Choose a reason for hiding this comment

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

Left a few notes! I ran out of time and didn't fully finish my review, will come back to it on monday :)

} else {
format!("{}{}", &agent_url, AGENT_TELEMETRY_URL_PATH)
format!("{}{AGENT_TELEMETRY_URL_PATH}", &agent_url)
Copy link
Member

Choose a reason for hiding this comment

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

Interesting -- is there no extended way for format! that can embed &agent_url directly?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, the inline format values can only contain simple identifiers https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html

For the sake of uniformity, maybe named arguments would be better

Suggested change
format!("{}{AGENT_TELEMETRY_URL_PATH}", &agent_url)
format!("{agent_url}{AGENT_TELEMETRY_URL_PATH}", agent_url=&agent_url)

/// * `local_root_span_id` - the value of the local root span id label to look for.
/// * `local_root_span_id`
Copy link
Member

Choose a reason for hiding this comment

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

Minor: This is a weird change -- was it intentional?

Comment on lines +343 to +348
labels.push(Label {
key,
str,
num: label.num,
num_unit,
});
Copy link
Member

Choose a reason for hiding this comment

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

Lol is it me or is this mix of positional and keyword arguments a bit weird? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't bother me 🤷🏻

profiling/src/profile/mod.rs Show resolved Hide resolved
This avoid doing runtime checking and simplifies some logic.
@morrisonlevi morrisonlevi marked this pull request as ready for review January 7, 2023 22:20
@morrisonlevi morrisonlevi requested review from a team as code owners January 7, 2023 22:20
Copy link
Member

@ivoanjo ivoanjo left a comment

Choose a reason for hiding this comment

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

👍 LGTM, thanks for this tiny but really needed improvement :)

profiling/src/profile/mod.rs Show resolved Hide resolved
profiling/src/profile/mod.rs Show resolved Hide resolved
profiling/src/profile/mod.rs Show resolved Hide resolved
@morrisonlevi morrisonlevi merged commit 2ed9132 into main Jan 11, 2023
@morrisonlevi morrisonlevi deleted the levi/endpoint-num branch January 11, 2023 16:59
ivoanjo added a commit that referenced this pull request Jan 11, 2023
**What does this PR do?**:

In #73, we changed a number of types, functions and enums to be
properly scoped inside or outside namespaces.

But while integrating the 1.0.1 release in Ruby, I noticed that by
mistake I forgot to properly configure the renaming for `Vec_U8`,
which resulted in two copies of `Vec_U8` being emitted in the header
files: `ddog_Vec_U8` and `ddof_prof_Vec_U8`, with some APIs using one
and others using the other.

This PR fixes the configuration.

**Motivation**:

Having two copies of the same struct is not correct and is really
annoying for clients as well -- I temporarily needed to duplicate
a helper on the Ruby profiler to have variants for both types
(since often we don't even use a pointer but the struct directly,
you can't even cast between them).

**Additional Notes**:

Here's the diff for the generated headers before/after this change:

```diff
diff -uwr old-include/datadog/common.h build-linux/x86_64-linux/include/datadog/common.h
--- old-include/datadog/common.h	2023-01-06 10:57:32.730644531 +0000
+++ build-linux/x86_64-linux/include/datadog/common.h	2023-01-06 10:58:41.954571430 +0000
@@ -106,16 +106,6 @@
  */
 typedef struct ddog_prof_Exporter_Request ddog_prof_Exporter_Request;

-/**
- * Holds the raw parts of a Rust Vec; it should only be created from Rust,
- * never from C.
- */
-typedef struct ddog_prof_Vec_U8 {
-  const uint8_t *ptr;
-  uintptr_t len;
-  uintptr_t capacity;
-} ddog_prof_Vec_U8;
-
 typedef enum ddog_prof_Exporter_NewResult_Tag {
   DDOG_PROF_EXPORTER_NEW_RESULT_OK,
   DDOG_PROF_EXPORTER_NEW_RESULT_ERR,
@@ -128,7 +118,7 @@
       struct ddog_prof_Exporter *ok;
     };
     struct {
-      struct ddog_prof_Vec_U8 err;
+      struct ddog_Vec_U8 err;
     };
   };
 } ddog_prof_Exporter_NewResult;
@@ -205,7 +195,7 @@
       struct ddog_HttpStatus http_response;
     };
     struct {
-      struct ddog_prof_Vec_U8 err;
+      struct ddog_Vec_U8 err;
     };
   };
 } ddog_prof_Exporter_SendResult;
@@ -399,7 +389,7 @@
 typedef struct ddog_prof_EncodedProfile {
   struct ddog_Timespec start;
   struct ddog_Timespec end;
-  struct ddog_prof_Vec_U8 buffer;
+  struct ddog_Vec_U8 buffer;
   struct ddog_prof_ProfiledEndpointsStats *endpoints_stats;
 } ddog_prof_EncodedProfile;

@@ -415,7 +405,7 @@
       struct ddog_prof_EncodedProfile ok;
     };
     struct {
-      struct ddog_prof_Vec_U8 err;
+      struct ddog_Vec_U8 err;
     };
   };
 } ddog_prof_Profile_SerializeResult;
diff -uwr old-include/datadog/profiling.h build-linux/x86_64-linux/include/datadog/profiling.h
--- old-include/datadog/profiling.h	2023-01-06 10:57:32.730644531 +0000
+++ build-linux/x86_64-linux/include/datadog/profiling.h	2023-01-06 10:58:41.954571430 +0000
@@ -224,7 +224,7 @@

 void ddog_prof_Profile_SerializeResult_drop(struct ddog_prof_Profile_SerializeResult _result);

-DDOG_CHECK_RETURN struct ddog_Slice_U8 ddog_Vec_U8_as_slice(const struct ddog_prof_Vec_U8 *vec);
+DDOG_CHECK_RETURN struct ddog_Slice_U8 ddog_Vec_U8_as_slice(const struct ddog_Vec_U8 *vec);

 /**
  * Resets all data in `profile` except the sample types and period. Returns
```

This is technically a breaking change for users of the C bindings,
since a type gets removed (even if the type was never supposed to
have existed).

Since #80 is already bumping the libdatadog version to 2.0.0,
I suggest only merging this PR after that one, and including it
in the 2.0.0 release.

**How to test the change?**:

This change doesn't actually affect the compiled library, only the
header files. Thus, the only way of noticing and testing this change,
is to compile something that includes the headers and validate that
only `ddog_Vec_U8` gets used.

Co-authored-by: Levi Morrison <levi.morrison@datadoghq.com>
ivoanjo added a commit that referenced this pull request Jan 13, 2023
**What does this PR do?**:

Documents the expected result of the `ddog_prof_Profile_add` API.

**Motivation**:

While validating #80 on Ruby, I called `add` in a way that should've
triggered an error, but saw none.

On closer inspection, I had no error handling for `add` because it
wasn't clear to me that its result could indicate an error.

Thus, I decided to document that fact.

**Additional Notes**:

At some point we could change `add` to return an actual result,
allowing us to pass the error message back to the caller, but for now
I decided to just document it.

**How to test the change?**:

Documentation change only.
ivoanjo added a commit that referenced this pull request Jan 16, 2023
**What does this PR do?**:

Documents the expected result of the `ddog_prof_Profile_add` API.

**Motivation**:

While validating #80 on Ruby, I called `add` in a way that should've
triggered an error, but saw none.

On closer inspection, I had no error handling for `add` because it
wasn't clear to me that its result could indicate an error.

Thus, I decided to document that fact.

**Additional Notes**:

At some point we could change `add` to return an actual result,
allowing us to pass the error message back to the caller, but for now
I decided to just document it.

**How to test the change?**:

Documentation change only.
@morrisonlevi morrisonlevi added the profiling Relates to the profiling* modules. label Jan 17, 2023
ivoanjo added a commit to DataDog/dd-trace-rb that referenced this pull request Feb 3, 2023
…tring

This is a newly-added feature to both the profiling backend, as well as
libdatadog.

Instead of sending the local root span ids as strings (that take up
space in the string table, etc), we can now send them as numbers.

We already did a similar migration for span ids in #2476, but at
the time we did not change the local root span ids because the
libdatadog `ddog_prof_Profile_set_endpoint` API could not properly
handle the numeric ids (this has been changed in
DataDog/libdatadog#80 ).

As far as testing goes, I could have chosen to abstract away this
change from our specs BUT I chose to make it explicit and changed the
specs to match. I like having visibility in the specs of the values
being encoded and passed around as numbers and not strings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
profiling Relates to the profiling* modules.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants