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
60 changes: 26 additions & 34 deletions process-context/c-and-cpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ These can be set in your own build system as needed.

### Protocol Buffers Definitions

- **`resource.proto`** - OpenTelemetry Resource protobuf definition (extracted from OpenTelemetry protocol)
- **`process_context.proto`** - OpenTelemetry Process Context protobuf definition
- **`resource.proto`** - OpenTelemetry Resource protobuf definition
- **`common.proto`** - OpenTelemetry common types protobuf definition (AnyValue, KeyValue, etc.)

These proto files are included for reference and for use with `protoc` when decoding the payload with the dump script. The implementation includes its own minimal protobuf encoder/decoder and does not depend on the protobuf library.
Expand Down Expand Up @@ -208,43 +209,44 @@ sudo ./otel_process_ctx_dump.sh <pid>
```
Found OTEL context for PID 267023
Start address: 756f28ce1000
00000000 4f 54 45 4c 5f 43 54 58 02 00 00 00 0b 68 55 47 |OTEL_CTX.....hUG|
00000010 70 24 7d 18 50 01 00 00 a0 82 6d 7e 6a 5f 00 00 |p$}.P.....m~j_..|
00000000 4f 54 45 4c 5f 43 54 58 02 00 00 00 53 01 00 00 |OTEL_CTX....S...|
00000010 4c 27 98 d5 cc aa 91 18 a0 b2 28 1e ee 5c 00 00 |L'........(..\..|
00000020
Parsed struct:
otel_process_ctx_signature : "OTEL_CTX"
otel_process_ctx_version : 2
otel_process_ctx_published_at_ns : 1764606693650819083 (2025-12-01 16:31:33 GMT)
otel_process_payload_size : 336
otel_process_payload : 0x00005f6a7e6d82a0
Payload dump (336 bytes):
00000000 0a 25 0a 1b 64 65 70 6c 6f 79 6d 65 6e 74 2e 65 |.%..deployment.e|
00000010 6e 76 69 72 6f 6e 6d 65 6e 74 2e 6e 61 6d 65 12 |nvironment.name.|
otel_process_payload_size : 339
otel_process_ctx_published_at_ns : 1770383925266884428 (2026-02-06 13:18:45 GMT)
otel_process_payload : 0x00005cee1e28b2a0
Payload dump (339 bytes):
00000000 0a d0 02 0a 25 0a 1b 64 65 70 6c 6f 79 6d 65 6e |....%..deploymen|
00000010 74 2e 65 6e 76 69 72 6f 6e 6d 65 6e 74 2e 6e 61 |t.environment.na|
...
```

If `protoc` is installed and the proto files are available, the script will also decode the payload:

```
Protobuf decode:
attributes {
key: "deployment.environment.name"
value {
string_value: "prod"
resource {
attributes {
key: "deployment.environment.name"
value {
string_value: "prod"
}
}
}
attributes {
key: "service.instance.id"
value {
string_value: "123d8444-2c7e-46e3-89f6-6217880f7123"
attributes {
key: "service.instance.id"
value {
string_value: "123d8444-2c7e-46e3-89f6-6217880f7123"
}
}
}
attributes {
key: "service.name"
value {
string_value: "my-service"
attributes {
key: "service.name"
value {
string_value: "my-service"
}
}
}
...
```

Expand Down Expand Up @@ -298,13 +300,3 @@ When integrating this into an OpenTelemetry SDK or application:
5. **Check result codes**: Always check the `success` field of the result and log errors appropriately.

6. **Consider no-op builds**: For non-Linux platforms, use the no-op variant or define `OTEL_PROCESS_CTX_NOOP=1`.

## Specification Compliance

