KEMBAR78
Support for OTLP Profile signals by fandreuz · Pull Request #1188 · async-profiler/async-profiler · GitHub
Skip to content

Conversation

fandreuz
Copy link
Contributor

@fandreuz fandreuz commented Mar 21, 2025

Changes

  • New output mode (otlp) to dump data in OpenTelemetry format
  • New Java API to dump Async-Profiler data in OpenTelemetry format
  • protobuf.cpp: Allow specifying the maximum bit count for the varint encoding the length of a nested message

Motivation

OpenTelemetry recently added support for Profile signals. The end goal of the initiative is to provide a standard, vendor-agnostic format for profiling data. Adopting this protocol as one of our supported output formats will help our users integrating Async-Profiler in their workflow easily.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

src/profiler.cpp Outdated
otlp_buffer.field(Sample::locations_start_index, frames_seen);
otlp_buffer.field(Sample::locations_length, sampleInfo.num_frames);

protobuf_mark_t sample_value_mark = otlp_buffer.startMessage(Sample::value);
Copy link
Member

Choose a reason for hiding this comment

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

There are many tiny messages whose length can be encoded in 1 byte.
Do you want to provide a version of startMessage (or add an optional argument) to specify maximum length?

Copy link
Contributor Author

@fandreuz fandreuz Jun 10, 2025

Choose a reason for hiding this comment

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

Yes, the idea is to have an optional argument eventually. Do you want this in this PR?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that would be a more valuable optimization than reusing a buffer :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check 84d0ce1 and e52f0be

src/profiler.cpp Outdated
otlp_buffer.field(Sample::locations_start_index, frames_seen);
otlp_buffer.field(Sample::locations_length, sampleInfo.num_frames);

protobuf_mark_t sample_value_mark = otlp_buffer.startMessage(Sample::value);
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that would be a more valuable optimization than reusing a buffer :)

src/protobuf.cpp Outdated
void ProtoBuffer::commitMessage(const protobuf_mark_t& mark) {
size_t actual_len = _offset - (mark.message_start + mark.expected_len_byte_count);
size_t actual_len_byte_count = varIntSize(actual_len);
if (actual_len_byte_count > mark.expected_len_byte_count) {
Copy link
Member

Choose a reason for hiding this comment

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

This should be an assertion. If reserved space was smaller than required, treat it as error.
Otherwise we may end up moving 100MB blocks without knowing there was a computation mistake.

Copy link
Contributor Author

@fandreuz fandreuz Jun 11, 2025

Choose a reason for hiding this comment

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

I don't agree with this. We cannot estimate the size of the message without low level (and error prone) computations at runtime. And any such estimation would be fragile and hard to change later. Any fixed size we decide now may be not enough for long profiling sessions. But either case, I don't think it's a "computation mistake".

Are you implying we should have an exact estimate of the size of the message, before we start writing it to the buffer?

Copy link
Member

@apangin apangin Jun 11, 2025

Choose a reason for hiding this comment

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

OK, let's call it "underestimation" rather than "computation mistake". I'm saying that startMessage should accept the maximum size of a message instead of an estimation. 5 bytes (which can encode up to 32 GiB messages) is sufficient for all use cases. So, let's use 5 bytes default for the top-level messages and provide an appropriate upper bound for inner ones. Exact estimate is not required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

src/protobuf.h Outdated
struct protobuf_mark_t {
size_t message_start;
size_t expected_len_byte_count;
};
Copy link
Member

Choose a reason for hiding this comment

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

It's not a good practice to return a structure larger than machine word from a function.
64-bit field for a value that needs at most 3 is too much anyway.
It would be natural (in system programming world) to fit protobuf_mark_t in a machine word or in u64 and pass it by value: 3 bits for len_bytes and remaining 61 bits for message_start. You may use either bit-field struct or just u64 and do manipulations by yourself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check 17084fe

src/protobuf.h Outdated
protobuf_mark_t startMessage(protobuf_index_t index);
void commitMessage(protobuf_mark_t mark);
protobuf_mark_t startMessage(protobuf_index_t index, size_t max_len_byte_count = NESTED_FIELD_BYTE_COUNT);
void commitMessage(const protobuf_mark_t& mark);
Copy link
Member

Choose a reason for hiding this comment

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

It does not need to be a reference, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, e5e8d33

_output = OUTPUT_TEXT;
_dump_flat = value == NULL ? INT_MAX : atoi(value);

CASE("otlp")
Copy link
Member

Choose a reason for hiding this comment

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

Do you mind updating all relevant docs and examples with new option?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, let me know if I forgot something: a90b347

src/profiler.cpp Outdated
#include "frameName.h"
#include "os.h"
#include "otlp.h"
#include "protobuf.h"
Copy link
Member

Choose a reason for hiding this comment

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

I guess protobuf.h is redundant here, since otlp.h already includes it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

src/protobuf.cpp Outdated

for (size_t i = 0; i < max_len_byte_count - 1; ++i) {
size_t idx = message_start + i;
_data[idx] = (unsigned char) (0x80 | (actual_len & 0x7f));
Copy link
Member

Choose a reason for hiding this comment

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

& 0x7f is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@apangin apangin merged commit b3f5842 into async-profiler:master Jun 12, 2025
32 of 34 checks passed
@apangin
Copy link
Member

apangin commented Jun 12, 2025

I already merged PR, but can I ask you to update its description with background, motivation, summary of changes, etc., so we could refer it in the future. Thanks.

@fandreuz
Copy link
Contributor Author

I already merged PR, but can I ask you to update its description with background, motivation, summary of changes, etc., so we could refer it in the future. Thanks.

Done, let me know if you'd like to have any changes or additions.

@apangin
Copy link
Member

apangin commented Jun 12, 2025

Looks good, thanks.

roy-soumadipta pushed a commit to roy-soumadipta/async-profiler that referenced this pull request Jun 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants