Crystal debugging with GDB
Asked Answered
S

1

5

I am trying to learn to debug programs written in Crystal with GDB. Here is a sample:

class Demo
  @array = [] of String

  def bar(url)
    ret = url.downcase * 2
    if ret == "alsj"
      return false
    else
      return ret
    end
  end

  def do(foo)
    @array.push(foo)
    html = bar(foo)
    puts "HI" # GDB breakpoint here
    return html
  end
end

a = Demo.new
puts a.do("HI")

I compiled the sample above with the --debug flag and loaded it into GDB. Then I let it run and stopped at the marked line (GDB breakpoint here). Now I have three four questions:

  1. Printing string values (e.g. foo): When I inspect a string variable, I often see something like $1 = (struct String *) 0x4b9f18. When I say printf "%s", foo, I get nothing back. How can I display the current value of a string variable?
  2. optimized out. Other times I just see $1 = <optimized out> when inspecting a variable. What does that mean and how can I see the value in that case?
  3. Accessing object variables How can I see the value of @array in the given situation? p array says no symbol in current context and p @array returns unknown address space modifier. Edit: I found a way: use p self.array
  4. Vanished variables In the given situation (breaking at the line puts "HI") I can't see the variable html at all: p html returns no symbol in current context. Why is that and how do I solve it?
Swagsman answered 22/9, 2017 at 16:56 Comment(1)
Not a comment on GDB debugging, but Ary presented a preview of an interactive debugger (like Pry in Ruby). It is not released yet, but potentially it will make gdb debugging less relevant: https://mcmap.net/q/658084/-is-there-an-equivalent-to-pry-for-crystalSanguinary
A
6

Crystal debugging capabilities are still on development so you can't see some symbols or symbols data is optimized by LLVM. About optimized output, sometimes crystal algorithms are optimized too much, even on --debug builds. By example:

Currently yield methods are inlined and optimized, so this isn't very debuggable.

3.times do |i|
  pp i
end

However, you can use pp keyword to print name => value.

pp i
i => 1
i => 2
i => 3

Also you can set breakpoints using debugger keyword and inspect macro output using {% debug() %} or $ crystal tool expand command.

On the other hand compound statement are easy to debug using tools like GDB.

i = 0
while i < 3
  debugger
  i += 1 # i variable is listed by GDB
end

Finally you can try some tricks I found in my debugging daily tasks.

  1. Printing string values: try @[NoInline] and p &foo.c this will enable args data and you will be able to print all struct string value

Using @[NoInline] attribute:

@[NoInline]
def do(foo)
  debugger
end

On GDB:

(gdb) p &foo.c
$1 = (UInt8 *) 0x10008e6c4 "HI"
  1. optimized out: maybe because LLVM optimizes some method calls. if you see <optimized out> use @[NoInline] and try assigning instance vars to local vars array = @array

  2. Accessing object variables: use self.var for instance vars.

Also use p array.buffer[0]@size to print array values.

(gdb) p &array.buffer[0].c
$19 = (UInt8 *) 0x10008e7f4 "HI"
  1. Vanished variables: this happens because debugging info is not complete enough.

Try adding debug info manually converting or casting values:

@[NoInline]
def do(foo)
  html = bar(foo).as(String)
  html = bar(foo).to_s
  debugger
end

Now html var is visible on GDB thanks to .as or .to_s methods

(gdb) p &html.c
$1 = (UInt8 *) 0x1002fcfec "hihi"
Apochromatic answered 4/10, 2017 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.