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
1 change: 1 addition & 0 deletions rust-crates/symblib-capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name = "symblib-capi"
edition = "2021"
version.workspace = true
rust-version.workspace = true
license.workspace = true

[lib]
crate-type = ["staticlib", "cdylib"]
Expand Down
1 change: 1 addition & 0 deletions rust-crates/symblib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name = "symblib"
edition = "2021"
version.workspace = true
rust-version.workspace = true
license.workspace = true

[dependencies]
base64.workspace = true
Expand Down
16 changes: 16 additions & 0 deletions rust-crates/symblib/src/gosym/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ impl<'obj> GoRuntimeInfo<'obj> {
iter: self.func_table.index_iter()?,
})
}

/// Locates the Go function containing the given virtual address.
///
/// Returns:
/// - `Ok(Some(Func))` if a function is found containing the address
/// - `Ok(None)` if no function contains the address
/// - `Err` if there was an error reading the function table
pub fn find_func<'rt>(&'rt self, addr: VirtAddr) -> Result<Option<Func<'rt, 'obj>>> {
Ok(self
.func_table
.func_by_addr(self.text_start, addr)?
.map(|raw_func| Func {
rt: self,
raw: raw_func,
}))
}
}

/// Internal helpers.
Expand Down
82 changes: 82 additions & 0 deletions rust-crates/symblib/src/gosym/raw/regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,88 @@ impl<'obj> FuncTable<'obj> {
pub fn func(&self, offs: FuncTabOffset) -> Result<Func<'obj>> {
Func::read(self.reader.sub_reader(offs.0 as usize..)?)
}

// Look up function information for a virtual address using binary search.
pub fn func_by_addr(&self, text_start: VirtAddr, addr: VirtAddr) -> Result<Option<Func<'obj>>> {
let sz = FuncTabIndexEntry::size_of(self.reader.header());
let mut left = 0;
let mut right = self.num_funcs as usize * sz;

while left < right {
let mid = (left + (right - left) / 2) / sz * sz;
let mut reader = self.reader.sub_reader(mid..)?;
let entry = FuncTabIndexEntry::read(&mut reader)?;

let entry_addr = match entry.entry {
CodePtr::Addr(addr) => addr,
CodePtr::Offs(offset) => text_start + offset.0,
};

if addr < entry_addr {
right = mid;
} else if mid + sz < right {
let mut next_reader = self.reader.sub_reader(mid + sz..)?;
let next_entry = FuncTabIndexEntry::read(&mut next_reader)?;
let next_addr = match next_entry.entry {
CodePtr::Addr(addr) => addr,
CodePtr::Offs(offset) => text_start + offset.0,
};

if addr >= next_addr {
left = mid + sz;
} else {
return Ok(Some(self.func(entry.funcoff)?));
}
} else {
return Ok(Some(self.func(entry.funcoff)?));
}
}

Ok(None)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::gosym::GoRuntimeInfo;
use crate::tests::testdata;

#[test]
fn test_func_by_addr() -> Result<()> {
for test_file in ["go-1.20.14", "go-1.22.12", "go-1.24.0"] {
let obj = objfile::File::load(&testdata(test_file))?;
let obj = obj.parse()?;

let runtime_info = GoRuntimeInfo::open(&obj)?;

let text_start = obj.load_section(b".text")?.unwrap().virt_addr();

for pc in (text_start..text_start + 0x10000).step_by(19) {
let bin_search_result = runtime_info.find_func(pc).unwrap();

let mut fn_iter = runtime_info.funcs().unwrap().peekable();
let mut found = None;
while let Some(func) = fn_iter.next().unwrap() {
let Some(next) = fn_iter.peek().unwrap() else {
break;
};

let pc_rng = func.start_addr()..next.start_addr();
if pc_rng.contains(&pc) {
found = Some(func);
break;
}
}

assert_eq!(
bin_search_result.map(|x| x.start_addr()),
found.map(|x| x.start_addr())
);
}
}
Ok(())
}
}

/// Iterator over the index in `.gopclntab`:`runtime.functab`.
Expand Down
9 changes: 7 additions & 2 deletions rust-crates/symblib/testdata/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.PHONY: inline inline-compressed-dwarf inline-split-dwarf inline-big-fake-compressed-dwarf \
inline-no-tco clean inline-compressed-dwarf-zstd
inline-no-tco clean inline-compressed-dwarf-zstd go-toolchains

all: inline inline-compressed-dwarf inline-split-dwarf inline-big-fake-compressed-dwarf \
inline-no-tco inline-compressed-dwarf-zstd
inline-no-tco inline-compressed-dwarf-zstd go-toolchains

inline: inline.c
cc $< -o $@ -O2 -g
Expand All @@ -27,5 +27,10 @@ inline-split-dwarf: inline
cp inline inline-split-dwarf
dwz -M meow -m inline-split-dwarf.dwp inline-split-dwarf inline-split-dwarf

go-toolchains:
GOTOOLCHAIN=go1.20.14 go build -o go-1.20.14 main.go
GOTOOLCHAIN=go1.22.12 go build -o go-1.22.12 main.go
GOTOOLCHAIN=go1.24.0 go build -o go-1.24.0 main.go

clean:
echo "not deleting anything: executables are meant to be kept under VC"
Binary file added rust-crates/symblib/testdata/go-1.20.14
Binary file not shown.
Binary file added rust-crates/symblib/testdata/go-1.22.12
Binary file not shown.
Binary file added rust-crates/symblib/testdata/go-1.24.0
Binary file not shown.
3 changes: 3 additions & 0 deletions rust-crates/symblib/testdata/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/open-telemetry/opentelemetry-ebpf-profiler/rust-crates/symblib/testdata

go 1.20
7 changes: 7 additions & 0 deletions rust-crates/symblib/testdata/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "fmt"

func main() {
fmt.Println("Hello, 世界")
}