Jun's Blog

Output, activities, memo and etc.

How to debug C code in Ruby (ruby/ruby).

This article is to debug the C source code running a specific unit test in Ruby itself (ruby/ruby).

First you can set your preferred compiler flags like this.

Build

$ autoconf

$ optflags=-O0 debugflags="-g3 -ggdb3 -gdwarf-4" \
  ./configure \
  --prefix=path/to/prefix \
  --enable-shared

$ make

Run a test

Then if you want to run a specific test in make test-all, the command is like this. The V=1 is the verbose option useful to see the actual command line executed in the make command, and the result clearly.

$ make test-all V=1 TESTS="-v test/socket/test_socket.rb -n TestSocket#test_timestamp"
exec ./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems "./test/runner.rb" --ruby="./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems" --excludes-dir=./test/excludes --name=!/memory_leak/  -v test/socket/test_socket.rb -n TestSocket#test_timestamp
...
[1/0] TestSocket#test_timestamp = 0.00 s
Finished tests in 0.005708s, 175.2008 tests/s, 2102.4094 assertions/s.
1 tests, 12 assertions, 0 failures, 0 errors, 0 skips

ruby -v: ruby 3.1.0dev (2021-06-23T12:48:42Z master 7c31ecd3ac) [aarch64-linux]

Debug with gdb

The first option is to add a gdb command before the binary miniruby of above command line, removing --name=!/memory_leak/.

$ gdb -q -ex "set breakpoint pending on" --args ./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems "./test/runner.rb" --ruby="./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems" --excludes-dir=./test/excludes -v test/socket/test_socket.rb -n TestSocket#test_timestamp
Reading symbols from ./miniruby...
warning: File "/home/jaruga/git/ruby/ruby/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
    add-auto-load-safe-path /home/jaruga/git/ruby/ruby/.gdbinit
line to your configuration file "/home/jaruga/.gdbinit".
To completely disable this security protection add
    set auto-load safe-path /
line to your configuration file "/home/jaruga/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
    info "(gdb)Auto-loading safe path"
(gdb) 

The second option is to use the specific make target. Here it is.

$ cp -p run.gdb run.gdb.org

$ vi run.gdb

$ diff -u run.gdb.org run.gdb
--- run.gdb.org 2021-06-25 16:10:56.044523121 +0000
+++ run.gdb 2021-06-25 16:10:19.452419169 +0000
@@ -1,4 +1,5 @@
 set breakpoint pending on
+b main
 b rb_assert_failure
 b rb_bug
 b ruby_debug_breakpoint

$ make gdb-ruby V=1 TESTRUN_SCRIPT='./test/runner.rb -v test/socket/test_socket.rb -n TestSocket#test_timestamp'
...
Breakpoint 1, main (argc=6, argv=0xfffffffff3a8) at ./main.c:35
35  {
(gdb) 

That's all.