From d130bb0dc300214318c0c4a23839af7b1c4d82b2 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Fri, 18 Jul 2025 14:15:33 +0200 Subject: [PATCH 1/2] generate synthetic build_id Follow the idea implemented with https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/41375/ for interpreted frames without build_id. Fixes #33 Signed-off-by: Florian Lehner --- src/collector/otlp/service.rs | 57 +++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/collector/otlp/service.rs b/src/collector/otlp/service.rs index 909cc11..89db878 100644 --- a/src/collector/otlp/service.rs +++ b/src/collector/otlp/service.rs @@ -160,6 +160,7 @@ fn get_attr<'tab>( fn ingest_locations(dic: &ProfilesDictionary) -> Result, Status> { let stab = &dic.string_table; let atab = &dic.attribute_table; + let ftab = &dic.function_table; let locs = &dic.location_table; let mut batch = DB.stack_frames.batched_insert(); let mut mappings = Vec::with_capacity(locs.len()); @@ -203,21 +204,57 @@ fn ingest_locations(dic: &ProfilesDictionary) -> Result, Status> { return Err(Status::invalid_argument("mapping index is out of bounds")); }; - let build_id = get_attr( - atab, - mapping.attribute_indices.to_vec(), - "process.executable.build_id.htlhash", // OTel Profiling specific build ID. - ) - .or_else(|_| { - get_attr( + let build_id; + let generated_build_id; + let build_id_str = if !mapping.attribute_indices.is_empty() { + build_id = get_attr( atab, mapping.attribute_indices.to_vec(), - "process.executable.build_id.profiling", // Legacy OTel Profiling specific build ID. + "process.executable.build_id.htlhash", // OTel Profiling specific build ID. ) - })?; + .or_else(|_| { + get_attr( + atab, + mapping.attribute_indices.to_vec(), + "process.executable.build_id.profiling", // Legacy OTel Profiling specific build ID. + ) + })?; + build_id + } else { + // Fallback option: Generate xxh3 hash over all fields of all loc.line elements + // if there is no build_id attribute. + let mut hasher = xxh3::Xxh3::new(); + for line in &loc.line { + if line.function_index != 0 { + if let Some(fn_ref) = ftab.get(line.function_index as usize) { + // Hash function name if available + if let Ok(Some(function_name)) = + get_str_opt(stab, fn_ref.name_strindex as usize, "function name") + { + hasher.update(function_name.as_bytes()); + } + // Hash function filename if available + if let Ok(Some(file_name)) = get_str_opt( + stab, + fn_ref.filename_strindex as usize, + "function filename", + ) { + hasher.update(file_name.as_bytes()); + } + } + } else { + // If function_index is 0, hash the index itself as fallback. + hasher.update(&line.function_index.to_le_bytes()); + } + hasher.update(&line.line.to_le_bytes()); + hasher.update(&line.column.to_le_bytes()); + } + generated_build_id = format!("{:016x}", hasher.digest()); + &generated_build_id + }; let Some(file_id) = - FileId::try_parse_es(build_id).or_else(|| FileId::try_parse_hex(build_id)) + FileId::try_parse_es(build_id_str).or_else(|| FileId::try_parse_hex(build_id_str)) else { return Err(Status::invalid_argument("failed to parse file ID")); }; From 6f4428333a09db99465668e056879d47251664ca Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Mon, 21 Jul 2025 07:47:08 +0200 Subject: [PATCH 2/2] fix: drop fallback for function index 0 Signed-off-by: Florian Lehner --- src/collector/otlp/service.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/collector/otlp/service.rs b/src/collector/otlp/service.rs index 89db878..eb72bea 100644 --- a/src/collector/otlp/service.rs +++ b/src/collector/otlp/service.rs @@ -242,9 +242,6 @@ fn ingest_locations(dic: &ProfilesDictionary) -> Result, Status> { hasher.update(file_name.as_bytes()); } } - } else { - // If function_index is 0, hash the index itself as fallback. - hasher.update(&line.function_index.to_le_bytes()); } hasher.update(&line.line.to_le_bytes()); hasher.update(&line.column.to_le_bytes());