Skip to content

Commit

Permalink
Refactor uses of LibC.dladdr
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Oct 21, 2024
1 parent 7cd5f73 commit 77bbbd4
Showing 1 changed file with 34 additions and 32 deletions.
66 changes: 34 additions & 32 deletions src/exception/call_stack/libunwind.cr
Original file line number Diff line number Diff line change
Expand Up @@ -122,52 +122,54 @@ struct Exception::CallStack
end
{% end %}

if frame = unsafe_decode_frame(repeated_frame.ip)
offset, sname, fname = frame
unsafe_decode_frame(repeated_frame.ip) do |offset, sname, fname|
Crystal::System.print_error "%s +%lld in %s", sname, offset.to_i64, fname
else
Crystal::System.print_error "???"
return
end
end

protected def self.decode_frame(ip, original_ip = ip)
if LibC.dladdr(ip, out info) != 0
offset = original_ip - info.dli_saddr
Crystal::System.print_error "???"
end

if offset == 0
return decode_frame(ip - 1, original_ip)
end
return if info.dli_sname.null? && info.dli_fname.null?
if info.dli_sname.null?
symbol = "??"
else
symbol = String.new(info.dli_sname)
end
if info.dli_fname.null?
file = "??"
else
file = String.new(info.dli_fname)
end
protected def self.decode_frame(ip)
decode_frame(ip) do |offset, symbol, file|
symbol = symbol ? String.new(symbol) : "??"
file = file ? String.new(file) : "??"
{offset, symbol, file}
end
end

# variant of `.decode_frame` that returns the C strings directly instead of
# wrapping them in `String.new`, since the SIGSEGV handler cannot allocate
# memory via the GC
protected def self.unsafe_decode_frame(ip)
protected def self.unsafe_decode_frame(ip, &)
decode_frame(ip) do |offset, symbol, file|
symbol ||= "??".to_unsafe
file ||= "??".to_unsafe
yield offset, symbol, file
end
end

private def self.decode_frame(ip, &)
original_ip = ip
while LibC.dladdr(ip, out info) != 0
offset = original_ip - info.dli_saddr
if offset == 0
ip -= 1
next
while true
retry = dladdr(ip) do |file, symbol, address|
offset = original_ip - address
if offset == 0
ip -= 1
true
elsif symbol.null? && file.null?
false
else
return yield offset, symbol, file
end
end
break unless retry
end
end

return if info.dli_sname.null? && info.dli_fname.null?
symbol = info.dli_sname || "??".to_unsafe
file = info.dli_fname || "??".to_unsafe
return {offset, symbol, file}
private def self.dladdr(ip, &)
if LibC.dladdr(ip, out info) != 0
yield info.dli_fname, info.dli_sname, info.dli_saddr
end
end
end

0 comments on commit 77bbbd4

Please sign in to comment.