This implementation follows the [OpenTelemetry Process Context specification](https://github.com/open-telemetry/opentelemetry-specification/pull/4719/). Key aspects:

- Uses the `OTEL_CTX` signature for discoverability
- Stores data in version 2 format (packed struct + protobuf payload)
- Encodes resource attributes using OpenTelemetry Protocol Buffers Resource format
- Supports both standard semantic conventions and custom resource attributes
- Provides proper memory ordering guarantees for concurrent readers
29 changes: 22 additions & 7 deletions process-context/c-and-cpp/otel_process_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,8 @@ static void write_attribute(char **ptr, const char *key, const char *value) {

// Encode the payload as protobuf bytes.
//
// This method implements an extremely compact but limited protobuf encoder for the Resource message.
// It encodes all fields as attributes (KeyValue pairs).
// This method implements an extremely compact but limited protobuf encoder for the ProcessContext message.
// It encodes all fields as Resource attributes (KeyValue pairs).
// For extra compact code, it fixes strings at up to 4096 bytes.
static otel_process_ctx_result otel_process_ctx_encode_protobuf_payload(char **out, uint32_t *out_size, otel_process_ctx_data data) {
const char *pairs[] = {
Expand All @@ -387,14 +387,20 @@ static otel_process_ctx_result otel_process_ctx_encode_protobuf_payload(char **o
if (!validation_result.success) return validation_result;
}

size_t total_size = pairs_size + resources_pairs_size;
size_t resource_size = pairs_size + resources_pairs_size;
// ProcessContext wrapper: tag (1 byte) + resource length varint + resource content
size_t total_size = 1 + protobuf_varint_size(resource_size) + resource_size;

char *encoded = (char *) calloc(total_size, 1);
if (!encoded) {
return (otel_process_ctx_result) {.success = false, .error_message = "Failed to allocate memory for payload (" __FILE__ ":" ADD_QUOTES(__LINE__) ")"};
}
char *ptr = encoded;

// ProcessContext.resource (field 1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: Maybe if you do additional modifications in the future to address OTEP comments, add 1-2 attributes in extra_attributes to demonstrate non-resource attribute handling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeap, sounds good! I need to make a few tweaks to support the thread context which is an extra_attribute so I'll include that as well.

write_protobuf_tag(&ptr, 1);
write_protobuf_varint(&ptr, resource_size);

for (size_t i = 0; pairs[i * 2] != NULL; i++) {
write_attribute(&ptr, pairs[i * 2], pairs[i * 2 + 1]);
}
Expand Down Expand Up @@ -497,19 +503,28 @@ static otel_process_ctx_result otel_process_ctx_encode_protobuf_payload(char **o

*data_out = empty_data;

// Parse ProcessContext wrapper - expect field 1 (resource)
uint8_t process_ctx_field;
if (!read_protobuf_tag(&ptr, end_ptr, &process_ctx_field) || process_ctx_field != 1) return false;

uint16_t resource_len;
if (!read_protobuf_varint(&ptr, end_ptr, &resource_len)) return false;
char *resource_end = ptr + resource_len;
if (resource_end > end_ptr) return false;

size_t resource_index = 0;
size_t resource_capacity = 201; // Allocate space for 100 pairs + NULL terminator entry
data_out->resources = (const char **) calloc(resource_capacity, sizeof(char *));
if (data_out->resources == NULL) return false;

while (ptr < end_ptr) {
while (ptr < resource_end) {
uint8_t field_number;
if (!read_protobuf_tag(&ptr, end_ptr, &field_number) || field_number != 1) return false;
if (!read_protobuf_tag(&ptr, resource_end, &field_number) || field_number != 1) return false;

uint16_t kv_len;
if (!read_protobuf_varint(&ptr, end_ptr, &kv_len)) return false;
if (!read_protobuf_varint(&ptr, resource_end, &kv_len)) return false;
char *kv_end = ptr + kv_len;
if (kv_end > end_ptr) return false;
if (kv_end > resource_end) return false;

bool key_found = false;
bool value_found = false;
Expand Down
2 changes: 1 addition & 1 deletion process-context/c-and-cpp/otel_process_ctx_dump.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ dd if="/proc/$pid/mem" bs=1 count="$payload_size" skip=$((16#$payload_ptr_hex))

if command -v protoc >/dev/null 2>&1; then
echo "Protobuf decode:"
dd if="/proc/$pid/mem" bs=1 count="$payload_size" skip=$((16#$payload_ptr_hex)) status=none | protoc --decode=opentelemetry.proto.resource.v1.Resource resource.proto common.proto
dd if="/proc/$pid/mem" bs=1 count="$payload_size" skip=$((16#$payload_ptr_hex)) status=none | protoc --decode=opentelemetry.proto.common.v1.ProcessContext process_context.proto resource.proto common.proto
else
echo
echo "protoc not available - skipping protobuf decode"
Expand Down
55 changes: 55 additions & 0 deletions process-context/c-and-cpp/process_context.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

// TODO: Is this in the right namespace? Since this is not only for profiling,
// `opentelemetry.proto.profiles` doesn't seem the right place, but perhaps common ain't
// it either? Feedback very welcome!
package opentelemetry.proto.common.v1;

import "common.proto";
import "resource.proto";

option csharp_namespace = "OpenTelemetry.Proto.Common.V1";
option java_multiple_files = true;
option java_package = "io.opentelemetry.proto.common.v1";
option java_outer_classname = "ProcessContextProto";
option go_package = "go.opentelemetry.io/proto/otlp/common/v1";

// ProcessContext represents the payload for the process context sharing mechanism.
//
// This message is designed to be published by OpenTelemetry SDKs via a memory-mapped
// region, allowing external readers (such as the OpenTelemetry eBPF Profiler) to
// discover and read resource attributes from instrumented processes without requiring
// direct integration or process activity.
message ProcessContext {
// The resource attributes describing this process.
//
// Attribute keys MUST be unique (it is not allowed to have more than one
// attribute with the same key). The behavior of software that receives
// duplicated keys can be unpredictable.
//
// Attributes SHOULD follow OpenTelemetry semantic conventions where applicable.
// See: https://opentelemetry.io/docs/specs/semconv/
opentelemetry.proto.resource.v1.Resource resource = 1;

// Additional attributes to share with external readers that are not part of
// the standard Resource. [Optional]
//
// This field allows publishers to include supplementary key-value pairs that
// may be useful for external readers but are not part of the SDK's configured
// Resource.
repeated opentelemetry.proto.common.v1.KeyValue extra_attributes = 2;
}