I'm debugging a binary without debugging information on linux with gdb. I want to break just before the first call to puts but not enter the function.
I have tried the following:
gdb> b puts
gdb> r
or
gdb> b main
gdb> b puts
gdb> r
gdb> c
But gdb always enters puts instead of breaking before the syscall.
But gdb always enters puts instead of breaking before the syscall.
That's what you asked GDB to do.
If you want to stop before the CALL puts instruction, you need to disas main, find the CALL puts instruction, and set a breakpoint on that instruction:
(gdb) start # stop in main to get the binary relocated
(gdb) disas
...
0x0000555555555147 <+14>: callq 0x555555555030 <puts#plt>
...
(gdb) break *0x0000555555555147
(gdb) cont
Breakpoint 2, 0x0000555555555147 in main ()
(gdb) x/i $pc
=> 0x555555555147 <main+14>: callq 0x555555555030 <puts#plt>
P.S. syscall means something else entirely.
Related
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:
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?
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?
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
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?
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.
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"
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
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"
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"
See this simple Ruby class:
require 'byebug'
class Foo
def run
byebug
puts defined?(bar)
puts bar.inspect
bar = 'local string'
puts defined?(bar)
puts bar.inspect
end
def bar
'string from method'
end
end
Foo.new.run
When running this class the following behavior can be observed in the debugger's console:
$ ruby byebug.rb
[2, 11] in /../test.rb
2:
3: class Foo
4: def run
5: byebug
6:
=> 7: puts defined?(bar)
8: puts bar.inspect
9:
10: bar = 'local string'
11:
At the breakpoint the debugger returns the following values:
(byebug) defined?(bar)
"local-variable"
(byebug) bar.inspect
"nil"
Note that - although the debugger's breakpoint is in line #5 - it already knows that there will be a local variable bar defined in line #10 that will shadow the method bar and the debugger is actually not able anymore to call the bar method. What is doesn't know at this point is that the string 'local string' will be assign to bar. The debugger returns nil for bar.
Let's continue with the original code in the Ruby file and look at its output:
(byebug) continue
method
"string from method"
local-variable
"local string"
At run time in line #7 Ruby still knowns that bar is indeed a method and it is still able to call it in line #8. Then l ine #10 actually defines the local variable that shadows the method with the same name and tTherefore Ruby returns like expected in line #12 and #13.
Questions: Why does the debugger return different values than the original code? It seems like it is able to look into the future. Is this considered a feature or a bug? Is this behavior documented?
Whenever you drop into a debugging session, you're effectively executing an eval against the binding at that spot in the code. Here's a simpler bit of code that recreates the behavior that's driving you nuts:
def make_head_explode
puts "== Proof bar isn't defined"
puts defined?(bar) # => nil
puts "== But WTF?! It shows up in eval"
eval(<<~RUBY)
puts defined?(bar) # => 'local-variable'
puts bar.inspect # => nil
RUBY
bar = 1
puts "\n== Proof bar is now defined"
puts defined?(bar) # => 'local-variable'
puts bar.inspect # => 1
end
When the method make_head_explode is fed to the interpreter, it's compiled to YARV instructions, a local table, which stores information about the method's arguments and all local variables in the method, and a catch table that includes information about rescues within the method if present.
The root cause of this issue is that since you're compiling code dynamically at runtime with eval, Ruby passes the local table, which includes an unset variable enry, to eval as well.
To start, let's use a use a very simple method that demonstrates the behavior we'd expect.
def foo_boom
foo # => NameError
foo = 1 # => 1
foo # => 1
end
We can inspect this by extracting the YARV byte code for the existing method with RubyVM::InstructionSequence.disasm(method). Note I'm going to ignore trace calls to keep the instructions tidy.
Output for RubyVM::InstructionSequence.disasm(method(:foo_boom)) less trace:
== disasm: #<ISeq:foo_boom#(irb)>=======================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1#-1, kwrest: -1])
[ 2] foo
0004 putself
0005 opt_send_without_block <callinfo!mid:foo, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0008 pop
0011 putobject_OP_INT2FIX_O_1_C_
0012 setlocal_OP__WC__0 2
0016 getlocal_OP__WC__0 2
0020 leave ( 253)
Now let's walk through the trace.
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1#-1, kwrest: -1])
[ 2] foo
We can see here that YARV has identified we have the local variable foo, and stored it in our local table at index [2]. If we had other local variables and arguments, they'd also appear in this table.
Next we have the instructions generated when we try to call foo before its assigned:
0004 putself
0005 opt_send_without_block <callinfo!mid:foo, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0008 pop
Let's dissect what happens here. Ruby compiles function calls for YARV according to the following pattern:
Push receiver: putself, referring to top-level scope of function
Push arguments: none here
Call the method/function: function call (FCALL) to foo
Next we have the instructions for setting at getting foo once it becomes a global variable:
0008 pop
0011 putobject_OP_INT2FIX_O_1_C_
0012 setlocal_OP__WC__0 2
0016 getlocal_OP__WC__0 2
0020 leave ( 253)
Key takeaway: when YARV has the entire source code at hand, it knows when locals are defined and treats premature calls to local variables as FCALLs just as you'd expect.
Now let's look at a "misbehaving" version that uses eval
def bar_boom
eval 'bar' # => nil, but we'd expect an errror
bar = 1 # => 1
bar
end
Output for RubyVM::InstructionSequence.disasm(method(:bar_boom)) less trace:
== disasm: #<ISeq:bar_boom#(irb)>=======================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1#-1, kwrest: -1])
[ 2] bar
0004 putself
0005 putstring "bar"
0007 opt_send_without_block <callinfo!mid:eval, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0010 pop
0013 putobject_OP_INT2FIX_O_1_C_
0014 setlocal_OP__WC__0 2
0018 getlocal_OP__WC__0 2
0022 leave ( 264)
Again we see a local variable, bar, in the locals table at index 2. We also have the following instructions for eval:
0004 putself
0005 putstring "bar"
0007 opt_send_without_block <callinfo!mid:eval, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0010 pop
Let's dissect what happens here:
Push receiver: again putself, referring to top-level scope of function
Push arguments: "bar"
Call the method/function: function call (FCALL) to eval
Afterward, we have the standard assignment to bar that we'd expect.
0013 putobject_OP_INT2FIX_O_1_C_
0014 setlocal_OP__WC__0 2
0018 getlocal_OP__WC__0 2
0022 leave ( 264)
Had we not had eval here, Ruby would have known to treat the call to bar as a function call, which would have blown up as it did in our previous example. However, since eval is dynamically evaluated and the instructions for its code won't be generated until runtime, the evaluation occurs in the context of the already determined instructions and local table, which holds the phantom bar that you see. Unfortunately, at this stage, Ruby is unaware that bar was initialized "below" the eval statement.
For a deeper dive, I'd recommend reading Ruby Under a Microscope and the Ruby Hacking Guide's section on Evaluation.
I am trying to trace all method calls in a Ruby program using TracePoint. It works well until I hit a method call to an "optimized" method call.
Ruby has operators that are "optimized" by replacing YARV instructions with specialized instructions to speed up method calls like greater than, less than. One of these optimized
You can see this using ruby directly by running
code = <<END
1 / 1
END
puts RubyVM::InstructionSequence.compile(code).disasm
# == disasm: <RubyVM::InstructionSequence:<compiled>#<compiled>>==========
# 0000 trace 1 ( 1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject_OP_INT2FIX_O_1_C_
# 0004 opt_div <callinfo!mid:/, argc:1, ARGS_SIMPLE>
# 0006 leave
Here you see opt_div is used rather than opt_send_without_block.
It appears you cannot trace these optimized method calls. For example:
trace = TracePoint.trace(:call, :c_call) do |tp|
tp.disable
puts "calling #{tp.defined_class}##{tp.method_id}"
tp.enable
end
trace.enable
1.div(1)
1 / 1
1.div(2)
You can see that 1.div is traced, but not 1/1
calling TracePoint#enable
calling Fixnum#div
calling Fixnum#div
So my question is this: How can I trace all method calls including "optimized" method calls in Ruby (MRI)?
From koichi, you can disable the optimizations using:
RubyVM::InstructionSequence.compile_option = { specialized_instruction: false }
This will work for my cases, but I imagine that it will slow down execution.
One other caveat if you are trying this at home, is that you cannot set that compile_option in the same file since by the time it is executed the file is already compiled. Instead you need need to execute this code before loading or requiring the file you are trying to trace.
You can also use this option in eval-d code:
iseq = RubyVM::InstructionSequence.compile(<<EOS, nil, nil, 1, specialized_instruction: false)
1 / 1
EOS
trace = TracePoint.trace(:call, :c_call) do |tp|
tp.disable
puts "calling #{tp.defined_class}##{tp.method_id}"
tp.enable
end
iseq.eval
That is the question. I called a function using a thread, but when I return to main control, main becomes in sleep status within a few moments.
Example (Look here is a line that calls a Win32API function GetMessage):
Start()
e = Thread.new { Look() }
for _i in 0..1000
puts e.status
end
This code should print the e's status 1000 times, but it just prints once. I cannot find a reasonable error. Look does not return until it gets a message, so I suspect that this could be causing an error.
My guess is that the status of e is nil, which puts displays as an empty string.
A nil status means the thread terminated abnormally.
Whatever is wrong, your code is printing something 1000 times, and you should put some visible text in the puts to show that.
Also, rather than use a throw-away variable _i, write
1000.times do
puts e.status
end
I solved thanks for all your help,
the error was in that i only need to change GetMessageA for PeekMessage
so thread doesn't interpret that is dead jeje
Some.Proxy
I followed the example from http://www.ruby-doc.org/stdlib/libdoc/monitor/rdoc/index.html and modified the code a bit:
require 'monitor.rb'
buf = []
buf.extend(MonitorMixin)
empty_cond = buf.new_cond
producer = Thread.start do
# producer
line = "produce at #{Time.now}"
#while line
buf.synchronize do
puts "==> #{line}"
buf.push(line)
empty_cond.signal
end
sleep(2)
#line = "produce at #{Time.now}"
#end
end
loop do
buf.synchronize do
empty_cond.wait_while { buf.empty? }
item = buf.shift
puts "got #{item.inspect}"
end
end
I let the program run. Around 5 min later, it throws a "Segmentation fault". Something related to a deadlock?
/Jack
As your code stands (with the commented out while-statement in producer-loop) the producer thread simply runs through the loop once and exits. The consumer reads the one produced line from buf and then is left in a deadlock waiting for more lines that will never arrive.
Ruby's Thread scheduler has inbuilt deadlock-detection, so it will terminate the program when it sees that the 'consumer'-loop has deadlocked.
To see the deadlock for yourself, turn the producer into a global variable $producer and wrap the loop-statement with $consumer = Thread.start do ... end. Loading the code into irb and evaluating $producer should result in => #< Thread:0x000000010afb58 dead > (and $consumer in a sleeping thread)
Take out the comments relating to producer's while-loop and you'll have a working (infinite) loop that produces the current time at 2 second intervals.